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>
{:else}
<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>
<span
class="fas fa-exclamation-triangle text-2xl text-yellow-500"
></span>
<strong>No results to show</strong>
<span
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 class="flex flex-col items-center gap-2 text-center">
<span class="fas fa-search text-3xl opacity-50" aria-hidden="true"></span>
<strong class="text-xl">No sessions found</strong>
<p class="text-base opacity-80">
Use the search bar above to find your session.
</p>
</div>
<div>
<strong>Search by:</strong>
<ul class="list-disc list-inside text-lg text-left">
<li>Session name</li>
<li>Session description</li>
<li>Presentation name</li>
<li>Presenter names</li>
<li>Presenter ID (member ID)</li>
<div class="bg-surface-50-900/60 rounded-lg p-3">
<span class="text-xs font-bold uppercase tracking-wide opacity-50 block mb-2">Search by any of:</span>
<ul class="space-y-1 text-sm">
<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 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 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 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 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>
</div>
</section>

View File

@@ -101,8 +101,8 @@
<!-- Pass observable STORES to child components (they use $) -->
<Session_page_menu {data} {lq__event_session_obj} {lq__auth__event_presenter_obj} />
<!-- Metadata Section -->
<div class="bg-surface-50-950 p-4 rounded-container-token shadow-sm border border-surface-200-800">
<!-- Metadata Section: Session identity card — name, time, room, host, description -->
<div class="bg-surface-50-950 p-4 rounded-xl shadow-sm border border-surface-200-800">
<Session_view
{lq__event_presenter_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]) {
$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}`);
core_func.generate_qr_code({
api_cfg: $ae_api,
account_id: $slct.account_id,
@@ -59,106 +59,132 @@
});
</script>
<!-- STABLE LAYOUT: No top-level #if gate. Children mount immediately. -->
<section class="p-2 space-y-4">
{#if $lq__event_session_obj && $events_sess.pres_mgmt.session_qr_url[$lq__event_session_obj.id]}
<div class="float-right m-1 p-1 flex flex-col items-center outline outline-gray-200 group transition-all">
<button
type="button"
onclick={() => $events_sess.pres_mgmt.qr_bigger = !$events_sess.pres_mgmt.qr_bigger}
class="qr_code_btn"
title="Toggle QR Code size"
>
<img
src={$events_sess.pres_mgmt.session_qr_url[$lq__event_session_obj.id]}
class="qr_code h-32 w-32 transition-all duration-500"
class:h-48={$events_sess.pres_mgmt.qr_bigger}
alt="URL QR code"
/>
</button>
<MyClipboard
value={encodeURI(`${$ae_loc.url_origin}/events/${$lq__event_session_obj.event_id}/session/${$lq__event_session_obj.id}`)}
btn_class="m-1 btn btn-sm preset-tonal-warning"
hide_icon={true}
hide_text={true}
>
<span class="fas fa-link"></span>
</MyClipboard>
<!--
SESSION VIEW — Speaker Ready Room & Remote Upload
Primary users: Presenters uploading files (remote) + Staff helping presenters (onsite).
Design intent: "Is this the right session?" must be answerable in <3 seconds.
Show: name, time, room, host, description. Hide admin noise unless edit_mode.
Section 508: all interactive elements labelled, focus rings, sufficient contrast.
-->
<section class="space-y-3">
<!-- SESSION HERO: Name + Schedule + Room -->
<div class="rounded-xl border border-surface-200-800 bg-surface-50-900 shadow-sm overflow-hidden">
<div class="px-4 pt-4 pb-3 flex flex-col gap-3">
<!-- QR code: floats top-right, compact by default, toggle to enlarge.
Only rendered once the async URL is resolved (string), never while
it is still the boolean `true` loading placeholder. -->
{#if $lq__event_session_obj && typeof $events_sess.pres_mgmt.session_qr_url?.[$lq__event_session_obj.id] === 'string'}
<div class="float-right ml-3 mb-1 flex flex-col items-center gap-1">
<button
type="button"
onclick={() => $events_sess.pres_mgmt.qr_bigger = !$events_sess.pres_mgmt.qr_bigger}
class="rounded focus-visible:ring-2 focus-visible:ring-primary-500"
title="Toggle QR code size"
aria-label="Toggle QR code size"
>
<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>
{/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>

View File

@@ -53,16 +53,7 @@
</script>
<section
class="
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}
"
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}"
>
<div class="float-right flex flex-row items-center">
{#if $ae_loc.trusted_access && $ae_loc.edit_mode}
@@ -126,9 +117,9 @@
</h3>
<!-- 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)}
<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">
{#if $ae_loc.trusted_access && $ae_loc.edit_mode}
<button
@@ -169,7 +160,7 @@
{/if}
</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
class:hidden={!event_presentation_obj.start_datetime ||
$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}
<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}"
>
<span class="fas fa-barcode"></span>
@@ -370,7 +361,7 @@
</button>
<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
.show_content__presentation_description !==
event_presentation_obj.event_presentation_id}>{event_presentation_obj.description}</pre>

View File

@@ -141,9 +141,9 @@
<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)}
<tr
class="relative"
class="relative transition-colors duration-200"
class:opacity-50={session_obj?.hide}
class:variant-soft-warning={!session_obj?.enable}
class:preset-tonal-warning={!session_obj?.enable}
>
<td>
{#if session_obj?.alert && $ae_loc.trusted_access}
@@ -157,7 +157,7 @@
<div class="flex items-center gap-2">
<a
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}
<EyeOff