diff --git a/documentation/GUIDE__AE_Events_Badges_Onsite.md b/documentation/GUIDE__AE_Events_Badges_Onsite.md index bf03444b..94bab28e 100644 --- a/documentation/GUIDE__AE_Events_Badges_Onsite.md +++ b/documentation/GUIDE__AE_Events_Badges_Onsite.md @@ -157,26 +157,58 @@ This layout hides `.badge_back` in `@media print` — only the front face prints --- -### Epson — Fan-Fold / Label Printer +### Epson ColorWorks C3500 — Fan-Fold Label Printer -**Status:** Not yet tested. Section to be filled in after testing. +**Card stock:** 4" × 6" fan-fold paper label stock +**Layout code:** `badge_4x6_fanfold` +**Status:** Configured. First live use: Axonius Adapt DC — June 9, 2026. -Common Epson models used for fan-fold name badge stock: TM-T88 series, C3500, LX series. -Fan-fold stock is typically 4" × 3" or 4" × 6" paper labels. +The C3500 is a color inkjet label printer — it prints continuous fan-fold paper stock, +not individual cards. Badges are separated along the perforation after printing. + +#### Physical Setup + +- Connect via USB or Ethernet +- Load 4" × 6" fan-fold stock per Epson instructions +- The C3500 is single-sided — only the front face prints. Back section is suppressed in CSS. +- The badge has a lanyard hole punch: 5/8" × 1/8", centered, 1/4" from the top. + Most fan-fold stock for badge use includes a pre-punched lanyard slot — verify stock matches. + +#### Driver + +- Epson ColorWorks C3500 CUPS driver available from epson.com (ColorWorks section) +- On Linux/CUPS: install the provided PPD and add the printer at `http://localhost:631` +- Set default paper size to **4" × 6"** in CUPS +- Print a test page from CUPS before going live + +#### Chrome Print Settings (C3500) + +| Setting | Value | +|---|---| +| Destination | Epson C3500 (CUPS name) | +| Paper size | 4 × 6 in (set in CUPS driver) | +| Margins | **None** | +| Background graphics | On | +| Pages | 1 (single-sided) | #### CSS Layout -Fan-fold badges would use a layout sized to the specific label stock. -A new CSS layout file will need to be created per stock size if not already present. -Naming convention: `badge_layout_epson_[model]_[size].css` +The C3500 uses the `badge_4x6_fanfold` layout. CSS file: +`src/lib/ae_events/badges/css/badge_layout_epson_4x6_fanfold.css` -#### Setup Notes - -*(To be filled in after testing — cover: driver source, CUPS setup, paper size, Chrome settings)* +Created 2026-05-15 for Axonius Adapt DC. Key specs: +- `badge_front` 4" × 6", portrait orientation +- `badge_header` max-height 1.5in +- Lanyard hole: 5/8" × 1/8", centered, 1/4" from top +- `@page { size: 4in 6in; margin: 0; }` set in the print page dynamically +- `.badge_back` suppressed in `@media print` (single-sided) #### Known Behaviors -*(To be filled in after testing)* +- Same Chrome margin rules apply: **Margins → None** prevents URL/date header clipping +- Firefox honors `@page { size: 4in 6in }` for PDF proofing — use it to verify layout +- Fan-fold stock separates along the perforation — no cutting needed, but verify the + perforation lands outside the badge content area --- diff --git a/documentation/GUIDE__AE_Events_Onsite_Runbook.md b/documentation/GUIDE__AE_Events_Onsite_Runbook.md index 71667a60..91ce520a 100644 --- a/documentation/GUIDE__AE_Events_Onsite_Runbook.md +++ b/documentation/GUIDE__AE_Events_Onsite_Runbook.md @@ -18,6 +18,14 @@ server needed. - **Stock:** 3.5" × 5.5" PVC cards. - **Orientation:** Cards face-up, landscape in the hopper. - **Single-Sided:** Only the front face prints; the back section is hidden via CSS. +- **Layout code:** `badge_3.5x5.5_pvc` + +### Printer Reference: Epson ColorWorks C3500 (Fan-Fold) +- **Stock:** 4" × 6" fan-fold paper label stock. +- **Single-Sided:** Only the front face prints; the back section is hidden via CSS. +- **Layout code:** `badge_4x6_fanfold` +- **Lanyard hole:** Pre-punched 5/8" × 1/8" slot at top center — verify stock matches. +- **First live use:** Axonius Adapt DC, June 9, 2026. ### Printing Workflow 1. **Search:** Find the attendee by name or QR scan in the Badges module. diff --git a/documentation/MODULE__AE_Events_Badge_Templates.md b/documentation/MODULE__AE_Events_Badge_Templates.md index 6ffd2fb0..0bb6c1a2 100644 --- a/documentation/MODULE__AE_Events_Badge_Templates.md +++ b/documentation/MODULE__AE_Events_Badge_Templates.md @@ -114,17 +114,19 @@ corresponding `ticket_N_text` on the template provides the HTML rendered on the | `priority`, `sort`, `group` | int/str | Standard AE sort fields | | `notes` | str | Internal notes | -### New Field (pending backend addition) +### Duplex / Single-Sided | Field | Type | Notes | |---|---|---| -| `duplex` | bool | **Planned** — when `false`, back section is hidden from print (`@media print`) | +| `duplex` | bool | When `false`, back section is hidden from print (`@media print`) | The `duplex` field controls whether the back-of-badge section renders during printing. When `false` (single-sided), `badge_back` gets `print:hidden` applied so only the front prints. The back section still displays on screen for configuration reference. -The first event using this system (Axonius, NYC, mid-April 2026) uses single-sided PVC -cards on a Zebra ZC10L — `duplex` will be `false` for that event's templates. +`duplex` is in `properties_to_save` and `show_badge_back` is derived from it in +`ae_comp__badge_obj_view.svelte`. (Verified 2026-03-18) + +Axonius events use `duplex = false` — single-sided printing only. --- @@ -155,7 +157,7 @@ The print page (`print/+page.svelte`) or the badge view should conditionally add ``` -This is not yet implemented — tracked as a pending Phase 1 item. +This is implemented — `style_href` loads via `` in `print/+page.svelte` and is included in `properties_to_save`. (Verified 2026-03-18) ### CSS Scope @@ -218,7 +220,6 @@ The `properties_to_save` array in `ae_events__event_badge_template.ts` controls gets cached locally. Current state — fields **NOT** in properties_to_save that exist in DB and may be needed: -- `style_href` — needed once external CSS is wired via `` - `passcode` — not needed client-side - `footer_title`, `footer_left`, `footer_right` — not needed (legacy) - `header_background`, `footer_background` — not needed (legacy) diff --git a/documentation/TODO__Agents.md b/documentation/TODO__Agents.md index 9b8c907d..ae3d9f1b 100644 --- a/documentation/TODO__Agents.md +++ b/documentation/TODO__Agents.md @@ -15,8 +15,8 @@ ## 🔴 Axonius DC — June 9 (Badge Printing) **Setup/Registration:** June 8 | **Show:** June 9 -- [ ] **[Badges] Epson C3500 fanfold badge layout** — Create/configure a fanfold badge layout - compatible with the Epson C3500 continuous stock format. +- [x] **[Badges] Epson C3500 fanfold badge layout** — `badge_4x6_fanfold` layout CSS created, + wired, and documented. First live use: Axonius Adapt DC, June 9, 2026. (2026-05-15) --- diff --git a/src/routes/events/[event_id]/(badges)/badges/ae_comp__badge_obj_li.svelte b/src/routes/events/[event_id]/(badges)/badges/ae_comp__badge_obj_li.svelte index 644519ad..b64ee8af 100644 --- a/src/routes/events/[event_id]/(badges)/badges/ae_comp__badge_obj_li.svelte +++ b/src/routes/events/[event_id]/(badges)/badges/ae_comp__badge_obj_li.svelte @@ -42,6 +42,7 @@ let copy_status: Record = $state({}); // Access level shortcuts let is_trusted = $derived($ae_loc.trusted_access === true); let is_admin = $derived($ae_loc.administrator_access === true); +let is_manager = $derived($ae_loc.manager_access === true); let is_public = $derived($ae_loc.public_access === true); // public passcode or higher — may print first prints let is_edit_mode = $derived($ae_loc.edit_mode === true); @@ -113,12 +114,24 @@ let visible_badge_obj_li = $derived( if (ps === 'not_printed') return (item.print_count ?? 0) < 1 && hide_ok; return hide_ok; // 'all' } - // Public (kiosk) / authenticated / anonymous: only unprinted, never hidden. - // Badge kiosks run at public_access — attendees should only see their own - // unprinted badge, never a list of already-printed ones. + // Public/Attendee (public_access, not trusted): show all non-hidden. + // Printed badges appear read-only so attendees can see their check-in status. + if (is_public) return !item.hide; + // Below public (anonymous, authenticated): only unprinted, never hidden. return (item.print_count ?? 0) < 1 && !item.hide; }); + // For public (attendee) kiosk: sort unprinted first so attendees find their + // own badge at the top; already-checked-in records fall to the bottom. + if (is_public && !is_trusted && filtered.length > 1) { + filtered.sort((a: any, b: any) => { + const a_p = (a.print_count ?? 0) > 0; + const b_p = (b.print_count ?? 0) > 0; + if (a_p !== b_p) return a_p ? 1 : -1; + return 0; // preserve existing name order within each group + }); + } + if (log_lvl) console.log( `visible_badge_obj_li: Input=${list.length}, Output=${filtered.length}` @@ -154,9 +167,13 @@ let visible_badge_obj_li = $derived( event_badge_obj?.full_name_override ?? event_badge_obj?.full_name ?? `${event_badge_obj?.given_name ?? ''} ${event_badge_obj?.family_name ?? ''}`.trim()} - {@const can_print = (is_public && !is_printed) || (is_trusted && is_edit_mode)} + {@const is_attendee_view = is_public && !is_trusted} + {@const is_attendee_printed = is_attendee_view && is_printed} + {@const can_print = is_trusted && (!is_printed || is_edit_mode)} + {@const row_clickable = can_print || (is_attendee_view && !is_printed)} {@const print_href = `/events/${event_badge_obj?.event_id}/badges/${event_badge_obj?.event_badge_id}/print`} {@const review_href = build_review_url(event_badge_obj)} + {@const row_href = is_trusted ? print_href : review_href}
  • + {:else if is_attendee_printed} + +
    + +
    + {display_name} + + Checked in · {print_count}× + {#if event_badge_obj.print_first_datetime} + · First: {ae_util.iso_datetime_formatter(event_badge_obj.print_first_datetime, 'datetime_iso_12_no_seconds')} + {/if} + {#if event_badge_obj.print_last_datetime} + · Last: {ae_util.iso_datetime_formatter(event_badge_obj.print_last_datetime, 'datetime_iso_12_no_seconds')} + {/if} + +
    +
    {:else}