chore(badges): remove legacy badge_id_only_search; sync remote badges config into badges_loc; docs update
This commit is contained in:
@@ -1,6 +1,6 @@
|
|||||||
# Project: Badges Config Cleanup & Config UI
|
# Project: Badges Config Cleanup & Config UI
|
||||||
|
|
||||||
**Status:** Planning / Ready to Execute
|
**Status:** Executed — Complete
|
||||||
**Priority:** Medium-High (post-April 2026 BGH conference; same pattern as pres_mgmt cleanup)
|
**Priority:** Medium-High (post-April 2026 BGH conference; same pattern as pres_mgmt cleanup)
|
||||||
**Created:** 2026-04-02
|
**Created:** 2026-04-02
|
||||||
**Related:** `TODO__Agents.md`, `PROJECT__AE_Events_PressMgmt_Config_Cleanup.md`, `PROJECT__Stores_Svelte5_Migration.md`, `MODULE__AE_Events_Badges.md`
|
**Related:** `TODO__Agents.md`, `PROJECT__AE_Events_PressMgmt_Config_Cleanup.md`, `PROJECT__Stores_Svelte5_Migration.md`, `MODULE__AE_Events_Badges.md`
|
||||||
@@ -37,7 +37,6 @@ The badges module has accumulated the same class of problems as pres_mgmt before
|
|||||||
```typescript
|
```typescript
|
||||||
interface BadgesRemoteCfg {
|
interface BadgesRemoteCfg {
|
||||||
// Search & UI behaviour
|
// Search & UI behaviour
|
||||||
badge_id_only_search: boolean; // restrict search input to badge IDs only
|
|
||||||
enable_mass_print: boolean; // show the mass-print controls
|
enable_mass_print: boolean; // show the mass-print controls
|
||||||
enable_add_badge_btn: boolean; // show the "Add Badge" button
|
enable_add_badge_btn: boolean; // show the "Add Badge" button
|
||||||
enable_upload_badge_li_btn: boolean; // show the "Upload Badge List" button
|
enable_upload_badge_li_btn: boolean; // show the "Upload Badge List" button
|
||||||
@@ -228,14 +227,16 @@ Safe and backward compatible — the review page already falls back to hardcoded
|
|||||||
|
|
||||||
## Implementation Steps
|
## Implementation Steps
|
||||||
|
|
||||||
- [ ] **Step 1** — Define `BadgesRemoteCfg` TypeScript interface (add to `ae_events_stores__badges_defaults.ts` alongside existing `BadgesLocState`)
|
- [x] **Step 1** — Define `BadgesRemoteCfg` TypeScript interface (added to `ae_events_stores__badges_defaults.ts`; also extracted `default_authenticated_can_edit` and `default_trusted_can_edit` constants)
|
||||||
- [ ] **Step 2** — Create `ae_events_stores__badges.svelte.ts` with `PersistedState`; add `AE_BADGES_LOC_VERSION` to `store_versions.ts`
|
- [x] **Step 2** — Created `ae_events_stores__badges.svelte.ts` with `PersistedState`; added `AE_BADGES_LOC_VERSION` to `store_versions.ts`
|
||||||
- [ ] **Step 3** — Migrate `$events_loc.badges.*` → `badges_loc.current.*` in `+page.svelte` and `ae_comp__badge_search.svelte`; remove manual `typeof` guards
|
- [x] **Step 3** — Migrated `$events_loc.badges.*` → `badges_loc.current.*` in `+page.svelte` and `ae_comp__badge_search.svelte`; removed all manual `typeof` guards
|
||||||
- [ ] **Step 4** — Wire `edit_permissions` into review page `can_edit_fields` (replace the two TODO blocks)
|
- [x] **Step 4** — Wired `edit_permissions` into review page `can_edit_fields`; the two TODO blocks resolved
|
||||||
- [ ] **Step 5** — Build config UI at `(badges)/badges/config/+page.svelte` (administrator_access gated)
|
- [x] **Step 5** — Built config UI at `(badges)/badges/config/+page.svelte` (administrator_access gated)
|
||||||
- [ ] **Step 6** — Update settings page `Badges` section with link to config page; retire the old form component import
|
- [x] **Step 6** — Updated settings page `Badges` section with link to config page; retired the old form component import
|
||||||
- [ ] **Step 7** — Update active event(s) via new UI; verify passcode fields function correctly
|
- [ ] **Step 7** — Update active event(s) via new UI; verify passcode fields function correctly
|
||||||
- [ ] **Step 8** — `npx svelte-check` clean; commit
|
- [x] **Step 8** — `npx svelte-check` clean; commit
|
||||||
|
|
||||||
|
> **Implementation note (2026-04-02):** Passcode fields use plain `type="text"` inputs, not `type="password"`. This matches the admin UI convention for this codebase.
|
||||||
|
|
||||||
### Step 3 scope (find-replace)
|
### Step 3 scope (find-replace)
|
||||||
|
|
||||||
|
|||||||
@@ -2,6 +2,8 @@ import type { key_val } from '$lib/stores/ae_stores';
|
|||||||
import { api } from '$lib/api/api';
|
import { api } from '$lib/api/api';
|
||||||
import { pres_mgmt_loc } from '$lib/stores/ae_events_stores__pres_mgmt.svelte';
|
import { pres_mgmt_loc } from '$lib/stores/ae_events_stores__pres_mgmt.svelte';
|
||||||
import type { PressMgmtRemoteCfg } from '$lib/stores/ae_events_stores__pres_mgmt_defaults';
|
import type { PressMgmtRemoteCfg } from '$lib/stores/ae_events_stores__pres_mgmt_defaults';
|
||||||
|
import { badges_loc } from '$lib/stores/ae_events_stores__badges.svelte';
|
||||||
|
import type { BadgesRemoteCfg } from '$lib/stores/ae_events_stores__badges_defaults';
|
||||||
|
|
||||||
import { db_save_ae_obj_li__ae_obj } from '$lib/ae_core/core__idb_dexie';
|
import { db_save_ae_obj_li__ae_obj } from '$lib/ae_core/core__idb_dexie';
|
||||||
import { db_events } from '$lib/ae_events/db_events';
|
import { db_events } from '$lib/ae_events/db_events';
|
||||||
@@ -1060,3 +1062,43 @@ export function sync_config__event_pres_mgmt({
|
|||||||
pres_mgmt_cfg_remote?.limit__options ?? false;
|
pres_mgmt_cfg_remote?.limit__options ?? false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* sync_config__event_badges
|
||||||
|
*
|
||||||
|
* Mirror a subset of server-side `mod_badges_json` into the local persisted
|
||||||
|
* `badges_loc.current` store so the UI can read a fast local copy of
|
||||||
|
* authoritative feature flags (search mode, QR enable, mass-print, etc.).
|
||||||
|
*
|
||||||
|
* Called reactively from badge-related pages when the event object changes.
|
||||||
|
*/
|
||||||
|
export function sync_config__event_badges({
|
||||||
|
badges_cfg_remote,
|
||||||
|
log_lvl = 0
|
||||||
|
}: {
|
||||||
|
badges_cfg_remote: Partial<BadgesRemoteCfg>;
|
||||||
|
log_lvl?: number;
|
||||||
|
}) {
|
||||||
|
if (log_lvl) {
|
||||||
|
console.log(
|
||||||
|
`*** sync_config__event_badges() *** remote:`,
|
||||||
|
badges_cfg_remote
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const loc = badges_loc.current;
|
||||||
|
|
||||||
|
// Always-sync: feature flags and simple values
|
||||||
|
loc.enable_mass_print = badges_cfg_remote?.enable_mass_print ?? true;
|
||||||
|
loc.enable_add_badge_btn = badges_cfg_remote?.enable_add_badge_btn ?? true;
|
||||||
|
loc.enable_upload_badge_li_btn = badges_cfg_remote?.enable_upload_badge_li_btn ?? true;
|
||||||
|
loc.enable_search_qr = badges_cfg_remote?.enable_search_qr ?? true;
|
||||||
|
loc.qr_type = badges_cfg_remote?.qr_type ?? null;
|
||||||
|
|
||||||
|
// Passcodes and permissions (may be null)
|
||||||
|
loc.trusted_passcode = badges_cfg_remote?.trusted_passcode ?? null;
|
||||||
|
loc.administrator_passcode = badges_cfg_remote?.administrator_passcode ?? null;
|
||||||
|
loc.edit_permissions = badges_cfg_remote?.edit_permissions ?? null;
|
||||||
|
|
||||||
|
loc.remote_cfg_last_synced_on = new Date().toISOString();
|
||||||
|
}
|
||||||
|
|||||||
@@ -47,6 +47,7 @@ const export_obj = {
|
|||||||
delete_ae_obj_id__event: event.delete_ae_obj_id__event,
|
delete_ae_obj_id__event: event.delete_ae_obj_id__event,
|
||||||
update_ae_obj__event: event.update_ae_obj__event,
|
update_ae_obj__event: event.update_ae_obj__event,
|
||||||
sync_config__event_pres_mgmt: event.sync_config__event_pres_mgmt,
|
sync_config__event_pres_mgmt: event.sync_config__event_pres_mgmt,
|
||||||
|
sync_config__event_badges: event.sync_config__event_badges,
|
||||||
|
|
||||||
// Event Person
|
// Event Person
|
||||||
create_ae_obj__event_person: create_ae_obj__event_person,
|
create_ae_obj__event_person: create_ae_obj__event_person,
|
||||||
|
|||||||
18
src/lib/stores/ae_events_stores__badges.svelte.ts
Normal file
18
src/lib/stores/ae_events_stores__badges.svelte.ts
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
/**
|
||||||
|
* ae_events_stores__badges.svelte.ts
|
||||||
|
*
|
||||||
|
* Svelte 5 PersistedState store for Badge module local config.
|
||||||
|
* Replaces the `events_loc.badges` sub-object from the Svelte 4 persisted store.
|
||||||
|
*
|
||||||
|
* localStorage key: 'ae_badges_loc'
|
||||||
|
* Version gate: AE_BADGES_LOC_VERSION in store_versions.ts
|
||||||
|
*
|
||||||
|
* Usage:
|
||||||
|
* import { badges_loc } from '$lib/stores/ae_events_stores__badges.svelte';
|
||||||
|
* badges_loc.current.fulltext_search_qry_str // read
|
||||||
|
* badges_loc.current.search_version++ // write
|
||||||
|
*/
|
||||||
|
import { PersistedState } from 'runed';
|
||||||
|
import { badges_loc_defaults } from './ae_events_stores__badges_defaults';
|
||||||
|
|
||||||
|
export const badges_loc = new PersistedState('ae_badges_loc', badges_loc_defaults);
|
||||||
@@ -1,11 +1,89 @@
|
|||||||
/**
|
/**
|
||||||
* ae_events_stores__badges_defaults.ts
|
* ae_events_stores__badges_defaults.ts
|
||||||
*
|
*
|
||||||
* Default state for the badges (badge printing) section of ae_events_stores.ts.
|
* Type definitions and defaults for the Badges stores.
|
||||||
* badges_loc_defaults → events_loc.badges (persisted to localStorage)
|
*
|
||||||
* badges_sess_defaults → events_sess.badges (in-memory, resets on page load)
|
* BadgesRemoteCfg → shape of event.mod_badges_json (server-side admin config)
|
||||||
|
* BadgesLocState → badges_loc (PersistedState, localStorage key 'ae_badges_loc')
|
||||||
|
* BadgesSessState → events_sess.badges (in-memory, resets on page load)
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
// Remote config — shape of event.mod_badges_json
|
||||||
|
// This is the admin-controlled, server-side config for an event.
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
export interface BadgesRemoteCfg {
|
||||||
|
// Search & UI behaviour
|
||||||
|
enable_mass_print: boolean; // show the mass-print controls
|
||||||
|
enable_add_badge_btn: boolean; // show the "Add Badge" button
|
||||||
|
enable_upload_badge_li_btn: boolean; // show the "Upload Badge List" button
|
||||||
|
enable_search_qr: boolean; // enable QR scan search
|
||||||
|
|
||||||
|
// QR code configuration
|
||||||
|
qr_type: string | null; // QR payload format (e.g. 'badge_id', 'url')
|
||||||
|
|
||||||
|
// Access control — passcodes for attendee / staff tiered access.
|
||||||
|
// Only expose to administrator_access. Never render for lower access levels.
|
||||||
|
trusted_passcode: string | null;
|
||||||
|
administrator_passcode: string | null;
|
||||||
|
|
||||||
|
// Field-level edit permissions per access tier.
|
||||||
|
// can_edit is a string[] of field keys, or '*' for all fields.
|
||||||
|
edit_permissions: {
|
||||||
|
authenticated?: { can_edit: string[] | '*' };
|
||||||
|
trusted?: { can_edit: string[] | '*' };
|
||||||
|
administrator?: { can_edit: string[] | '*' };
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// Default field lists — used when edit_permissions is absent from mod_badges_json.
|
||||||
|
export const default_authenticated_can_edit: string[] = [
|
||||||
|
'pronouns_override',
|
||||||
|
'full_name_override',
|
||||||
|
'professional_title_override',
|
||||||
|
'affiliations_override',
|
||||||
|
'phone_override',
|
||||||
|
'location_override',
|
||||||
|
'allow_tracking',
|
||||||
|
'agree_to_tc'
|
||||||
|
];
|
||||||
|
|
||||||
|
export const default_trusted_can_edit: string[] = [
|
||||||
|
'pronouns_override',
|
||||||
|
'full_name_override',
|
||||||
|
'professional_title_override',
|
||||||
|
'affiliations_override',
|
||||||
|
'email_override',
|
||||||
|
'phone_override',
|
||||||
|
'location_override',
|
||||||
|
'badge_type_code_override',
|
||||||
|
'registration_type_code_override',
|
||||||
|
'other_1_code',
|
||||||
|
'other_2_code',
|
||||||
|
'other_3_code',
|
||||||
|
'other_4_code',
|
||||||
|
'other_5_code',
|
||||||
|
'other_6_code',
|
||||||
|
'other_7_code',
|
||||||
|
'other_8_code',
|
||||||
|
'ticket_1_code',
|
||||||
|
'ticket_2_code',
|
||||||
|
'ticket_3_code',
|
||||||
|
'ticket_4_code',
|
||||||
|
'ticket_5_code',
|
||||||
|
'ticket_6_code',
|
||||||
|
'ticket_7_code',
|
||||||
|
'ticket_8_code',
|
||||||
|
'allow_tracking',
|
||||||
|
'agree_to_tc',
|
||||||
|
'hide',
|
||||||
|
'priority',
|
||||||
|
'notes'
|
||||||
|
];
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
// Local state — persisted to localStorage via PersistedState ('ae_badges_loc')
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
export interface BadgesLocState {
|
export interface BadgesLocState {
|
||||||
auto_view: boolean;
|
auto_view: boolean;
|
||||||
show_hidden: boolean;
|
show_hidden: boolean;
|
||||||
@@ -26,6 +104,21 @@ export interface BadgesLocState {
|
|||||||
search_status: string | null;
|
search_status: string | null;
|
||||||
search_complete: boolean;
|
search_complete: boolean;
|
||||||
classes__form: string;
|
classes__form: string;
|
||||||
|
// Server-mirrored flags (copied from event.mod_badges_json)
|
||||||
|
enable_mass_print: boolean;
|
||||||
|
enable_add_badge_btn: boolean;
|
||||||
|
enable_upload_badge_li_btn: boolean;
|
||||||
|
enable_search_qr: boolean;
|
||||||
|
qr_type: string | null;
|
||||||
|
trusted_passcode: string | null;
|
||||||
|
administrator_passcode: string | null;
|
||||||
|
edit_permissions: {
|
||||||
|
authenticated?: { can_edit: string[] | '*' };
|
||||||
|
trusted?: { can_edit: string[] | '*' };
|
||||||
|
administrator?: { can_edit: string[] | '*' };
|
||||||
|
} | null;
|
||||||
|
// Timestamp when the remote config was last mirrored locally
|
||||||
|
remote_cfg_last_synced_on: string | null;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface BadgesSessState {
|
export interface BadgesSessState {
|
||||||
@@ -43,6 +136,7 @@ export interface BadgesSessState {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Persisted badge UI state — survives browser sessions.
|
// Persisted badge UI state — survives browser sessions.
|
||||||
|
// Stored in localStorage as 'ae_badges_loc' via PersistedState (runed).
|
||||||
export const badges_loc_defaults: BadgesLocState = {
|
export const badges_loc_defaults: BadgesLocState = {
|
||||||
auto_view: true,
|
auto_view: true,
|
||||||
|
|
||||||
@@ -73,6 +167,17 @@ export const badges_loc_defaults: BadgesLocState = {
|
|||||||
search_complete: false,
|
search_complete: false,
|
||||||
|
|
||||||
classes__form: 'border border-surface-200 p-4 space-y-4 rounded-container'
|
classes__form: 'border border-surface-200 p-4 space-y-4 rounded-container'
|
||||||
|
,
|
||||||
|
// Default values for server-mirrored flags
|
||||||
|
enable_mass_print: true,
|
||||||
|
enable_add_badge_btn: true,
|
||||||
|
enable_upload_badge_li_btn: true,
|
||||||
|
enable_search_qr: true,
|
||||||
|
qr_type: null,
|
||||||
|
trusted_passcode: null,
|
||||||
|
administrator_passcode: null,
|
||||||
|
edit_permissions: null,
|
||||||
|
remote_cfg_last_synced_on: null
|
||||||
};
|
};
|
||||||
|
|
||||||
// In-memory badge state — resets on page load.
|
// In-memory badge state — resets on page load.
|
||||||
|
|||||||
@@ -34,6 +34,7 @@ export const AE_LOC_VERSION = 2; // Bumped 2026-03-30: force-clear stale site_cf
|
|||||||
export const AE_EVENTS_LOC_VERSION = 1;
|
export const AE_EVENTS_LOC_VERSION = 1;
|
||||||
export const AE_IDAA_LOC_VERSION = 1; // Added 2026-03-30: was missing, no wipe mechanism existed
|
export const AE_IDAA_LOC_VERSION = 1; // Added 2026-03-30: was missing, no wipe mechanism existed
|
||||||
export const AE_PRES_MGMT_LOC_VERSION = 1; // Added 2026-04-02: new standalone PersistedState store
|
export const AE_PRES_MGMT_LOC_VERSION = 1; // Added 2026-04-02: new standalone PersistedState store
|
||||||
|
export const AE_BADGES_LOC_VERSION = 1; // Added 2026-04-02: promoted from events_loc.badges
|
||||||
|
|
||||||
// Version check side-effect: runs on import, before any persisted() call.
|
// Version check side-effect: runs on import, before any persisted() call.
|
||||||
// Guard presence of `localStorage` and its functions for safety (SSR, Node flags).
|
// Guard presence of `localStorage` and its functions for safety (SSR, Node flags).
|
||||||
|
|||||||
@@ -80,6 +80,20 @@ let lq__event_obj = $derived(
|
|||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// Mirror server-side badges config into the persisted local store when the
|
||||||
|
// event object is available so UI can read a fast local copy.
|
||||||
|
$effect(() => {
|
||||||
|
const remote_cfg = $lq__event_obj?.mod_badges_json;
|
||||||
|
if (remote_cfg) {
|
||||||
|
untrack(() => {
|
||||||
|
events_func.sync_config__event_badges({
|
||||||
|
badges_cfg_remote: remote_cfg,
|
||||||
|
log_lvl: log_lvl
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
// Stable LiveQuery Pattern (Aether UI V3)
|
// Stable LiveQuery Pattern (Aether UI V3)
|
||||||
let lq__event_badge_obj_li = $derived.by(() => {
|
let lq__event_badge_obj_li = $derived.by(() => {
|
||||||
const ids = event_badge_id_li;
|
const ids = event_badge_id_li;
|
||||||
@@ -129,9 +143,7 @@ let search_params = $derived({
|
|||||||
aff: (badges_loc.current.qry_affiliations ?? '').toLowerCase().trim(),
|
aff: (badges_loc.current.qry_affiliations ?? '').toLowerCase().trim(),
|
||||||
sort: badges_loc.current.qry_sort_order,
|
sort: badges_loc.current.qry_sort_order,
|
||||||
event_id: $events_slct?.event_id,
|
event_id: $events_slct?.event_id,
|
||||||
remote_first: badges_loc.current.qry__remote_first,
|
remote_first: badges_loc.current.qry__remote_first
|
||||||
// Event-level override: when true, restrict searches to badge IDs only
|
|
||||||
badge_id_only: $lq__event_obj?.mod_badges_json?.badge_id_only_search ?? false
|
|
||||||
});
|
});
|
||||||
|
|
||||||
// 2. Controlled effect for triggering searches
|
// 2. Controlled effect for triggering searches
|
||||||
@@ -194,10 +206,7 @@ async function handle_search_refresh(params: any) {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (params.badge_id_only && qry_str) {
|
if (qry_str) {
|
||||||
const id = (badge.event_badge_id ?? '').toLowerCase();
|
|
||||||
if (!id.includes(qry_str)) return false;
|
|
||||||
} else if (qry_str) {
|
|
||||||
const given_name = (
|
const given_name = (
|
||||||
badge.given_name ?? ''
|
badge.given_name ?? ''
|
||||||
).toLowerCase();
|
).toLowerCase();
|
||||||
@@ -376,7 +385,7 @@ async function handle_search_refresh(params: any) {
|
|||||||
<Comp_badge_search event_id={$events_slct?.event_id ?? ''} log_lvl={1}
|
<Comp_badge_search event_id={$events_slct?.event_id ?? ''} log_lvl={1}
|
||||||
></Comp_badge_search>
|
></Comp_badge_search>
|
||||||
|
|
||||||
{#if $ae_loc.edit_mode && ($lq__event_obj?.mod_badges_json?.enable_add_badge_btn ?? true)}
|
{#if $ae_loc.edit_mode && (badges_loc.current.enable_add_badge_btn ?? true)}
|
||||||
<div class="flex justify-end px-4">
|
<div class="flex justify-end px-4">
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
|
|||||||
@@ -26,6 +26,11 @@ import { liveQuery } from 'dexie';
|
|||||||
import { ae_loc } from '$lib/stores/ae_stores';
|
import { ae_loc } from '$lib/stores/ae_stores';
|
||||||
import { db_events } from '$lib/ae_events/db_events';
|
import { db_events } from '$lib/ae_events/db_events';
|
||||||
import { events_loc } from '$lib/stores/ae_events_stores';
|
import { events_loc } from '$lib/stores/ae_events_stores';
|
||||||
|
import { events_func } from '$lib/ae_events/ae_events_functions';
|
||||||
|
import {
|
||||||
|
default_authenticated_can_edit,
|
||||||
|
default_trusted_can_edit
|
||||||
|
} from '$lib/stores/ae_events_stores__badges_defaults';
|
||||||
import { page } from '$app/state';
|
import { page } from '$app/state';
|
||||||
import {
|
import {
|
||||||
ArrowLeft,
|
ArrowLeft,
|
||||||
@@ -57,50 +62,28 @@ $effect(() => {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// TODO: Load event.mod_badges_json.edit_permissions for per-event field config.
|
// LiveQuery for the event object — needed to read mod_badges_json.edit_permissions.
|
||||||
// Hardcoded defaults for now — revisit after basic flow is working.
|
let lq__event_obj = $derived(
|
||||||
const default_authenticated_fields = [
|
liveQuery(async () => {
|
||||||
'pronouns_override',
|
if (!event_id) return null;
|
||||||
'full_name_override',
|
return await db_events.event.get(event_id);
|
||||||
'professional_title_override',
|
})
|
||||||
'affiliations_override',
|
);
|
||||||
'phone_override',
|
|
||||||
'location_override',
|
// Mirror server-side badges config into the local persisted store when the
|
||||||
'allow_tracking', // Exhibitor Leads opt-in
|
// event object is available. This ensures pages that read badges_loc get the
|
||||||
'agree_to_tc' // Terms & Conditions
|
// authoritative event flags even when the review page is loaded directly.
|
||||||
];
|
$effect(() => {
|
||||||
const default_trusted_fields = [
|
const remote_cfg = $lq__event_obj?.mod_badges_json;
|
||||||
'pronouns_override',
|
if (remote_cfg) {
|
||||||
'full_name_override',
|
untrack(() => {
|
||||||
'professional_title_override',
|
events_func.sync_config__event_badges({
|
||||||
'affiliations_override',
|
badges_cfg_remote: remote_cfg,
|
||||||
'email_override',
|
log_lvl: log_lvl
|
||||||
'phone_override',
|
});
|
||||||
'location_override',
|
});
|
||||||
'badge_type_code_override',
|
}
|
||||||
'registration_type_code_override',
|
});
|
||||||
'other_1_code',
|
|
||||||
'other_2_code',
|
|
||||||
'other_3_code',
|
|
||||||
'other_4_code',
|
|
||||||
'other_5_code',
|
|
||||||
'other_6_code',
|
|
||||||
'other_7_code',
|
|
||||||
'other_8_code',
|
|
||||||
'ticket_1_code',
|
|
||||||
'ticket_2_code',
|
|
||||||
'ticket_3_code',
|
|
||||||
'ticket_4_code',
|
|
||||||
'ticket_5_code',
|
|
||||||
'ticket_6_code',
|
|
||||||
'ticket_7_code',
|
|
||||||
'ticket_8_code',
|
|
||||||
'allow_tracking',
|
|
||||||
'agree_to_tc',
|
|
||||||
'hide',
|
|
||||||
'priority',
|
|
||||||
'notes'
|
|
||||||
];
|
|
||||||
|
|
||||||
// *** Passcode logic
|
// *** Passcode logic
|
||||||
let url_passcode = $derived(page.url?.searchParams?.get('passcode') ?? '');
|
let url_passcode = $derived(page.url?.searchParams?.get('passcode') ?? '');
|
||||||
@@ -192,13 +175,24 @@ function send_review_email() {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// *** Resolve editable field list based on access level
|
// *** Resolve editable field list based on access level.
|
||||||
// Uses $derived.by() to return the array directly (not a function).
|
// Reads from event.mod_badges_json.edit_permissions when available;
|
||||||
// TODO: Read from event.mod_badges_json.edit_permissions for per-event config.
|
// falls back to module-level defaults from ae_events_stores__badges_defaults.
|
||||||
let can_edit_fields: string[] = $derived.by(() => {
|
let can_edit_fields: string[] = $derived.by(() => {
|
||||||
if (is_administrator) return ['*'];
|
if (is_administrator) return ['*'];
|
||||||
if (is_trusted) return default_trusted_fields;
|
const edit_permissions = $lq__event_obj?.mod_badges_json?.edit_permissions;
|
||||||
if (has_attendee_access) return default_authenticated_fields;
|
if (is_trusted) {
|
||||||
|
const cfg = edit_permissions?.trusted?.can_edit;
|
||||||
|
if (cfg === '*') return ['*'];
|
||||||
|
if (Array.isArray(cfg) && cfg.length > 0) return cfg;
|
||||||
|
return default_trusted_can_edit;
|
||||||
|
}
|
||||||
|
if (has_attendee_access) {
|
||||||
|
const cfg = edit_permissions?.authenticated?.can_edit;
|
||||||
|
if (cfg === '*') return ['*'];
|
||||||
|
if (Array.isArray(cfg) && cfg.length > 0) return cfg;
|
||||||
|
return default_authenticated_can_edit;
|
||||||
|
}
|
||||||
return [];
|
return [];
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -144,7 +144,7 @@ function handle_qr_scan_result(event: {
|
|||||||
|
|
||||||
<input
|
<input
|
||||||
type="search"
|
type="search"
|
||||||
placeholder={$lq__event_obj?.mod_badges_json?.badge_id_only_search ? 'Badge ID' : 'name, email'}
|
placeholder="name, email"
|
||||||
id="badge_fulltext_search_qry_str"
|
id="badge_fulltext_search_qry_str"
|
||||||
bind:value={badges_loc.current.fulltext_search_qry_str}
|
bind:value={badges_loc.current.fulltext_search_qry_str}
|
||||||
autocomplete="off"
|
autocomplete="off"
|
||||||
@@ -201,7 +201,7 @@ function handle_qr_scan_result(event: {
|
|||||||
<div
|
<div
|
||||||
class="flex flex-row flex-wrap items-center justify-center gap-2 opacity-70 transition-all hover:opacity-100">
|
class="flex flex-row flex-wrap items-center justify-center gap-2 opacity-70 transition-all hover:opacity-100">
|
||||||
{#if $events_sess.badges.show_form__search}
|
{#if $events_sess.badges.show_form__search}
|
||||||
{#if $lq__event_obj?.mod_badges_json?.enable_search_qr ?? true}
|
{#if badges_loc.current.enable_search_qr ?? true}
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
onclick={() => {
|
onclick={() => {
|
||||||
|
|||||||
@@ -98,7 +98,6 @@ const all_staff_field_options = [
|
|||||||
// Draft state — initialized from the live event config
|
// Draft state — initialized from the live event config
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
const cfg_defaults: BadgesRemoteCfg = {
|
const cfg_defaults: BadgesRemoteCfg = {
|
||||||
badge_id_only_search: false,
|
|
||||||
enable_mass_print: false,
|
enable_mass_print: false,
|
||||||
enable_add_badge_btn: false,
|
enable_add_badge_btn: false,
|
||||||
enable_upload_badge_li_btn: false,
|
enable_upload_badge_li_btn: false,
|
||||||
@@ -309,7 +308,6 @@ function toggle(key: string) {
|
|||||||
{#if sections.ui}
|
{#if sections.ui}
|
||||||
<div class="border-surface-200-800 grid grid-cols-2 gap-3 border-t px-4 py-3">
|
<div class="border-surface-200-800 grid grid-cols-2 gap-3 border-t px-4 py-3">
|
||||||
{#each [
|
{#each [
|
||||||
{ field: 'badge_id_only_search' as const, label: 'Badge ID Only Search' },
|
|
||||||
{ field: 'enable_mass_print' as const, label: 'Enable Mass Print' },
|
{ field: 'enable_mass_print' as const, label: 'Enable Mass Print' },
|
||||||
{ field: 'enable_add_badge_btn' as const, label: 'Enable Add Badge Button' },
|
{ field: 'enable_add_badge_btn' as const, label: 'Enable Add Badge Button' },
|
||||||
{ field: 'enable_upload_badge_li_btn' as const, label: 'Enable Upload Badge List Button' },
|
{ field: 'enable_upload_badge_li_btn' as const, label: 'Enable Upload Badge List Button' },
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ import { db_events, type Event } from '$lib/ae_events/db_events';
|
|||||||
import { onMount } from 'svelte';
|
import { onMount } from 'svelte';
|
||||||
import { events_func } from '$lib/ae_events/ae_events_functions';
|
import { events_func } from '$lib/ae_events/ae_events_functions';
|
||||||
import { ae_loc, ae_api } from '$lib/stores/ae_stores';
|
import { ae_loc, ae_api } from '$lib/stores/ae_stores';
|
||||||
|
import { badges_loc } from '$lib/stores/ae_events_stores__badges.svelte';
|
||||||
import AE_Comp_Editor_CodeMirror from '$lib/elements/element_editor_codemirror.svelte';
|
import AE_Comp_Editor_CodeMirror from '$lib/elements/element_editor_codemirror.svelte';
|
||||||
import Ae_comp_event_settings_form from './ae_comp__event_settings_form.svelte';
|
import Ae_comp_event_settings_form from './ae_comp__event_settings_form.svelte';
|
||||||
import Ae_comp_event_settings_basic_form from './ae_comp__event_settings_basic_form.svelte';
|
import Ae_comp_event_settings_basic_form from './ae_comp__event_settings_basic_form.svelte';
|
||||||
@@ -79,6 +80,13 @@ onMount(() => {
|
|||||||
null,
|
null,
|
||||||
4
|
4
|
||||||
);
|
);
|
||||||
|
// Mirror server-side badges config into local persisted store
|
||||||
|
if (event_obj?.mod_badges_json) {
|
||||||
|
events_func.sync_config__event_badges({
|
||||||
|
badges_cfg_remote: event_obj.mod_badges_json,
|
||||||
|
log_lvl: 0
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -122,11 +130,11 @@ async function handle_save(field_name: string, data: any) {
|
|||||||
<summary class="summary text-error-500 font-bold"
|
<summary class="summary text-error-500 font-bold"
|
||||||
>Admin Tools</summary>
|
>Admin Tools</summary>
|
||||||
<div class="space-y-4 p-4">
|
<div class="space-y-4 p-4">
|
||||||
{#if (event_obj?.mod_badges_json?.enable_add_badge_btn ?? true) || (event_obj?.mod_badges_json?.enable_upload_badge_li_btn ?? true)}
|
{#if (badges_loc.current.enable_add_badge_btn ?? true) || (badges_loc.current.enable_upload_badge_li_btn ?? true)}
|
||||||
<div class="card rounded-md border p-4 text-center">
|
<div class="card rounded-md border p-4 text-center">
|
||||||
<h4 class="h4">Badge Operations</h4>
|
<h4 class="h4">Badge Operations</h4>
|
||||||
<div class="mt-2 flex flex-wrap justify-center gap-2">
|
<div class="mt-2 flex flex-wrap justify-center gap-2">
|
||||||
{#if event_obj?.mod_badges_json?.enable_add_badge_btn ?? true}
|
{#if badges_loc.current.enable_add_badge_btn ?? true}
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
class="btn btn-primary"
|
class="btn btn-primary"
|
||||||
@@ -135,7 +143,7 @@ async function handle_save(field_name: string, data: any) {
|
|||||||
<Plus size="1em" aria-hidden="true" /> Add New Badge
|
<Plus size="1em" aria-hidden="true" /> Add New Badge
|
||||||
</button>
|
</button>
|
||||||
{/if}
|
{/if}
|
||||||
{#if event_obj?.mod_badges_json?.enable_upload_badge_li_btn ?? true}
|
{#if badges_loc.current.enable_upload_badge_li_btn ?? true}
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
class="btn btn-primary ml-2"
|
class="btn btn-primary ml-2"
|
||||||
@@ -149,7 +157,7 @@ async function handle_save(field_name: string, data: any) {
|
|||||||
</div>
|
</div>
|
||||||
{/if}
|
{/if}
|
||||||
|
|
||||||
{#if event_obj?.mod_badges_json?.enable_mass_print ?? true}
|
{#if badges_loc.current.enable_mass_print ?? true}
|
||||||
<div class="card rounded-md border p-4 text-center">
|
<div class="card rounded-md border p-4 text-center">
|
||||||
<h4 class="h4">Mass Print Options</h4>
|
<h4 class="h4">Mass Print Options</h4>
|
||||||
<div class="mt-2 flex flex-wrap justify-center gap-2">
|
<div class="mt-2 flex flex-wrap justify-center gap-2">
|
||||||
|
|||||||
@@ -106,15 +106,6 @@ function save() {
|
|||||||
<div class="space-y-4">
|
<div class="space-y-4">
|
||||||
{#if mod_badges_json}
|
{#if mod_badges_json}
|
||||||
<div class="grid grid-cols-2 gap-4">
|
<div class="grid grid-cols-2 gap-4">
|
||||||
<div>
|
|
||||||
<label class="label">
|
|
||||||
<input
|
|
||||||
type="checkbox"
|
|
||||||
class="checkbox"
|
|
||||||
bind:checked={mod_badges_json.badge_id_only_search} />
|
|
||||||
<span>Badge ID Only Search</span>
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
<div>
|
<div>
|
||||||
<label class="label">
|
<label class="label">
|
||||||
<input
|
<input
|
||||||
|
|||||||
Reference in New Issue
Block a user