feat(pres_mgmt): restore location and description editing in session view

Event location (FK lookup) and description were both visible in the session
view but had no edit controls — lost during V3 migration. Restored both:

- event_location_id: select dropdown populated from this event's location list
  (liveQuery on db_events.location filtered by event_id from the session object)
- description: textarea editor shown directly in edit_mode (no collapse needed
  when actively editing)

Also added event_location_id to editable_fields__event_session, which was
missing and would have caused backend rejections on PATCH.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Scott Idem
2026-04-02 13:44:00 -04:00
parent 2a5adda6cb
commit 75664ad2e1
2 changed files with 100 additions and 25 deletions

View File

@@ -2,6 +2,7 @@ export const editable_fields__event_session = [
'external_id', 'external_id',
'code', 'code',
'type_code', 'type_code',
'event_location_id',
'poc_agree', 'poc_agree',
'poc_kv_json', 'poc_kv_json',
'name', 'name',

View File

@@ -17,6 +17,7 @@ let {
// Imports // Imports
import { Modal } from 'flowbite-svelte'; import { Modal } from 'flowbite-svelte';
import { liveQuery } from 'dexie';
import type { key_val } from '$lib/stores/ae_stores'; import type { key_val } from '$lib/stores/ae_stores';
import { ae_util } from '$lib/ae_utils/ae_utils'; import { ae_util } from '$lib/ae_utils/ae_utils';
import Element_ae_obj_field_editor from '$lib/elements/element_ae_obj_field_editor.svelte'; import Element_ae_obj_field_editor from '$lib/elements/element_ae_obj_field_editor.svelte';
@@ -29,6 +30,7 @@ import {
events_slct events_slct
} from '$lib/stores/ae_events_stores'; } from '$lib/stores/ae_events_stores';
import { events_func } from '$lib/ae_events/ae_events_functions'; import { events_func } from '$lib/ae_events/ae_events_functions';
import { db_events } from '$lib/ae_events/db_events';
import Comp_event_session_poc_profile from './ae_comp__event_session_poc_profile.svelte'; import Comp_event_session_poc_profile from './ae_comp__event_session_poc_profile.svelte';
import Comp_event_session_poc_form_agree from './ae_comp__event_session_poc_form_agree.svelte'; import Comp_event_session_poc_form_agree from './ae_comp__event_session_poc_form_agree.svelte';
@@ -52,6 +54,29 @@ $events_sess.pres_mgmt.show_content__presenter_start = false;
// Description expand/collapse — collapsed by default (descriptions can be long) // Description expand/collapse — collapsed by default (descriptions can be long)
let desc_expanded = $state(false); let desc_expanded = $state(false);
// Location list for the session's event — used to build the select options in edit mode.
// WHY: event_location_id is a FK; the editor needs the full list so staff can pick from
// known rooms instead of typing a raw UUID. Keyed by event_id from the session object.
let current_event_id = $derived($lq__event_session_obj?.event_id ?? null);
let lq__event_location_obj_li = $derived(
liveQuery(async () => {
if (!current_event_id) return [];
return await db_events.location
.where('event_id')
.equals(current_event_id)
.sortBy('name');
})
);
// { event_location_id: location_name } — consumed by Element_ae_obj_field_editor select
let location_select_options = $derived(
Object.fromEntries(
($lq__event_location_obj_li ?? []).map((loc) => [
(loc as { event_location_id: string }).event_location_id,
(loc as { name: string }).name
])
)
);
// QR Code Generation Logic // QR Code Generation Logic
$events_sess.pres_mgmt.session__updated_on = null; $events_sess.pres_mgmt.session__updated_on = null;
$effect(() => { $effect(() => {
@@ -177,7 +202,33 @@ $effect(() => {
$events_loc.pres_mgmt.time_format $events_loc.pres_mgmt.time_format
)} )}
</span> </span>
{#if $lq__event_session_obj.event_location_name}
<!-- Room/Location: editable in edit_mode via select from this event's locations.
WHY: event_location_id is a FK — staff need to pick from known rooms,
not type a raw UUID. The liveQuery above loads locations for this event. -->
{#if $ae_loc.edit_mode}
<Element_ae_obj_field_editor
display_block={true}
object_type="event_session"
object_id={$lq__event_session_obj.id}
field_name="event_location_id"
field_type="select"
edit_label="Room / Location"
current_value={$lq__event_session_obj.event_location_id ?? null}
allow_null={true}
select_options={location_select_options}
on_success={() =>
events_func.load_ae_obj_id__event_session({
api_cfg: $ae_api,
event_session_id: $lq__event_session_obj.id
})}>
<span
class="bg-tertiary-500/10 text-tertiary-700 dark:text-tertiary-300 inline-flex items-center gap-1.5 rounded-full px-3 py-1 text-sm font-semibold transition-colors duration-200">
<MapPin size="1em" class="text-xs" />
{$lq__event_session_obj.event_location_name ?? 'No room assigned'}
</span>
</Element_ae_obj_field_editor>
{:else if $lq__event_session_obj.event_location_name}
<span <span
class="bg-tertiary-500/10 text-tertiary-700 dark:text-tertiary-300 inline-flex items-center gap-1.5 rounded-full px-3 py-1 text-sm font-semibold transition-colors duration-200"> class="bg-tertiary-500/10 text-tertiary-700 dark:text-tertiary-300 inline-flex items-center gap-1.5 rounded-full px-3 py-1 text-sm font-semibold transition-colors duration-200">
<MapPin size="1em" class="text-xs" /> <MapPin size="1em" class="text-xs" />
@@ -228,32 +279,55 @@ $effect(() => {
{/if} {/if}
</div> </div>
<!-- Description: shown only when present. Collapsed by default (can be long). --> <!-- Description: collapsed by default in view mode (can be long).
{#if $lq__event_session_obj?.description} In edit mode, show the editor directly — no collapse needed when staff are updating it. -->
{#if $lq__event_session_obj?.description || $ae_loc.edit_mode}
<div <div
class="border-surface-200-800 bg-surface-50-900 rounded-lg border px-4 py-3"> class="border-surface-200-800 bg-surface-50-900 rounded-lg border px-4 py-3">
<button {#if $ae_loc.edit_mode}
type="button" <Element_ae_obj_field_editor
class="focus-visible:ring-primary-500 flex w-full items-center justify-between gap-2 rounded text-left focus-visible:ring-2" display_block={true}
onclick={() => (desc_expanded = !desc_expanded)} object_type="event_session"
aria-expanded={desc_expanded}> object_id={$lq__event_session_obj?.id}
<span field_name="description"
class="text-xs font-bold tracking-wide uppercase opacity-40" field_type="textarea"
>Description</span> edit_label="Description"
<span class="shrink-0 text-xs opacity-40"> current_value={$lq__event_session_obj?.description ?? ''}
{#if desc_expanded} placeholder="Session description..."
<ChevronUp size="1em" /> textarea_rows={4}
<span class="sr-only">Collapse description</span> on_success={() =>
{:else} events_func.load_ae_obj_id__event_session({
<ChevronDown size="1em" /> api_cfg: $ae_api,
<span class="sr-only">Expand description</span> event_session_id: $lq__event_session_obj?.id
{/if} })}>
</span> <span class="text-xs font-bold tracking-wide uppercase opacity-40">
</button> Description
{#if desc_expanded} </span>
<p class="mt-2 text-sm leading-relaxed whitespace-pre-wrap"> </Element_ae_obj_field_editor>
{$lq__event_session_obj.description} {:else}
</p> <button
type="button"
class="focus-visible:ring-primary-500 flex w-full items-center justify-between gap-2 rounded text-left focus-visible:ring-2"
onclick={() => (desc_expanded = !desc_expanded)}
aria-expanded={desc_expanded}>
<span
class="text-xs font-bold tracking-wide uppercase opacity-40"
>Description</span>
<span class="shrink-0 text-xs opacity-40">
{#if desc_expanded}
<ChevronUp size="1em" />
<span class="sr-only">Collapse description</span>
{:else}
<ChevronDown size="1em" />
<span class="sr-only">Expand description</span>
{/if}
</span>
</button>
{#if desc_expanded}
<p class="mt-2 text-sm leading-relaxed whitespace-pre-wrap">
{$lq__event_session_obj?.description}
</p>
{/if}
{/if} {/if}
</div> </div>
{/if} {/if}