Files
OSIT-AE-App-Svelte/documentation/PROJECT__AE_Events_Badges_Review_Print.md
Scott Idem c4e85b1fe3 feat(badges): print/review pages, 4-button list, Lucide icons, permissions doc
Badge search results list (ae_comp__badge_obj_li):
- 4 action buttons per row: Print, Review (nav link), Copy Link (clipboard), Email Link
- Visibility rules: unprinted-only for non-edit mode; all non-hidden for trusted+edit
- Plain name display (User/EyeOff icon) — name is no longer a print link
- Obscured email for non-trusted users
- Debug row (ID, CR, UP, PC, FP, LP) in edit mode
- All icons converted to Lucide (Font Awesome removed)

Badge print page (/print):
- 3 header action buttons: Print Now, Review (nav), Email Link
- Removed old [badge_id]/+page.svelte placeholder (moved to trash)
- Added is_trusted, is_edit_mode, print state derived vars
- "Already printed Nx — last [timestamp]" warning inline with name
- Removed unused imports (browser, onMount, events_slct)

Badge review page (/review):
- 3 header action buttons: Print (nav), Copy Link (clipboard), Email Link
- Added events_loc for email placeholder + title event name
- Added is_edit_mode, print_count, is_printed, copy_status
- FA icons replaced with Lucide (ShieldCheck, UserCheck, User)
- Title now includes event name (was missing)

Infrastructure:
- print/+page.ts and review/+page.ts added (non-blocking badge loaders)
- ae_comp__badge_review_form.svelte stub created (fields pending)
- Fixed: components no longer write to $ae_loc.edit_mode (critical bug)

Docs:
- NEW: AE__Permissions_and_Security.md — full permissions hierarchy reference
- NEW: PROJECT__AE_Events_Badges_Review_Print.md — agent task brief for review form + print font controls
- UPDATED: MODULE__AE_Events_Badges.md rev 5 — field permissions spec, header buttons, still-needed list by priority

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-27 15:12:22 -05:00

340 lines
13 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
**Branch:** `ae_app_3x_llm`
**Priority:** HIGH — first live event is Axonius, NYC, mid-April 2026
**Owner:** Scott Idem / One Sky IT
---
## Context
The Events Badges module is mostly complete for navigation and search. Two key pieces of
functional UI remain unbuilt and are needed before the first show:
1. **Badge Review Form**`ae_comp__badge_review_form.svelte` is currently a stub. It
needs actual field rendering, edit inputs gated by access level, save/cancel API calls,
and display-only sections (QR code, print status, option/ticket checkmarks).
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)
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]
```
Use `$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)
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.
**Check the legacy AE Badge version for existing QR generation code.** Look in:
- `src/lib/ae_events/` for any QR-related utilities
- `ae_comp__badge_obj_view.svelte` — the badge render component almost certainly generates
a QR code already for the printed badge. Reuse that logic/component if possible.
The QR code value should encode the badge ID or a URL that resolves to the badge.
---
### Section 3: Editable Fields
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.
#### 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` |
| `professional_title_override` | text input | Fallback display: `professional_title` |
| `affiliations_override` | textarea | Fallback display: `affiliations` |
| `phone_override` | text input (tel) | Fallback display: `phone` |
| `location_override` | text input | Fallback display: `location` |
| `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`)
| 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 |
| `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`)
**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)
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)
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 = `administrator_access || trusted_access`.
---
## 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`