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',
'code',
'type_code',
'event_location_id',
'poc_agree',
'poc_kv_json',
'name',

View File

@@ -17,6 +17,7 @@ let {
// Imports
import { Modal } from 'flowbite-svelte';
import { liveQuery } from 'dexie';
import type { key_val } from '$lib/stores/ae_stores';
import { ae_util } from '$lib/ae_utils/ae_utils';
import Element_ae_obj_field_editor from '$lib/elements/element_ae_obj_field_editor.svelte';
@@ -29,6 +30,7 @@ import {
events_slct
} from '$lib/stores/ae_events_stores';
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_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)
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
$events_sess.pres_mgmt.session__updated_on = null;
$effect(() => {
@@ -177,7 +202,33 @@ $effect(() => {
$events_loc.pres_mgmt.time_format
)}
</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
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" />
@@ -228,32 +279,55 @@ $effect(() => {
{/if}
</div>
<!-- Description: shown only when present. Collapsed by default (can be long). -->
{#if $lq__event_session_obj?.description}
<!-- Description: collapsed by default in view mode (can be long).
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
class="border-surface-200-800 bg-surface-50-900 rounded-lg border px-4 py-3">
<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 $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="description"
field_type="textarea"
edit_label="Description"
current_value={$lq__event_session_obj?.description ?? ''}
placeholder="Session description..."
textarea_rows={4}
on_success={() =>
events_func.load_ae_obj_id__event_session({
api_cfg: $ae_api,
event_session_id: $lq__event_session_obj?.id
})}>
<span class="text-xs font-bold tracking-wide uppercase opacity-40">
Description
</span>
</Element_ae_obj_field_editor>
{:else}
<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}
</div>
{/if}