Adds a compact print info row below each printed badge for trusted users
(not in edit mode — debug row already covers that):
Printed 2× · First: June 9 9:14 AM · Last: June 9 11:32 AM
Gives staff quick at-a-glance confirmation that a badge was printed and when,
without needing to enter edit mode.
Also fixes a logic bug in the attendee "Checked in" card: the "last print"
line was comparing == (same datetime) instead of !== (different datetime),
so it only appeared when first = last (single print) — backwards. Fixed to
show only when multiple prints have occurred.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Pressing the Search button or Enter with fewer than the required min chars
now focuses the input field instead of silently doing nothing, so the user
gets clear feedback about where to type.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Was: "Enter your name above to find your badge."
Now: "Search by name, email, or organization above to find your badge."
Mirrors the actual fast-path fields (given/family name, email, default_qry_str)
and the search input placeholder text.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
The +page.svelte {#if search_status=loading && ids.length===0} gate is now
redundant — ae_comp__badge_obj_li handles all states internally (Searching...,
Enter your name, No results). Removing it also fixes a UX regression where
trusted-user IDB fallback results would be hidden by the gate whenever a new
API search fired. Component is now always mounted and manages its own state.
Also replaces deprecated BarChart2 with ChartColumnBig (lucide rename).
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
transition:fade on the initial spinner caused Svelte to keep the outgoing
element in the DOM for the full 200ms outro while the incoming badge list
was already rendered — both were live simultaneously, colliding on height
and producing the visible bounce. Initial cold-start load doesn't need a
transition; instant swap is fine.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Scrollbar shift:
- Add [scrollbar-gutter:stable] to #ae_main_content in events layout so a
scrollbar appearing on first results load no longer reflows the centered
search form (was shifting ~8px left on Linux)
Empty/loading state consistency:
- Move search_status prop into ae_comp__badge_obj_li so it can swap its own
empty state: spinner + "Searching..." while a search is in progress,
UserSearch icon + prompt text otherwise
- Unify p-16 / size-3em / mb-2 / text-xl across all three states (initial
load, searching, no results) so height never jumps between transitions
- Pass search_status from +page.svelte to the component
Transitions:
- transition:fade on initial-load spinner div
- transition:slide on Create/Upload badge button row (appears with edit mode)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Adds fade/slide transitions throughout the search form: form mount/unmount,
filter row, QR scan button, QR scanner panel, Show Hidden, Remote First labels
- Min-chars hint switches from class:invisible to opacity-0/opacity-50 +
transition-opacity so it fades instead of snapping
- Clear button switches from class:hidden to opacity-0 + pointer-events-none
+ transition-all so it fades without causing layout shifts
- "Start Here" button gets transition-opacity for smooth dim on first keystroke
- Replaces FileSearch with UserSearch icon in the empty state
- Adds w-full to empty state div to prevent subtle page-width shift between
no-results and results states
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Public (attendee) kiosk: unprinted badges link to /review; printed
badges show green "Checked in · Nx · First/Last" row (non-clickable)
- Public attendees no longer see Print button (staff-only action)
- Printed badges sort to end of list for public non-trusted users
- Manager access: Print (reprint) and Email Link buttons always visible
without requiring Edit Mode; main row behavior unchanged
- Empty state wording: context-aware — "Enter your name above to find
your badge" for public users with no query vs "No badges found" after
an actual search
- Docs: Epson C3500 fanfold section filled in (was empty placeholder);
style_href/duplex implementation status corrected in badge templates
doc; Axonius C3500 layout TODO marked complete
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Add 'Cvent Splash XLSX (registrant export)' upload mode hitting the new
/event/{id}/badge/import/splash_xlsx endpoint
- Admin controls: begin_at/end_at/return_detail (shared with Zoom mode) +
import_status_filter (splash only, default 'Attending')
- File picker accept attribute switches between .csv and .xlsx per mode
- Set timeout=300000 and retry_count=1 on both server-side upload paths to
prevent false 'no response' failures on slow imports; upsert-by-email on
the backend makes retries safe but a single attempt is sufficient
- Replace misleading 0/0 progress bar with an indeterminate progress bar
during server-side processing; real counter kept for client-side CSV mode
- Show 'Processing on server…' message once upload completes and server work begins
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Two layout fixes for the badge_4x6_fanfold layout (no background_image_path):
1. badge_header max-height: 1.5in — the Axonius logo (624×232px) renders at
~1.49in tall at full badge width. The inherited max-h-[1.00in] was cropping
the bottom half of the image.
2. badge_body margin-top: 0 — overrides the component-level mt-54 (≈2.25in).
That margin was needed for the PVC layout where a full-bleed background image
covered the badge and body text needed to start in the image's designated zone.
For fanfold badges with a standalone header_path, mt-54 created a 2.25in blank
gap between the header and the attendee name.
Also updates default fit_heights for badge_4x6_fanfold to match the 4.0in body
height (was sized for 4.5in before the header zone was properly accounted for).
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Introduces badge_layout_epson_4x6_fanfold.css (layout code badge_4x6_fanfold)
for the Axonius Adapt 2026 June show. Wires @page size to 4in×6in in the print
page and cleans up the stale 4in×12in default. Imports new CSS in badge component.
Measured stock: 4in × 6in portrait with 5/8in lanyard hole 1/4in from top.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Extract session search form into ae_comp__pres_mgmt_session_search.svelte
(parallels ae_comp__badge_search.svelte); removes ~145 lines from +page.svelte
- Add time window filter: Clock icon toggle button reveals compact before/after
selects; trusted users get 3d/7d options; active state highlighted in amber
- Add passes_hide_filter to IDB fast path to mirror API qry_hidden logic and
eliminate the hidden-session blink on revalidation
- Add passes_time_window applied to both IDB fast path and API results
- Add time window state fields to PresMgmtLocState + pres_mgmt_loc_defaults
- Add contextual warning in "No sessions found" when time filter is active
- badges: hide "Start Here" button for trusted_access users; tweak button shade
- badges: scope placeholder CSS fix to input only (not textarea)
- Add MODULE__AE_Events_PressMgmt_Launcher.md doc
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
The old guard locked on badge ID after the first liveQuery tick. If
Dexie had a cached badge without cfg_json.font_sizes, the guard fired
with no sizes to apply, then blocked the SWR background refresh that
delivered the real saved sizes. Result: font sizes appeared unsaved on
any browser that had visited the badge before sizes were set.
Fix: track the cfg_json string last applied (_font_sizes_applied_cfg)
instead of just the badge ID. Re-applies whenever cfg_json changes on
a background refresh, but skips if local sizes have drifted from the
last apply (user is mid-adjustment — auto-save will sync shortly).
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Tailwind v4 renders placeholder text too dark on light backgrounds,
making it indistinguishable from real input values. Same scoped CSS
fix already applied to ae_comp__badge_print_controls.svelte.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
badge_type_code_li was returning [] when the template had no badge_type_list,
causing the Badge Type field to be hidden entirely in the Staff section.
Add same fallback default as create form and search filter.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Was at the bottom after print position controls — now first item in
Staff Adjustments, before Hide/Unhide Badge, where staff expect it.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Was: visible to everyone pre-print, Trusted+Edit for reprints.
Now: Administrator + Edit Mode only (all three locations).
Temporarily restricted for Axonius 2026 — restore broader access after event.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- default_badge_type_code_li now matches ae_comp__badge_search.svelte list
(attendee, sponsor, staff, guest, volunteer, member, nonmember, test)
- badge_type_code initializes to 'attendee' (In-Person Attendee)
- $effect re-applies preferred default when template badge_type_list loads async,
falling back to first item if 'attendee' isn't in the template's list
- Remove the blank '-- Select Badge Type --' placeholder option
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
btn + preset-filled-* renders transparent on gray/surface backgrounds
(Skeleton v4 CSS variable specificity issue — documented in
GUIDE__AE_UI_Style_Guidelines.md §12).
Replace all three buttons in field_actions (Save, Revert, Cancel) with
direct Tailwind token classes: bg-warning-500, bg-error-500,
bg-success-500, bg-surface-200-800 etc. Save button now visibly renders
in amber (dirty), red + pulse (pending_close), green (saved).
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
When a field accordion has unsaved changes and the user tries to close
(X button, same-header click, or switching to another field), we now set
pending_close = true instead of silently discarding.
- Save button turns bright red + animate-pulse with label "Save first (or × to discard)"
- X button turns red with "Discard changes" tooltip
- Field stays open — no data is lost
- Second close attempt (pending_close already true) actually discards
- Saving normally clears pending_close and closes the accordion
WHY: kiosk attendees at a live event were silently losing typed overrides
(professional title, affiliations, etc.) when switching fields mid-queue.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
toggle_field only changed active_field — it never called cancel_field for the
previously open field. Unsaved typed values stayed in edit_full_name_override etc.,
so reopening a field would show the stale typed value and re-apply it to the badge
preview, even though the user had already moved on.
New logic: capture was_open, always call cancel_field for the current field (resets
edit vars + sets active_field = null), then open the new field if it wasn't the one
being closed. Closing a field by re-clicking its pencil now also discards unsaved state,
consistent with the explicit [X] button behavior.
Also: add global placeholder CSS fix to TODO__Agents.md (scoped workaround already
in ae_comp__badge_print_controls; long-term fix belongs in app.css or theme file).
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Auto-focus: requestAnimationFrame (~16ms) fired before the 185ms accordion
animation ended — input was still 0px/clipped so focus() silently failed.
Changed to setTimeout(210ms) so focus lands after the animation completes.
Placeholder color: placeholders show the current badge value (e.g. 'John Smith')
so without explicit styling they look identical to filled text. Added scoped
CSS rules setting placeholder to gray-400 (light) / gray-500 (dark) so it reads
clearly as a hint rather than existing content.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
update_ae_obj__event_badge returns false on API failure without throwing,
so the old code always treated a failed PATCH as success — badge printed,
count not saved, page navigated away silently.
Now: check the return value explicitly. On failure — still fire window.print()
(physical print must never be blocked) and still navigate back, but show a
visible red error state ('Printed — count NOT saved (see staff)') and hold
for 4s so a kiosk operator can see it before the loop resets.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
MariaDB rejects ISO 8601 with milliseconds ('2026-04-14T20:29:15.784Z').
print_last_datetime and print_first_datetime must be 'YYYY-MM-DDTHH:MM:SS'.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Without this, a public user who navigates to a printed badge's print page
sees a blank controls panel with no explanation. Now shows an amber notice
directing them to event staff. Gated on !can_print && is_printed && !is_trusted
so it never shows to staff.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
The name|layout|v3 info row was always visible. Gate it on
trusted_access && edit_mode — attendees and volunteers should
not see internal template metadata above their badge.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
HTML comments don't suppress Svelte {#if} blocks — the content was rendering
unconditionally. Switch to {#if false} so the blocks are genuinely hidden.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Lead scanning was canceled last-minute; pronouns not on this badge template.
Both blocks left in source with AXONIUS 2026 markers for easy restoration.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Hide/Unhide and print count edit belong on the per-badge page (print controls
staff section), not the search list — the list was getting too crowded.
- ae_comp__badge_obj_li: removed hide toggle, print count input, and the
ae_api/events_func imports that were only there to support them
- ae_comp__badge_print_controls: added Hide Badge button (Trusted, top of staff
section) and Print Count editor (Admin+, below hide); both reuse the existing
save_field/field_save_status pattern for consistent spinner/done/error feedback
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Lower access levels (authenticated, public) can have edit_mode active.
Show Hidden must be trusted+ only — split it out of the generic edit_mode block.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Hide/Unhide toggle button (Trusted + Edit Mode) on each badge row in the list; badge disappears immediately when hidden unless Show Hidden is active
- Print count inline editor in debug row (Admin + Edit Mode); updates count only, no timestamp changes
- "Show Hidden" checkbox in search filters (Trusted + Edit Mode); wires through IDB fast-path, API hidden param, and visible_badge_obj_li filter
- show_hidden requires edit_mode to be active — reverts to hiding hidden badges when edit mode is off
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Edit mode should not override the filter state — staff set their
filters and turn off edit mode all the time. The real split is
trusted staff vs kiosk/public, not edit mode on/off.
Trusted and above: qry_printed_status is the sole control over
printed badge visibility, regardless of edit mode state.
Public (kiosk) / authenticated / anonymous: always unprinted only.
Badge kiosks run at public_access and should never expose a list
of already-printed badges to attendees.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Two bugs:
1. visible_badge_obj_li gated on trusted+edit_mode, but the filter
dropdown is also accessible to manager+ without edit_mode. Changed
gate to (trusted+edit) || manager_access to match the filter's own
access condition.
2. not_printed API query used print_count eq 0, which does not match
NULL in SQL. Unprinted badges have print_count = NULL, so the API
was returning 0 results and overwriting the correct IDB fast-path
results. Removed the not_printed condition from the API query —
IDB fast path (print_count ?? 0) < 1 and visible_badge_obj_li
both handle NULL correctly and are the authoritative filter for
that case.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Previously edit_mode was a blunt override: trusted+edit showed all
badges regardless of the filter setting. This meant the Printed Status
dropdown had no effect on what was visible in the list.
Now trusted+edit mode respects qry_printed_status as the single source
of truth: 'all' shows everything, 'printed' shows only printed, and
'not_printed' shows only unprinted. The filter dropdown is only
accessible to trusted+edit users so it is safe to use as the control.
Kiosk/attendee behavior (trusted no edit, public, anonymous) unchanged:
only unprinted badges are shown regardless of filter state.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Sort changes without a text query were falling through to the fallback
liveQuery (50 badges sorted by given_name), ignoring the selected sort
entirely. Added params.sort to has_active_filters so any explicit sort
selection triggers the full search path.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
All four sort options in the dropdown were falling through to the
default (given_name ASC) because their cases were missing from both
the IDB fast-path sort and the API order_by_li mapping:
- Affiliations ASC: IDB sorts by affiliations_override → affiliations;
API sorts by affiliations column
- Badge Type ASC: badge_type_code ASC
- First Printed DESC: print_first_datetime DESC
- Last Printed DESC: print_last_datetime DESC
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Update badge type codes for Axonius 2026 (replaces ISHLT 2024 list).
Added TODO to drive this from event templates in the future.
Fix printed status and badge type filters not working without a text
query. The min_chars guard was blocking all filter-only searches,
causing "Printed" and "Not Printed" to always return empty results.
Now bypasses min_chars when any non-default filter is active (printed
status, type code, or affiliations), since selecting a filter is
explicit user intent regardless of the text query.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Badge print kiosks authenticate at the public_access level (site-wide
passcode). Previously the print gate was trusted_access, meaning kiosk
operators had to sign in at the trusted level just to print.
Changed in both the list view and the badge detail controls panel:
- First print: public_access and above (kiosk use case)
- Reprint: still requires trusted_access + edit_mode
ae_comp__badge_obj_li.svelte: added is_public derived; updated
can_print and the print button #if condition.
ae_comp__badge_print_controls.svelte: added is_public derived; updated
can_print comment and logic.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Font size changes now persist automatically (600ms debounce) without
requiring the user to find and click Lock Sizes in the collapsed Staff
section. reset_font_sizes_to_auto continues to handle its own save.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Per V3 convention, {obj_type}_id IS the random string — send
event_badge_template_id (not _random). The backend not saving it is
a backend bug, not a frontend concern.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
The IDB stores the random string in event_badge_template_id (overwritten
by _process_generic_props). Sending this as event_badge_template_id
passed a string to an int(11) FK column — backend silently ignored it.
Using event_badge_template_id_random lets the V3 CRUD handler resolve
it to the correct integer FK.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Single-template events auto-select silently but gave no visual
confirmation. Added a read-only display of the template name so staff
can verify the correct template is in use before submitting.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>