Commit Graph

673 Commits

Author SHA1 Message Date
Scott Idem
b3ce65f7f6 docs: update TODO — mark error handling, PWA SW, launch_profiles editor as done; annotate slide control + kill_processes status
- Error handling + fallback: confirmed done (launcher_file_cont.svelte)
- PWA service worker chrome-extension guard: confirmed done (service-worker.js)
- Launcher config UI / launch_profiles editor: confirmed done (launcher_cfg_launch_timing.svelte)
- Slide control scripts: annotated partial state — Svelte buttons wired, Electron scripts still hardcoded, deferred post June 10
- kill_processes: documented as not started on Svelte side; noted Device tab length concern

Also bundle two prior-session Launcher fixes:
- VLC post_delay_ms: 2000 → 1500ms
- launcher_cfg_launch_timing: add min-w-22 to built-in/current delay display
2026-05-14 12:24:36 -04:00
Scott Idem
054775b0f8 feat(launcher): skip wallpaper gsettings on Linux, show dev popup instead
Running gsettings on the dev workstation resets monitors on every test cycle.
Linux Electron handler now returns linux_test_mode:true with would_run details
instead of applying. Svelte cfg component shows a debug popup (mirrors Native
Test Mode style). Background sync logs to console and leaves applied-URL unset.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-13 18:54:56 -04:00
Scott Idem
af28fba263 feat(launcher): restore macOS default wallpaper + external-only apply fix
- electron_relay: add restore_macos_default_wallpaper() — uses run_osascript
  to find first .heic in /System/Library/Desktop Pictures/ (version-agnostic)
- wallpaper cfg: Restore macOS Default button (native or edit_mode); clears
  applied-URL tracking so next configured URL re-applies correctly
- wallpaper cfg: fix Apply Now / Save & Apply enabled when only external URL
  is filled; display target becomes 'external' to leave built-in unchanged

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-13 18:37:15 -04:00
Scott Idem
17e522f826 feat(launcher): wallpaper auto-apply from device config
Stores wallpaper URL(s) in event_device.other_json.launcher.wallpaper and
auto-applies on all native Launcher instances within one heartbeat cycle (~60s),
eliminating manual per-Mac setup at events.

- electron_relay: typed set_wallpaper wrapper (url, url_external, display, auth headers)
- launcher_defaults: wallpaper_applied_url tracking + section_state__wallpaper
- launcher_cfg_wallpaper: new config section — save to device config + apply now
- launcher_cfg: add wallpaper section to device tab
- launcher_background_sync: auto-apply if config URL changed since last apply;
  external-only config targets only the secondary display, leaving built-in unchanged

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-13 18:31:39 -04:00
Scott Idem
e5883cd53c fix(launcher): skip url files in sync 2026-05-13 13:36:57 -04:00
Scott Idem
8d5c5e39c9 fix(launcher): guard health device metadata 2026-05-13 12:50:25 -04:00
Scott Idem
39749c608a fix(launcher): use per-profile timing overrides 2026-05-13 12:48:43 -04:00
Scott Idem
4923099cfb feat(launcher): add device launch timing override 2026-05-13 12:34:36 -04:00
Scott Idem
49c6a2351e refactor(launcher): use launch_profiles only\n\nRemove the temporary launch_scripts compatibility alias and keep the launcher
configuration surface focused on launch_profiles everywhere in the Svelte app and
docs.
2026-05-13 10:30:10 -04:00
Scott Idem
b697126495 refactor(launcher): prefer launch_profiles naming\n\nRename the public launcher override concept to launch_profiles across the task list\nand docs, while keeping launch_scripts as a compatibility alias for older device
records. Update the Svelte resolver to read both keys so per-device tweaks remain
backward compatible during the transition.
2026-05-13 10:26:01 -04:00
Scott Idem
8dd22912c3 fix(launcher): keep native open state off download spinner\n\nAdd an opt-out on the shared hosted-file button so promise-returning native opens\ncan use Launcher status text without being treated like active downloads. Apply it\nto the Electron launcher open path so cache hits no longer show 'Downloading...'\nwhen the row status already reports the correct cache/open state. 2026-05-12 13:49:55 -04:00
Scott Idem
f8fe4ac5a2 fix(launcher): button state hygiene across all 3 modes
Fix 1 (stale error_detail):
  open_file_error_detail is now cleared at the start of every mode's
  branch (native, onsite, default) alongside open_file_clicked=true.
  Previously it was cleared mid-way through the native flow (Step 1),
  so a stale error from a previous failed run could flash briefly.

Fix 2 (stuck 'Opening...' during post_script sleep):
  After the Step 4 open call returns (run_cmd or open_local_file_v2),
  update the status message immediately to 'App opened — running setup...'
  so the button doesn't appear frozen for the full post_delay_ms wait.

Fix 3 (safety valve for hanging native calls):
  A 60s setTimeout is registered at the start of the native branch.
  If any native IPC call hangs indefinitely and the existing per-path
  reset timeouts never fire, this force-resets open_file_clicked after
  60s. All normal paths complete within ~8s so this only triggers on
  true hangs. ERR_NETWORK_CHANGED cannot re-enter the download path
  because the open_file_clicked guard blocks re-entry.
2026-05-12 13:37:11 -04:00
Scott Idem
2c1e9d294e fix(launcher): swallow orphaned IPC reply on open_local_file_v2
shell.openPath always resolves on the Electron side, but if the Svelte
renderer navigates before the IPC reply arrives, the promise rejects
with 'reply was never sent'. The file is already open at that point.

Catch the rejection and treat it as success on both call sites (Step 4
primary path and Step 7 fallback). This eliminates the unhandled
promise error without masking real failures.
2026-05-12 13:18:53 -04:00
Scott Idem
768fdbfb21 feat(launcher): URL-type event_file support + displayplacer Electron task
URL files: event_file.filename = 'https://...' is now a first-class
file type in the launcher.

- is_url derived rune detects https/http filename prefix
- URL branch in handle_open_file() runs before cache/native branches
  (no download, no temp copy, no hash)
- Offline guard: warns and blocks click if navigator.onLine is false;
  online/offline listeners registered only for URL rows (no-op on files)
- Native mode: opens via native.open_external({ url, app: 'chrome' })
  with silent fallback to default browser
- Browser mode: window.open() with noopener
- display_mode default: 'mirror' (URLs typically just need the screen
  mirrored, not extended presenter view)
- Button badge shows Link2 icon + WifiOff warning when offline
- Button text uses event_file.title as label (falls back to URL)
- Test mode popup: skips Steps 1-2 (N/A), shows open_external call

DEFAULT_LAUNCH_PROFILES: add 'url' key (display_mode: 'mirror')

Electron TODO: added set_display_layout / displayplacer per-device
config task to aether_app_native_electron/documentation/TODO_AGENTS.md
with full contract details and resources
2026-05-12 13:14:58 -04:00
Scott Idem
e74dc7a388 fix(launcher): skip post_delay sleep when no post_script
The sleep step was running unconditionally, meaning files that open
with the 'default' catch-all profile (zip, unknown ext, etc.) would
wait 2 seconds for no reason — there's no AppleScript to prepare for.

Gate the sleep inside the post_script check so it only delays when
there's actually automation to run after app launch.

Also update the test mode popup to show '0ms — skipped (no post_script)'
and display post_delay_ms as '(default: 2000ms)' when unset.
2026-05-12 12:49:50 -04:00
Scott Idem
6c73812187 fix(launcher): improve test mode popup contrast for light/dark mode
- Warning text in cfg_app_modes: replace text-yellow-400/70 with
  preset-tonal-warning badge (theme-aware, readable in both modes).
- Popup dialog: bg-surface-900 → bg-surface-50/95 dark:bg-surface-900/95
  so the card adapts to light/dark instead of being locked dark.
- Code blocks: bg-black/30 → bg-surface-500/10 (neutral opacity).
- Header title: text-warning-400 → text-warning-600 dark:text-warning-400.
- Semantic text colors: all -300 variants → 500-level equivalents:
  text-green-300/400 → text-success-600 dark:text-success-400
  text-primary-300 → text-primary-500
  text-yellow-300/400 → text-warning-500
  text-purple-300 → text-purple-500
2026-05-12 12:40:39 -04:00
Scott Idem
ff824ebbe5 feat(launcher): add Native Test Mode for profile/command preview
Enables testing the LaunchProfile system from any device (no Mac/Electron
needed). When active, the Open button simulates the full native flow and
shows a debug popup with everything that WOULD be sent to Electron.

- ae_events_stores__launcher_defaults.ts: add native_test_mode boolean
  (persisted, default false) to LauncherLocState and launcher_loc_defaults.

- launcher_cfg_app_modes.svelte: add Native Test Mode checkbox toggle in
  the Advanced Toggles (Edit Mode Only) section with active-state warning.

- launcher_file_cont.svelte:
  - Add test_mode_popup_open/test_mode_popup_data state vars.
  - Add branch 0 in handle_open_file(): when native_test_mode + app_mode=native,
    skip all Electron calls; resolve the real LaunchProfile, build a data
    snapshot, open the debug popup.
  - Debug popup shows: file info, simulated temp path, cache/copy pass,
    resolved LaunchProfile fields, set_display_layout call, open command
    (run_cmd or open_local_file_v2 fallback), sleep delay, post-script
    (AppleScript or shell: prefix). Click backdrop or Close to dismiss.
2026-05-12 12:33:24 -04:00
Scott Idem
422c9c341c feat(launcher): implement LaunchProfile system — MasterKey replacement
- Add ae_launcher__default_launch_profiles.ts with LaunchProfile interface,
  DEFAULT_LAUNCH_PROFILES constant, and resolve_launch_profile() helper.
  Covers pptx/ppt/key/odp/pdf, all VLC media formats, Windows/Parallels
  variants (pptxwin/pptwin/odpwin/pdfwin), and a catch-all 'default'.

- Replace get_launch_script_template() with get_launch_profile() in
  launcher_file_cont.svelte. Override priority: device API config >
  local persistent config > built-in defaults > 'default' catch-all.

- Rewrite handle_open_file() native branch with 9-step profile-driven flow:
  copy_from_cache_to_temp → resolve profile → set_display_layout (silent fail)
  → open (run_cmd or OS default) → sleep(post_delay_ms) → run post_script
  → fallback to OS default on open failure → surface status/error detail.

- Add open_file_error_detail state var; show error pre block in status
  alert for native error state, show fallback note for fallback state.

- Add display override toggle button in event_file_meta (visible when
  trusted_access + is_native): cycles null → extend → mirror → null,
  PATCHes event_file.cfg_json.display_override via V3 CRUD.
2026-05-12 12:17:43 -04:00
Scott Idem
c5c5292715 feat(launcher): configurable launch scripts + composable native primitives
- electron_relay: type launch_from_cache with script_template param;
  add copy_from_cache_to_temp export; add JSDoc for run_osascript hardening
- launcher_file_cont: add get_launch_script_template() helper reading from
  device-level (event_device.data_json.launch_scripts) and event-level
  (events_loc.launcher.launch_scripts) config; wire into handle_open_file()
- PROJECT__AE_Events_Launcher_Native_integration.md: add Section 8
  (Configurable Launch Scripts); update IPC reference for new/changed handlers
- MODULE__AE_Events_PressMgmt_Launcher.md: add configurable launch behavior
  note to Native Mode Safe Handover section
2026-05-11 13:48:54 -04:00
Scott Idem
611b1e6b51 Remove _random ID references and fix hosted file download ID
- Fix download button to use hosted_file_id instead of id (which resolved
  to event_file_id via _process_generic_props, hitting the wrong endpoint)
- Fix Dexie file table query in event_file_obj_tbl_wrapper to use _id
  fields (the indexed ones) instead of _id_random variants
- Remove _random fields from properties_to_save in event, event_session
- Drop _id_random fallbacks from launcher device ID resolution and
  background sync heartbeat
- Clean up dead comments and old FA anchor in post edit component
- Update TODO__Agents.md: BGH section removed, CMSC/Axonius shows added,
  download button fix marked complete

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-11 12:26:21 -04:00
Scott Idem
d7b4d8c37c Hide drop zone container during upload to fix empty border flash
The outer bordered div was always in the DOM; only the label and input
inside were hidden during upload, leaving a visible empty dashed box.
Apply the same hidden guard to the container div.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-07 17:53:14 -04:00
Scott Idem
d9f704fe25 Enable upload progress tracking in both file upload components
Pass track_progress: true to post_object so the XHR path fires live
percent_completed events, making the upload % display functional.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-07 17:46:42 -04:00
Scott Idem
09f1a6ee57 Fix .ttf accept typo and remove $bindable from log_lvl in event file upload
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-07 17:33:15 -04:00
Scott Idem
6507fb82c0 Bug fix for showing and hiding location filter part.
Co-authored-by: Copilot <copilot@github.com>
2026-04-27 14:13:06 -04:00
Scott Idem
d692d7cfde Minor code clean up, style improvements, and bug fixes.
Co-authored-by: Copilot <copilot@github.com>
2026-04-27 13:53:12 -04:00
Scott Idem
4ae9ecc381 fix(files): show legacy file warning banner for trusted-access users
Trusted-access users (Pres Mgmt admins) were getting file_list_status='ready'
when selecting .ppt/.doc/.xls files, so the prominent warning banner never
rendered — only the small per-row warning in the file table was visible.

- element_input_files_tbl: introduce 'warn_legacy' status for trusted users;
  show a yellow warning banner (vs red blocked banner for non-trusted users)
- ae_comp__event_files_upload: change button disabled check from != 'ready'
  to === 'blocked_legacy' so 'warn_legacy' does not accidentally block upload

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-22 23:24:55 -04:00
Scott Idem
e15a26f6c6 fix(launcher): load location files into Dexie on location select/refresh
refresh_location_config() was missing inc_file_li:true, so location-level
files were never fetched from the API and lq__location_event_file_obj_li
always returned empty from Dexie. Files only appeared when Pres Mgmt had
previously loaded them on the same device.

Also added a reactive $effect so files load immediately when the operator
switches rooms, rather than waiting up to 60s for the next timer tick.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-22 09:22:11 -04:00
Scott Idem
18cbe256de fix(pres_mgmt): increase file upload timeout to 20 min, guard null result
- Set post_object timeout to 1200000ms (20 min) for hosted file uploads;
  the 90s default was killing large presentation file uploads
- Guard result[0] access in .then() to prevent crash when upload
  times out or is aborted (TypeError: can't access property "hosted_file_id")

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-22 00:36:14 -04:00
Scott Idem
6c6fccdfb4 Tweaking the colors and timing for the Session Menu in the Launcher 2026-04-20 13:33:54 -04:00
Scott Idem
ef5188aa6d refactor(launcher): remove duplicate session load from menu_session_list
On session click/hover, the menu was calling load_ae_obj_id__event_session
directly AND then navigating via goto(), which re-runs +page.ts and calls
it again. Both fired concurrently on cold cache, causing two identical API
requests for the same session.

Fix: remove the direct load call entirely. The goto() promise is assigned
to ae_promises.slct__event_session_id so the existing #await spinner still
works — it now reflects actual navigation + page.ts load time rather than
a redundant parallel fetch. Remove events_func and ae_api imports (unused).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-20 13:01:16 -04:00
Scott Idem
c4fdc8efa4 fix(launcher): hidden sessions collapse space, sort by datetime, rename internal-file flag
- menu_session_list: move class:hidden to <li> so fixed-height rows fully collapse
- launcher/+layout.svelte: sort sessions by start_datetime (ascending) instead of name
- Rename hide_content__draft_files → show_content__internal_files (default false);
  remove redundant show_content__draft_files; rename prop hide_draft →
  show_internal_purpose_files in launcher_file_cont; update all 7 call sites and
  the menu_launcher_controls toggle. Now hides admin/draft/outline purpose files
  by default with consistent naming across the flag, prop, and toggle.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-20 12:49:39 -04:00
Scott Idem
1ad3d2030d fix(launcher/files): hide admin-purpose files and fix event_file_id in PATCH body
- 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>
2026-04-19 13:02:56 -04:00
Scott Idem
721facf7ba fix(locations): auto-load locations on page open; fix session query and POC visibility
- 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>
2026-04-19 11:54:22 -04:00
Scott Idem
a42b49dd50 fix(launcher): auto-set app_mode to native when running in Electron
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>
2026-04-18 19:51:51 -04:00
Scott Idem
5fcf2e86f1 Making things look nicer 2026-04-16 19:48:09 -04:00
Scott Idem
2595664dd1 feat(pres_mgmt): extract session search component + time window filter
- 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>
2026-04-16 19:01:35 -04:00
Scott Idem
e4265f69af fix(badges): fix stale-Dexie race in font size initialization
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>
2026-04-14 21:51:18 -04:00
Scott Idem
1df17e68bb fix(badges): lighten placeholder text in create form
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>
2026-04-14 20:29:43 -04:00
Scott Idem
deea250a85 fix(badges): add fallback badge_type_code_li when template has no badge_type_list
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>
2026-04-14 18:07:24 -04:00
Scott Idem
4d43fd8a67 fix(badges): move Badge Type field to top of Staff section
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>
2026-04-14 18:03:50 -04:00
Scott Idem
9180384ec1 temp(badges): restrict Copy Link to Administrator + Edit Mode only
Was: Trusted + Edit Mode. Now: Administrator + Edit Mode.
Temporarily restricted for Axonius 2026.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-14 17:46:50 -04:00
Scott Idem
75f755c660 temp(badges): restrict Email Link to Administrator + Edit Mode only
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>
2026-04-14 17:41:00 -04:00
Scott Idem
4780be7a00 fix(badges): sync create form badge types with search filter; default to attendee
- 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>
2026-04-14 17:32:36 -04:00
Scott Idem
5d6cc4ca35 fix(badges): shorten pending_close Save button label to prevent wrapping
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-14 17:28:54 -04:00
Scott Idem
a68b85e1f1 fix(badges): use direct token classes for field_actions buttons
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>
2026-04-14 17:19:03 -04:00
Scott Idem
0199c2e2c9 fix(badges): guard unsaved edits — warn on close, error on second close
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>
2026-04-14 17:07:26 -04:00
Scott Idem
126eb77be2 fix(badges): cancel edit state on field switch, not just on explicit cancel
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>
2026-04-14 16:55:22 -04:00
Scott Idem
7733ef8708 fix(badges): auto-focus input after accordion animation + mute placeholder text
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>
2026-04-14 16:42:58 -04:00
Scott Idem
a81d65ce7e fix(badges): detect and surface PATCH failure in handle_print_badge
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>
2026-04-14 16:36:56 -04:00
Scott Idem
3d81cb5a83 fix(badges): strip milliseconds/Z from datetime before PATCH
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>
2026-04-14 16:32:07 -04:00