From f87ab1025114ea23900b7d044c8807f0a0248e58 Mon Sep 17 00:00:00 2001 From: Scott Idem Date: Fri, 27 Mar 2026 09:59:46 -0400 Subject: [PATCH] feat(badges): add manual one-off badge create modal MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Two-step creation: POST event_person first, then event_badge linked to it. Badge create route (event_person parent) pending backend fix — frontend is ready and passing event_person_id + event_badge_template_id in payload. - ae_events__event_person.ts: new create function (nested under event) - ae_events_functions.ts: export create_ae_obj__event_person - ae_comp__badge_create_form.svelte: modal form with live name preview, conditional display-name override, template selector (auto-selects when only one template), badge_type_code_li derived from selected template's badge_type_list JSON, two-step submit status labels - +page.svelte: load template list via liveQuery, wire Create Badge button (edit_mode only), native modal with backdrop, remote-first refresh on success Co-Authored-By: Claude Sonnet 4.6 --- src/lib/ae_events/ae_events__event_person.ts | 42 +++ src/lib/ae_events/ae_events_functions.ts | 5 + .../[event_id]/(badges)/badges/+page.svelte | 80 ++++- .../badges/ae_comp__badge_create_form.svelte | 300 +++++++++++++----- 4 files changed, 344 insertions(+), 83 deletions(-) create mode 100644 src/lib/ae_events/ae_events__event_person.ts diff --git a/src/lib/ae_events/ae_events__event_person.ts b/src/lib/ae_events/ae_events__event_person.ts new file mode 100644 index 00000000..5fe37495 --- /dev/null +++ b/src/lib/ae_events/ae_events__event_person.ts @@ -0,0 +1,42 @@ +import type { key_val } from '$lib/stores/ae_stores'; +import { api } from '$lib/api/api'; + +const ae_promises: key_val = {}; + +/** + * create_ae_obj__event_person + * Creates a new event_person record linked to an event. + * Used as the first step of manual one-off badge creation. + * The returned event_person_id is then passed to create_ae_obj__event_badge. + */ +export async function create_ae_obj__event_person({ + api_cfg, + event_id, + data_kv, + params = {}, + log_lvl = 0 +}: { + api_cfg: any; + event_id: string; + data_kv: key_val; + params?: key_val; + log_lvl?: number; +}): Promise { + if (log_lvl) { + console.log( + `*** create_ae_obj__event_person() *** event_id=${event_id}` + ); + } + + ae_promises.create__event_person = await api.create_nested_obj({ + api_cfg, + parent_type: 'event', + parent_id: event_id, + child_type: 'event_person', + fields: data_kv, + params, + log_lvl + }); + + return ae_promises.create__event_person; +} diff --git a/src/lib/ae_events/ae_events_functions.ts b/src/lib/ae_events/ae_events_functions.ts index 1242e38b..ab7a991e 100644 --- a/src/lib/ae_events/ae_events_functions.ts +++ b/src/lib/ae_events/ae_events_functions.ts @@ -33,6 +33,8 @@ import * as event_presenter from '$lib/ae_events/ae_events__event_presenter'; import * as event_badge from '$lib/ae_events/ae_events__event_badge'; +import { create_ae_obj__event_person } from '$lib/ae_events/ae_events__event_person'; + import * as event_badge_template from '$lib/ae_events/ae_events__event_badge_template'; const export_obj = { @@ -46,6 +48,9 @@ const export_obj = { update_ae_obj__event: event.update_ae_obj__event, sync_config__event_pres_mgmt: event.sync_config__event_pres_mgmt, + // Event Person + create_ae_obj__event_person: create_ae_obj__event_person, + // Event Badges load_ae_obj_id__event_badge: event_badge.load_ae_obj_id__event_badge, load_ae_obj_li__event_badge: event_badge.load_ae_obj_li__event_badge, diff --git a/src/routes/events/[event_id]/(badges)/badges/+page.svelte b/src/routes/events/[event_id]/(badges)/badges/+page.svelte index 0e7576ab..972a0ce9 100644 --- a/src/routes/events/[event_id]/(badges)/badges/+page.svelte +++ b/src/routes/events/[event_id]/(badges)/badges/+page.svelte @@ -28,8 +28,32 @@ import { events_func } from '$lib/ae_events/ae_events_functions'; import Comp_badge_search from './ae_comp__badge_search.svelte'; import Comp_badge_obj_li from './ae_comp__badge_obj_li.svelte'; +import Comp_badge_create_form from './ae_comp__badge_create_form.svelte'; -import { LoaderCircle } from '@lucide/svelte'; +import { LoaderCircle, UserPlus } from '@lucide/svelte'; + +// Load templates for this event so the create form can show the selector and +// derive badge_type_code_li from whichever template the user picks. +$effect(() => { + const event_id = $events_slct?.event_id; + if (!event_id) return; + events_func.load_ae_obj_li__event_badge_template({ + api_cfg: $ae_api, + event_id, + log_lvl: 0 + }); +}); + +let lq__badge_template_li = $derived( + liveQuery(async () => { + const event_id = $events_slct?.event_id; + if (!event_id) return []; + return await db_events.badge_template + .where('event_id') + .equals(event_id) + .sortBy('name'); + }) +); // *** Initialization & Store Guard *** // Ensure all search fields are initialized to prevent circular undefined triggers if ($events_loc.badges) { @@ -52,6 +76,7 @@ if ($events_loc.badges) { // Variables let show_create_badge_modal: boolean = $state(false); let show_upload_badge_modal: boolean = $state(false); +let create_badge_dialog: HTMLDialogElement | undefined = $state(); let event_badge_id_li: Array = $state([]); let search_debounce_timer: any = null; @@ -362,6 +387,49 @@ async function handle_search_refresh(params: any) { +{#if $ae_loc.edit_mode} +
+ +
+{/if} + + + { if (e.target === create_badge_dialog) { create_badge_dialog?.close(); show_create_badge_modal = false; } }} + onclose={() => { show_create_badge_modal = false; }}> +
+

Create Badge

+
+ {#if show_create_badge_modal} + { + create_badge_dialog?.close(); + show_create_badge_modal = false; + // Trigger a remote-first refresh so the new badge appears in results + $events_loc.badges.search_version = ($events_loc.badges.search_version ?? 0) + 1; + $events_loc.badges.qry__remote_first = true; + }} + oncancel={() => { + create_badge_dialog?.close(); + show_create_badge_modal = false; + }} /> + {/if} +
+ {#if $events_sess?.badges?.search_status === 'loading' && event_badge_id_li.length === 0}
@@ -371,3 +439,13 @@ async function handle_search_refresh(params: any) { {:else} {/if} + + 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 f42f68df..d5c51b9d 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 @@ -1,20 +1,38 @@ -
+ +
+ + +
+ + +
+ Badge will show: + {full_name_preview} + + +
+ + {#if show_name_override} + + {/if} + + {#if template_li.length > 1} + + + {/if} - - - - - -
+
+ + + + + + + {#if submit_status === 'error'} +

{error_msg || 'An error occurred. Please try again.'}

+ {/if} + +
+ {#if is_submitting} + + + {step_label} + + {/if}
- -{#if submit_status === 'success'} -

Badge created successfully!

-{:else if submit_status === 'error'} -

Error creating badge. Please try again.

-{/if}