# PROJECT: AE Events Badges — Review Form & Print Font Controls **Created:** 2026-02-27 **Last Updated:** 2026-02-27 **Branch:** `ae_app_3x_llm` **Priority:** HIGH — first live event is Axonius, NYC, mid-April 2026 **Owner:** Scott Idem / One Sky IT **Status:** ✅ TASK 1 (Badge Review Form) COMPLETE | ✅ TASK 2 (Print Font Controls) COMPLETE — v1 --- ## Implementation Status (2026-02-27) ### ✅ TASK 1: Badge Review Form — COMPLETE The badge review form (`ae_comp__badge_review_form.svelte`) is now fully functional with: - ✅ All editable fields with access-level gating - ✅ Print status display section - ✅ QR code generation and display (hover zoom + click expand) - ✅ Options and Tickets fields (staff edit / attendee view) - ✅ Save/Cancel with change detection - ✅ Override field revert buttons - ✅ **HTML rendering** for full_name, professional_title, affiliations, location - ✅ **Accessibility toggle** for text enlargement (text-2xl ↔ text-4xl) - ✅ **Help modal** with 6 sections of attendee guidance (Flowbite Modal component) - ✅ Local edit mode (never writes to `$ae_loc.edit_mode`) **Bug fixed (2026-02-27):** `default_authenticated_fields` and `default_trusted_fields` in `review/+page.svelte` had incorrect field names causing `can_edit()` to silently drop saves. Fixed to use exact names matching the form's `can_edit()` checks. ### ✅ TASK 2: Badge Print Font Controls — COMPLETE (v1) Implemented in commit `3d7279da`. This is a **first draft** — auto font scaling using mm/inch units is planned as a future iteration. **What was built:** - `ae_comp__badge_obj_view.svelte`: 4 new optional props (`font_size_name`, `font_size_title`, `font_size_affiliations`, `font_size_location`, all `number` in px). When provided, replaces auto inch-based Tailwind class sizing with an inline `font-size: Npx` style. Existing auto-sizing behavior is completely unchanged when props are absent. - `print/+page.svelte`: Screen-only (`print:hidden`) control panel with 4 rows, one per field. Each row: label, `[−]` button, value display (`58px` or `Auto`), `[+]` button, `[↺]` reset. Step: 2px. `null` state = auto (uses existing inch-based auto-sizing). First `+` click activates at a sensible default that approximates the current auto inch values. **Default px values when first activated (≈ inch equivalents at 96dpi):** | Field | Default px | Approx. inch | Range | |--------------|------------|--------------|----------| | Name | 58px | ≈ .60in | 20–80px | | Title | 34px | ≈ .35in | 14–56px | | Affiliations | 38px | ≈ .40in | 14–60px | | Location | 34px | ≈ .35in | 14–56px | **Future:** Auto font scaling using mm/inch units (physical paper stock measurements). Will likely need to revisit the inch ↔ mm conversion and potentially expose the auto-sizing logic as adjustable rather than replacing it with px overrides. --- ## Context The Events Badges module is mostly complete for navigation and search. Two key pieces of functional UI were needed before the first show: 1. **Badge Review Form** ✅ — `ae_comp__badge_review_form.svelte` now has complete field rendering, edit inputs gated by access level, save/cancel API calls, and display-only sections (QR code, print status, option/ticket checkmarks). Also includes accessibility features (text enlargement) and help modal for attendee guidance. 2. **Badge Print Font Controls** ⏳ — The print page header needs screen-only controls (hidden during `window.print()`) to bump font sizes for the name, professional title, affiliations, and location sections before printing. These only affect the `ae_comp__badge_obj_view.svelte` render — not the page layout/template structural dimensions. Read `documentation/MODULE__AE_Events_Badges.md` for full module context before starting. --- ## MANDATORY: Before You Start 1. Run `ae_describe event_badge` (MCP tool) to confirm which fields actually exist in the DB. Several fields in the spec below may need to be added to `properties_to_save` in `src/lib/ae_events/ae_events__event_badge.ts` if they are not already saved to IDB. 2. Fields to specifically confirm exist in `event_badge` schema: - `pronouns`, `pronouns_override` - `phone`, `phone_override` - `allow_tracking` - `agree_to_tc` - `other_1_code` through `other_8_code` (the "option" fields) - `ticket_1_code` through `ticket_8_code` - `registration_type`, `registration_type_code` - `registration_type_override`, `registration_type_code_override` 3. Run `npx svelte-check` before committing. Baseline is **77 errors** (all pre-existing, none in the badge module files). Do not introduce new errors. 4. Do NOT write to `$ae_loc.edit_mode` from any badge component. This was a critical bug (fixed 2026-02-27). See `documentation/AE__Permissions_and_Security.md`. --- ## TASK 1: Badge Review Form (HIGH PRIORITY) ### File to build `src/routes/events/[event_id]/(badges)/badges/[badge_id]/ae_comp__badge_review_form.svelte` This component is already imported and used by `review/+page.svelte`. Props it receives: ```typescript interface Props { event_id: string; event_badge_id: string; lq__event_badge_obj: any; // Svelte 5 store from liveQuery can_edit_fields: string[]; // Which fields this user can edit is_staff: boolean; // True if trusted_access or higher log_lvl?: number; } ``` `can_edit_fields` values: - `['*']` — administrator (all fields) - Array of field names — specific editable fields - `[]` — read-only (shouldn't normally reach this component, but handle it) ### Helper Use a helper derived inside the component: ```typescript function can_edit(field: string): boolean { return can_edit_fields.includes('*') || can_edit_fields.includes(field); } ``` ### Save / Cancel Pattern Follow the Journals module pattern (`src/lib/ae_journals/`). Key points: - Use `import { events_func } from '$lib/ae_events_functions'` - Call `events_func.update_ae_obj__event_badge({ event_badge_id, event_id, data_kv })` - Only send changed fields in `data_kv` (compare against `$lq__event_badge_obj` values) - Show save/cancel buttons only when something has changed (`has_changes` derived) - Show a success/error state briefly after save (1-2 seconds, then reset) - Cancel resets local state back to `$lq__event_badge_obj` values - Use `data-testid="badge-review-save-btn"` and `data-testid="badge-review-cancel-btn"` ### Save API Call ```typescript await events_func.update_ae_obj__event_badge({ api_cfg: $ae_api, // from ae_loc store or passed as prop — check how ae_comp__badge_obj_view.svelte does it event_badge_id: event_badge_id, event_id: event_id, data_kv: { /* only changed fields */ } }); ``` Check `ae_comp__badge_obj_view.svelte` for the existing save pattern — it already works and can be used as reference. --- ### Section 1: Display-Only Status Bar (all access levels) ✅ IMPLEMENTED Always show at top of form. Read-only. No edit controls. ``` Print Status: [Not yet printed] OR [Printed 3× — first: Jan 5 2026, last: Jan 5 2026] ``` **Implemented:** Shows print count with first/last print datetimes. Hidden if `print_count < 1`. Uses `$lq__event_badge_obj.print_count`, `print_first_datetime`, `print_last_datetime`. Format datetimes with `ae_util.iso_datetime_formatter(dt, 'datetime_iso_12_no_seconds')`. Import `ae_util` from `$lib/ae_utils/ae_utils`. --- ### Section 2: QR Code (all access levels) ✅ IMPLEMENTED Display the attendee's badge QR code. This is the same QR code shown on the printed badge itself — scanning it at the badge station triggers automatic badge search and print. **Implemented using `core_func.js_generate_qr_code()`:** ```typescript qr_data_url = await core_func.js_generate_qr_code('obj', { obj_type: 'event_badge', obj_id: event_badge_id }); ``` **Features:** - Hover: Zoom overlay effect (`qr_hovered` state) - Click: Expand/collapse that pushes content down (`qr_expanded` state) - Displays as data URL image from QR code generation - Reactive: automatically regenerates when `event_badge_id` changes --- ### Section 3: Editable Fields ✅ IMPLEMENTED Render each field as: read-only display when `!can_edit(field)`, or an `` / `