Closes a gap where $ae_loc could be reset externally (sign-out) while
$idaa_loc retained novi_verified within TTL, causing Case 2 to return
early and skip the IDB purge even though the render gate shows Access Denied.
Now Case 2 only preserves the session when $ae_loc also reflects active auth;
inconsistent state falls through to Case 1 (purge).
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
SvelteKit load functions fire during link prefetch before Novi auth completes;
`if (browser)` guards do not prevent this. Moving all IDAA data fetching into
$effect hooks gated on `novi_verified || trusted_access` closes the IDB
pre-population race across archives, bb/[post_id], and recovery_meetings/[event_id].
Also documents the Auth-Before-Cache rule and per-route status in
AE__Permissions_and_Security.md.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
+layout.ts was firing on SvelteKit link prefetch, writing events to IDB
before Novi auth ran. Stripped to thin shell; the existing search $effect
in +page.svelte already handles SWR load+revalidation — just needed an
auth gate (novi_verified || trusted_access) at the top.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Case 1 purge in the layout was firing for manager/trusted users (no UUID),
causing a loop: db_events.event cleared → liveQuery updates → refetch →
store write → Effect 2 re-runs → clear again.
BB $effect was also blocking managers since novi_verified is always false
for non-Novi auth paths.
Both now check trusted_access before gating/purging.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
+page.ts runs before layout effects and fires on SvelteKit link prefetch,
causing private IDAA posts to be written to IDB before Novi auth runs.
Moving to $effect gated on novi_verified eliminates the race entirely —
$effect only runs post-mount, after the layout has verified the user.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Without this, +page.ts fires the API call before +layout.svelte
effects run, causing posts to be written to IDB after the purge.
Anonymous users (novi_verified=false) now return early with no fetch.
Cached verified sessions (within TTL) continue to load normally.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Three distinct log messages for each trigger:
- No UUID / no session path
- Novi auth failure (catch block)
- Reset & Retry button
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
The previous purge only fired inside verify_novi_uuid() catch,
which requires a UUID in the URL. Unauthenticated visits without
a UUID (Case 1 in Effect 2) now also clear posts, comments,
archives, and events from IDB.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Extends the IDB purge from the previous commit to include
db_events.event — covers cached IDAA recovery meeting records.
No module overlap in current client deployments.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
When Novi UUID verification fails (or the manual Reset & Retry is
triggered), clear db_posts.post, db_posts.comment, db_archives.archive,
and db_archives.content from IndexedDB. Prevents private IDAA data
from persisting in the browser after a session ends or auth is denied.
db_events.event intentionally excluded — shared with conference modules.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- TODO: mark BGH file-warning and hide-draft items complete; add detailed
Dockerfile env-file simplification task (deferred post-April 21 show);
strip stale completed DevOps entries from the active list
- package.json: remove build:docker:test/prod (never used locally; deploys
go through remote deploy.sh on Linode)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- element_input_files_tbl: only block upload for non-trusted users; trusted_access
users see the same warnings but can still proceed
- element_input_files_tbl: improved warning message wording for .ppt and .doc
- element_manage_event_file_li: minor tweaks
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Set file_list_status to 'blocked_legacy' when any selected file is .ppt or .doc,
disabling the Upload button until the file is removed
- Show a red banner at the top when upload is blocked
- Add a per-file warning message row in the file table for all legacy/untrusted
extensions (previously computed but never rendered — only a pink cell highlight)
- Red styling for blocking extensions (.ppt/.doc), yellow for warn-only
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- launcher_file_cont: add 'admin' file_purpose to hide_draft filter (alongside outline/draft)
- element_manage_event_file_li: remove event_file_id from data_kv passed to update_ae_obj;
it was being sent in the PATCH body causing 'Unknown column event_file_id in SET' (400)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Add +page.ts to trigger load_ae_obj_li__event_location on page load (locations
were never fetched without a manual trigger)
- Fix ae_comp__event_session_obj_li_wrapper: query used event_location_id_random
(deprecated index) instead of event_location_id, causing empty session lists
under each location
- Wire hide__session_poc to pres_mgmt_loc.current.show__session_li_poc_field so
the Options toggle actually takes effect in the per-location session list
- Also set hide__session_location=true since location is implicit in that context
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
On a fresh Electron install the events_loc persisted store has no
app_mode value set, causing the native file launch path to fall through
to a browser save dialog. Auto-initialise app_mode='native' in the
launcher layout when is_native is detected so all three modes (default,
onsite, native) continue to work correctly.
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>