Files
OSIT-AE-App-Svelte/documentation/PROJECT__AE_Events_Badges_Review_Print.md
2026-04-19 16:55:10 -04:00

626 lines
29 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# PROJECT: AE Events Badges — Review Form & Print Font Controls
**Created:** 2026-02-27
**Last Updated:** 2026-03-18
**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 COMPLETE | ✅ TASK 2 COMPLETE | ✅ TASK 3 COMPLETE | ✅ TASK 4.1 COMPLETE | ⏳ TASK 4.0 OPEN
---
## Design Intent — Two Complementary Flows
### Flow 1: Remote Badge Review (email link)
- Staff emails a review link to the attendee before the event.
- Attendee opens the link on their own device, reviews their badge info, and edits permitted fields.
- **Email address rule:** Always send to `event_badge.email` — never `email_override`.
`email_override` is a display/badge field only. It cannot be trusted as a delivery address
(attendee may have changed it to something different for badge display purposes).
- Component: `ae_comp__badge_review_form.svelte` — plain form, no badge render.
- Route: `/events/[event_id]/badges/[badge_id]/review/`
### Flow 2: Kiosk / Onsite Badge Station (print page)
- Hardware: a laptop + badge printer (Epson fanfold or Zebra PVC card) at the check-in table.
- At the event, an attendee walks up to a badge station (check-in kiosk).
- A staff member or volunteer pulls up the attendee's badge on the print page.
- The **print page is a kiosk tool**, not just a print queue:
- Attendee reviews their badge info and can edit permitted fields **in real time**,
with the live badge render updating as they make changes.
- Staff/volunteers are present to assist with any questions.
- Once satisfied, staff prints the badge.
- The key differentiator vs the review form: **the live badge render** shows exactly how
the badge will print. Attendees and staff can see changes immediately.
- Component: `ae_comp__badge_obj_view.svelte`
- Route: `/events/[event_id]/badges/[badge_id]/print/`
### Permission Model — Same Logic, Both Flows
Both flows should respect the same permission model:
- **Attendee-level** (basic Authenticated access): can edit `pronouns_override`,
`full_name_override`, `professional_title_override`, `affiliations_override`,
`location_override`, `phone_override`, `email_override`, `allow_tracking`, `agree_to_tc`.
- **Staff-level** (trusted_access+): all attendee fields + `email`, `badge_type_code_override`,
`badge_type_override`, `hide`, `priority`, `notes`, and font size controls.
- Permissions are configured per-event in `event.mod_badges_json.edit_permissions`.
Hardcoded defaults are used until that config is implemented.
**Current gap (TASK 4):** The print page edit button is currently gated to trusted_access only.
It needs to be accessible to attendees at the kiosk (with appropriate field-level gating),
matching the permission model already implemented in `ae_comp__badge_review_form.svelte`.
---
## Next Up for Badges (TASK 4)
### 0. Kiosk Editing — Print Page Permission Model Alignment
**This is the most important gap before the first live event.**
Currently the print page edit button is staff-only (trusted_access gate). At the kiosk,
attendees need to be able to edit their own fields (same attendee-level permissions as the
review form), with staff-only fields gated appropriately.
Work needed:
- Wire the same `can_edit_fields` / `can_edit(field)` permission logic into the print page
that `ae_comp__badge_review_form.svelte` already uses.
- The edit panel on the print page should show attendee-editable fields to all authenticated
users, and staff-only fields to trusted_access+.
- The badge render (v1 or v2) should update live as the attendee edits fields.
- Consider whether the print page needs its own inline edit panel (sidebar or overlay)
or whether it should share/reuse the review form component alongside the badge render.
- **Do NOT use `email_override` as the send-to address** — always use `event_badge.email`.
### 1. Auto-Scaling Badge Text — In Progress
`ae_comp__badge_obj_view.svelte` using `element_fit_text.svelte` (binary search auto-scale).
Toggle between v1 (heuristic) and v2 (auto-scale) on the print page via the `v1`/`v2` header button.
Heights tuned per layout in `fit_heights` derived object. Still needs visual tuning with real badges.
### 2. QR Code on Badge Front — `ae_comp__badge_obj_view.svelte`
The badge template has a `show_qr` flag (or similar). When toggled on, the QR code should
appear on the front face of the printed badge. Currently QR is only shown on the review form.
- Check the badge template fields for the QR toggle field name (`show_qr`, `qr_enabled`, etc.)
via `ae_describe event_badge_template` and inspect `ae_comp__badge_obj_view.svelte`.
- The QR code data URL is generated with:
```typescript
qr_data_url = await core_func.js_generate_qr_code('obj', {
obj_type: 'event_badge',
obj_id: event_badge_id
});
```
See `ae_comp__badge_review_form.svelte` for the working pattern.
- Position on badge: typically bottom-right corner of the badge face, sized to fit within
the template's layout constraints. Do NOT alter structural badge dimensions.
- Must be hidden on `ae_comp__badge_obj_view.svelte` when `show_qr` is falsy.
### 2. Badge Print Controls — UX Improvements (ae_comp__badge_print_controls.svelte)
- Consider: keyboard shortcuts (+ / -) for font sizing while a field is active
- Consider: "Apply to all badges" workflow for font size presets
### 4. Leads Module
Next major work after badge polish. See `documentation/MODULE__AE_Events_Leads.md` (if it
exists) for context. Exhibitor lead scanning via QR code at exhibitor booth → capture attendee
badge data, gated by `allow_tracking` on the badge.
---
## Implementation Status
### ⏳ TASK 4.0: Kiosk Editing — NOT STARTED (updated 2026-03-18)
Print page edit access needs to be opened to attendee-level permissions, not just trusted_access.
The permission model, field list, and `can_edit()` helper from `ae_comp__badge_review_form.svelte`
should be the reference. See Design Intent section above.
**Note (2026-03-18):** `style_href` and `duplex` are both fully implemented and verified in code —
the MODULE doc TODO list was stale. `duplex` is in `properties_to_save`; v2 badge render gates
`show_badge_back` on it. `style_href` loads via `<svelte:head>` in `print/+page.svelte`.
### ✅ TASK 4.1: Auto-Scaling Badge Text v2 — COMPLETE (2026-03-12)
**Files created/updated:**
- `src/lib/elements/action_fit_text.ts` — Svelte action
- `src/lib/elements/element_fit_text.svelte` — Component wrapper
- `src/routes/events/.../ae_comp__badge_obj_view.svelte` — V2 badge render (canonical)
Debug blocks gated behind `$ae_loc.edit_mode` (hidden in production).
- `print/+page.svelte` — Always uses v2 now. v1/v2 toggle removed. Header redesigned for kiosk UX.
- `ae_comp__badge_print_controls.svelte` — Identity card at top, pronouns moved to attendee section,
"Staff adjustments" divider before badge_type field.
- `print_list/+page.svelte` — Updated to import v2.
- `ae_comp__badge_obj_view.svelte` (v1) — **Moved to ~/tmp/agents_trash/**
**Kiosk UX improvements (2026-03-12):**
- Print page header: cleaner, shows name + "Ready"/"Printed N×" status chip, event name.
Header Print Now button removed (duplicate); only Re-print shortcut visible in trusted+edit mode.
- Controls right panel: identity card at top confirms who the badge belongs to before printing.
Pronouns field is now an attendee-level field (was trusted-only). Staff section labelled.
- Debug JSON blocks in v2 badge render hidden behind global edit_mode flag.
**Print page CSS centering work (also 2026-03-12) — ⚠️ Chromium PDF issue pending:**
Multiple iterations to center the badge on the printed page, working around SvelteKit layout
hierarchy issues. Current approach: `position: fixed; top: 50%; left: 50%; transform: translate(-50%, -50%)`
on `.event_badge_wrapper`. Key findings:
- `#ae_main_content` has `overflow: auto` — Firefox (spec-compliant) won't let `display: contents`
dissolve it. Workaround: explicit passthrough block (`display: block; overflow: visible; width: 100%`).
- `app.css` global `overflow: hidden` on `html, body` creates a BFC that collapses body to badge-width.
Override with `overflow: visible !important` in print CSS.
- **Chrome ignores `@page { size }` for Save as PDF** — verified 2026-03-12. Firefox honors it.
Chrome uses the system default paper size; Firefox locks to the CSS `@page { size }` value.
Neither browser lets you change paper size in "Save to PDF" mode — only when printing to a
physical printer. For actual Epson/Zebra printing, the driver controls paper size; unaffected.
- **Chrome "Default" margins cause the "squish"** — Chrome's Default margin setting inserts
URL/date/page-number headers and footers into the printable area. These eat into the space
that `position: fixed; top: 50%` references, making the badge off-center or clipped. Fix:
set **Margins → None** (or Minimum) in Chrome's print dialog. Content centering verified
correct: Chrome A4 + None margins produces badge center = 297.5 pts on a 297.5 pt wide page.
Use Firefox for accurate PDF proofing.
- See `documentation/MODULE__AE_Events_Badge_Templates.md` → "Print Layout Architecture" for full
technical details.
### ✅ TASK 3: Badge Print Controls Panel — COMPLETE (2026-03-02)
**Files created/modified:**
- `ae_comp__badge_print_controls.svelte` — NEW. Right-edge control panel with per-field
accordion sections. Font size controls + inline edit forms gated by access level.
- `print/+page.svelte` — layout changed from `flex-row` to fixed right panel.
**Design decisions:**
- Controls panel is `position: fixed right-0 top-20 bottom-0 w-64` — out of normal flow,
always visible regardless of viewport width. `top-20` (80px) clears the page header.
- Badge area gets `pr-64` to prevent content from hiding under the fixed panel.
`print:pr-0` and `print:hidden` on the panel restore a clean print layout.
- `bg-white dark:bg-zinc-900` gives the panel a solid background to prevent bleed-through.
**Per-field accordion structure (one open at a time):**
| Field | Access | Font Controls |
| --- | --- | --- |
| Name | Trusted+ edit | ✅ |
| Professional Title | All auth edit | ✅ |
| Affiliations | All auth edit (textarea) | ✅ |
| Location | All auth edit | ✅ |
| Lead Scanning (allow_tracking) | All auth edit | — |
| Pronouns | Trusted+ edit | — |
| Badge Type | Trusted+, only when template has badge_type_list | — |
**Access level note:**
`is_trusted = $derived($ae_loc.trusted_access === true)` — covers Trusted, Administrator,
Manager, Super (cascade). No need to OR in `administrator_access`.
**badge_type_override coupling:**
When badge type is changed via dropdown, both `badge_type_code_override` AND
`badge_type_override` are saved together (name comes from template list). Same behavior in
`ae_comp__badge_obj_view.svelte` and `ae_comp__badge_review_form.svelte`.
Edge case: custom names (e.g. code=`member`, name=`"Life Member"`) must be set manually in DB.
**Font size config (moved from print page to controls component):**
| Field | Default px | Range | Step |
|--------------|------------|----------|------|
| Name | 58px | 2080px | 2px |
| Title | 34px | 1456px | 2px |
| Affiliations | 38px | 1460px | 2px |
| Location | 34px | 1456px | 2px |
Font sizes flow back to the parent via `$bindable()` props so `ae_comp__badge_obj_view`
stays in sync without prop-drilling through a third component.
---
### ✅ 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 | 2080px |
| Title | 34px | ≈ .35in | 1456px |
| Affiliations | 38px | ≈ .40in | 1460px |
| Location | 34px | ≈ .35in | 1456px |
**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 `<input>` /
`<select>` / `<textarea>` when `can_edit(field)`.
Show `(overridden)` label next to override fields when the override value differs from
the base field value.
**HTML Rendering (implemented 2026-02-27):**
The following fields render HTML markup using `{@html}` when viewing (not when editing):
- `full_name_override` / `full_name`
- `professional_title_override` / `professional_title`
- `affiliations_override` / `affiliations`
- `location_override` / `location`
This allows for rich text formatting (bold, italic, line breaks, etc.) in badge displays.
**Accessibility Features (implemented 2026-02-27):**
- Text enlargement toggle button in sticky header
- Normal size: `text-2xl` on field values
- Enlarged size: `text-4xl` on field values
- Button shows visual feedback (gray → blue, "Larger" → "Normal" label)
- Applied consistently across all text fields
**Help Modal (implemented 2026-02-27):**
- Flowbite Modal component with 6 sections
- Sections: Reviewing Badge, Editing Info, Accessibility, QR Code, Lead Scanning, Assistance
- Triggered by Help button (BadgeQuestionMark icon) in sticky header
- Currently has placeholder text — can be customized per event/client
#### Attendee-Editable Fields (shown to all access levels with link)
| Field | Input Type | Notes |
|---|---|---|
| `pronouns_override` | text input | Fallback display: `pronouns` |
| `full_name_override` | text input | Fallback display: `full_name`; **renders HTML** when viewing |
| `professional_title_override` | text input | Fallback display: `professional_title`; **renders HTML** when viewing |
| `affiliations_override` | textarea | Fallback display: `affiliations`; **renders HTML** when viewing |
| `phone_override` | text input (tel) | Fallback display: `phone` |
| `location_override` | text input | Fallback display: `location`; **renders HTML** when viewing |
| `allow_tracking` | checkbox | Label: "Allow exhibitor lead scanning" |
| `agree_to_tc` | checkbox | Label: "I agree to the Terms and Conditions" + placeholder T&C text block |
#### Staff-Only Additional Fields (shown when `is_staff === true`) ✅ IMPLEMENTED
| Field | Input Type | Notes |
|---|---|---|
| `email_override` | email input | Fallback display: `email` |
| `badge_type_code_override` | select | Options: member, non-member, guest, exhibitor, staff, test; also updates `badge_type_override` text |
> **Edge case — custom badge type name:** If an attendee needs a standard badge type code (for
> CSS styling) but a slightly different displayed name (e.g. code=`member`, name=`"Life Member"`),
> set `badge_type_override` directly in the DB. Do **not** use the dropdown — selecting from the
> dropdown in the UI overwrites `badge_type_override` with the standard name from the template
> list. This is an intentional trade-off: coded for the normal case (dropdown keeps both fields in
> sync), special cases handled manually by Scott in the DB.
| `registration_type_code_override` | select | Same options as badge_type for now; also updates `registration_type_override` |
| `hide` | checkbox | Label: "Hidden from search results" |
| `priority` | number input | |
| `notes` | textarea | |
#### Staff-Only: Options & Tickets (read-edit, shown when `is_staff === true`) ✅ IMPLEMENTED
**Other/Options** (`other_1_code` through `other_8_code`):
- If field has a value: show as editable text input with label "Option X"
- If field is empty/null: show faintly as "Option X (empty)" — staff can still set it
- These represent event-specific add-ons or membership indicators
**Tickets** (`ticket_1_code` through `ticket_8_code`):
- Same pattern as options above, label "Ticket X"
#### Attendee-Only: Options & Tickets (display only) ✅ IMPLEMENTED
When `!is_staff` and the field has a value: show `[✓] Option X` or `[✓] Ticket X`.
When the field is empty: hide entirely (attendees don't see empty slots).
---
### Section 4: Terms & Conditions Block (all, only when `agree_to_tc` in can_edit_fields) ✅ IMPLEMENTED
Placeholder text for now:
```
By checking this box, I confirm that the information on my badge is correct to the best
of my knowledge. I agree that this badge may be used for identification purposes during
the event and that my attendance may be recorded by exhibitors using the lead scanning
feature if I permit it.
```
Show this before the `agree_to_tc` checkbox. If `agree_to_tc` is not in `can_edit_fields`,
hide the entire block.
---
### Field State Pattern (Svelte 5 runes)
```typescript
// Initialize local editable state from badge object
let local_full_name_override = $state($lq__event_badge_obj?.full_name_override ?? '');
let local_pronouns_override = $state($lq__event_badge_obj?.pronouns_override ?? '');
// ... etc for each editable field
// Detect changes
let has_changes = $derived(
local_full_name_override !== ($lq__event_badge_obj?.full_name_override ?? '')
|| local_pronouns_override !== ($lq__event_badge_obj?.pronouns_override ?? '')
// ... etc
);
// Build changed-fields-only payload
function build_save_payload(): Record<string, any> {
const payload: Record<string, any> = {};
if (local_full_name_override !== ($lq__event_badge_obj?.full_name_override ?? ''))
payload.full_name_override = local_full_name_override || null; // empty string → null
// ... etc
return payload;
}
```
**Important:** Empty string inputs should save as `null` (clears the override, falls back
to base field). Use `value || null` in the payload.
---
## TASK 2: Badge Print Font Size Controls (MEDIUM PRIORITY)
### Where to add
`src/routes/events/[event_id]/(badges)/badges/[badge_id]/print/+page.svelte`
Add a screen-only (`print:hidden`) control panel between the header and the badge render.
This panel lets staff adjust font sizes for the four text-heavy sections before clicking Print.
### Controls needed
```
Font Size Controls (screen only, hidden during print):
[Name] [] [14px] [+]
[Title] [] [12px] [+]
[Affiliations] [] [11px] [+]
[Location] [] [10px] [+]
```
- Start with sensible defaults (match what `ae_comp__badge_obj_view.svelte` currently uses)
- Min/max per field (e.g., 8px24px for name, 7px18px for others)
- Pass the sizes as props into `ae_comp__badge_obj_view`
### Props to add to `ae_comp__badge_obj_view.svelte`
`ae_comp__badge_obj_view.svelte` currently has internal font size logic. It needs to
accept optional override props:
```typescript
// New optional props:
font_size_name?: number; // px
font_size_title?: number; // px
font_size_affiliations?: number; // px
font_size_location?: number; // px
```
When these props are provided, use them instead of the internally computed sizes.
When not provided, fall back to existing auto-sizing behavior.
**IMPORTANT:** Do NOT touch structural dimensions (overall badge width/height, header/footer
sizes, template layout). Only the text content font sizes.
---
## Key Files
| File | Role |
|---|---|
| `[badge_id]/ae_comp__badge_review_form.svelte` | **BUILD THIS** — review form stub |
| `[badge_id]/ae_comp__badge_obj_view.svelte` | Badge render + print button; add font size props |
| `[badge_id]/print/+page.svelte` | Print page; add font size control panel |
| `[badge_id]/review/+page.svelte` | Review page; already wired, passes `can_edit_fields` |
| `src/lib/ae_events/ae_events__event_badge.ts` | API functions: `update_ae_obj__event_badge` |
| `src/lib/ae_events/db_events.ts` | Dexie schema — `properties_to_save` for badge |
| `src/lib/ae_utils/ae_utils.ts` | `ae_util.iso_datetime_formatter()` |
| `documentation/MODULE__AE_Events_Badges.md` | Full module reference |
| `documentation/AE__Permissions_and_Security.md` | Permission flags, edit_mode rules |
| `documentation/GUIDE__AE_API_V3_for_Frontend.md` | V3 API reference |
## Access Level Reference
```typescript
// From $ae_loc store (persisted localStorage)
$ae_loc.trusted_access // true = trusted and above (onsite staff)
$ae_loc.administrator_access // true = administrator and above
$ae_loc.edit_mode // boolean — user preference toggle (NEVER write to this from components)
```
`is_staff` prop on the review form = `$ae_loc.trusted_access`.
`trusted_access` is `true` for Trusted and every level above it (Administrator, Manager, Super)
— no need to OR in `administrator_access` since it's already implied by the cascade.
---
## Patterns to Follow
- **Canonical module reference:** `src/lib/ae_journals/` — most complete, most advanced
- **Svelte 5 runes:** `$state`, `$derived`, `$derived.by()`, `$effect` — no legacy `$:` syntax
- **Icons:** Lucide Svelte only — `import { Save, X, Check, ... } from 'lucide-svelte'`
- **No Font Awesome** (`fas fa-*`) anywhere in the badge module
- **Styling:** Tailwind CSS v4 + Skeleton UI utility classes (`btn`, `preset-tonal-*`, `input`, `card`)
- **Commits:** Atomic — one component per commit; run `npx svelte-check` before every commit
---
## What NOT to Do
- Do NOT touch `@page` CSS or badge template structural dimensions — print layout is out of scope
- Do NOT write to `$ae_loc.edit_mode` from any component
- Do NOT connect `mod_badges_json.edit_permissions` yet — hardcoded field lists are intentional for now
- Do NOT implement the email API — `send_review_email()` placeholder stays as `alert()`
- Do NOT add `person_passcode` DB field — out of scope for this sprint
---
## Testing
Run existing badge tests after any changes:
```bash
npm run test:unit
npx playwright test tests/events/badges/
```
Baseline: all badge tests passing as of 2026-02-26 (`f5e98b8c`).
Add `data-testid` attributes to key interactive elements:
- `badge-review-save-btn`
- `badge-review-cancel-btn`
- `badge-review-full-name-input`
- `badge-review-agree-to-tc-checkbox`