From 2595664dd170a47834e994fc875b6827aee7204d Mon Sep 17 00:00:00 2001 From: Scott Idem Date: Thu, 16 Apr 2026 19:01:35 -0400 Subject: [PATCH] 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 --- .../MODULE__AE_Events_PressMgmt_Launcher.md | 464 ++++++++++++++++++ .../ae_events_stores__pres_mgmt_defaults.ts | 10 + .../badges/ae_comp__badge_create_form.svelte | 10 +- .../badges/ae_comp__badge_search.svelte | 3 +- .../(pres_mgmt)/pres_mgmt/+page.svelte | 154 ++---- .../ae_comp__pres_mgmt_session_search.svelte | 243 +++++++++ 6 files changed, 775 insertions(+), 109 deletions(-) create mode 100644 documentation/MODULE__AE_Events_PressMgmt_Launcher.md create mode 100644 src/routes/events/[event_id]/(pres_mgmt)/pres_mgmt/ae_comp__pres_mgmt_session_search.svelte diff --git a/documentation/MODULE__AE_Events_PressMgmt_Launcher.md b/documentation/MODULE__AE_Events_PressMgmt_Launcher.md new file mode 100644 index 00000000..7044a163 --- /dev/null +++ b/documentation/MODULE__AE_Events_PressMgmt_Launcher.md @@ -0,0 +1,464 @@ +# Aether Events — Presentation Management & Launcher + +Notes on setup, workflow, configuration, and onsite operation for the Events Presentation +Management module and the companion Launcher (podium display) system. + +--- + +## Overview + +The Presentation Management (Pres Mgmt) module handles the full lifecycle of conference +content: sessions, presentations, presenters, presentation files, and room/location +assignments. The Launcher module provides the podium display interface that runs on each +session room's kiosk machine. + +These two modules are deployed together — Pres Mgmt is the back office, Launcher is the +front-of-house display. Every client show is at least slightly customized. Some clients +have extensive presenter/presentation data; others just have sessions and files. The +platform is flexible enough to handle the full range. + +**Reference clients (current/repeat):** +- **BGH** (Business Group on Health) — most basic setup; session-only, no named Presenters +- **LCI** (Lean Construction Institute) — most complex current setup +- **AAPOR**, **ASCM**, **CMSC** — other active/repeat clients + +**Module paths:** +- Pres Mgmt: `/events/[event_id]/pres_mgmt` +- Launcher: `/events/[event_id]/launcher` + +**Key source directories:** +- `src/routes/events/[event_id]/(pres_mgmt)/` +- `src/routes/events/[event_id]/(launcher)/` +- `src/lib/ae_events/` — data types and API functions for all event objects + +--- + +## Data Model + +### Object Hierarchy + +``` +Event +├── Event File (walk-in/out, hold slides for the whole event) +├── Location (physical room — assigned to Sessions, not the other way around) +├── Track (optional grouping; rarely used — see note below) +└── Session (time block; Location assigned here, but may be unset initially) + ├── Session File (moderator slides, group/hold slides for this session) + └── Presentation (a talk within the session; must belong to exactly one Session) + └── Presenter (belongs to exactly one Presentation) + └── Presenter File (their slides/materials — the common case) +``` + +> **Import note:** When program data is initially imported (sessions, presentations, +> presenters), Locations are often not assigned to Sessions yet — rooms may not be +> finalized or the venue's room list may not be set up in Aether. Location assignment +> typically happens as a separate step once the room list is confirmed. + +### Relationships + +- **Session → Location:** Many-to-one. A Session is assigned to one Location; a Location + hosts many Sessions across the event timeline. Location may be null initially. +- **Presentation → Session:** Many-to-one. A Presentation belongs to exactly one Session. + A Session can have many Presentations (or none, for session-only setups). +- **Presenter → Presentation:** Many-to-one. A Presenter belongs to exactly one Presentation. + Optionally linked to an `event_person_id` for cross-referencing the person record. +- **Event File:** Can be attached at any level — Presenter, Presentation, Session, + Location, or Event. See the File Attachment Levels table. + +### File Attachment Levels + +Files (`event_file`) can be attached at five levels: + +| Level | When Used | Typical Content | +|---|---|---| +| **Presenter** | 99% of the time for individual speakers | Their PowerPoint/PDF/video | +| **Session** | Moderator slides; group/hold content for a specific session | "Session 3 — Group Discussion.pptx" | +| **Location** | Walk-in/out or hold slides for a room across all sessions | Looped PPTX playing between sessions | +| **Event** | Walk-in/out or hold slides used everywhere | Looped PPTX; branding overlay | +| **Presentation** | File attached to the presentation record itself (less common) | Varies | + +### Key Objects + +| Object | Table | Purpose | +|---|---|---| +| Session | `event_session` | Time block; Location and datetime range assigned here | +| Location | `event_location` | Physical room; the Launcher's primary unit of display | +| Presentation | `event_presentation` | A talk within a session; belongs to exactly one Session | +| Presenter | `event_presenter` | Person linked to exactly one Presentation; optionally linked to `event_person` | +| Event Person | `event_person` | Person record within the event context | +| Event File | `event_file` | Uploaded file; attached at Presenter, Presentation, Session, Location, or Event level | +| Event Device | `event_device` | Registered Launcher kiosk (Electron native instance) | +| Event Track | `event_track` | Optional content grouping (see note below) | + +### Event Tracks + +The API supports Event Tracks — an optional grouping layer above Sessions. Used twice +historically; could have been omitted both times. Tracks may become genuinely useful for +larger events running many parallel Locations where thematic grouping helps navigation. +Not in active use currently and not wired into the standard Pres Mgmt UI workflow. + +### Session → Location + +The Launcher's primary display unit is the Location. It shows the active Session for that +Location based on `datetime_start` / `datetime_end` or manual selection. A Location hosts +many Sessions over the event's run; typically only one is active at a time. + +--- + +## Client Setup Variation + +There are no rigid "modes" — events are configured with as much or as little structure +as needed. The platform handles the full range: + +**Minimal setup (BGH):** +Sessions have room and time info. No Presentations or Presenters defined. +Staff upload files directly at the session or location level onsite. + +**Mid-range setup:** +Sessions defined with named Presentations. Presenters may or may not be tracked. +Mix of pre-uploaded and onsite files. QR codes may be used for quick session/presenter lookup. + +**Full setup (LCI):** +Sessions, Presentations, Presenters all defined and managed. External ID labeling +(e.g., "LCI Member ID"). Agreement tracking for presenters. Files managed per-presenter. +Launcher linked from Pres Mgmt views. + +The config that drives this is `event.mod_pres_mgmt_json` — see the Configuration section. + +--- + +## Speaker Ready Room (SRR) + +The Speaker Ready Room is a dedicated space where presenters check in and staff manage +content before it goes live in the session rooms. Setup varies by client: + +- **Small/private:** Only a few client staff and OSIT. Not open to presenters at large. +- **Open SRR:** Open to all presenters as long as sessions are running. People come and go + all day — reviewing silently, editing with a group, practicing at a station. + +### SRR Practice Stations + +Stations mirror the session room setup exactly: +- Same Mac laptop model and adapter/dongle configuration as the podiums +- Projector and screen (same as session rooms where possible) +- Launcher running in Native (Electron) mode — cached files open immediately +- Full dry-run capability: load their file, start the deck, confirm everything works + +### Remote Monitoring + +SRR staff typically monitor the session room Launchers in real time via **VNC or RustDesk**. +This lets one person watch multiple podium displays simultaneously without being in each room. + +### QR Codes (Session and Presenter) + +QR codes are available for Sessions and Presenters and have been useful onsite for quick +lookups — scanning a code takes staff directly to the session or presenter record. +Whether to enable this depends on the SRR flow for each show. It gets toggled on or off +per event via config. + +### SRR Staffing Roles + +| Role | Access Level | Typical Tasks | +|---|---|---| +| OSIT Staff | `trusted_access` or higher | Upload files, edit sessions/presentations, manage devices, monitor via VNC | +| Client Staff | `authenticated_access` | Upload files, view session list | +| Presenter (self-service) | `authenticated_access` (if enabled) | Upload their own files via QR link | + +### SRR Workflow — Day-of-Show + +1. **Presenter checks in** — staff looks up their session(s) in Pres Mgmt +2. **File upload** — staff or presenter uploads file to the correct presenter/session record +3. **File verification** — staff opens the file on a practice station to confirm it renders +4. **Launcher sync** — file appears in the Launcher within the next polling cycle +5. **Presenter proceeds to room** — podium kiosk already has the file cached + +--- + +## File Upload Workflows + +### Pre-Show (Remote / Staff Ahead of Time) + +Files can be uploaded anytime before the event via the Pres Mgmt web UI: +1. Navigate to the presenter, session, or appropriate level +2. Use the file upload panel (drag & drop or browse) +3. File is stored server-side and immediately available to the Launcher + +Some clients enable presenter self-upload via a direct link (requires `authenticated_access`). +Controlled per-event via config. + +### Day Before — SRR Setup + +For higher-volume shows, the SRR opens the day before the event: +- Pre-uploaded files are already loaded and can be verified +- Early-arriving presenters check in; staff upload their files +- Electron Launcher instances on podium Macs begin pre-caching files overnight +- Problems (corrupt files, wrong format, wrong codec) surface with time to fix them + +### Live Onsite Upload + +For late arrivals and last-minute changes: +1. Presenter arrives at SRR (or sends file via USB/email to staff) +2. Staff uploads via Pres Mgmt web UI +3. File propagates to Launcher within one polling cycle (~30 seconds on fast networks) +4. VNC or RustDesk confirms the podium received the file before the presenter walks in + +--- + +## Onsite Operation — Managing 4–12 Parallel Rooms + +### Overview Page + +The Pres Mgmt overview (`/events/[event_id]/pres_mgmt`) shows: +- All sessions, filterable by location and time +- File status per session +- Quick links to each session's file management + +For events with multiple parallel rooms, filtering by location and time block is essential +for SRR staff staying on top of what's active right now. + +### Per-Room Workflow + +Each room/location has its own Launcher display: +- `/events/[event_id]/launcher` → select location → Launcher for that room +- The Launcher shows the active session based on the current time or manual selection +- VNC/RustDesk gives SRR staff a real-time view of all podiums simultaneously + +### Session Display Timing + +Ideally, sessions would automatically show and hide based on `datetime_start` / +`datetime_end` — appearing a configurable number of minutes before the session starts +and disappearing after it ends. This is a planned/desired behavior. In practice: + +- Some clients run tight schedules and could rely on time-based transitions +- Others drift significantly from the published schedule; time-based auto-advance + would cause more problems than it solves +- Currently, session transitions can be managed manually via Launcher controls + +> **TODO (future):** Configurable `show_before_minutes` / `hide_after_minutes` per event +> so well-run shows can automate transitions while looser shows stay manual. + +### Device (Laptop) Assignment + +Each Launcher kiosk Mac is registered as an `event_device` and typically assigned to one +Location for the duration of the event. However, laptops do get moved: +- Venues add or lose rooms as spaces are reconfigured +- A session room may open for one day only +- Devices can be reassigned to a different Location in the `event_device` record as needed + +The Electron app reads its location assignment from the API at startup, so reassigning a +device takes effect on the next launch (or app restart). + +--- + +## Alert Fields + +Sessions, Presenters, and Locations each have alert fields that can display a visible +notice in the Pres Mgmt UI and/or the Launcher. + +Useful for: +- "Presenter requested no recording" +- "Room change — moved to Hall B" +- "File not received — follow up" +- "AV note: needs confidence monitor" + +> **Status:** Alert fields exist but the implementation and display behavior needs review +> and cleanup. Not a blocking issue for BGH next week — revisit for a future show. + +--- + +## Launcher Module + +### Operational Modes + +| Mode | Use Case | File Handling | +|---|---|---| +| **Default** | Browser on any machine | Files downloaded on demand | +| **Onsite** | Browser on event network | Faster polling; browser-managed files | +| **Native** | Electron app on dedicated podium Mac | Background pre-cache; atomic file handover | + +For production onsite use, **Native mode on Mac laptops** is the target. The Electron +app pre-caches all session files in the background so presentations open instantly without +a network round-trip at the moment of launch. + +### Native Mode — Electron App + +- **Repo:** `~/OSIT_dev/aether_app_native_electron/` +- **Platform:** macOS (primary); Linux/Windows as fallback +- **Seed config:** `seed.json` (Device ID + API key) — loaded at startup +- **File cache:** `~/Library/Caches/OSIT/file_cache/` (hashed by SHA-256) +- **Doc:** `documentation/PROJECT__AE_Events_Launcher_Native_integration.md` + +The Electron app zero-configs itself: +1. Reads `seed.json` → gets device code +2. Calls Aether API → pulls device config and location assignment +3. Navigates directly to the Launcher for that location +4. Begins pre-caching session files in the background + +### Launcher Display Views + +| View | Shown When | +|---|---| +| Session view | Active session with session-level files | +| Presentation view | Active session with named presentations | +| Presenter view | Presentation selected; shows presenter bio/photo | +| Poster/group view | Special layout for poster sessions | +| Screensaver | No active session; idle state | + +### File Opening (Native Mode) — Safe Handover + +1. Verify SHA-256 hash in permanent cache +2. Atomic copy to system `[tmp]` directory +3. Rename to original filename (e.g., `Abstract_101.pptx`) +4. OS opens the file (Keynote, PowerPoint, Preview, etc.) + +Versioning is handled automatically: when a presenter uploads an updated file, the new +hash is cached separately and the old one remains intact. + +--- + +## Configuration — `mod_pres_mgmt_json` + +The event's Pres Mgmt behavior is controlled by `event.mod_pres_mgmt_json`. + +> **Note:** The config schema is being cleaned up — see +> `documentation/PROJECT__AE_Events_PressMgmt_Config_Cleanup.md` for the canonical +> `PressMgmtRemoteCfg` interface and naming conventions. + +### Convention + +| Prefix | Default state | Meaning | +|---|---|---| +| `hide__` | `false` = visible | Feature is ON by default; set `true` to suppress | +| `show__` | `false` = hidden | Feature is OFF by default; set `true` to enable | + +### Common Config Keys + +| Key | Default | Notes | +|---|---|---| +| `lock_config` | `false` | `true` = force remote→local sync; prevents user overrides of local config | +| `hide__session_code` | `false` | Hide session code column/field | +| `hide__session_description` | `false` | Hide session description field | +| `hide__session_location` | `false` | Hide location field on session view | +| `hide__session_datetime` | `false` | Hide datetime fields | +| `hide__presentation_code` | `false` | Hide presentation code | +| `hide__presenter_code` | `false` | Hide presenter code | +| `hide__location_code` | `false` | Hide location code | +| `show__launcher_link` | `false` | Show direct Launcher link in session view | +| `show__session_qr` | `false` | Show QR code for session (SRR lookup) | +| `show__presenter_qr` | `false` | Show QR code for presenter (SRR lookup) | +| `label__person_external_id` | `null` | Override label for external ID field (e.g., `"Member ID"`) | +| `label__session_poc_name` | `null` | Override label for session POC (e.g., `"Champion"`) | +| `file_purpose_option_kv` | `{}` | Key-value map of file purpose options (e.g., `{"ppt": "PowerPoint", "pdf": "PDF"}`) | + +### Per-Show Config Examples + +**BGH (session-only, minimal; no named Presentations or Presenters):** +```json +{ + "lock_config": false, + "hide__presentation_code": true, + "hide__presenter_code": true +} +``` + +**LCI (full setup, member ID label, Launcher link enabled):** +```json +{ + "lock_config": true, + "label__person_external_id": "LCI Member ID", + "show__launcher_link": true +} +``` + +> Admin must currently edit `mod_pres_mgmt_json` directly in the DB or via the event +> settings page. A proper Config UI is planned — see `PROJECT__AE_Events_PressMgmt_Config_Cleanup.md`. + +--- + +## Route Map + +| URL | Purpose | +|---|---| +| `/events/[id]/pres_mgmt` | Overview — sessions list, search, filter by location | +| `/events/[id]/pres_mgmt/config` | Config editor (admin only) | +| `/events/[id]/session/[session_id]` | Session detail — files, presentations, timing, alert | +| `/events/[id]/presenter/[presenter_id]` | Presenter detail — bio, files, agreement, alert | +| `/events/[id]/location/[location_id]` | Location detail — session schedule for this room, alert | +| `/events/[id]/locations` | All locations list | +| `/events/[id]/reports` | Reports — sessions, presenters, files | +| `/events/[id]/launcher` | Launcher home — select location | +| `/events/[id]/launcher/[location_id]` | Launcher display for a specific room | + +--- + +## Device Management + +Each Electron kiosk is registered as an `event_device` record: +- `code` — matches the device's `seed.json` code +- `name` — human-readable (e.g., "Ballroom A Podium") +- `data_json.location_id` — the `event_location_id` this device is assigned to + +Devices can be managed in Pres Mgmt (`/events/[id]/device/device`). Location reassignment +takes effect on the next Electron app launch. + +--- + +## Access Levels + +| Feature | Minimum Access | +|---|---| +| View pres_mgmt overview | `authenticated_access` | +| Upload files | `authenticated_access` | +| Edit sessions / presentations | `trusted_access` | +| Edit config | `administrator_access` + `edit_mode` | +| View Launcher display | `authenticated_access` | +| Manual session selection in Launcher | `trusted_access` | +| Device management | `administrator_access` | + +--- + +## Pre-Show Checklist + +### 1–2 Weeks Before + +- [ ] Event created in Aether with correct dates +- [ ] `mod_pres_mgmt_json` configured for this client's needs +- [ ] Locations (rooms) created and named +- [ ] Sessions created, assigned to locations, datetime ranges set +- [ ] If using Presentations/Presenters: records imported or entered +- [ ] File purpose options configured in `file_purpose_option_kv` +- [ ] Launcher devices registered (`event_device` records with correct codes) +- [ ] Device-to-location assignments confirmed +- [ ] Decide: QR codes for Sessions / Presenters needed? Enable/disable in config + +### Day Before (SRR Setup) + +- [ ] Mac laptops at podiums booted and Electron app running +- [ ] Each podium confirms it loaded the correct location's Launcher +- [ ] SRR practice stations confirmed — projector, same Mac/dongle setup as session rooms +- [ ] Pre-loaded files verified in Launcher (open at least one per room to test Safe Handover) +- [ ] SRR staff briefed on upload workflow and VNC/RustDesk monitoring setup +- [ ] VNC/RustDesk connections established to all podium displays + +### Day of Show + +- [ ] Confirm all session times in Aether are accurate before first session +- [ ] Monitor SRR queue — upload files as presenters check in +- [ ] Verify each file opens on a practice station before the presenter walks to their room +- [ ] Monitor podium displays via VNC/RustDesk — flag any stuck or offline devices + +--- + +## Troubleshooting + +| Symptom | Cause | Fix | +|---|---|---| +| Session not showing in Launcher | Session datetime wrong or location not assigned | Verify session location and datetime range | +| File uploaded but not in Launcher | Polling cycle lag; or file attached at wrong level | Wait one cycle; check that file is attached to session/location (not just a presenter record) if using session-only setup | +| Electron app shows wrong location | Device code mismatch or stale device config | Re-check `event_device` record; restart Electron app | +| File opens slowly at podium | Not in native cache yet | Check background sync in Launcher; pre-cache may not have completed | +| File won't open | Corrupt upload, wrong format, or missing codec on Mac | Test on SRR practice station; re-upload or convert | +| Session out of sync with schedule | Timing drifted; manual advance needed | Use Launcher controls to manually select the current session | +| Alert field not showing | Alert fields need implementation review | Known — lower priority than active operations | +| `lock_config: true` resets local changes | Expected behavior — remote config wins | Change the remote config in `mod_pres_mgmt_json` | +| Device needs to move to different room | Location reassigned mid-event | Update `data_json.location_id` on `event_device` record; restart Electron app on that machine | diff --git a/src/lib/stores/ae_events_stores__pres_mgmt_defaults.ts b/src/lib/stores/ae_events_stores__pres_mgmt_defaults.ts index 5769dd67..27d1652d 100644 --- a/src/lib/stores/ae_events_stores__pres_mgmt_defaults.ts +++ b/src/lib/stores/ae_events_stores__pres_mgmt_defaults.ts @@ -106,6 +106,11 @@ export interface PresMgmtLocState { location_name_qry_str: string | null; // persisted location filter text refresh_interval: number; // auto-refresh interval in seconds (0 = disabled) + // --- Time window filter (onsite use: show only sessions near current time) --- + enable_time_window: boolean; // false = off (show all); true = apply window + time_window_before_minutes: number; // include sessions that started up to N min ago + time_window_after_minutes: number; // include sessions starting within next N min + // --- Report display preferences (user-controlled, persisted) --- rpt__session_no_files: boolean; // show "sessions with no files" report section rpt__session_poc_agree: boolean; // show "session POC agreement" report section @@ -282,6 +287,11 @@ export const pres_mgmt_loc_defaults: PresMgmtLocState = { location_name_qry_str: null, refresh_interval: 0, + // Time window filter + enable_time_window: false, + time_window_before_minutes: 30, + time_window_after_minutes: 720, + // Report display preferences rpt__session_no_files: true, rpt__session_poc_agree: false, diff --git a/src/routes/events/[event_id]/(badges)/badges/ae_comp__badge_create_form.svelte b/src/routes/events/[event_id]/(badges)/badges/ae_comp__badge_create_form.svelte index 513b4e7c..5446a714 100644 --- a/src/routes/events/[event_id]/(badges)/badges/ae_comp__badge_create_form.svelte +++ b/src/routes/events/[event_id]/(badges)/badges/ae_comp__badge_create_form.svelte @@ -313,13 +313,15 @@ let step_label = $derived( /* Placeholder text must read as a hint, not as filled content. Tailwind v4 sets placeholder color too dark on light backgrounds. Same fix as ae_comp__badge_print_controls.svelte. */ -form input::placeholder, -form textarea::placeholder { + +/* form textarea::placeholder */ +form input::placeholder { color: #9ca3af; /* gray-400 */ opacity: 1; /* Firefox: override default 0.54 opacity */ } -:global(.dark) form input::placeholder, -:global(.dark) form textarea::placeholder { + +/* :global(.dark) form textarea::placeholder */ +:global(.dark) form input::placeholder { color: #6b7280; /* gray-500 in dark mode */ } diff --git a/src/routes/events/[event_id]/(badges)/badges/ae_comp__badge_search.svelte b/src/routes/events/[event_id]/(badges)/badges/ae_comp__badge_search.svelte index 583e0a40..fd895320 100644 --- a/src/routes/events/[event_id]/(badges)/badges/ae_comp__badge_search.svelte +++ b/src/routes/events/[event_id]/(badges)/badges/ae_comp__badge_search.svelte @@ -177,8 +177,9 @@ function handle_qr_scan_result(event: { onclick={() => document.getElementById('badge_fulltext_search_qry_str')?.focus()} aria-label="Start here — focus search field" data-testid="badge-start-btn" - class="btn btn-sm preset-filled-secondary-300-700 font-semibold" + class="btn btn-sm preset-filled-secondary-200-800 font-semibold" class:opacity-30={!!badges_loc.current.fulltext_search_qry_str} + class:hidden={$ae_loc.trusted_access} > Start Here diff --git a/src/routes/events/[event_id]/(pres_mgmt)/pres_mgmt/+page.svelte b/src/routes/events/[event_id]/(pres_mgmt)/pres_mgmt/+page.svelte index dbb7e191..9abb8c04 100644 --- a/src/routes/events/[event_id]/(pres_mgmt)/pres_mgmt/+page.svelte +++ b/src/routes/events/[event_id]/(pres_mgmt)/pres_mgmt/+page.svelte @@ -46,12 +46,11 @@ import { ListChecks, LoaderCircle, Mails, - MapPin, - RemoveFormatting, Search, TriangleAlert, Upload } from '@lucide/svelte'; +import Comp_pres_mgmt_session_search from './ae_comp__pres_mgmt_session_search.svelte'; // Quickly save the data passed from the parent(s) to the Svelte stores, localStorage, and other. // NOTE: Derived from data.account_id (prop) instead of $slct.account_id (store) // to prevent circular dependency loops during hydration. @@ -156,7 +155,11 @@ let search_params = $derived({ .trim(), location: pres_mgmt_loc.current.location_name_qry_str, event_id: $events_slct?.event_id, - remote_first: pres_mgmt_loc.current.qry__remote_first + remote_first: pres_mgmt_loc.current.qry__remote_first, + qry_hidden: pres_mgmt_loc.current.qry_hidden, + enable_time_window: pres_mgmt_loc.current.enable_time_window, + time_window_before_minutes: pres_mgmt_loc.current.time_window_before_minutes, + time_window_after_minutes: pres_mgmt_loc.current.time_window_after_minutes }); $effect(() => { @@ -180,6 +183,35 @@ async function handle_search_refresh(params: any) { const event_id = params.event_id; const remote_first = params.remote_first; + // Snapshot filter params at search time — avoids stale reads across async boundaries. + const { enable_time_window, time_window_before_minutes, time_window_after_minutes, qry_hidden } = params; + + // Mirror the API's hide filter in the fast path so IDB results match what the + // API will return. Without this, hidden sessions briefly appear for trusted users + // before the API revalidation removes them (the blink). + // + // NOTE: only event_session.hide is checked here — NOT hide_event_launcher. + // hide_event_launcher removes sessions from the Launcher only (used to drop past + // blocks from the kiosk view). Those sessions remain fully accessible in pres_mgmt. + function passes_hide_filter(session: { hide?: boolean | null }): boolean { + if (qry_hidden === 'not_hidden') return !session.hide; + if (qry_hidden === 'hidden') return !!session.hide; + return true; // 'all' + } + + // Sessions with no start_datetime are always shown when the time filter is active + // (e.g. unscheduled placeholder sessions at BGH). + function passes_time_window(session: { start_datetime?: Date | string | null }): boolean { + if (!enable_time_window) return true; + if (!session.start_datetime) return true; + const now = Date.now(); + const start = new Date(session.start_datetime).getTime(); + return ( + start >= now - time_window_before_minutes * 60_000 && + start <= now + time_window_after_minutes * 60_000 + ); + } + if (log_lvl) console.log( `[Session Search #${current_search_id}] Refreshing (remote=${remote_first}, event=${event_id}, str=${params.str})...` @@ -200,6 +232,9 @@ async function handle_search_refresh(params: any) { .where('event_id') .equals(event_id) .filter((session) => { + if (!passes_hide_filter(session)) return false; + if (!passes_time_window(session)) return false; + if ( location_name && session.event_location_name !== location_name @@ -276,6 +311,7 @@ async function handle_search_refresh(params: any) { // Client-side Filter Guard: Ensure API results match local criteria (Backup filter) api_results = api_results.filter((session) => { + if (!passes_time_window(session)) return false; if ( location_name && session.event_location_name !== location_name @@ -339,16 +375,6 @@ if ( pres_mgmt_loc.current.saved_search__session_location_name; } -function handle_search_trigger() { - pres_mgmt_loc.current.search_version++; -} - -function prevent_default(fn: (event: T) => void) { - return function (event: T) { - event.preventDefault(); - fn(event); - }; -} @@ -422,97 +448,8 @@ function prevent_default(fn: (event: T) => void) { {#if !pres_mgmt_loc.current.show_content__event_view || pres_mgmt_loc.current.show_content__event_view == 'default'} -
-
handle_search_trigger())} - autocomplete="off" - class="form flex w-full grow flex-row flex-wrap items-center justify-center gap-1"> - - - - - - - - { - if (e.key === 'Enter') handle_search_trigger(); - }} - autofocus - data-ignore="true" /> - - - - {#if $ae_loc.edit_mode} - - {/if} -
-
+ {#if event_session_id_li.length} (fn: (event: T) => void) { Use the search bar above to find your session.

+ + {#if pres_mgmt_loc.current.enable_time_window} + +
+ ⏰ Time filter is ON — sessions outside the current window are hidden. + Disable the clock button in the search bar to see all sessions. +
+ {/if}
+interface Props { + event_location_obj_li: any[] | undefined; + log_lvl?: number; +} + +let { event_location_obj_li, log_lvl = 0 }: Props = $props(); + +import { Clock, LoaderCircle, MapPin, RemoveFormatting, Search } from '@lucide/svelte'; +import { ae_loc } from '$lib/stores/ae_stores'; +import { events_sess } from '$lib/stores/ae_events_stores'; +import { pres_mgmt_loc } from '$lib/stores/ae_events_stores__pres_mgmt.svelte'; + +function handle_search_trigger() { + pres_mgmt_loc.current.search_version++; +} + +function prevent_default(fn: (event: T) => void) { + return function (event: T) { + event.preventDefault(); + fn(event); + }; +} + + +