From b6c55a5042d2932c311cb5b182d1369302f50272 Mon Sep 17 00:00:00 2001 From: Scott Idem Date: Tue, 10 Mar 2026 11:30:26 -0400 Subject: [PATCH] [Launcher] Fix $bindable warning on slct_event_location_id; audit TODO items - menu_location_list.svelte: mark slct_event_location_id as $bindable(null) to resolve Svelte 5 compiler warning (bind:value used on non-bindable prop) - TODO__Agents.md: audit and close resolved launcher items: - Location select auto-load bug: fixed via $derived.by() liveQuery pattern - Session Search button visibility: was never a real bug, hardcoded false - Dark mode select fix: already applied via app.css color-scheme rules --- documentation/TODO__Agents.md | 37 +++++++++++++++---- .../(launcher)/menu_location_list.svelte | 4 +- 2 files changed, 31 insertions(+), 10 deletions(-) diff --git a/documentation/TODO__Agents.md b/documentation/TODO__Agents.md index 8bf49aee..42b45613 100644 --- a/documentation/TODO__Agents.md +++ b/documentation/TODO__Agents.md @@ -5,21 +5,21 @@ ## 📋 Active Task: Post-Incident Security Recovery - [ ] **Step 1:** Conduct full audit of `PUBLIC_AE_API_SECRET_KEY` usage. Determine if it can be moved to server-side only. - [x] **Step 2:** Replace simulation tests (`tests/verify_jwt_logic.js`) with real Playwright integration tests hitting the local dev API. -- [ ] **Step 3:** Implement formal error boundaries for 403/401 API responses to provide user-friendly "Session Expired" or "Access Denied" UI. +- [x] **Step 3:** ~~Implement formal error boundaries for 403/401 API responses~~ — **Reclassified as UX, not a security item.** See `[UX] Session Expired & Access Denied` in General below. ## 🚧 Upcoming High Priority ### [Launcher] Active bugs & features (identified 2026-03-06) -- **Location select → session auto-load bug:** Starting from bare `/events/[id]/launcher` (no location in URL), selecting a location in the menu doesn't reliably trigger session loading. Root cause: double-load race — `onchange` in `menu_location_list.svelte` calls `handle_load_ae_obj_li__event_session()` directly AND then `goto()` which fires `[event_location_id]/+page.ts` which also calls `load_ae_obj_li__event_session()`. Also: `slct_event_location_id` prop is **not** `$bindable()` but template uses `bind:value={slct_event_location_id}` (compiler warning). Fix: remove the direct `await handle_load_ae_obj_li__event_session()` call from `onchange` and rely solely on the `goto()` → `+page.ts` flow. Validate that `menu_session_list.svelte` liveQuery correctly watches `$events_slct.event_location_id`. +- ~~**Location select → session auto-load bug:**~~ ✅ Fixed (2026-03-10) — The `$derived.by()` liveQuery pattern in `+layout.svelte` correctly recreates the session observable when `$events_slct.event_location_id` changes (including the null-to-value case). The double-load (`onchange` + `+page.ts`) still exists but is benign: `onchange` awaits the detailed load first, then `+page.ts` runs a background shell load. Sessions reliably appear. **Minor remaining:** `slct_event_location_id` prop in `menu_location_list.svelte` is not `$bindable()` but `bind:value={slct_event_location_id}` is used — Svelte 5 compiler warning. Functionally fine since `onchange` writes directly to `$events_slct.event_location_id`. - **Font size cycler (Launcher sidebar):** Staff onsite may not have access to the system menu, so the launcher sidebar needs its own font size cycler. Add `font_size_step: number` to `$events_loc.launcher` store. Add a cycle button in `launcher_menu.svelte` alongside the "All Files"/"All Sessions" show/hide buttons. Three steps: compact (`text-xs`) → default (`text-sm`) → large (`text-base`). Apply the class to the launcher sidebar root container `
`. -- **Session Search button visible on search page:** The "Session Search" menu button remains visible even when already on the session search page — should be hidden to avoid confusion. Find the render location (likely a launcher layout nav), add a `page.route.id` or `page.url.pathname` check to suppress it on the search page. +- ~~**Session Search button visible on search page:**~~ Not an actual bug — `event_page_menu.svelte` already passes `events__session_search={false}` to `ae_comp__events_menu_nav.svelte`, correctly hiding the link on the pres_mgmt page. The TODO item was inaccurate. ### [UI] Dark mode fix (identified 2026-03-06) -- **Dark mode select option hover (Manage Files):** In dark mode, the Event File Purpose `` element when in dark mode, or (2) replace with a custom Flowbite/Skeleton select. File to find: search for `event_file_purpose` in event file management components. +- ~~**Dark mode select option hover (Manage Files):**~~ ✅ Fixed (2026-03-10) — Added `html.dark { color-scheme: dark }` / `html.light { color-scheme: light }` to `app.css`. This globally syncs all native browser controls (select dropdowns, scrollbars, inputs) to the app's class-based dark mode, rather than a per-element fix. ### [Badges] Remaining badge work before first live event - **QR code on badge front:** `ae_comp__badge_obj_view.svelte` — display QR on the printed @@ -35,10 +35,11 @@ Key questions before starting: which routes, does the Electron app scan, what do lead record look like in the DB? ### [DevOps] Deployment Optimization (identified 2026-03-09) -- **Consolidate Service Architecture:** Simplify `ae_env_node_app/docker-compose.yml` by removing the manual Red/Green/Blue/etc. container definitions. Transition to a single service definition that can be scaled using Docker's native `--scale` flag if needed. -- **Unified Port Mapping:** Standardize on a single exposed port (e.g., 3000 or 3001) for the reverse proxy to point to, rather than managing 4+ separate ports for staging subdomains. -- **Auto-Healing & Healthchecks:** Implement a `/health` endpoint in the SvelteKit app (`src/routes/health/+server.ts`) and re-enable Docker/Nginx health-based routing to ensure zero-downtime deploys and auto-recovery. -- **Build Optimization:** Explore using a private container registry to separate the build phase from the deployment phase (Build once, deploy anywhere). +- [ ] **Consolidate Service Architecture:** Simplify `ae_env_node_app/docker-compose.yml` by removing the manual Red/Green/Blue/etc. container definitions. Transition to a single service definition that can be scaled using Docker's native `--scale` flag if needed. +- [ ] **Unified Port Mapping:** Standardize on a single exposed port (e.g., 3000 or 3001) for the reverse proxy to point to, rather than managing 4+ separate ports for staging subdomains. +- [x] **Auto-Healing & Healthchecks:** Implement a `/health` endpoint in the SvelteKit app (`src/routes/health/+server.ts`) and re-enable Docker/Nginx health-based routing to ensure zero-downtime deploys and auto-recovery. ✅ (Done 2026-03-10) +- [ ] **Build Optimization:** Explore using a private container registry to separate the build phase from the deployment phase (Build once, deploy anywhere). + ### [General] - ~~**CRUD v2 Refactor:** Finalize retirement of `Element_ae_crud_v2.svelte` in favor of V3 Editor.~~ ✅ Done (2026-03-05) — all non-IDAA usages migrated; IDAA had zero usages. @@ -46,6 +47,26 @@ lead record look like in the DB? - **`window.print()` for badge print button:** Wire the existing `handle_print_badge()` to trigger `window.print()`. Browser print works well across Chrome/Chromium/Firefox — no Electron needed. - **Input Field Audit:** Several input fields are missing `name`/`id` attributes or `data-testid`. Known examples: badge override fields in `ae_comp__badge_obj_view.svelte`; template name input in `ae_comp__badge_template_form.svelte`. Matters for: accessibility, autofill, label associations, and test targeting. (For tests, use `getByLabel()` rather than `input[value*=...]` which only checks the HTML attribute, not the Svelte-bound DOM property.) +### [UX] Session Expired & Access Denied (identified 2026-03-10) + +Two related UX gaps to handle together: + +**1. Session Expired banner (API 401/403 mid-session):** +- `flag_expired` in root `+layout.svelte` is declared but never set — it was always intended for this +- Add a small writable store or custom event (e.g., `ae_auth_error` in `ae_stores`) that API helpers (`api_get_object.ts`, `api_post_object.ts`, `api_patch_object.ts`) can fire when they get a 401 or 403 +- Root layout watches the store and sets `flag_expired = true` +- Render a non-blocking dismissible banner (not full-screen): "Session expired. Please sign in again." with a link to the sign-in control +- Especially relevant for Launcher (event staff on tablets may not notice silent failures) + +**2. Standardize Access Denied UI (non-IDAA routes only — IDAA layout is intentionally custom):** +- Currently inconsistent across the app: + - Root layout: full-screen `flag_denied` (site access key gate — keep this, it's correct) + - `/core` layout: silent redirect to home — should show a brief message instead + - `/events/[event_id]/settings`: inline raw text string — should use a consistent banner component + - `/events/.../badges/.../review`: inline `

Access Denied

` with no context or action +- Create a reusable `element_access_denied.svelte` component (small: icon + message + optional action button) +- Swap the ad-hoc patterns to use it consistently + ## ✅ Completed Recently - [x] **[Svelte]** **`state_referenced_locally` warning fixes (2026-03-09):** Resolved 10 Svelte 5 warnings where `$state`/`$props()` variables were read in top-level synchronous script code instead of inside a reactive closure. Fixed by moving `if (browser) { ... }` blocks and timezone-loading blocks into `onMount`. Files: `archives/[archive_id]/+page.svelte`, `archives/[archive_id]/ae_idaa_comp__archive_obj_id_edit.svelte`, `archives/[archive_id]/ae_idaa_comp__archive_content_obj_id_edit.svelte`, `bb/[post_id]/+page.svelte`. Note: 42 similar warnings remain in `recovery_meetings/ae_idaa_comp__event_obj_id_edit.svelte` and `..._v2.svelte` — same pattern, fix next session. - [x] **[TypeScript]** **Sign In/Out TS errors fixed (2026-03-09):** `user_id` and `person_id` in `e_app_sign_in_out.svelte` were implicitly typed `null` from `$state(null)`, causing assignment errors. Explicitly typed as `string | null`. diff --git a/src/routes/events/[event_id]/(launcher)/menu_location_list.svelte b/src/routes/events/[event_id]/(launcher)/menu_location_list.svelte index 44c04bdf..5982aaf8 100644 --- a/src/routes/events/[event_id]/(launcher)/menu_location_list.svelte +++ b/src/routes/events/[event_id]/(launcher)/menu_location_list.svelte @@ -2,7 +2,7 @@ interface Props { loading__session_li_status?: null | boolean | string; lq__event_location_obj_li: any; - slct_event_location_id: string | null; + slct_event_location_id?: string | null; trigger_reload__event_session_obj_li?: boolean; trigger_reload__event_location_obj_li?: boolean; @@ -13,7 +13,7 @@ let { loading__session_li_status = $bindable(null), lq__event_location_obj_li, - slct_event_location_id = null, + slct_event_location_id = $bindable(null), trigger_reload__event_session_obj_li = $bindable(false), trigger_reload__event_location_obj_li = $bindable(false),