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'}
-
-
-
+
{#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);
+ };
+}
+
+
+