# JS Module Split Design ## Goal Split `public/app.js` (1025 lines, single file) into ES modules under `public/js/` — no build step, no npm. Modernize `var` to `const`/`let` in the process. ## Module Structure ``` public/js/ state.js shared mutable state object i18n.js translations, locale logic api.js fetch wrapper, loadGoals(), saveGoal() goals.js pure goal calculations (calc, tOff, dcls, …) ui.js tpl(), showToast(), overlay helpers, updateHeader() auth.js login/register/password screens sheets.js openNew(), openData(), openAdmin() render.js buildCard(), render(), wire() app.js entry point: init, URL params, stopwatch, midnight timer ``` ## State Management Single exported object in `state.js`. All modules import and mutate it directly: ```js // state.js export const state = { goals: [], userName: '', isAdmin: false, prefs: {}, selDay: {}, addAmt: {}, renamingId: null, renameVal: '', collapsed: {}, TODAY: new Date(), }; ``` Replaces all global `var` declarations at the top of `app.js`. Mutations like `goals = data` become `state.goals = data`. ## Dependency Graph ``` state.js ← all modules i18n.js ← goals.js, ui.js, auth.js, sheets.js, render.js, app.js api.js ← auth.js, sheets.js, app.js goals.js ← render.js, sheets.js ui.js ← auth.js, sheets.js, render.js auth.js ← sheets.js, app.js render.js ← app.js ``` ## Circular Import Solution `api.js` previously called `showLogin()` on 401 — creating a circular dependency with `auth.js`. Resolved via a Custom Event: ```js // api.js — on 401: document.dispatchEvent(new CustomEvent('session-expired')); // app.js — on startup: document.addEventListener('session-expired', () => showLogin()); ``` Events are used only for this cross-cutting concern. All other communication is via direct imports. ## const/let Conversion - ES modules run in strict mode automatically - `var` → `const` where the binding is never reassigned - `var` → `let` for loop counters and mutated locals - State re-assignments (`goals = newArray`) → `state.goals = newArray` - `TODAY` moves into `state.TODAY`, updated in `scheduleMidnight()` and `visibilitychange` ## HTML Change `templates/app.html.twig`: ```html ``` `public/app.js` is deleted after the migration is complete. ## File Size Estimates | File | Est. lines | |---|---| | state.js | ~15 | | i18n.js | ~185 | | api.js | ~35 | | goals.js | ~60 | | ui.js | ~35 | | auth.js | ~130 | | sheets.js | ~135 | | render.js | ~185 | | app.js | ~60 | | **Total** | **~840** | (Reduction due to removed comments and tighter const/let style.) ## Migration Strategy One module at a time, keeping the app functional throughout: 1. Create `public/js/` directory 2. Write modules in dependency order: state → i18n → api → goals → ui → auth → sheets → render → app 3. Switch HTML to `