pres_mgmt: redesign Session View, clean Presentation list, fix transitions

- session_view.svelte: replace flat <ul> with hero card layout
  - Name as <h1>, date/time chip (primary teal), room chip (tertiary indigo)
  - QR only rendered when URL is string (not true loading placeholder)
  - Skeleton pulse placeholders while LiveQuery resolves
  - Description in surface card with uppercase label
  - Accessible: aria labels, focus rings, aria-live on no-results section
- ae_comp__event_session_obj_li.svelte:
  - variant-soft-warning (Skeleton v3) -> preset-tonal-warning (v4)
  - Add transition-colors duration-200 to <tr> rows and session <a> links
- ae_comp__event_presentation_obj_li.svelte:
  - Remove debug breakpoint border colors (red/yellow/gray)
  - overflow-x-scroll -> overflow-x-auto
  - Remove heavy preset-filled-surface-400-600 from <ul> container
  - <li> cards: surface tokens, rounded-xl, shadow-sm, transition
  - <h4> title bar: bg-surface-100-900 with flex wrap layout
  - Code badge: hardcoded yellow -> preset-tonal-warning
  - Description <pre>: hardcoded bg-gray-100 -> bg-surface-100-900
- pres_mgmt/+page.svelte: 'no results' section
  - bg-yellow-100 + text-yellow-500 -> preset-tonal-warning
  - Search icon, aria-live, cleaner list in surface card
- [session_id]/+page.svelte: rounded-container-token (v3) -> rounded-xl
This commit is contained in:
Scott Idem
2026-03-06 21:15:27 -05:00
parent b39ce19fdc
commit 1dd8e35720
5 changed files with 154 additions and 141 deletions

View File

@@ -552,29 +552,25 @@
</div> </div>
{:else} {:else}
<section <section
class="text-center text-2xl bg-yellow-100 p-4 rounded-md lg:max-w-lg space-y-2 mx-auto" class="preset-tonal-warning p-6 rounded-xl lg:max-w-lg space-y-4 mx-auto shadow-sm"
role="status"
aria-live="polite"
> >
<div> <div class="flex flex-col items-center gap-2 text-center">
<span <span class="fas fa-search text-3xl opacity-50" aria-hidden="true"></span>
class="fas fa-exclamation-triangle text-2xl text-yellow-500" <strong class="text-xl">No sessions found</strong>
></span> <p class="text-base opacity-80">
<strong>No results to show</strong> Use the search bar above to find your session.
<span </p>
class="fas fa-exclamation-triangle text-2xl text-yellow-500"
></span>
<br />
<div class="text-lg">
Please use the search above to find your session.
</div>
</div> </div>
<div> <div class="bg-surface-50-900/60 rounded-lg p-3">
<strong>Search by:</strong> <span class="text-xs font-bold uppercase tracking-wide opacity-50 block mb-2">Search by any of:</span>
<ul class="list-disc list-inside text-lg text-left"> <ul class="space-y-1 text-sm">
<li>Session name</li> <li class="flex items-center gap-1.5"><span class="fas fa-angle-right text-xs opacity-50" aria-hidden="true"></span> Session name</li>
<li>Session description</li> <li class="flex items-center gap-1.5"><span class="fas fa-angle-right text-xs opacity-50" aria-hidden="true"></span> Session description</li>
<li>Presentation name</li> <li class="flex items-center gap-1.5"><span class="fas fa-angle-right text-xs opacity-50" aria-hidden="true"></span> Presentation name</li>
<li>Presenter names</li> <li class="flex items-center gap-1.5"><span class="fas fa-angle-right text-xs opacity-50" aria-hidden="true"></span> Presenter names</li>
<li>Presenter ID (member ID)</li> <li class="flex items-center gap-1.5"><span class="fas fa-angle-right text-xs opacity-50" aria-hidden="true"></span> Presenter ID (member ID)</li>
</ul> </ul>
</div> </div>
</section> </section>

View File

@@ -101,8 +101,8 @@
<!-- Pass observable STORES to child components (they use $) --> <!-- Pass observable STORES to child components (they use $) -->
<Session_page_menu {data} {lq__event_session_obj} {lq__auth__event_presenter_obj} /> <Session_page_menu {data} {lq__event_session_obj} {lq__auth__event_presenter_obj} />
<!-- Metadata Section --> <!-- Metadata Section: Session identity card — name, time, room, host, description -->
<div class="bg-surface-50-950 p-4 rounded-container-token shadow-sm border border-surface-200-800"> <div class="bg-surface-50-950 p-4 rounded-xl shadow-sm border border-surface-200-800">
<Session_view <Session_view
{lq__event_presenter_obj} {lq__event_presenter_obj}
{lq__event_session_obj} {lq__event_session_obj}

View File

@@ -44,7 +44,7 @@
if ($lq__event_session_obj?.id && $events_loc.pres_mgmt.show_content__session_qr && !$events_sess.pres_mgmt.session_qr_url[$lq__event_session_obj.id]) { if ($lq__event_session_obj?.id && $events_loc.pres_mgmt.show_content__session_qr && !$events_sess.pres_mgmt.session_qr_url[$lq__event_session_obj.id]) {
$events_sess.pres_mgmt.session_qr_url[$lq__event_session_obj.id] = true; $events_sess.pres_mgmt.session_qr_url[$lq__event_session_obj.id] = true;
let url_str = encodeURI(`${$ae_loc.url_origin}/events/${$lq__event_session_obj.event_id}/session/${$lq__event_session_obj.id}`); let url_str = encodeURI(`${$ae_loc.url_origin}/events/${$lq__event_session_obj.event_id}/session/${$lq__event_session_obj.id}`);
core_func.generate_qr_code({ core_func.generate_qr_code({
api_cfg: $ae_api, api_cfg: $ae_api,
account_id: $slct.account_id, account_id: $slct.account_id,
@@ -59,106 +59,132 @@
}); });
</script> </script>
<!-- STABLE LAYOUT: No top-level #if gate. Children mount immediately. --> <!--
<section class="p-2 space-y-4"> SESSION VIEW — Speaker Ready Room & Remote Upload
{#if $lq__event_session_obj && $events_sess.pres_mgmt.session_qr_url[$lq__event_session_obj.id]} Primary users: Presenters uploading files (remote) + Staff helping presenters (onsite).
<div class="float-right m-1 p-1 flex flex-col items-center outline outline-gray-200 group transition-all"> Design intent: "Is this the right session?" must be answerable in <3 seconds.
<button Show: name, time, room, host, description. Hide admin noise unless edit_mode.
type="button" Section 508: all interactive elements labelled, focus rings, sufficient contrast.
onclick={() => $events_sess.pres_mgmt.qr_bigger = !$events_sess.pres_mgmt.qr_bigger} -->
class="qr_code_btn" <section class="space-y-3">
title="Toggle QR Code size"
> <!-- SESSION HERO: Name + Schedule + Room -->
<img <div class="rounded-xl border border-surface-200-800 bg-surface-50-900 shadow-sm overflow-hidden">
src={$events_sess.pres_mgmt.session_qr_url[$lq__event_session_obj.id]} <div class="px-4 pt-4 pb-3 flex flex-col gap-3">
class="qr_code h-32 w-32 transition-all duration-500"
class:h-48={$events_sess.pres_mgmt.qr_bigger} <!-- QR code: floats top-right, compact by default, toggle to enlarge.
alt="URL QR code" Only rendered once the async URL is resolved (string), never while
/> it is still the boolean `true` loading placeholder. -->
</button> {#if $lq__event_session_obj && typeof $events_sess.pres_mgmt.session_qr_url?.[$lq__event_session_obj.id] === 'string'}
<MyClipboard <div class="float-right ml-3 mb-1 flex flex-col items-center gap-1">
value={encodeURI(`${$ae_loc.url_origin}/events/${$lq__event_session_obj.event_id}/session/${$lq__event_session_obj.id}`)} <button
btn_class="m-1 btn btn-sm preset-tonal-warning" type="button"
hide_icon={true} onclick={() => $events_sess.pres_mgmt.qr_bigger = !$events_sess.pres_mgmt.qr_bigger}
hide_text={true} class="rounded focus-visible:ring-2 focus-visible:ring-primary-500"
> title="Toggle QR code size"
<span class="fas fa-link"></span> aria-label="Toggle QR code size"
</MyClipboard> >
<img
src={$events_sess.pres_mgmt.session_qr_url[$lq__event_session_obj.id]}
class="transition-all duration-500 rounded border border-surface-200-800"
class:h-20={!$events_sess.pres_mgmt.qr_bigger}
class:w-20={!$events_sess.pres_mgmt.qr_bigger}
class:h-40={$events_sess.pres_mgmt.qr_bigger}
class:w-40={$events_sess.pres_mgmt.qr_bigger}
alt="QR code link to this session page"
/>
</button>
<MyClipboard
value={encodeURI(`${$ae_loc.url_origin}/events/${$lq__event_session_obj.event_id}/session/${$lq__event_session_obj.id}`)}
btn_class="btn btn-sm preset-tonal-surface text-xs"
hide_icon={false}
hide_text={true}
>
<span class="fas fa-link" aria-hidden="true"></span>
</MyClipboard>
</div>
{/if}
<!-- Session Name: the primary identity check -->
{#if $lq__event_session_obj}
<div>
<Element_ae_obj_field_editor_v3
object_type={'event_session'}
object_id={$lq__event_session_obj.id}
field_name={'name'}
field_type={'text'}
current_value={$lq__event_session_obj.name}
on_success={() => events_func.load_ae_obj_id__event_session({api_cfg: $ae_api, event_session_id: $lq__event_session_obj.id})}
>
<h1 class="text-2xl font-bold leading-snug">{$lq__event_session_obj.name}</h1>
</Element_ae_obj_field_editor_v3>
{#if $ae_loc.edit_mode}
<span class="badge preset-tonal-surface text-xs mt-1">code: {$lq__event_session_obj.code}</span>
{/if}
</div>
{:else}
<!-- Skeleton placeholder while LiveQuery resolves -->
<div class="h-7 w-2/3 bg-surface-200-800 animate-pulse rounded"></div>
{/if}
<!-- Date/Time + Room as info chips -->
{#if $lq__event_session_obj}
<div class="flex flex-wrap gap-2 items-center">
<span class="inline-flex items-center gap-1.5 text-sm font-semibold px-3 py-1 rounded-full bg-primary-500/10 text-primary-700 dark:text-primary-300 transition-colors duration-200">
<span class="fas fa-clock text-xs" aria-hidden="true"></span>
{ae_util.iso_datetime_formatter($lq__event_session_obj.start_datetime, 'dddd')},
{ae_util.iso_datetime_formatter($lq__event_session_obj.start_datetime, $events_loc.pres_mgmt.datetime_format)}
&ndash;
{ae_util.iso_datetime_formatter($lq__event_session_obj.end_datetime, $events_loc.pres_mgmt.time_format)}
</span>
{#if $lq__event_session_obj.event_location_name}
<span class="inline-flex items-center gap-1.5 text-sm font-semibold px-3 py-1 rounded-full bg-tertiary-500/10 text-tertiary-700 dark:text-tertiary-300 transition-colors duration-200">
<span class="fas fa-map-marker-alt text-xs" aria-hidden="true"></span>
{$lq__event_session_obj.event_location_name}
</span>
{/if}
</div>
{:else}
<div class="h-5 w-1/2 bg-surface-200-800 animate-pulse rounded-full"></div>
{/if}
</div>
</div>
<!-- Host / POC: visible when assigned and layout config allows -->
<div class:hidden={$events_loc.pres_mgmt?.hide__session_poc}>
{#if $lq__event_session_obj?.poc_person_id}
<div class="flex items-center gap-2">
<span class="text-sm font-semibold opacity-60">Host:</span>
<button
type="button"
class="btn btn-sm preset-tonal-primary transition-colors duration-200"
onclick={() => $events_sess.pres_mgmt.show__session_poc_profile = true}
aria-haspopup="dialog"
>
<span class="fas fa-id-card mr-1" aria-hidden="true"></span>
{$lq__event_session_obj.poc_person_full_name}
</button>
<Modal title="Host Profile" bind:open={$events_sess.pres_mgmt.show__session_poc_profile}>
<Comp_event_session_poc_profile lq__event_session_obj={$lq__event_session_obj} />
{#snippet footer()}
<button onclick={() => $events_sess.pres_mgmt.show__session_poc_profile = false} class="btn preset-tonal-warning">Close</button>
{/snippet}
</Modal>
</div>
{:else if $lq__event_session_obj && !$events_loc.pres_mgmt?.hide__session_poc}
<span class="text-sm opacity-40 italic">No host assigned</span>
{/if}
</div>
<!-- Description: shown only when present —
long descriptions are scrollable via the parent container -->
{#if $lq__event_session_obj?.description}
<div class="rounded-lg border border-surface-200-800 bg-surface-50-900 px-4 py-3">
<span class="text-xs font-bold uppercase tracking-wide opacity-40 block mb-1">Description</span>
<p class="whitespace-pre-wrap text-sm leading-relaxed">{$lq__event_session_obj.description}</p>
</div> </div>
{/if} {/if}
<ul class="space-y-2 px-4">
<!-- Name & Code -->
<li>
{#if $lq__event_session_obj}
<Element_ae_obj_field_editor_v3
object_type={'event_session'}
object_id={$lq__event_session_obj.id}
field_name={'name'}
field_type={'text'}
current_value={$lq__event_session_obj.name}
on_success={() => events_func.load_ae_obj_id__event_session({api_cfg: $ae_api, event_session_id: $lq__event_session_obj.id})}
>
<span class="font-bold text-xl">{$lq__event_session_obj.name}</span>
</Element_ae_obj_field_editor_v3>
{#if $ae_loc.edit_mode}
<span class="badge preset-tonal-surface">code: {$lq__event_session_obj.code}</span>
{/if}
{/if}
</li>
<!-- Date & Time -->
<li>
{#if $lq__event_session_obj}
<span class="text-sm font-semibold opacity-70">Date & Time:</span>
<span class="fas fa-calendar-alt opacity-50 ml-1"></span>
<span class="font-medium">
{ae_util.iso_datetime_formatter($lq__event_session_obj.start_datetime, 'dddd')},
{ae_util.iso_datetime_formatter($lq__event_session_obj.start_datetime, $events_loc.pres_mgmt.datetime_format)}
-
{ae_util.iso_datetime_formatter($lq__event_session_obj.end_datetime, $events_loc.pres_mgmt.time_format)}
</span>
{/if}
</li>
<!-- Host / POC -->
<li class:hidden={$events_loc.pres_mgmt?.hide__session_poc}>
{#if $lq__event_session_obj}
<span class="text-sm font-semibold opacity-70">Host:</span>
{#if $lq__event_session_obj.poc_person_id}
<button
type="button"
class="btn btn-sm preset-tonal-primary"
onclick={() => $events_sess.pres_mgmt.show__session_poc_profile = true}
>
<span class="fas fa-id-card mr-1"></span>
{$lq__event_session_obj.poc_person_full_name}
</button>
<Modal title="Host Profile" bind:open={$events_sess.pres_mgmt.show__session_poc_profile}>
<Comp_event_session_poc_profile lq__event_session_obj={$lq__event_session_obj} />
{#snippet footer()}
<button onclick={() => $events_sess.pres_mgmt.show__session_poc_profile = false} class="btn preset-tonal-warning">Close</button>
{/snippet}
</Modal>
{:else}
<span class="italic opacity-50">Not assigned</span>
{/if}
{/if}
</li>
<!-- Description -->
<li>
{#if $lq__event_session_obj}
<div class="mt-2">
<span class="text-sm font-semibold opacity-70">Description:</span>
{#if $lq__event_session_obj.description}
<pre class="whitespace-pre-wrap p-3 bg-neutral-100 dark:bg-neutral-800 rounded-md mt-1 text-sm">{$lq__event_session_obj.description}</pre>
{:else}
<span class="italic opacity-50 ml-1">No description provided</span>
{/if}
</div>
{/if}
</li>
</ul>
</section> </section>

View File

@@ -53,16 +53,7 @@
</script> </script>
<section <section
class=" class="ae_comp event_presentation_obj_li px-0.5 py-2 space-y-2 min-w-full w-full container overflow-x-auto {container_class_li}"
ae_comp event_presentation_obj_li
border-dashed border-y-transparent border-r-transparent
sm:border-l-red-400 md:border-l-yellow-400 lg:border-l-gray-100
sm:dark:border-l-red-600 md:dark:border-l-yellow-600 lg:dark:border-l-gray-700
px-0.5 py-2 space-y-2
min-w-full
w-full
container overflow-x-scroll {container_class_li}
"
> >
<div class="float-right flex flex-row items-center"> <div class="float-right flex flex-row items-center">
{#if $ae_loc.trusted_access && $ae_loc.edit_mode} {#if $ae_loc.trusted_access && $ae_loc.edit_mode}
@@ -126,9 +117,9 @@
</h3> </h3>
<!-- Show presentations for this LiveQuery --> <!-- Show presentations for this LiveQuery -->
<ul class="space-y-4 p-4 m-2 rounded-md preset-filled-surface-400-600"> <ul class="space-y-4">
{#each lq__event_presentation_obj_li ?? [] as event_presentation_obj (event_presentation_obj.event_presentation_id)} {#each lq__event_presentation_obj_li ?? [] as event_presentation_obj (event_presentation_obj.event_presentation_id)}
<li class="space-y-2 border border-gray-200 p-2 rounded-md"> <li class="space-y-3 border border-surface-200-800 bg-surface-50-900 p-4 rounded-xl shadow-sm transition-colors duration-200">
<div class="float-right space-2 flex flex-row items-center"> <div class="float-right space-2 flex flex-row items-center">
{#if $ae_loc.trusted_access && $ae_loc.edit_mode} {#if $ae_loc.trusted_access && $ae_loc.edit_mode}
<button <button
@@ -169,7 +160,7 @@
{/if} {/if}
</div> </div>
<h4 class="h5 rounded-md p-2 preset-filled-surface-300-700"> <h4 class="text-lg font-bold rounded-lg px-3 py-2 bg-surface-100-900 flex flex-wrap items-center gap-2">
<span <span
class:hidden={!event_presentation_obj.start_datetime || class:hidden={!event_presentation_obj.start_datetime ||
$events_loc.pres_mgmt $events_loc.pres_mgmt
@@ -215,7 +206,7 @@
> >
{#if (event_presentation_obj?.code || event_presentation_obj?.abstract_code) && !$events_loc.pres_mgmt.hide__presentation_code} {#if (event_presentation_obj?.code || event_presentation_obj?.abstract_code) && !$events_loc.pres_mgmt.hide__presentation_code}
<span <span
class="text-sm text-gray-500 bg-yellow-100 p-1 rounded-md border border-yellow-200" class="text-xs preset-tonal-warning px-2 py-0.5 rounded-md leading-none"
title="Presentation code {event_presentation_obj?.code} and abstract code {event_presentation_obj?.abstract_code}" title="Presentation code {event_presentation_obj?.code} and abstract code {event_presentation_obj?.abstract_code}"
> >
<span class="fas fa-barcode"></span> <span class="fas fa-barcode"></span>
@@ -370,7 +361,7 @@
</button> </button>
<pre <pre
class="whitespace-pre-wrap p-2 bg-gray-100 rounded-md" class="whitespace-pre-wrap p-3 bg-surface-100-900 rounded-lg text-sm"
class:hidden={$events_sess.pres_mgmt class:hidden={$events_sess.pres_mgmt
.show_content__presentation_description !== .show_content__presentation_description !==
event_presentation_obj.event_presentation_id}>{event_presentation_obj.description}</pre> event_presentation_obj.event_presentation_id}>{event_presentation_obj.description}</pre>

View File

@@ -141,9 +141,9 @@
<tbody> <tbody>
{#each visible_session_obj_li as session_obj, index (session_obj.id || session_obj.event_session_id || session_obj.event_session_id_random || index)} {#each visible_session_obj_li as session_obj, index (session_obj.id || session_obj.event_session_id || session_obj.event_session_id_random || index)}
<tr <tr
class="relative" class="relative transition-colors duration-200"
class:opacity-50={session_obj?.hide} class:opacity-50={session_obj?.hide}
class:variant-soft-warning={!session_obj?.enable} class:preset-tonal-warning={!session_obj?.enable}
> >
<td> <td>
{#if session_obj?.alert && $ae_loc.trusted_access} {#if session_obj?.alert && $ae_loc.trusted_access}
@@ -157,7 +157,7 @@
<div class="flex items-center gap-2"> <div class="flex items-center gap-2">
<a <a
href="/events/{session_obj?.event_id}/session/{session_obj?.event_session_id}" href="/events/{session_obj?.event_id}/session/{session_obj?.event_session_id}"
class="flex flex-row gap-2 items-center font-bold text-lg hover:text-primary-500 text-left" class="flex flex-row gap-2 items-center font-bold text-lg hover:text-primary-500 text-left transition-colors duration-200"
> >
{#if session_obj?.hide} {#if session_obj?.hide}
<EyeOff <EyeOff