diff --git a/documentation/AE__Architecture.md b/documentation/AE__Architecture.md index 21702253..012e1f86 100644 --- a/documentation/AE__Architecture.md +++ b/documentation/AE__Architecture.md @@ -135,7 +135,34 @@ A list of potential future standard fields, often prefixed with `obj_`. These ar - `obj_id`, `obj_ext_uid`, `obj_ext_id`, `obj_import_id`, `obj_code`, `obj_account_id`, `obj_passcode`, `obj_type`, `obj_type_ver_id`, `obj_name`, `obj_summary`, `obj_outline`, `obj_description`, `obj_enable`, `obj_enable_on`, `obj_archive_on`, `obj_hide`, `obj_priority`, `obj_sort`, `obj_group`, `obj_cfg_json`, `obj_notes`, `obj_created_on`, `obj_updated_on`. -## 7. IndexedDB LiveQuery Usage +## 7. Runtime Environment: Browser vs Electron + +The Aether SvelteKit frontend runs in a standard web browser in almost all cases. The Electron native app is a **very specialized exception** with a narrow, specific purpose. + +### 7.1. Standard Browser (Default) + +All Aether modules run in a regular browser (Chrome, Chromium, Firefox, Safari). This includes: +- Badge printing — `window.print()` works well across Chrome, Chromium, and Firefox. Chrome is recommended for onsite badge printing stations. +- All CRUD operations, event management, journals, IDAA, reports, etc. +- No special browser configuration required. + +### 7.2. Electron Native App (Specialized — Pres Mgmt Launcher Only) + +The Electron app (`aether_app_native_electron/`) exists **solely** to support the **Events Presentation Management Launcher**. It provides OS-level capabilities that a browser sandbox cannot: +- Control of third-party presentation software (PowerPoint, Keynote, LibreOffice Impress) +- Local filesystem access for slide file management +- Hardware telemetry for connected devices + +**What Electron is NOT used for:** +- Badge printing (browser works fine) +- Any other Aether module +- Any general-purpose Aether functionality + +The bridge is exposed as `window.aetherNative` (set by Electron's preload script). All code that calls `window.aetherNative` should degrade gracefully when it is `undefined` (i.e., in a normal browser). See: `src/lib/electron/electron_relay.ts`. + +**When to assume Electron is available:** Only inside the `/events/[event_id]/(launcher)/` route group, and only when the page was loaded from the native app. + +## 8. IndexedDB LiveQuery Usage - `lq__xyz_obj`: Used for general read-only access to liveQuery results. - `lqw__xyz_obj`: Used for forms and binding values, representing a writable snapshot of liveQuery results. diff --git a/documentation/MODULE__AE_Events_Badges.md b/documentation/MODULE__AE_Events_Badges.md index 94938c47..57a9da0d 100644 --- a/documentation/MODULE__AE_Events_Badges.md +++ b/documentation/MODULE__AE_Events_Badges.md @@ -3,7 +3,7 @@ **Module Path:** `src/routes/events/[event_id]/(badges)/badges/` **API Module:** `src/lib/ae_events/ae_events__event_badge.ts` **Database:** `db_events.badge` (Dexie IndexedDB table) -**Last Updated:** 2026-02-26 +**Last Updated:** 2026-02-26 (rev 3) --- @@ -208,6 +208,18 @@ editable_badge_type_code: string | null ### Search Component **File:** `ae_comp__badge_search.svelte` +### Multi-Word Search Fix (2026-02-26) +Fulltext search now correctly handles multi-word queries by splitting on whitespace and applying AND logic per word: +```typescript +// "scott idem" → LIKE '%scott%' AND LIKE '%idem%' +// Previously: LIKE '%scott idem%' (failed to match) +const words = qry.split(/\s+/).filter(w => w.length > 0); +for (const word of words) { + search_query.and.push({ field: 'default_qry_str', op: 'like', value: `%${word}%` }); +} +``` +**Committed:** dc0f3066 + ### Available Filters **Fulltext Search** (All Users) @@ -345,13 +357,34 @@ print_first_datetime: string // ISO datetime of first print print_last_datetime: string // ISO datetime of most recent print ``` -### Print Workflow (Future) +### Print Button (Implemented 2026-02-26) +The `handle_print_badge()` function in `ae_comp__badge_obj_view.svelte` increments the count and records timestamps: +```typescript +async function handle_print_badge() { + const now = new Date().toISOString(); + const current_print_count = $lq__event_badge_obj.print_count ?? 0; + const data_to_update = { + print_count: current_print_count + 1, + print_last_datetime: now + }; + if (current_print_count === 0) { + data_to_update.print_first_datetime = now; // Only set on first print + } + await events_func.update_ae_obj__event_badge({ ... }); +} +``` + +Button has `data-testid="badge-print-btn"` and shows loading/done/error states with icon feedback. + +### Print Workflow 1. **Pre-Print:** Check `print_count` to warn if already printed -2. **Print:** Send to printer via Electron native bridge or browser print -3. **Post-Print:** Increment `print_count`, update `print_last_datetime` +2. **Print:** `window.print()` — standard browser print dialog. Works well in Chrome, Chromium, and Firefox. Chrome is the recommended browser for onsite badge printing (most stable in production). +3. **Post-Print:** Handled by `handle_print_badge()` — count + timestamps updated 4. **Audit:** Print history available for staff review -**Current Status:** UI placeholder for print button, tracking fields exist in database, print functionality pending. +**Browser vs Electron:** Badge printing does NOT require the Electron native app. The standard browser print dialog works well across Chrome, Chromium, and Firefox. The Electron native app is specialized for the **Events Pres Mgmt Launcher only** and should not be assumed available for badge stations. + +**Current Status:** Button records print event and updates IDB. The `window.print()` call still needs to be wired to the button — see Future Enhancements. --- @@ -483,32 +516,61 @@ delete_ae_obj_id__event_badge({ event_badge_id, event_id, method }) - **Metadata:** ID, created/updated timestamps (edit mode only) **Badge Detail View** (`ae_comp__badge_obj_view.svelte`) -- **Edit Mode:** Activated by `#review` URL hash or edit button +- **Edit Mode:** Activated by edit button (or `#review` URL hash for future self-service) +- **Condition:** Renders only when BOTH `$lq__event_badge_obj` AND `$lq__event_badge_template_obj` are non-null - **Form Binding:** Direct `bind:value` on editable fields - **Dynamic Sizing:** Font size adjusts based on text length - **Print Preview:** Full badge layout with template - **Save Handler:** Only sends changed fields to API +- **`data-testid` attributes:** `badge-edit-btn`, `badge-save-btn`, `badge-cancel-btn`, `badge-print-btn`, `badge-professional-title-input` — use these in tests --- ## Testing Status ### Current Test Coverage -- ❌ Badge list cold-start (failing — mock API issue) -- ❌ Badge data integrity (failing — mock API issue) -- ❌ Badge template list (failing — route mismatch) +- ✅ Badge list loads (all 6 data integrity tests passing) +- ✅ Badge template list loads and displays +- ✅ Badge template form renders and populates correctly +- ✅ Badge template values persist in edit form +- ✅ Electron bridge compatibility (graceful degradation in browser) +- ✅ Badge field processor handles missing optional fields - ✅ Badge type filter tests - ✅ Badge template relationship tests -- ✅ Electron bridge compatibility (graceful degradation) +- ✅ **Attendee workflow test** — navigate → edit professional title → print → return (d1ded2d4) -### Test Issues -**Root Cause:** Mock API routes not matching actual request patterns. Tests provide correct mock data but page shows "No badges found". +### Key Test Lessons Learned -**Browser Error:** `Object is missing a valid ID for table "badge"` — Mock data reaching IDB with only `{tmp_sort_1, tmp_sort_2, event_id}`, all other fields stripped. +**Search API path is FLAT, not nested.** `search_ae_obj_v3` builds `/v3/crud/{obj_type}/search` — always flat regardless of the parent relationship. Mocks must match this: +```typescript +// CORRECT — flat path +url.includes('/v3/crud/event_badge/search') && method === 'POST' +// WRONG — nested path, mock will never fire +url.includes(`/v3/crud/event/${event_id}/event_badge/search`) && method === 'POST' +``` -**Fix Required:** Debug actual API request URLs, adjust mock route patterns. +**List API (GET) is also FLAT with query params.** `get_ae_obj_li_v3` builds `/v3/crud/{obj_type}/?for_obj_id=...` — always flat. Mocks must check `url.includes('/v3/crud/event_badge_template/') && url.includes('for_obj_id')`. -**Manual Testing Recommended:** Navigate to `/events/{event_id}/badges` in browser to verify actual functionality before fixing test infrastructure. +**CSS `input[value*=...]` selectors don't work with Svelte bind:value.** The CSS selector checks the HTML *attribute*; Svelte's `bind:value` sets the DOM *property* only. In Playwright tests, use `page.getByLabel()` or `locator.inputValue()` instead. + +**Dexie requires `_random` ID fields.** Badge objects saved to IDB must include: +```typescript +event_badge_id_random: string // Must be present or Dexie skips the object +id_random: string // Also checked +// Error: "Object is missing a valid ID for table 'badge'" +``` +All API mock responses in tests need these fields. + +**Badge view requires both badge AND template.** `ae_comp__badge_obj_view.svelte` wraps everything in `{#if $lq__event_badge_obj && $lq__event_badge_template_obj}` — if the template isn't loaded, edit/print buttons and the badge itself don't render. Tests must mock the badge template endpoint. + +**Badge GET endpoint (single object):** `/v3/crud/event_badge/{id}` (NOT nested under event). Matches `api.get_ae_obj_v3()` which uses the flat path. + +**Badge PATCH endpoint (update):** `/v3/crud/event/${event_id}/event_badge/${badge_id}` (nested under event). Matches `api.patch_ae_obj_v3()` which uses the nested path. + +**Use `data-testid` for test selectors.** Key buttons have targets: `badge-edit-btn`, `badge-save-btn`, `badge-cancel-btn`, `badge-print-btn`, `badge-professional-title-input`. + +### Remaining Test Issues +None — all current badge tests passing as of 2026-02-26 (f5e98b8c). --- @@ -521,7 +583,7 @@ delete_ae_obj_id__event_badge({ event_badge_id, event_id, method }) ### Future Enhancements 1. **Access-Based Edit Permissions:** Implement JSON config for field-level access control -2. **Print Functionality:** Wire up print button to Electron native print or browser print API +2. **Print Functionality:** Wire up `window.print()` to the print button. Standard browser print API — works well in Chrome/Chromium/Firefox for badge label printing. Electron is NOT needed. 3. **Batch Operations:** Bulk update, bulk print, bulk export 4. **Audit Log:** Track who edited which fields and when 5. **Photo Badges:** Support badge photo upload and display @@ -585,5 +647,5 @@ db_events.badge.toArray().then(console.log) --- **Document Status:** ✅ Complete -**Last Verified:** 2026-02-26 -**Verified Against:** Code commit b28595da (badge data fixes) +**Last Verified:** 2026-02-26 (rev 3 — all badge tests passing) +**Verified Against:** Code commit f5e98b8c (all data integrity tests passing) diff --git a/documentation/TODO__Agents.md b/documentation/TODO__Agents.md index 2f1925ac..7a043189 100644 --- a/documentation/TODO__Agents.md +++ b/documentation/TODO__Agents.md @@ -10,6 +10,8 @@ ## 🚧 Upcoming High Priority - **CRUD v2 Refactor:** Finalize retirement of `Element_ae_crud_v2.svelte` in favor of V3 Editor. - **Temp Cleanup:** Auto-removal of native `.tmp` files older than 24h. +- **`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.) ## ✅ Completed Recently - [x] **[API]** **V3 Lookup System Integration:** Implemented standardized `/v3/lookup/` endpoints for Countries, Subdivisions, and Time Zones. Added support for `only_priority` filtering in IDAA editors. @@ -23,3 +25,8 @@ - [x] **[API]** Unified all CRUD helpers to standard V3 `/v3/crud/...` paths. - [x] **[Framework]** Implemented `AE_Obj_Field_Editor_V3` with Svelte 5 Runes. - [x] **[IDAA]** Verify Bulletin Board and Recovery Meetings functionality. +- [x] **[Badges]** **Multi-word fulltext search fix:** Split query on whitespace, apply AND logic per word. `"scott idem"` now matches records containing both words. (dc0f3066) +- [x] **[Badges]** **Print button implemented:** `handle_print_badge()` increments `print_count`, records `print_first_datetime`/`print_last_datetime`. Button has loading/done/error states. (d1ded2d4) +- [x] **[Badges]** **`data-testid` attributes added** to badge view interactive elements (`badge-edit-btn`, `badge-save-btn`, `badge-cancel-btn`, `badge-print-btn`, `badge-professional-title-input`) for reliable test targeting. +- [x] **[Tests]** **Attendee badge workflow test passing:** `event_badge_attendee_workflow.test.ts` — navigate → edit professional title → save (verify PATCH body) → print (verify count/timestamps) → return to search. (d1ded2d4) +- [x] **[Tests]** **All badge data integrity tests fixed:** All 6 tests in `event_badge_data_integrity.test.ts` now pass. Root causes: (1) search mock used nested URL instead of flat `/v3/crud/event_badge/search`, (2) template list mock used nested URL instead of flat with `for_obj_id`, (3) missing `_random` ID fields in mock badge objects, (4) CSS `input[value*=...]` selector doesn’t work for Svelte-bound inputs — fixed to `getByLabel()`. (f5e98b8c)