Prettier for Events as a whole. Everything else under that primary directory.

This commit is contained in:
Scott Idem
2026-03-24 12:16:44 -04:00
parent 6e67534454
commit 6018a94499
11 changed files with 1459 additions and 1465 deletions

View File

@@ -1,73 +1,73 @@
<script lang="ts"> <script lang="ts">
// console.log(`ae_events_pres_mgmt +page data:`, data); // console.log(`ae_events_pres_mgmt +page data:`, data);
// console.log(`ae_events_pres_mgmt Data Params:`, data.url.searchParams.get('event_id')); // console.log(`ae_events_pres_mgmt Data Params:`, data.url.searchParams.get('event_id'));
import { onMount } from 'svelte'; import { onMount } from 'svelte';
import { liveQuery } from 'dexie'; import { liveQuery } from 'dexie';
import { db_events } from '$lib/ae_events/db_events'; import { db_events } from '$lib/ae_events/db_events';
import { import {
ae_loc, ae_loc,
ae_sess, ae_sess,
ae_api, ae_api,
slct, slct,
slct_trigger slct_trigger
} from '$lib/stores/ae_stores'; } from '$lib/stores/ae_stores';
// import { // import {
// events_loc, // events_loc,
// events_slct, // events_slct,
// events_trigger // events_trigger
// } from '$lib/stores/ae_events_stores'; // } from '$lib/stores/ae_events_stores';
import { ae_util } from '$lib/ae_utils/ae_utils'; import { ae_util } from '$lib/ae_utils/ae_utils';
import { CalendarDays, LoaderCircle, TriangleAlert } from '@lucide/svelte'; import { CalendarDays, LoaderCircle, TriangleAlert } from '@lucide/svelte';
import { page } from '$app/stores'; import { page } from '$app/stores';
interface Props { interface Props {
data: any; data: any;
}
let { data }: Props = $props();
let lq__event_obj_li = $derived(
liveQuery(async () => {
const account_id = $page.data.account_id;
if (!account_id) return [];
let results = await db_events.event
.where('account_id')
.equals(account_id)
.reverse()
.sortBy('start_datetime');
return results;
})
);
let items_per_page = 10;
let current_page = $state(1);
let paginated_events = $derived(() => {
const start = (current_page - 1) * items_per_page;
const end = start + items_per_page;
return $lq__event_obj_li?.slice(start, end) ?? [];
});
let total_events = $derived($lq__event_obj_li?.length ?? 0);
let total_pages = $derived(Math.ceil(total_events / items_per_page));
function next_page() {
if (current_page < total_pages) {
current_page++;
} }
}
let { data }: Props = $props(); function prev_page() {
if (current_page > 1) {
let lq__event_obj_li = $derived( current_page--;
liveQuery(async () => {
const account_id = $page.data.account_id;
if (!account_id) return [];
let results = await db_events.event
.where('account_id')
.equals(account_id)
.reverse()
.sortBy('start_datetime');
return results;
})
);
let items_per_page = 10;
let current_page = $state(1);
let paginated_events = $derived(() => {
const start = (current_page - 1) * items_per_page;
const end = start + items_per_page;
return $lq__event_obj_li?.slice(start, end) ?? [];
});
let total_events = $derived($lq__event_obj_li?.length ?? 0);
let total_pages = $derived(Math.ceil(total_events / items_per_page));
function next_page() {
if (current_page < total_pages) {
current_page++;
}
} }
}
function prev_page() { onMount(() => {
if (current_page > 1) { // console.log('Events - Presentation Management: +page.svelte');
current_page--; });
}
}
onMount(() => {
// console.log('Events - Presentation Management: +page.svelte');
});
</script> </script>
<h2 class="h3"> <h2 class="h3">
@@ -119,12 +119,11 @@
{#each $lq__event_obj_li as event_obj (event_obj.event_id)} {#each $lq__event_obj_li as event_obj (event_obj.event_id)}
<li class:dim={event_obj?.hide}> <li class:dim={event_obj?.hide}>
<span <span
class="w-full flex flex-row gap-1 items-center justify-between" class="flex w-full flex-row items-center justify-between gap-1">
>
<!-- We do not want to show events more than 8 months old. --> <!-- We do not want to show events more than 8 months old. -->
{#if new Date(event_obj.start_datetime ?? '').getTime() > new Date().getTime() - 1000 * 60 * 60 * 24 * 30 * 8 || $ae_loc.trusted_access} {#if new Date(event_obj.start_datetime ?? '').getTime() > new Date().getTime() - 1000 * 60 * 60 * 24 * 30 * 8 || $ae_loc.trusted_access}
<span> <span>
<CalendarDays size="1em" class="inline mx-1" /> <CalendarDays size="1em" class="mx-1 inline" />
{ae_util.iso_datetime_formatter( {ae_util.iso_datetime_formatter(
event_obj.start_datetime, event_obj.start_datetime,
'date_long' 'date_long'
@@ -135,7 +134,7 @@
</strong> </strong>
{:else} {:else}
<span> <span>
<CalendarDays size="1em" class="inline mx-1" /> <CalendarDays size="1em" class="mx-1 inline" />
{ae_util.iso_datetime_formatter( {ae_util.iso_datetime_formatter(
event_obj.start_datetime, event_obj.start_datetime,
'date_long' 'date_long'
@@ -148,36 +147,31 @@
</span> </span>
<span <span
class="w-full flex flex-row gap-1 items-center justify-evenly" class="flex w-full flex-row items-center justify-evenly gap-1">
>
{#if $ae_loc.authenticated_access} {#if $ae_loc.authenticated_access}
<a <a
data-sveltekit-reload data-sveltekit-reload
href="/events/{event_obj.event_id}" href="/events/{event_obj.event_id}"
class="btn btn-sm preset-tonal-secondary border border-secondary-500 hover:preset-filled-secondary-500" class="btn btn-sm preset-tonal-secondary border-secondary-500 hover:preset-filled-secondary-500 border"
title="Presentation Management for {event_obj.name}" title="Presentation Management for {event_obj.name}">
>
Pres Mgmt Pres Mgmt
</a> </a>
<a <a
href="/events/{event_obj.event_id}/badges" href="/events/{event_obj.event_id}/badges"
class="btn btn-sm preset-tonal-secondary border border-secondary-500 hover:preset-filled-secondary-500" class="btn btn-sm preset-tonal-secondary border-secondary-500 hover:preset-filled-secondary-500 border"
title="Badge Management for {event_obj.name}" title="Badge Management for {event_obj.name}">
>
Badges Badges
</a> </a>
<a <a
href="/events/{event_obj.event_id}/leads" href="/events/{event_obj.event_id}/leads"
class="btn btn-sm preset-tonal-secondary border border-secondary-500 hover:preset-filled-secondary-500" class="btn btn-sm preset-tonal-secondary border-secondary-500 hover:preset-filled-secondary-500 border"
title="Exhibitor Leads for {event_obj.name}" title="Exhibitor Leads for {event_obj.name}">
>
Leads Leads
</a> </a>
<a <a
href="/events/{event_obj.event_id}/launcher" href="/events/{event_obj.event_id}/launcher"
class="btn btn-sm preset-tonal-secondary border border-secondary-500 hover:preset-filled-secondary-500" class="btn btn-sm preset-tonal-secondary border-secondary-500 hover:preset-filled-secondary-500 border"
title="Event Launcher for {event_obj.name}" title="Event Launcher for {event_obj.name}">
>
Launcher Launcher
</a> </a>
{/if} {/if}
@@ -185,9 +179,8 @@
<a <a
data-sveltekit-reload data-sveltekit-reload
href="/event/{event_obj.event_id}" href="/event/{event_obj.event_id}"
class="btn btn-sm preset-tonal-warning border border-warning-500 hover:preset-filled-warning-500" class="btn btn-sm preset-tonal-warning border-warning-500 hover:preset-filled-warning-500 border"
title="Legacy Presentation Management System (Flask/Svelte) for {event_obj.name}" title="Legacy Presentation Management System (Flask/Svelte) for {event_obj.name}">
>
Legacy Pres Mgmt Legacy Pres Mgmt
</a> </a>
{/if} {/if}
@@ -195,31 +188,29 @@
</li> </li>
{/each} {/each}
</ul> </ul>
<div class="flex justify-center items-center space-x-4 mt-4"> <div class="mt-4 flex items-center justify-center space-x-4">
<button <button
type="button" type="button"
class="btn btn-sm" class="btn btn-sm"
onclick={prev_page} onclick={prev_page}
disabled={current_page === 1}>Previous</button disabled={current_page === 1}>Previous</button>
>
<span>Page {current_page} of {total_pages}</span> <span>Page {current_page} of {total_pages}</span>
<button <button
type="button" type="button"
class="btn btn-sm" class="btn btn-sm"
onclick={next_page} onclick={next_page}
disabled={current_page === total_pages}>Next</button disabled={current_page === total_pages}>Next</button>
>
</div> </div>
{:else} {:else}
<div class="flex flex-row items-center justify-center"> <div class="flex flex-row items-center justify-center">
<TriangleAlert size="1em" class="inline text-error-500 mx-1" /> <TriangleAlert size="1em" class="text-error-500 mx-1 inline" />
<span>No events available to display.</span> <span>No events available to display.</span>
<TriangleAlert size="1em" class="inline text-error-500 mx-1" /> <TriangleAlert size="1em" class="text-error-500 mx-1 inline" />
</div> </div>
{/if} {/if}
{:else} {:else}
<div class="flex flex-row items-center justify-center"> <div class="flex flex-row items-center justify-center">
<LoaderCircle size="1em" class="inline animate-spin mx-1" /> <LoaderCircle size="1em" class="mx-1 inline animate-spin" />
<span>Loading...</span> <span>Loading...</span>
</div> </div>
{/if} {/if}

View File

@@ -1,314 +1,328 @@
<script lang="ts"> <script lang="ts">
interface Props { interface Props {
// Exports // Exports
container_class_li?: string | Array<string>; container_class_li?: string | Array<string>;
event_file_id_random_li?: Array<string>; event_file_id_random_li?: Array<string>;
lq__event_file_obj_li: any; lq__event_file_obj_li: any;
allow_basic?: boolean; allow_basic?: boolean;
allow_moderator?: boolean; allow_moderator?: boolean;
// export let max_records: number = 100; // export let max_records: number = 100;
show_direct_download?: boolean; show_direct_download?: boolean;
show_location_fields?: boolean; show_location_fields?: boolean;
show_presentation_fields?: boolean; show_presentation_fields?: boolean;
// export let show_presenter_fields: boolean = false; // export let show_presenter_fields: boolean = false;
show_session_fields?: boolean; show_session_fields?: boolean;
hide_session_code?: boolean; hide_session_code?: boolean;
log_lvl?: number; log_lvl?: number;
} }
let { let {
container_class_li = [], container_class_li = [],
event_file_id_random_li = [], event_file_id_random_li = [],
lq__event_file_obj_li, lq__event_file_obj_li,
allow_basic = false, allow_basic = false,
allow_moderator = false, allow_moderator = false,
show_direct_download = $bindable(false), show_direct_download = $bindable(false),
show_location_fields = false, show_location_fields = false,
show_presentation_fields = false, show_presentation_fields = false,
show_session_fields = false, show_session_fields = false,
hide_session_code = false, hide_session_code = false,
log_lvl = $bindable(0) log_lvl = $bindable(0)
}: Props = $props(); }: Props = $props();
// Imports // Imports
// import { liveQuery } from 'dexie'; // 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 { api } from '$lib/api/api'; // import { api } from '$lib/api/api';
// import { db_events } from '$lib/ae_events/db_events'; // import { db_events } from '$lib/ae_events/db_events';
import { import {
ae_snip, ae_snip,
ae_loc, ae_loc,
ae_sess, ae_sess,
ae_api, ae_api,
ae_trig, ae_trig,
slct, slct,
slct_trigger slct_trigger
} from '$lib/stores/ae_stores'; } from '$lib/stores/ae_stores';
// import { events_loc, events_sess, events_slct, events_trigger, events_trig_kv } from '$lib/stores/ae_events_stores'; // import { events_loc, events_sess, events_slct, events_trigger, events_trig_kv } 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 MyClipboard from '$lib/app_components/e_app_clipboard.svelte'; import MyClipboard from '$lib/app_components/e_app_clipboard.svelte';
import AE_Comp_Hosted_Files_Download_Button from '$lib/ae_core/ae_comp__hosted_files_download_button.svelte'; import AE_Comp_Hosted_Files_Download_Button from '$lib/ae_core/ae_comp__hosted_files_download_button.svelte';
import { Check, Download, FileImage, FileSpreadsheet, ListOrdered, LoaderCircle, Presentation, ToggleLeft, ToggleRight, TriangleAlert, User } from '@lucide/svelte'; import {
// export let display_mode: string = 'default'; // 'default', 'compact', 'minimal', 'launcher' Check,
Download,
FileImage,
FileSpreadsheet,
ListOrdered,
LoaderCircle,
Presentation,
ToggleLeft,
ToggleRight,
TriangleAlert,
User
} from '@lucide/svelte';
// export let display_mode: string = 'default'; // 'default', 'compact', 'minimal', 'launcher'
// Variables // Variables
let ae_promises: key_val = $state({}); let ae_promises: key_val = $state({});
// *** Functions and Logic // *** Functions and Logic
// Define the list of unacceptable characters if not using the default. // Define the list of unacceptable characters if not using the default.
// const unacceptable_chars = /[ <>:"/\\|?*]/g; // const unacceptable_chars = /[ <>:"/\\|?*]/g;
let horiz_scroll_warning: boolean = $state(false); let horiz_scroll_warning: boolean = $state(false);
let horiz_check_element: HTMLElement | null = $state(null); let horiz_check_element: HTMLElement | null = $state(null);
// PDF → Image conversion state (keyed by event_file_id) // PDF → Image conversion state (keyed by event_file_id)
// WHY: Poster sessions display files in the Launcher modal via <img> tag — PDFs can't render there. // WHY: Poster sessions display files in the Launcher modal via <img> tag — PDFs can't render there.
// Presenters upload PDFs which must be converted server-side to high-res webp images. // Presenters upload PDFs which must be converted server-side to high-res webp images.
// Button is only shown in edit_mode for PDF files linked to a poster-type session. // Button is only shown in edit_mode for PDF files linked to a poster-type session.
// Backend: /v3/action/hosted_file/{id}/convert_file runs pdf2image (3840px wide, first page only) // Backend: /v3/action/hosted_file/{id}/convert_file runs pdf2image (3840px wide, first page only)
// and saves the result as a new hosted_file record linked to the same parent object. // and saves the result as a new hosted_file record linked to the same parent object.
type ConvertStatus = 'idle' | 'converting' | 'done' | 'error'; type ConvertStatus = 'idle' | 'converting' | 'done' | 'error';
let convert_status_kv: Record<string, ConvertStatus> = $state({}); let convert_status_kv: Record<string, ConvertStatus> = $state({});
let convert_result_kv: Record<string, any> = $state({}); let convert_result_kv: Record<string, any> = $state({});
async function handle_convert_pdf_to_image(event_file_obj: any) { async function handle_convert_pdf_to_image(event_file_obj: any) {
const file_id = event_file_obj.event_file_id; const file_id = event_file_obj.event_file_id;
convert_status_kv[file_id] = 'converting'; convert_status_kv[file_id] = 'converting';
try { try {
// Link the new image to the most specific parent available // Link the new image to the most specific parent available
const link_to_type = event_file_obj.event_session_id ? 'event_session' const link_to_type = event_file_obj.event_session_id
: event_file_obj.event_presentation_id ? 'event_presentation' ? 'event_session'
: 'event'; : event_file_obj.event_presentation_id
const link_to_id = event_file_obj.event_session_id ? 'event_presentation'
|| event_file_obj.event_presentation_id : 'event';
|| event_file_obj.event_id; const link_to_id =
const filename_no_ext = (event_file_obj.filename ?? 'poster_image').replace(/\.pdf$/i, ''); event_file_obj.event_session_id ||
const url = `${$ae_api.base_url}/v3/hosted_file/${event_file_obj.hosted_file_id}/convert_file` event_file_obj.event_presentation_id ||
+ `?link_to_type=${encodeURIComponent(link_to_type)}` event_file_obj.event_id;
+ `&link_to_id=${encodeURIComponent(link_to_id)}` const filename_no_ext = (
+ `&filename_no_ext=${encodeURIComponent(filename_no_ext)}` event_file_obj.filename ?? 'poster_image'
+ `&to_type=webp`; ).replace(/\.pdf$/i, '');
const resp = await fetch(url, { const url =
headers: { `${$ae_api.base_url}/v3/hosted_file/${event_file_obj.hosted_file_id}/convert_file` +
'x-aether-api-key': $ae_api.api_secret_key, `?link_to_type=${encodeURIComponent(link_to_type)}` +
'x-account-id': String($ae_api.account_id ?? '') `&link_to_id=${encodeURIComponent(link_to_id)}` +
} `&filename_no_ext=${encodeURIComponent(filename_no_ext)}` +
}); `&to_type=webp`;
const body = await resp.json(); const resp = await fetch(url, {
if (resp.ok && body?.data) { headers: {
convert_result_kv[file_id] = body.data; 'x-aether-api-key': $ae_api.api_secret_key,
convert_status_kv[file_id] = 'done'; 'x-account-id': String($ae_api.account_id ?? '')
} else {
console.error('[convert_pdf] API error:', body);
convert_status_kv[file_id] = 'error';
} }
} catch (err) { });
console.error('[convert_pdf] Fetch failed:', err); const body = await resp.json();
if (resp.ok && body?.data) {
convert_result_kv[file_id] = body.data;
convert_status_kv[file_id] = 'done';
} else {
console.error('[convert_pdf] API error:', body);
convert_status_kv[file_id] = 'error'; convert_status_kv[file_id] = 'error';
} }
} catch (err) {
console.error('[convert_pdf] Fetch failed:', err);
convert_status_kv[file_id] = 'error';
}
}
// Check if element is scrolling horizontally
$effect(() => {
if (
horiz_check_element &&
horiz_check_element.scrollWidth > horiz_check_element.offsetWidth
) {
horiz_scroll_warning = true;
// console.log('Element is too wide for the container. Horizontal scrolling detected.');
} else {
horiz_scroll_warning = false;
// console.log('Element fits within the container. No horizontal scrolling.', horiz_check_element);
}
});
function generate_file_export_csv(ae_obj_li: any[]) {
console.log(`*** generate_file_export_csv() ***`, ae_obj_li);
// We need to create a list with the column names and then a list of lists with the data.
let csv_data = [];
let csv_columns = [
'File ID',
'Filename',
'Extension',
'Size',
'SHA256 Hash',
'Uploaded On',
'Updated On',
'Session ID',
'Session Code',
'Session Name',
'Session Start Datetime',
'Presentation ID',
'Presentation Name',
'Presentation Time',
'Presenter ID',
'Name',
'Email',
'Download Link',
'Download Link - Session Code'
];
csv_data.push(csv_columns);
for (let i = 0; i < ae_obj_li.length; i++) {
let csv_row = [];
csv_row.push(ae_obj_li[i].event_file_id);
csv_row.push(
ae_obj_li[i].filename
? `"${ae_util.clean_filename(ae_obj_li[i].filename)}"`
: ''
);
csv_row.push(ae_obj_li[i].extension ? ae_obj_li[i].extension : '');
csv_row.push(
ae_obj_li[i].file_size || ae_obj_li[i].hosted_file_size
? ae_util.format_bytes(
ae_obj_li[i].file_size || ae_obj_li[i].hosted_file_size
)
: ''
);
csv_row.push(ae_obj_li[i].hash_sha256?.slice(0, 10) ?? 'N/A');
csv_row.push(
ae_obj_li[i].created_on
? ae_util.iso_datetime_formatter(
ae_obj_li[i].created_on,
'datetime_iso_12_no_seconds'
)
: ''
);
csv_row.push(
ae_obj_li[i].updated_on
? ae_util.iso_datetime_formatter(
ae_obj_li[i].updated_on,
'datetime_iso_12_no_seconds'
)
: ''
);
csv_row.push(
ae_obj_li[i].event_session_id ? ae_obj_li[i].event_session_id : ''
);
csv_row.push(
ae_obj_li[i].event_session_code
? ae_obj_li[i].event_session_code
: ''
);
csv_row.push(ae_obj_li[i].event_session_name ?? '');
csv_row.push(
ae_obj_li[i].event_session_start_datetime
? ae_util.iso_datetime_formatter(
ae_obj_li[i].event_session_start_datetime,
'datetime_iso_12_no_seconds'
)
: ''
);
csv_row.push(
ae_obj_li[i].event_presentation_id
? ae_obj_li[i].event_presentation_id
: ''
);
csv_row.push(ae_obj_li[i].event_presentation_name ?? '');
csv_row.push(
ae_obj_li[i].event_presentation_start_datetime
? ae_util.iso_datetime_formatter(
ae_obj_li[i].event_presentation_start_datetime,
'datetime_iso_12_no_seconds'
)
: ''
);
csv_row.push(
ae_obj_li[i].event_presenter_id
? ae_obj_li[i].event_presenter_id
: ''
);
csv_row.push(ae_obj_li[i].event_presenter_full_name ?? '');
csv_row.push(
ae_obj_li[i].event_presenter_email
? ae_obj_li[i].event_presenter_email
: ''
);
csv_row.push(
encodeURI(
`${$ae_api.base_url}/event/file/${ae_obj_li[i]?.event_file_id}/download?filename=${ae_util.clean_filename(ae_obj_li[i]?.filename)}&key=${$ae_api.account_id}`
)
);
csv_row.push(
encodeURI(
`${$ae_api.base_url}/event/file/${ae_obj_li[i]?.event_file_id}/download?filename=${ae_obj_li[i]?.event_session_code}-${ae_util.clean_filename(ae_obj_li[i]?.filename)}&key=${$ae_api.account_id}`
)
);
csv_data.push(csv_row);
} }
// Check if element is scrolling horizontally console.log('CSV Data:', csv_data);
$effect(() => {
if ( let csv_content_str = '';
horiz_check_element && csv_data.forEach(function (row) {
horiz_check_element.scrollWidth > horiz_check_element.offsetWidth csv_content_str += row.join(';');
) { csv_content_str += '\n';
horiz_scroll_warning = true;
// console.log('Element is too wide for the container. Horizontal scrolling detected.');
} else {
horiz_scroll_warning = false;
// console.log('Element fits within the container. No horizontal scrolling.', horiz_check_element);
}
}); });
function generate_file_export_csv(ae_obj_li: any[]) { const blob = new Blob([csv_content_str], {
console.log(`*** generate_file_export_csv() ***`, ae_obj_li); type: 'text/csv;charset=utf-8;'
});
const obj_url = URL.createObjectURL(blob);
// We need to create a list with the column names and then a list of lists with the data. const download_link = document.createElement('a');
let csv_data = []; download_link.setAttribute('href', obj_url);
let csv_columns = [ download_link.setAttribute(
'File ID', 'download',
'Filename', `file_list_${ae_util.iso_datetime_formatter()}.csv`
'Extension', );
'Size', download_link.setAttribute('style', 'display: none;');
'SHA256 Hash', download_link.textContent = 'Download CSV';
'Uploaded On',
'Updated On',
'Session ID',
'Session Code',
'Session Name',
'Session Start Datetime',
'Presentation ID',
'Presentation Name',
'Presentation Time',
'Presenter ID',
'Name',
'Email',
'Download Link',
'Download Link - Session Code'
];
csv_data.push(csv_columns);
for (let i = 0; i < ae_obj_li.length; i++) { const container = document.getElementById('download_csv_container');
let csv_row = []; if (container) {
csv_row.push(ae_obj_li[i].event_file_id); container.appendChild(download_link);
csv_row.push( } else {
ae_obj_li[i].filename document.body.appendChild(download_link);
? `"${ae_util.clean_filename(ae_obj_li[i].filename)}"`
: ''
);
csv_row.push(ae_obj_li[i].extension ? ae_obj_li[i].extension : '');
csv_row.push(
ae_obj_li[i].file_size || ae_obj_li[i].hosted_file_size
? ae_util.format_bytes(ae_obj_li[i].file_size || ae_obj_li[i].hosted_file_size)
: ''
);
csv_row.push(ae_obj_li[i].hash_sha256?.slice(0, 10) ?? 'N/A');
csv_row.push(
ae_obj_li[i].created_on
? ae_util.iso_datetime_formatter(
ae_obj_li[i].created_on,
'datetime_iso_12_no_seconds'
)
: ''
);
csv_row.push(
ae_obj_li[i].updated_on
? ae_util.iso_datetime_formatter(
ae_obj_li[i].updated_on,
'datetime_iso_12_no_seconds'
)
: ''
);
csv_row.push(
ae_obj_li[i].event_session_id
? ae_obj_li[i].event_session_id
: ''
);
csv_row.push(
ae_obj_li[i].event_session_code
? ae_obj_li[i].event_session_code
: ''
);
csv_row.push(ae_obj_li[i].event_session_name ?? '');
csv_row.push(
ae_obj_li[i].event_session_start_datetime
? ae_util.iso_datetime_formatter(
ae_obj_li[i].event_session_start_datetime,
'datetime_iso_12_no_seconds'
)
: ''
);
csv_row.push(
ae_obj_li[i].event_presentation_id
? ae_obj_li[i].event_presentation_id
: ''
);
csv_row.push(ae_obj_li[i].event_presentation_name ?? '');
csv_row.push(
ae_obj_li[i].event_presentation_start_datetime
? ae_util.iso_datetime_formatter(
ae_obj_li[i].event_presentation_start_datetime,
'datetime_iso_12_no_seconds'
)
: ''
);
csv_row.push(
ae_obj_li[i].event_presenter_id
? ae_obj_li[i].event_presenter_id
: ''
);
csv_row.push(ae_obj_li[i].event_presenter_full_name ?? '');
csv_row.push(
ae_obj_li[i].event_presenter_email
? ae_obj_li[i].event_presenter_email
: ''
);
csv_row.push(
encodeURI(
`${$ae_api.base_url}/event/file/${ae_obj_li[i]?.event_file_id}/download?filename=${ae_util.clean_filename(ae_obj_li[i]?.filename)}&key=${$ae_api.account_id}`
)
);
csv_row.push(
encodeURI(
`${$ae_api.base_url}/event/file/${ae_obj_li[i]?.event_file_id}/download?filename=${ae_obj_li[i]?.event_session_code}-${ae_util.clean_filename(ae_obj_li[i]?.filename)}&key=${$ae_api.account_id}`
)
);
csv_data.push(csv_row);
}
console.log('CSV Data:', csv_data);
let csv_content_str = '';
csv_data.forEach(function (row) {
csv_content_str += row.join(';');
csv_content_str += '\n';
});
const blob = new Blob([csv_content_str], {
type: 'text/csv;charset=utf-8;'
});
const obj_url = URL.createObjectURL(blob);
const download_link = document.createElement('a');
download_link.setAttribute('href', obj_url);
download_link.setAttribute(
'download',
`file_list_${ae_util.iso_datetime_formatter()}.csv`
);
download_link.setAttribute('style', 'display: none;');
download_link.textContent = 'Download CSV';
const container = document.getElementById('download_csv_container');
if (container) {
container.appendChild(download_link);
} else {
document.body.appendChild(download_link);
}
// Automatically download the file
download_link.click();
return csv_data;
} }
// Automatically download the file
download_link.click();
return csv_data;
}
</script> </script>
<section <section
class:border-r-2={horiz_scroll_warning} class:border-r-2={horiz_scroll_warning}
class:border-dashed={horiz_scroll_warning} class:border-dashed={horiz_scroll_warning}
class:border-warning-900-100={horiz_scroll_warning} class:border-warning-900-100={horiz_scroll_warning}
class="ae_comp event_file_obj_tbl {container_class_li} container overflow-auto max-w-screen" class="ae_comp event_file_obj_tbl {container_class_li} container max-w-screen overflow-auto">
>
<!-- {#if event_file_id_random_li && $lq_kv__event_file_obj_li && $lq_kv__event_file_obj_li?.length > 0 && $lq_kv__event_file_obj_li?.length == event_file_id_random_li?.length} --> <!-- {#if event_file_id_random_li && $lq_kv__event_file_obj_li && $lq_kv__event_file_obj_li?.length > 0 && $lq_kv__event_file_obj_li?.length == event_file_id_random_li?.length} -->
{#if $lq__event_file_obj_li && $lq__event_file_obj_li?.length} {#if $lq__event_file_obj_li && $lq__event_file_obj_li?.length}
<div <div
bind:this={horiz_check_element} bind:this={horiz_check_element}
id="tbl_container" id="tbl_container"
class="space-y-2 pb-8" class="space-y-2 pb-8">
>
<header <header
class="flex flex-row flex-wrap gap-1 items-center justify-between" class="flex flex-row flex-wrap items-center justify-between gap-1">
>
<h2 class="h3"> <h2 class="h3">
<span class="text-base"> Results: </span> <span class="text-base"> Results: </span>
{#if $lq__event_file_obj_li.length} {#if $lq__event_file_obj_li.length}
<span <span
class="text-3xl font-bold preset-filled-success-100-900 px-4 rounded-lg" class="preset-filled-success-100-900 rounded-lg px-4 text-3xl font-bold"
title="Count {$lq__event_file_obj_li.length ?? title="Count {$lq__event_file_obj_li.length ??
'None'}" 'None'}">
>
<ListOrdered size="1em" class="mx-4" /> <ListOrdered size="1em" class="mx-4" />
{$lq__event_file_obj_li.length ?? 'None'}&times; {$lq__event_file_obj_li.length ?? 'None'}&times;
</span> </span>
@@ -316,12 +330,11 @@
</h2> </h2>
<div <div
class="flex flex-row flex-wrap gap-1 items-center justify-end" class="flex flex-row flex-wrap items-center justify-end gap-1"
class:hidden={!$ae_loc.edit_mode} class:hidden={!$ae_loc.edit_mode}>
>
<button <button
type="button" type="button"
class="btn btn-sm preset-tonal-warning border border-warning-500 mb-1 generate_csv_btn" class="btn btn-sm preset-tonal-warning border-warning-500 generate_csv_btn mb-1 border"
onclick={() => { onclick={() => {
if ( if (
!confirm( !confirm(
@@ -335,8 +348,7 @@
$lq__event_file_obj_li $lq__event_file_obj_li
); );
console.log('CSV Data:', csv_data); console.log('CSV Data:', csv_data);
}} }}>
>
<FileSpreadsheet size="1em" class="mx-1" /> <FileSpreadsheet size="1em" class="mx-1" />
Export Files CSV Export Files CSV
</button> </button>
@@ -351,8 +363,7 @@
class="btn btn-sm {show_session_fields class="btn btn-sm {show_session_fields
? 'ae_btn_surface' ? 'ae_btn_surface'
: 'ae_btn_surface_outlined'}" : 'ae_btn_surface_outlined'}"
title="Show or hide the session-related column fields." title="Show or hide the session-related column fields.">
>
<ToggleRight size="1em" class="m-1" /> <ToggleRight size="1em" class="m-1" />
Showing Session Fields Showing Session Fields
</button> </button>
@@ -365,8 +376,7 @@
class="btn btn-sm {show_session_fields class="btn btn-sm {show_session_fields
? 'ae_btn_surface' ? 'ae_btn_surface'
: 'ae_btn_surface_outlined'}" : 'ae_btn_surface_outlined'}"
title="Show or hide the session-related column fields." title="Show or hide the session-related column fields.">
>
<ToggleLeft size="1em" class="m-1" /> <ToggleLeft size="1em" class="m-1" />
Show Session Fields Show Session Fields
</button> </button>
@@ -380,8 +390,7 @@
hide_session_code = true; hide_session_code = true;
}} }}
class="btn btn-sm ae_btn_surface" class="btn btn-sm ae_btn_surface"
title="Hide the session code column from view. Currently showing the Session Code column." title="Hide the session code column from view. Currently showing the Session Code column.">
>
<ToggleRight size="1em" class="m-1" /> <ToggleRight size="1em" class="m-1" />
Showing Session Code Showing Session Code
</button> </button>
@@ -392,8 +401,7 @@
hide_session_code = false; hide_session_code = false;
}} }}
class="btn btn-sm ae_btn_surface_outlined" class="btn btn-sm ae_btn_surface_outlined"
title="Show the session code column. Currently hiding the Session Code column from view." title="Show the session code column. Currently hiding the Session Code column from view.">
>
<ToggleLeft size="1em" class="m-1" /> <ToggleLeft size="1em" class="m-1" />
Show Session Code Show Session Code
</button> </button>
@@ -409,8 +417,7 @@
class="btn btn-sm {show_presentation_fields class="btn btn-sm {show_presentation_fields
? 'ae_btn_surface' ? 'ae_btn_surface'
: 'ae_btn_surface_outlined'}" : 'ae_btn_surface_outlined'}"
title="Show or hide the extra presentation-related column fields." title="Show or hide the extra presentation-related column fields.">
>
<ToggleRight size="1em" class="m-1" /> <ToggleRight size="1em" class="m-1" />
Showing Presentation Fields Showing Presentation Fields
</button> </button>
@@ -424,8 +431,7 @@
class="btn btn-sm {show_presentation_fields class="btn btn-sm {show_presentation_fields
? 'ae_btn_surface' ? 'ae_btn_surface'
: 'ae_btn_surface_outlined'}" : 'ae_btn_surface_outlined'}"
title="Show or hide the extra presentation-related column fields." title="Show or hide the extra presentation-related column fields.">
>
<ToggleLeft size="1em" class="m-1" /> <ToggleLeft size="1em" class="m-1" />
Show Presentation Fields Show Presentation Fields
</button> </button>
@@ -434,8 +440,7 @@
</header> </header>
<table <table
class="table table-auto table-striped w-full text-xs lg:text-sm" class="table-striped table w-full table-auto text-xs lg:text-sm">
>
<thead class=""> <thead class="">
<tr> <tr>
<th class="px-4 py-2"> <th class="px-4 py-2">
@@ -444,8 +449,7 @@
</th> </th>
<th <th
class="px-4 py-2" class="px-4 py-2"
class:hidden={!show_direct_download} class:hidden={!show_direct_download}>
>
Link Link
</th> </th>
<th class="px-4 py-2">Size</th> <th class="px-4 py-2">Size</th>
@@ -456,8 +460,7 @@
{#if show_session_fields} {#if show_session_fields}
<th <th
class="px-4 py-2" class="px-4 py-2"
class:hidden={hide_session_code} class:hidden={hide_session_code}>
>
Code Code
</th> </th>
<th class="px-4 py-2"> Session </th> <th class="px-4 py-2"> Session </th>
@@ -482,41 +485,58 @@
show_divider={true} show_divider={true}
{show_direct_download} {show_direct_download}
max_filename={50} max_filename={50}
classes="btn btn-sm preset-tonal-primary hover:preset-filled-primary-500 min-w-72" classes="btn btn-sm preset-tonal-primary hover:preset-filled-primary-500 min-w-72" />
/>
<!-- PDF → webp convert button: only for poster sessions in edit mode --> <!-- PDF → webp convert button: only for poster sessions in edit mode -->
{#if $ae_loc.edit_mode && event_file_obj?.extension === 'pdf' && event_file_obj?.event_session_type_code === 'poster'} {#if $ae_loc.edit_mode && event_file_obj?.extension === 'pdf' && event_file_obj?.event_session_type_code === 'poster'}
<div class="mt-1"> <div class="mt-1">
{#if !convert_status_kv[event_file_obj.event_file_id] || convert_status_kv[event_file_obj.event_file_id] === 'idle'} {#if !convert_status_kv[event_file_obj.event_file_id] || convert_status_kv[event_file_obj.event_file_id] === 'idle'}
<button <button
type="button" type="button"
class="btn btn-sm preset-tonal-warning border border-warning-500" class="btn btn-sm preset-tonal-warning border-warning-500 border"
title="Convert this PDF to a high-res webp image for use in the Launcher poster display." title="Convert this PDF to a high-res webp image for use in the Launcher poster display."
onclick={() => handle_convert_pdf_to_image(event_file_obj)} onclick={() =>
> handle_convert_pdf_to_image(
<FileImage size="1em" class="mx-1" /> event_file_obj
)}>
<FileImage
size="1em"
class="mx-1" />
Convert PDF → Image Convert PDF → Image
</button> </button>
{:else if convert_status_kv[event_file_obj.event_file_id] === 'converting'} {:else if convert_status_kv[event_file_obj.event_file_id] === 'converting'}
<span class="btn btn-sm preset-tonal-surface opacity-60 cursor-wait"> <span
<LoaderCircle size="1em" class="mx-1 animate-spin" /> class="btn btn-sm preset-tonal-surface cursor-wait opacity-60">
<LoaderCircle
size="1em"
class="mx-1 animate-spin" />
Converting… Converting…
</span> </span>
{:else if convert_status_kv[event_file_obj.event_file_id] === 'done'} {:else if convert_status_kv[event_file_obj.event_file_id] === 'done'}
<span class="btn btn-sm preset-tonal-success" title="Conversion complete. New webp hosted_file created: {convert_result_kv[event_file_obj.event_file_id]?.filename ?? ''}"> <span
<Check size="1em" class="mx-1" /> class="btn btn-sm preset-tonal-success"
Done — {convert_result_kv[event_file_obj.event_file_id]?.filename ?? 'image created'} title="Conversion complete. New webp hosted_file created: {convert_result_kv[
event_file_obj.event_file_id
]?.filename ?? ''}">
<Check
size="1em"
class="mx-1" />
Done — {convert_result_kv[
event_file_obj.event_file_id
]?.filename ?? 'image created'}
</span> </span>
{:else if convert_status_kv[event_file_obj.event_file_id] === 'error'} {:else if convert_status_kv[event_file_obj.event_file_id] === 'error'}
<button <button
type="button" type="button"
class="btn btn-sm preset-tonal-error border border-error-500" class="btn btn-sm preset-tonal-error border-error-500 border"
title="Conversion failed. Click to retry." title="Conversion failed. Click to retry."
onclick={() => { onclick={() => {
convert_status_kv[event_file_obj.event_file_id] = 'idle'; convert_status_kv[
}} event_file_obj.event_file_id
> ] = 'idle';
<TriangleAlert size="1em" class="mx-1" /> }}>
<TriangleAlert
size="1em"
class="mx-1" />
Failed — Retry? Failed — Retry?
</button> </button>
{/if} {/if}
@@ -524,23 +544,20 @@
{/if} {/if}
</td> </td>
<td <td
class="px-4 py-2 flex flex-col gap-0.5" class="flex flex-col gap-0.5 px-4 py-2"
class:hidden={!show_direct_download} class:hidden={!show_direct_download}>
>
<div <div
class:hidden={!show_direct_download} class:hidden={!show_direct_download}
class="flex flex-row gap-0.5" class="flex flex-row gap-0.5">
> <span class="w-32 text-xs text-gray-500">
<span class="text-xs text-gray-500 w-32">
Original: Original:
</span> </span>
<a <a
href="{$ae_api.base_url}/event/file/{event_file_obj?.event_file_id}/download?filename={ae_util.clean_filename( href="{$ae_api.base_url}/event/file/{event_file_obj?.event_file_id}/download?filename={ae_util.clean_filename(
event_file_obj?.filename event_file_obj?.filename
)}&key={$ae_api.account_id}" )}&key={$ae_api.account_id}"
class="btn btn-sm p-1 preset-tonal-secondary *:hover:inline lg:text-xs underline" class="btn btn-sm preset-tonal-secondary p-1 underline *:hover:inline lg:text-xs"
title={`Download this file:\n${ae_util.clean_filename(event_file_obj?.filename)}\n[API] SHA256: ${event_file_obj?.hash_sha256?.slice(0, 10) ?? 'N/A'}\nHosted ID: ${event_file_obj?.hosted_file_id} Event File ID: ${event_file_obj?.event_file_id}`} title={`Download this file:\n${ae_util.clean_filename(event_file_obj?.filename)}\n[API] SHA256: ${event_file_obj?.hash_sha256?.slice(0, 10) ?? 'N/A'}\nHosted ID: ${event_file_obj?.hosted_file_id} Event File ID: ${event_file_obj?.event_file_id}`}>
>
<Download size="1em" class="mx-1" /> <Download size="1em" class="mx-1" />
<span class="hidden"> Download </span> <span class="hidden"> Download </span>
</a> </a>
@@ -560,9 +577,8 @@
<div <div
class="flex flex-row gap-0.5" class="flex flex-row gap-0.5"
class:hidden={!show_direct_download} class:hidden={!show_direct_download}>
> <span class="w-32 text-xs text-gray-500">
<span class="text-xs text-gray-500 w-32">
Session Name: Session Name:
</span> </span>
<a <a
@@ -576,9 +592,8 @@
)}-{ae_util.clean_filename( )}-{ae_util.clean_filename(
event_file_obj?.event_presenter_full_name event_file_obj?.event_presenter_full_name
)}.{event_file_obj?.extension}&key={$ae_api.account_id}" )}.{event_file_obj?.extension}&key={$ae_api.account_id}"
class="btn btn-sm p-1 preset-tonal-secondary *:hover:inline lg:text-xs underline" class="btn btn-sm preset-tonal-secondary p-1 underline *:hover:inline lg:text-xs"
title={`Download renamed with session name to: ${event_file_obj?.event_session_code}-${ae_util.clean_filename(event_file_obj?.event_session_name).substring(0, 20)}-${ae_util.clean_filename(event_file_obj?.event_presenter_full_name)}.${event_file_obj?.extension}`} title={`Download renamed with session name to: ${event_file_obj?.event_session_code}-${ae_util.clean_filename(event_file_obj?.event_session_name).substring(0, 20)}-${ae_util.clean_filename(event_file_obj?.event_presenter_full_name)}.${event_file_obj?.extension}`}>
>
<Download size="1em" class="mx-1" /> <Download size="1em" class="mx-1" />
<span class="hidden"> Renamed </span> <span class="hidden"> Renamed </span>
</a> </a>
@@ -595,9 +610,8 @@
<div <div
class:hidden={!show_direct_download} class:hidden={!show_direct_download}
class="flex flex-row gap-0.5" class="flex flex-row gap-0.5">
> <span class="w-32 text-xs text-gray-500">
<span class="text-xs text-gray-500 w-32">
Presentation Name: Presentation Name:
</span> </span>
<a <a
@@ -611,9 +625,8 @@
)}-{ae_util.clean_filename( )}-{ae_util.clean_filename(
event_file_obj?.event_presenter_full_name event_file_obj?.event_presenter_full_name
)}.{event_file_obj?.extension}&key=${$ae_api.account_id}" )}.{event_file_obj?.extension}&key=${$ae_api.account_id}"
class="btn btn-sm p-1 preset-tonal-secondary *:hover:inline lg:text-xs underline" class="btn btn-sm preset-tonal-secondary p-1 underline *:hover:inline lg:text-xs"
title={`Download renamed with presentation name to: ${event_file_obj?.event_session_code}-${ae_util.clean_filename(event_file_obj?.event_presentation_name).substring(0, 20)}-${ae_util.clean_filename(event_file_obj?.event_presenter_full_name)}.${event_file_obj?.extension}`} title={`Download renamed with presentation name to: ${event_file_obj?.event_session_code}-${ae_util.clean_filename(event_file_obj?.event_presentation_name).substring(0, 20)}-${ae_util.clean_filename(event_file_obj?.event_presenter_full_name)}.${event_file_obj?.extension}`}>
>
<Download size="1em" class="mx-1" /> <Download size="1em" class="mx-1" />
<span class="hidden"> Renamed </span> <span class="hidden"> Renamed </span>
</a> </a>
@@ -630,9 +643,9 @@
</td> </td>
<td class="px-4 py-2" <td class="px-4 py-2"
>{ae_util.format_bytes( >{ae_util.format_bytes(
event_file_obj?.file_size || event_file_obj?.hosted_file_size event_file_obj?.file_size ||
)}</td event_file_obj?.hosted_file_size
> )}</td>
<td class="px-4 py-2"> <td class="px-4 py-2">
<div> <div>
<span> <span>
@@ -669,8 +682,7 @@
event_file_obj?.created_on, event_file_obj?.created_on,
minutes: 2880 minutes: 2880
} }
)} )}>
>
{ae_util.iso_datetime_formatter( {ae_util.iso_datetime_formatter(
event_file_obj?.created_on, event_file_obj?.created_on,
'time_12_short' 'time_12_short'
@@ -691,8 +703,7 @@
{#if show_session_fields} {#if show_session_fields}
<td <td
class="px-4 py-2 lg:text-xs" class="px-4 py-2 lg:text-xs"
class:hidden={hide_session_code} class:hidden={hide_session_code}>
>
{event_file_obj?.event_session_code ?? {event_file_obj?.event_session_code ??
'-- not set --'} '-- not set --'}
</td> </td>
@@ -700,8 +711,7 @@
<Presentation size="1em" /> <Presentation size="1em" />
<a <a
href="/events/{event_file_obj?.event_id}/session/{event_file_obj?.event_session_id}" href="/events/{event_file_obj?.event_id}/session/{event_file_obj?.event_session_id}"
class="text-blue-500 underline hover:text-blue-800" class="text-blue-500 underline hover:text-blue-800">
>
{event_file_obj?.event_session_name} {event_file_obj?.event_session_name}
</a> </a>
</td> </td>
@@ -709,8 +719,7 @@
>{ae_util.iso_datetime_formatter( >{ae_util.iso_datetime_formatter(
event_file_obj?.event_session_start_datetime, event_file_obj?.event_session_start_datetime,
'datetime_iso_12_no_seconds' 'datetime_iso_12_no_seconds'
)}</td )}</td>
>
{/if} {/if}
{#if show_presentation_fields} {#if show_presentation_fields}
<td class="px-4 py-2 lg:text-xs"> <td class="px-4 py-2 lg:text-xs">
@@ -736,8 +745,7 @@
<User size="1em" /> <User size="1em" />
<a <a
href="/events/{event_file_obj?.event_id}/presenter/{event_file_obj?.event_presenter_id}" href="/events/{event_file_obj?.event_id}/presenter/{event_file_obj?.event_presenter_id}"
class="text-blue-500 underline hover:text-blue-800" class="text-blue-500 underline hover:text-blue-800">
>
{event_file_obj?.event_presenter_full_name} {event_file_obj?.event_presenter_full_name}
</a> </a>
{:else} {:else}
@@ -745,7 +753,6 @@
{@html ae_snip.html__not_set} {@html ae_snip.html__not_set}
{/if} {/if}
</td> </td>
</tr> </tr>
{/each} {/each}
</tbody> </tbody>
@@ -757,8 +764,8 @@
</section> </section>
<style> <style>
.dim { .dim {
opacity: 0.5; opacity: 0.5;
color: #999; color: #999;
} }
</style> </style>

View File

@@ -1,124 +1,124 @@
<script lang="ts"> <script lang="ts">
interface Props { interface Props {
// Exports // Exports
container_class_li?: string | Array<string>; container_class_li?: string | Array<string>;
event_file_obj_li?: Array<any>; event_file_obj_li?: Array<any>;
link_to_type?: string; link_to_type?: string;
link_to_id?: string; link_to_id?: string;
allow_basic?: boolean; allow_basic?: boolean;
allow_moderator?: boolean; allow_moderator?: boolean;
show_direct_download?: boolean; show_direct_download?: boolean;
show_location_fields?: boolean; show_location_fields?: boolean;
show_presentation_fields?: boolean; show_presentation_fields?: boolean;
show_session_fields?: boolean; show_session_fields?: boolean;
hide_session_code?: boolean; hide_session_code?: boolean;
log_lvl?: number; log_lvl?: number;
}
let {
container_class_li = [],
// display_mode = 'default',
// event_file_id_random_li = $bindable(),
event_file_obj_li = $bindable(),
link_to_type,
link_to_id,
allow_basic = true,
allow_moderator = true,
show_direct_download = $bindable(false),
show_location_fields = false,
show_presentation_fields = true,
show_session_fields = true,
hide_session_code = false,
log_lvl = $bindable(0)
}: Props = $props();
// Imports
import Comp_event_file_obj_tbl from './ae_comp__event_file_obj_tbl.svelte';
import { liveQuery } from 'dexie';
import {
events_loc,
events_sess,
events_slct,
events_trigger
} from '$lib/stores/ae_events_stores';
import { db_events } from '$lib/ae_events/db_events';
$effect(() => {
if (log_lvl) {
console.log(`link_to_type: ${link_to_type}; link_to_id: ${link_to_id}`);
} }
});
let { // Variables
container_class_li = [], // let ae_promises: key_val = {};
// display_mode = 'default', // let ae_tmp: key_val = {};
// event_file_id_random_li = $bindable(), // let ae_triggers: key_val = {};
event_file_obj_li = $bindable(),
link_to_type,
link_to_id,
allow_basic = true,
allow_moderator = true,
show_direct_download = $bindable(false),
show_location_fields = false,
show_presentation_fields = true,
show_session_fields = true,
hide_session_code = false,
log_lvl = $bindable(0)
}: Props = $props();
// Imports let event_file_id_random_li: Array<string> = $state([]);
import Comp_event_file_obj_tbl from './ae_comp__event_file_obj_tbl.svelte';
import { liveQuery } from 'dexie'; let dq__where_type_id_val = $derived(`${link_to_type}_id_random`);
import { let dq__where_eq_id_val = $derived(link_to_id ?? '');
events_loc,
events_sess,
events_slct,
events_trigger
} from '$lib/stores/ae_events_stores';
import { db_events } from '$lib/ae_events/db_events';
$effect(() => { // *** Functions and Logic
if (log_lvl) {
console.log(`link_to_type: ${link_to_type}; link_to_id: ${link_to_id}`); // OPTIMIZATION: If event_file_obj_li is provided (from API/reports),
// use it directly instead of fetching from IDB. The API returns fully
// enriched data with all joined fields (session names, presentation names, etc.)
// that aren't available in the base IDB file table.
let lq__event_file_obj_li = $derived(
liveQuery(async () => {
let results: any;
// If data is already provided (from API reports with joins), use it directly
if (event_file_obj_li?.length) {
if (log_lvl) {
console.log(
`LQ - Using provided event_file_obj_li (${event_file_obj_li.length} files with API joins)`
);
}
return event_file_obj_li;
} }
});
// Variables // Otherwise fetch from IDB for link_to_type/link_to_id lookups
// let ae_promises: key_val = {}; if (link_to_type && link_to_id) {
// let ae_tmp: key_val = {}; if (log_lvl) {
// let ae_triggers: key_val = {}; console.log(
`LQ - Fetching from IDB where: ${dq__where_type_id_val} = ${dq__where_eq_id_val}`
let event_file_id_random_li: Array<string> = $state([]); );
let dq__where_type_id_val = $derived(`${link_to_type}_id_random`);
let dq__where_eq_id_val = $derived(link_to_id ?? '');
// *** Functions and Logic
// OPTIMIZATION: If event_file_obj_li is provided (from API/reports),
// use it directly instead of fetching from IDB. The API returns fully
// enriched data with all joined fields (session names, presentation names, etc.)
// that aren't available in the base IDB file table.
let lq__event_file_obj_li = $derived(
liveQuery(async () => {
let results: any;
// If data is already provided (from API reports with joins), use it directly
if (event_file_obj_li?.length) {
if (log_lvl) {
console.log(
`LQ - Using provided event_file_obj_li (${event_file_obj_li.length} files with API joins)`
);
}
return event_file_obj_li;
} }
results = await db_events.file
.where(dq__where_type_id_val)
.equals(dq__where_eq_id_val)
.sortBy('name');
} else {
results = [];
}
// Otherwise fetch from IDB for link_to_type/link_to_id lookups // Check if results are different than the current session version stored under $events_slct
if (link_to_type && link_to_id) { if (
if (log_lvl) { $events_slct.event_file_obj_li &&
console.log( JSON.stringify($events_slct.event_file_obj_li) !==
`LQ - Fetching from IDB where: ${dq__where_type_id_val} = ${dq__where_eq_id_val}` JSON.stringify(results)
); ) {
} $events_slct.event_file_obj_li = [...results];
results = await db_events.file if (log_lvl) {
.where(dq__where_type_id_val) console.log(
.equals(dq__where_eq_id_val) `Session slct li stored version has changed for ID = ${$events_slct.journal_id}`,
.sortBy('name'); $events_slct.event_file_obj_li
} else { );
results = [];
} }
} else {
// Check if results are different than the current session version stored under $events_slct if (log_lvl > 1) {
if ( console.log(
$events_slct.event_file_obj_li && `Session slct li stored version has not changed for ID = ${$events_slct.journal_id}`
JSON.stringify($events_slct.event_file_obj_li) !== );
JSON.stringify(results)
) {
$events_slct.event_file_obj_li = [...results];
if (log_lvl) {
console.log(
`Session slct li stored version has changed for ID = ${$events_slct.journal_id}`,
$events_slct.event_file_obj_li
);
}
} else {
if (log_lvl > 1) {
console.log(
`Session slct li stored version has not changed for ID = ${$events_slct.journal_id}`
);
}
} }
}
return results; return results;
}) })
); );
</script> </script>
{#if event_file_obj_li && event_file_obj_li?.length} {#if event_file_obj_li && event_file_obj_li?.length}
@@ -132,10 +132,9 @@
{show_presentation_fields} {show_presentation_fields}
{show_session_fields} {show_session_fields}
{hide_session_code} {hide_session_code}
{log_lvl} {log_lvl}></Comp_event_file_obj_tbl>
></Comp_event_file_obj_tbl>
{:else} {:else}
<section class="grow px-1 md:px-2 pb-28 flex flex-col gap-1 items-center"> <section class="flex grow flex-col items-center gap-1 px-1 pb-28 md:px-2">
<p>No files available to show in table.</p> <p>No files available to show in table.</p>
</section> </section>
{/if} {/if}

View File

@@ -1,206 +1,203 @@
<script lang="ts"> <script lang="ts">
/** /**
* src/routes/events/ae_comp__event_files_upload.svelte * src/routes/events/ae_comp__event_files_upload.svelte
* Specialized component for uploading files and automatically linking them to Event objects. * Specialized component for uploading files and automatically linking them to Event objects.
*/ */
import { LoaderCircle, Upload, UploadCloud } from '@lucide/svelte'; import { LoaderCircle, Upload, UploadCloud } from '@lucide/svelte';
import Element_input_files_tbl from '$lib/elements/element_input_files_tbl.svelte'; import Element_input_files_tbl from '$lib/elements/element_input_files_tbl.svelte';
// Import storage, functions, and libraries // Import storage, functions, and libraries
import type { key_val } from '$lib/stores/ae_stores'; import type { key_val } from '$lib/stores/ae_stores';
import { api } from '$lib/api/api'; import { api } from '$lib/api/api';
import { import {
ae_loc, ae_loc,
ae_sess, ae_sess,
ae_api, ae_api,
ae_trig, ae_trig,
slct, slct,
slct_trigger slct_trigger
} from '$lib/stores/ae_stores'; } from '$lib/stores/ae_stores';
import { import {
events_loc, events_loc,
events_sess, events_sess,
events_slct, events_slct,
events_trig events_trig
} 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 { db_events } from '$lib/ae_events/db_events';
interface Props { interface Props {
log_lvl?: number; log_lvl?: number;
// Expecting these for link_to_type: 'event', 'event_location', 'event_presentation', 'event_presenter', 'event_session' // Expecting these for link_to_type: 'event', 'event_location', 'event_presentation', 'event_presenter', 'event_session'
link_to_type: string; link_to_type: string;
link_to_id: string; link_to_id: string;
input_name?: string; input_name?: string;
multiple?: boolean; multiple?: boolean;
required?: boolean; required?: boolean;
accept?: string; accept?: string;
class_li_default?: string; class_li_default?: string;
class_li?: string; class_li?: string;
input_class_li?: string[]; input_class_li?: string[];
table_class_li?: string[]; table_class_li?: string[];
upload_complete?: boolean; upload_complete?: boolean;
submit_status?: null | string; submit_status?: null | string;
label?: import('svelte').Snippet; label?: import('svelte').Snippet;
}
let {
log_lvl = $bindable(0),
link_to_type,
link_to_id,
input_name = 'file_list',
multiple = true,
required = true,
accept = 'audio/*, image/*, video/*, .bak, .cfg, .css, .csv, .doc, .docx, .gz, .htm, .html, .ini, .iso, .j2, .json, .key, .keynote, .md, .pdf, .ppt, .pptx, .rar, .rtf, .sql, .svelte, ttf, .txt, .xls, .xlsx, .xz, .zip, .bin, .dmg, .exe, .js, .msi, .php, .py, .sh',
class_li_default = 'flex flex-col gap-1 items-center justify-center w-full max-w-2xl mx-auto my-1',
class_li = '',
input_class_li = ['file_drop_area'],
table_class_li = ['table', 'table-sm', 'table-striped', '', 'text-sm'],
upload_complete = $bindable(false),
submit_status = $bindable(null),
label
}: Props = $props();
// Local Variables
let task_id: string = $state('');
let input_file_list: any = $state(null);
let ae_promises: key_val = $state({});
let input_element_id = 'ae_comp__event_files_upload__input';
// WHY: Keep task_id in sync with prop if it hasn't been set by an upload cycle yet
$effect(() => {
if (!ae_promises.upload__hosted_file_obj) {
task_id = link_to_id;
} }
});
let { function prevent_default<T extends Event>(fn: (event: T) => void) {
log_lvl = $bindable(0), return function (event: T) {
link_to_type, event.preventDefault();
link_to_id, fn(event);
input_name = 'file_list', };
multiple = true, }
required = true,
accept = 'audio/*, image/*, video/*, .bak, .cfg, .css, .csv, .doc, .docx, .gz, .htm, .html, .ini, .iso, .j2, .json, .key, .keynote, .md, .pdf, .ppt, .pptx, .rar, .rtf, .sql, .svelte, ttf, .txt, .xls, .xlsx, .xz, .zip, .bin, .dmg, .exe, .js, .msi, .php, .py, .sh',
class_li_default = 'flex flex-col gap-1 items-center justify-center w-full max-w-2xl mx-auto my-1',
class_li = '',
input_class_li = ['file_drop_area'],
table_class_li = ['table', 'table-sm', 'table-striped', '', 'text-sm'],
upload_complete = $bindable(false),
submit_status = $bindable(null),
label
}: Props = $props();
// Local Variables async function handle_submit_form_files(event: SubmitEvent) {
let task_id: string = $state(''); if (log_lvl) console.log('*** handle_submit_form() ***');
let input_file_list: any = $state(null);
let ae_promises: key_val = $state({});
let input_element_id = 'ae_comp__event_files_upload__input';
// WHY: Keep task_id in sync with prop if it hasn't been set by an upload cycle yet $events_sess.files.disable_submit__event_file_obj = true;
$effect(() => { $events_sess.files.submit_status = 'saving';
if (!ae_promises.upload__hosted_file_obj) { submit_status = 'saving';
task_id = link_to_id; upload_complete = false;
}
});
function prevent_default<T extends Event>(fn: (event: T) => void) { const target = event.currentTarget as HTMLFormElement;
return function (event: T) { const file_input = target
event.preventDefault(); ? (target[input_element_id] as HTMLInputElement)
fn(event); : null;
};
}
async function handle_submit_form_files(event: SubmitEvent) { if (file_input && file_input.files && file_input.files.length > 0) {
if (log_lvl) console.log('*** handle_submit_form() ***'); // Sequential upload to provide reliable progress and avoid server race conditions
for (let i = 0; i < file_input.files.length; i++) {
const tmp_file = file_input.files[i];
task_id = $events_sess.files.processed_file_list[i].hash_sha256;
$events_sess.files.disable_submit__event_file_obj = true; await handle_input_upload_files({
$events_sess.files.submit_status = 'saving'; input_upload_files: [tmp_file],
submit_status = 'saving'; task_id: task_id
upload_complete = false;
const target = event.currentTarget as HTMLFormElement;
const file_input = target
? (target[input_element_id] as HTMLInputElement)
: null;
if (file_input && file_input.files && file_input.files.length > 0) {
// Sequential upload to provide reliable progress and avoid server race conditions
for (let i = 0; i < file_input.files.length; i++) {
const tmp_file = file_input.files[i];
task_id = $events_sess.files.processed_file_list[i].hash_sha256;
await handle_input_upload_files({
input_upload_files: [tmp_file],
task_id: task_id
});
}
// Cleanup after batch
$events_sess.files.processed_file_list = [];
target.reset();
// Refresh Local Cache (Optimistic Revalidation)
await db_events.file.clear();
events_func.load_ae_obj_li__event_file({
api_cfg: $ae_api,
for_obj_type: link_to_type,
for_obj_id: link_to_id,
enabled: 'all',
hidden: 'all',
try_cache: true
}); });
} }
$events_sess.files.disable_submit__event_file_obj = false; // Cleanup after batch
$events_sess.files.submit_status = 'saved'; $events_sess.files.processed_file_list = [];
submit_status = 'saved'; target.reset();
upload_complete = true;
// Refresh Local Cache (Optimistic Revalidation)
await db_events.file.clear();
events_func.load_ae_obj_li__event_file({
api_cfg: $ae_api,
for_obj_type: link_to_type,
for_obj_id: link_to_id,
enabled: 'all',
hidden: 'all',
try_cache: true
});
} }
async function handle_input_upload_files({ $events_sess.files.disable_submit__event_file_obj = false;
input_upload_files, $events_sess.files.submit_status = 'saved';
task_id submit_status = 'saved';
}: { upload_complete = true;
input_upload_files: any[]; }
task_id: string;
}) {
if (log_lvl)
console.log(
`*** handle_input_upload_files() *** task_id = ${task_id}`
);
const form_data = new FormData(); async function handle_input_upload_files({
form_data.append('account_id', $ae_loc.account_id); input_upload_files,
form_data.append('link_to_type', link_to_type); task_id
form_data.append('link_to_id', link_to_id); }: {
input_upload_files: any[];
task_id: string;
}) {
if (log_lvl)
console.log(`*** handle_input_upload_files() *** task_id = ${task_id}`);
for (let i = 0; i < input_upload_files.length; i++) { const form_data = new FormData();
form_data.append(`file_list`, input_upload_files[i]); form_data.append('account_id', $ae_loc.account_id);
} form_data.append('link_to_type', link_to_type);
form_data.append('link_to_id', link_to_id);
// STEP 1: Upload to Hosted Files for (let i = 0; i < input_upload_files.length; i++) {
ae_promises.upload__hosted_file_obj = api form_data.append(`file_list`, input_upload_files[i]);
.post_object({ }
api_cfg: $ae_api,
endpoint: '/hosted_file/upload_files',
form_data: form_data,
task_id: task_id,
log_lvl: log_lvl
})
.then(async function (result) {
// WARNING: endpoint returns a list, we sequentially handle one at a time.
const hosted_file_obj = result[0];
const hosted_file_id = hosted_file_obj.hosted_file_id;
const event_file_data: key_val = { // STEP 1: Upload to Hosted Files
ae_promises.upload__hosted_file_obj = api
.post_object({
api_cfg: $ae_api,
endpoint: '/hosted_file/upload_files',
form_data: form_data,
task_id: task_id,
log_lvl: log_lvl
})
.then(async function (result) {
// WARNING: endpoint returns a list, we sequentially handle one at a time.
const hosted_file_obj = result[0];
const hosted_file_id = hosted_file_obj.hosted_file_id;
const event_file_data: key_val = {
hosted_file_id: hosted_file_id,
for_type: link_to_type,
for_id: link_to_id,
filename: hosted_file_obj.filename,
extension: hosted_file_obj.extension,
enable: true
};
// STEP 2: Create Event File Link
return await events_func.create_event_file_obj_from_hosted_file_async(
{
api_cfg: $ae_api,
hosted_file_id: hosted_file_id, hosted_file_id: hosted_file_id,
for_type: link_to_type, data: event_file_data,
for_id: link_to_id, log_lvl: log_lvl
filename: hosted_file_obj.filename, }
extension: hosted_file_obj.extension, );
enable: true })
}; .catch(function (error: any) {
console.error('Upload Process Failed:', error);
return false;
})
.finally(function () {
$slct_trigger = 'load__event_file_obj_li';
});
// STEP 2: Create Event File Link return ae_promises.upload__hosted_file_obj;
return await events_func.create_event_file_obj_from_hosted_file_async( }
{
api_cfg: $ae_api,
hosted_file_id: hosted_file_id,
data: event_file_data,
log_lvl: log_lvl
}
);
})
.catch(function (error: any) {
console.error('Upload Process Failed:', error);
return false;
})
.finally(function () {
$slct_trigger = 'load__event_file_obj_li';
});
return ae_promises.upload__hosted_file_obj;
}
</script> </script>
<form <form
onsubmit={prevent_default(handle_submit_form_files)} onsubmit={prevent_default(handle_submit_form_files)}
class="{class_li_default} {class_li}" class="{class_li_default} {class_li}">
>
{#await ae_promises.upload__hosted_file_obj} {#await ae_promises.upload__hosted_file_obj}
<div class="text-lg flex flex-row gap-1 items-center justify-center"> <div class="flex flex-row items-center justify-center gap-1 text-lg">
<LoaderCircle class="animate-spin m-1" /> <LoaderCircle class="m-1 animate-spin" />
<span class=""> <span class="">
Uploading Uploading
{#if $ae_sess.api_upload_kv[task_id]} {#if $ae_sess.api_upload_kv[task_id]}
@@ -212,31 +209,27 @@
<div <div
class=" class="
border-2 hover:border-2 border-dashed border-primary-500 border-primary-500 preset-filled-primary-50-950 hover:border-primary-800 w-full
p-1 w-full rounded-lg cursor-pointer rounded-lg border-2
preset-filled-primary-50-950 hover:border-primary-800 border-dashed p-1
cursor-pointer transition-colors transition-colors hover:border-2
" ">
>
<label <label
for={input_element_id} for={input_element_id}
class=" class="
svelte_input_file_label svelte_input_file_label
text-center
cursor-pointer cursor-pointer
text-center
" "
class:hidden={$events_sess.files.disable_submit__event_file_obj} class:hidden={$events_sess.files.disable_submit__event_file_obj}>
>
{#if label}{@render label()}{:else} {#if label}{@render label()}{:else}
<div class="flex items-center justify-center gap-2 mb-2 pt-2"> <div class="mb-2 flex items-center justify-center gap-2 pt-2">
<Upload class="text-primary-500" /> <Upload class="text-primary-500" />
<strong class="preset-tonal-primary px-3 py-1 rounded-full" <strong class="preset-tonal-primary rounded-full px-3 py-1"
>Select Files</strong >Select Files</strong>
>
</div> </div>
<div <div
class="text-sm text-gray-600 dark:text-gray-400 italic pb-2" class="pb-2 text-sm text-gray-600 italic dark:text-gray-400">
>
<strong>Presentation materials only</strong><br /> <strong>Presentation materials only</strong><br />
(PPTX, Keynote, PDF, MP4, etc) (PPTX, Keynote, PDF, MP4, etc)
</div> </div>
@@ -254,39 +247,36 @@
class:hidden={$events_sess.files.disable_submit__event_file_obj} class:hidden={$events_sess.files.disable_submit__event_file_obj}
class=" class="
svelte_input_file_element file-dropzone-input svelte_input_file_element file-dropzone-input
block w-full text-lg text-center preset-tonal-primary preset-outlined-primary-200-800 hover:preset-filled-success-200-800 block
rounded-lg w-full
cursor-pointer cursor-pointer
p-1 rounded-lg
preset-tonal-primary preset-outlined-primary-200-800 hover:preset-filled-success-200-800 p-1 text-center text-lg
transition-all transition-all
{input_class_li.join(' ')} {input_class_li.join(' ')}
" " />
/>
</div> </div>
<Element_input_files_tbl <Element_input_files_tbl
bind:input_file_list bind:input_file_list
bind:file_list_status={$events_sess.files.status__file_list} bind:file_list_status={$events_sess.files.status__file_list}
bind:processed_file_list={$events_sess.files.processed_file_list} bind:processed_file_list={$events_sess.files.processed_file_list}
{table_class_li} {table_class_li} />
/>
<button <button
type="submit" type="submit"
class=" class="
btn btn-lg btn btn-lg
preset-tonal-primary
border-primary-500 hover:preset-tonal-success hover:border-success-500 flex w-54
items-center justify-center border
text-lg text-lg
preset-tonal-primary border border-primary-500 hover:preset-tonal-success hover:border-success-500
flex items-center justify-center
w-54
transition-all transition-all
" "
disabled={$events_sess.files.disable_submit__event_file_obj || disabled={$events_sess.files.disable_submit__event_file_obj ||
$events_sess.files.status__file_list != 'ready'} $events_sess.files.status__file_list != 'ready'}>
>
{#await ae_promises.upload__hosted_file_obj} {#await ae_promises.upload__hosted_file_obj}
<LoaderCircle class="animate-spin m-1" /> <LoaderCircle class="m-1 animate-spin" />
<span class=""> <span class="">
{#if $ae_sess.api_upload_kv[task_id]} {#if $ae_sess.api_upload_kv[task_id]}
{$ae_sess.api_upload_kv[task_id].percent_completed}% {$ae_sess.api_upload_kv[task_id].percent_completed}%
@@ -297,7 +287,7 @@
{:then} {:then}
<UploadCloud class="m-1" size={20} /> <UploadCloud class="m-1" size={20} />
<span class="text-sm"> Upload </span> <span class="text-sm"> Upload </span>
<span class="grow font-bold ml-2"> <span class="ml-2 grow font-bold">
{#if $events_sess.files.processed_file_list?.length > 0} {#if $events_sess.files.processed_file_list?.length > 0}
{$events_sess.files.processed_file_list.length} {$events_sess.files.processed_file_list.length}
{$events_sess.files.processed_file_list.length === 1 {$events_sess.files.processed_file_list.length === 1

View File

@@ -1,55 +1,61 @@
<script lang="ts"> <script lang="ts">
interface Props { interface Props {
// Exports // Exports
container_class_li?: string | Array<string>; container_class_li?: string | Array<string>;
display_mode?: string; // 'default', 'compact', 'minimal', 'launcher' display_mode?: string; // 'default', 'compact', 'minimal', 'launcher'
// export let link_to_id: string; // export let link_to_id: string;
lq__event_presentation_obj_li: any; lq__event_presentation_obj_li: any;
log_lvl?: number; // Variables log_lvl?: number; // Variables
} }
let { let {
container_class_li = [], container_class_li = [],
display_mode = 'default', display_mode = 'default',
lq__event_presentation_obj_li, lq__event_presentation_obj_li,
log_lvl = 0 log_lvl = 0
}: Props = $props(); }: Props = $props();
// Imports // Imports
import { ae_util } from '$lib/ae_utils/ae_utils'; import { ae_util } from '$lib/ae_utils/ae_utils';
import { import {
ae_snip, ae_snip,
ae_loc, ae_loc,
ae_sess, ae_sess,
ae_api, ae_api,
ae_trig, ae_trig,
slct, slct,
slct_trigger slct_trigger
} from '$lib/stores/ae_stores'; } from '$lib/stores/ae_stores';
import { events_func } from '$lib/ae_events/ae_events_functions'; import { events_func } from '$lib/ae_events/ae_events_functions';
import { import {
events_loc, events_loc,
events_sess, events_sess,
events_slct, events_slct,
events_trigger, events_trigger,
events_trig_kv events_trig_kv
} from '$lib/stores/ae_events_stores'; } from '$lib/stores/ae_events_stores';
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';
import Comp_event_presenter_obj_li from '../events/[event_id]/(pres_mgmt)/presenter/ae_comp__event_presenter_obj_li_wrapper.svelte'; import Comp_event_presenter_obj_li from '../events/[event_id]/(pres_mgmt)/presenter/ae_comp__event_presenter_obj_li_wrapper.svelte';
import Element_manage_event_file_li_wrap from '$lib/elements/element_manage_event_file_li_all.svelte'; import Element_manage_event_file_li_wrap from '$lib/elements/element_manage_event_file_li_all.svelte';
import { Barcode, CalendarDays, Eye, EyeOff, ListOrdered, Plus } from '@lucide/svelte'; import {
// export let link_to_type: string; Barcode,
CalendarDays,
Eye,
EyeOff,
ListOrdered,
Plus
} from '@lucide/svelte';
// export let link_to_type: string;
// let ae_promises: key_val = {}; // let ae_promises: key_val = {};
// let ae_tmp: key_val = {}; // let ae_tmp: key_val = {};
// let ae_triggers: key_val = {}; // let ae_triggers: key_val = {};
// *** Functions and Logic // *** Functions and Logic
</script> </script>
<section <section
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}" class="ae_comp event_presentation_obj_li container w-full min-w-full space-y-2 overflow-x-auto px-0.5 py-2 {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}
<button <button
@@ -77,8 +83,7 @@
log_lvl: 1 log_lvl: 1
}); });
}} }}
class="btn btn-sm preset-tonal-warning hover:preset-filled-warning-500" class="btn btn-sm preset-tonal-warning hover:preset-filled-warning-500">
>
<Plus size="1em" class="mx-1" /> <Plus size="1em" class="mx-1" />
Add Presentation Add Presentation
</button> </button>
@@ -93,10 +98,9 @@
{#if lq__event_presentation_obj_li?.length} {#if lq__event_presentation_obj_li?.length}
<span <span
class="text-3xl font-bold preset-filled-success-100-900 px-4 rounded-lg" class="preset-filled-success-100-900 rounded-lg px-4 text-3xl font-bold"
title="Count {lq__event_presentation_obj_li.length ?? title="Count {lq__event_presentation_obj_li.length ??
'None'}" 'None'}">
>
<ListOrdered size="1em" class="mx-4" /> <ListOrdered size="1em" class="mx-4" />
{lq__event_presentation_obj_li.length ?? 'None'}&times; {lq__event_presentation_obj_li.length ?? 'None'}&times;
</span> </span>
@@ -114,8 +118,9 @@
<!-- Show presentations for this LiveQuery --> <!-- Show presentations for this LiveQuery -->
<ul class="space-y-4"> <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-3 border border-surface-200-800 bg-surface-50-900 p-4 rounded-xl shadow-sm transition-colors duration-200"> <li
<div class="float-right space-2 flex flex-row items-center"> class="border-surface-200-800 bg-surface-50-900 space-y-3 rounded-xl border p-4 shadow-sm transition-colors duration-200">
<div class="space-2 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}
<button <button
type="button" type="button"
@@ -147,21 +152,19 @@
log_lvl: 1 log_lvl: 1
}); });
}} }}
class="btn btn-sm preset-tonal-warning hover:preset-filled-warning-500" class="btn btn-sm preset-tonal-warning hover:preset-filled-warning-500">
>
<Plus size="1em" class="mx-1" /> <Plus size="1em" class="mx-1" />
Add Presenter Add Presenter
</button> </button>
{/if} {/if}
</div> </div>
<h4 class="text-lg font-bold rounded-lg px-3 py-2 bg-surface-100-900 flex flex-wrap items-center gap-2"> <h4
class="bg-surface-100-900 flex flex-wrap items-center gap-2 rounded-lg px-3 py-2 text-lg font-bold">
<span <span
class:hidden={!event_presentation_obj.start_datetime || class:hidden={!event_presentation_obj.start_datetime ||
$events_loc.pres_mgmt $events_loc.pres_mgmt.hide__presentation_datetime}
.hide__presentation_datetime} class="border-r-2 border-gray-800/50 px-1 text-base">
class="text-base border-r-2 border-gray-800/50 px-1"
>
{ae_util.iso_datetime_formatter( {ae_util.iso_datetime_formatter(
event_presentation_obj.start_datetime, event_presentation_obj.start_datetime,
'dddd' 'dddd'
@@ -182,8 +185,13 @@
field_type={'text'} field_type={'text'}
current_value={event_presentation_obj?.name} current_value={event_presentation_obj?.name}
display_block={true} display_block={true}
on_success={() => events_func.load_ae_obj_id__event_presentation({ api_cfg: $ae_api, event_presentation_id: event_presentation_obj.event_presentation_id, log_lvl: 1 })} on_success={() =>
> events_func.load_ae_obj_id__event_presentation({
api_cfg: $ae_api,
event_presentation_id:
event_presentation_obj.event_presentation_id,
log_lvl: 1
})}>
<span class="italic"> <span class="italic">
{event_presentation_obj?.name} {event_presentation_obj?.name}
</span> </span>
@@ -197,28 +205,29 @@
field_type={'text'} field_type={'text'}
current_value={event_presentation_obj?.code} current_value={event_presentation_obj?.code}
display_block={true} display_block={true}
on_success={() => events_func.load_ae_obj_id__event_presentation({ api_cfg: $ae_api, event_presentation_id: event_presentation_obj.event_presentation_id, log_lvl: 1 })} on_success={() =>
> events_func.load_ae_obj_id__event_presentation({
api_cfg: $ae_api,
event_presentation_id:
event_presentation_obj.event_presentation_id,
log_lvl: 1
})}>
{#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-xs preset-tonal-warning px-2 py-0.5 rounded-md leading-none" class="preset-tonal-warning rounded-md px-2 py-0.5 text-xs 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}">
>
<Barcode size="1em" /> <Barcode size="1em" />
{event_presentation_obj?.code ?? ''} {event_presentation_obj?.code ?? ''}
{event_presentation_obj?.abstract_code ?? {event_presentation_obj?.abstract_code ?? ''}
''}
</span> </span>
{:else if $ae_loc.trusted_access && $ae_loc.edit_mode} {:else if $ae_loc.trusted_access && $ae_loc.edit_mode}
<span <span
class="text-sm text-semibold text-success-800-400" class="text-semibold text-success-800-400 text-sm">
>
<Barcode size="1em" /> <Barcode size="1em" />
Code: Code:
<span <span
class="" class=""
title="No code provided for this presentation" title="No code provided for this presentation">
>
{@html event_presentation_obj?.code ?? {@html event_presentation_obj?.code ??
ae_snip.html__not_set} ae_snip.html__not_set}
</span> </span>
@@ -231,18 +240,13 @@
<div <div
class:hidden={!( class:hidden={!(
$ae_loc.trusted_access && $ae_loc.edit_mode $ae_loc.trusted_access && $ae_loc.edit_mode
)} )}>
> <span class="text-semibold text-success-800-400 text-sm">
<span
class="text-sm text-semibold text-success-800-400"
>
Date & Date &
<button <button
type="button" type="button"
onclick={() => { onclick={() => {
if ( if ($events_loc.pres_mgmt.time_hours == 12) {
$events_loc.pres_mgmt.time_hours == 12
) {
$events_loc.pres_mgmt.time_hours = 24; $events_loc.pres_mgmt.time_hours = 24;
$events_loc.pres_mgmt.datetime_format = $events_loc.pres_mgmt.datetime_format =
'datetime_long'; 'datetime_long';
@@ -255,12 +259,10 @@
$events_loc.pres_mgmt.time_format = $events_loc.pres_mgmt.time_format =
'time_12_short'; 'time_12_short';
} }
}} }}>
>
time time
</button> </button>
:</span :</span>
>
<CalendarDays size="1em" class="text-success-800-400" /> <CalendarDays size="1em" class="text-success-800-400" />
<Element_ae_obj_field_editor <Element_ae_obj_field_editor
object_type={'event_presentation'} object_type={'event_presentation'}
@@ -268,8 +270,12 @@
field_name={'start_datetime'} field_name={'start_datetime'}
field_type={'datetime'} field_type={'datetime'}
current_value={event_presentation_obj.start_datetime} current_value={event_presentation_obj.start_datetime}
on_success={() => events_func.load_ae_obj_id__event_presentation({ api_cfg: $ae_api, event_presentation_id: event_presentation_obj?.event_presentation_id })} on_success={() =>
> events_func.load_ae_obj_id__event_presentation({
api_cfg: $ae_api,
event_presentation_id:
event_presentation_obj?.event_presentation_id
})}>
{ae_util.iso_datetime_formatter( {ae_util.iso_datetime_formatter(
event_presentation_obj.start_datetime, event_presentation_obj.start_datetime,
'dddd' 'dddd'
@@ -286,8 +292,12 @@
field_name={'end_datetime'} field_name={'end_datetime'}
field_type={'datetime'} field_type={'datetime'}
current_value={event_presentation_obj.end_datetime} current_value={event_presentation_obj.end_datetime}
on_success={() => events_func.load_ae_obj_id__event_presentation({ api_cfg: $ae_api, event_presentation_id: event_presentation_obj?.event_presentation_id })} on_success={() =>
> events_func.load_ae_obj_id__event_presentation({
api_cfg: $ae_api,
event_presentation_id:
event_presentation_obj?.event_presentation_id
})}>
{ae_util.iso_datetime_formatter( {ae_util.iso_datetime_formatter(
event_presentation_obj.end_datetime, event_presentation_obj.end_datetime,
$events_loc.pres_mgmt.time_format $events_loc.pres_mgmt.time_format
@@ -298,8 +308,7 @@
<div <div
class:hidden={!$events_loc.pres_mgmt class:hidden={!$events_loc.pres_mgmt
.show_content__presentation_description && .show_content__presentation_description &&
!($ae_loc.trusted_access && $ae_loc.edit_mode)} !($ae_loc.trusted_access && $ae_loc.edit_mode)}>
>
<Element_ae_obj_field_editor <Element_ae_obj_field_editor
object_type={'event_presentation'} object_type={'event_presentation'}
object_id={event_presentation_obj?.event_presentation_id} object_id={event_presentation_obj?.event_presentation_id}
@@ -309,11 +318,15 @@
allow_null={true} allow_null={true}
display_block={true} display_block={true}
textarea_rows={15} textarea_rows={15}
on_success={() => events_func.load_ae_obj_id__event_presentation({ api_cfg: $ae_api, event_presentation_id: event_presentation_obj.event_presentation_id, log_lvl: 1 })} on_success={() =>
> events_func.load_ae_obj_id__event_presentation({
api_cfg: $ae_api,
event_presentation_id:
event_presentation_obj.event_presentation_id,
log_lvl: 1
})}>
<span <span
class="text-sm text-semibold text-success-800-400" class="text-semibold text-success-800-400 text-sm">
>
Description: Description:
</span> </span>
@@ -342,8 +355,7 @@
event_presentation_obj.event_presentation_id; event_presentation_obj.event_presentation_id;
} }
}} }}
class="btn btn-sm preset-tonal-surface hover:preset-filled-surface-500 text-xs" class="btn btn-sm preset-tonal-surface hover:preset-filled-surface-500 text-xs">
>
{#if $events_sess.pres_mgmt.show_content__presentation_description == event_presentation_obj.event_presentation_id} {#if $events_sess.pres_mgmt.show_content__presentation_description == event_presentation_obj.event_presentation_id}
<EyeOff size="1em" class="mx-1" /> <EyeOff size="1em" class="mx-1" />
<span>Hide Description</span> <span>Hide Description</span>
@@ -354,7 +366,7 @@
</button> </button>
<pre <pre
class="whitespace-pre-wrap p-3 bg-surface-100-900 rounded-lg text-sm" class="bg-surface-100-900 rounded-lg p-3 text-sm whitespace-pre-wrap"
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>
@@ -377,8 +389,7 @@
link_to_type={'event_presentation'} link_to_type={'event_presentation'}
link_to_id={event_presentation_obj.event_presentation_id} link_to_id={event_presentation_obj.event_presentation_id}
event_presenter_id_li={[]} event_presenter_id_li={[]}
log_lvl={2} log_lvl={2}></Comp_event_presenter_obj_li>
></Comp_event_presenter_obj_li>
<!-- Show files for this presentation --> <!-- Show files for this presentation -->
<Element_manage_event_file_li_wrap <Element_manage_event_file_li_wrap
@@ -393,8 +404,7 @@
allow_moderator={$events_loc.auth__kv.session[ allow_moderator={$events_loc.auth__kv.session[
$events_slct.event_session_id $events_slct.event_session_id
]} ]}
container_class_li={''} container_class_li={''} />
/>
</li> </li>
{/each} {/each}
</ul> </ul>

View File

@@ -1,104 +1,120 @@
<script lang="ts"> <script lang="ts">
interface Props { interface Props {
log_lvl?: number; log_lvl?: number;
container_class_li?: string | Array<string>; container_class_li?: string | Array<string>;
lq__event_session_obj_li?: any; lq__event_session_obj_li?: any;
event_session_obj_li?: any[] | null; event_session_obj_li?: any[] | null;
hide__session_location?: boolean; hide__session_location?: boolean;
hide__session_poc?: boolean; hide__session_poc?: boolean;
hide__admin?: boolean; hide__admin?: boolean;
hide__launcher_link_legacy?: boolean; hide__launcher_link_legacy?: boolean;
hide__launcher_link?: boolean; hide__launcher_link?: boolean;
hide__location_link?: boolean; hide__location_link?: boolean;
show__session_files?: boolean; show__session_files?: boolean;
show__session_presentations?: boolean; show__session_presentations?: boolean;
} }
let { let {
log_lvl = 0, log_lvl = 0,
container_class_li = [], container_class_li = [],
lq__event_session_obj_li = null, lq__event_session_obj_li = null,
event_session_obj_li = null, event_session_obj_li = null,
hide__session_location = $bindable(false), hide__session_location = $bindable(false),
hide__session_poc = $bindable(false), hide__session_poc = $bindable(false),
hide__admin = $bindable(false), hide__admin = $bindable(false),
hide__launcher_link_legacy = $bindable(false), hide__launcher_link_legacy = $bindable(false),
hide__launcher_link = $bindable(false), hide__launcher_link = $bindable(false),
hide__location_link = $bindable(false), hide__location_link = $bindable(false),
show__session_files = $bindable(false), show__session_files = $bindable(false),
show__session_presentations = $bindable(false) show__session_presentations = $bindable(false)
}: Props = $props(); }: Props = $props();
import { ae_util } from '$lib/ae_utils/ae_utils'; import { ae_util } from '$lib/ae_utils/ae_utils';
import { Bell, BellOff, CalendarDays, Check, ChevronDown, ChevronUp, Clock, Edit, Eye, EyeOff, FileSearch, LoaderCircle, Mail, MapPin, Presentation, Rocket, Search, SendHorizontal, User } from '@lucide/svelte'; import {
import { api } from '$lib/api/api'; Bell,
import Comp_event_presenter_obj_li from './[event_id]/(pres_mgmt)/presenter/ae_comp__event_presenter_obj_li_wrapper.svelte'; BellOff,
import Element_manage_event_file_li from '$lib/elements/element_manage_event_file_li_direct.svelte'; CalendarDays,
import Comp_event_session_alert from './[event_id]/(pres_mgmt)/session/ae_comp__event_session_alert.svelte'; Check,
ChevronDown,
ChevronUp,
Clock,
Edit,
Eye,
EyeOff,
FileSearch,
LoaderCircle,
Mail,
MapPin,
Presentation,
Rocket,
Search,
SendHorizontal,
User
} from '@lucide/svelte';
import { api } from '$lib/api/api';
import Comp_event_presenter_obj_li from './[event_id]/(pres_mgmt)/presenter/ae_comp__event_presenter_obj_li_wrapper.svelte';
import Element_manage_event_file_li from '$lib/elements/element_manage_event_file_li_direct.svelte';
import Comp_event_session_alert from './[event_id]/(pres_mgmt)/session/ae_comp__event_session_alert.svelte';
import { events_func } from '$lib/ae_events/ae_events_functions'; import { events_func } from '$lib/ae_events/ae_events_functions';
import { ae_loc, ae_api, ae_snip } from '$lib/stores/ae_stores'; import { ae_loc, ae_api, ae_snip } from '$lib/stores/ae_stores';
import { import {
events_loc, events_loc,
events_sess, events_sess,
events_slct events_slct
} from '$lib/stores/ae_events_stores'; } from '$lib/stores/ae_events_stores';
let show_details_kv: Record<string, boolean> = $state({}); let show_details_kv: Record<string, boolean> = $state({});
// Derived list of visible items (Standardized Pattern 2026-01-27) // Derived list of visible items (Standardized Pattern 2026-01-27)
// Supports both a liveQuery observable (lq__event_session_obj_li) and a // Supports both a liveQuery observable (lq__event_session_obj_li) and a
// plain pre-fetched array (event_session_obj_li) as a fallback. // plain pre-fetched array (event_session_obj_li) as a fallback.
let visible_session_obj_li = $derived( let visible_session_obj_li = $derived(
(() => { (() => {
const list = $lq__event_session_obj_li ?? event_session_obj_li; const list = $lq__event_session_obj_li ?? event_session_obj_li;
if (list === undefined || list === null) return null; if (list === undefined || list === null) return null;
if (!Array.isArray(list)) return []; if (!Array.isArray(list)) return [];
const filtered = list.filter((item: any) => { const filtered = list.filter((item: any) => {
if (!item) return false; if (!item) return false;
if ($ae_loc.trusted_access) return true; if ($ae_loc.trusted_access) return true;
return !item.hide; return !item.hide;
}); });
if (log_lvl) if (log_lvl)
console.log( console.log(
`visible_session_obj_li: Input=${list.length}, Output=${filtered.length}` `visible_session_obj_li: Input=${list.length}, Output=${filtered.length}`
); );
return filtered; return filtered;
})() })()
); );
function toggle_details(id: string) { function toggle_details(id: string) {
show_details_kv[id] = !show_details_kv[id]; show_details_kv[id] = !show_details_kv[id];
} }
</script> </script>
<section <section
class="ae_comp event_session_obj_li px-0.5 py-2 space-y-2 min-w-full w-full container overflow-x-auto {container_class_li}" class="ae_comp event_session_obj_li container w-full min-w-full space-y-2 overflow-x-auto px-0.5 py-2 {container_class_li}">
>
{#if visible_session_obj_li === null} {#if visible_session_obj_li === null}
<div class="flex flex-col items-center justify-center p-10 opacity-50"> <div class="flex flex-col items-center justify-center p-10 opacity-50">
<LoaderCircle size="3em" class="animate-spin mb-2" /> <LoaderCircle size="3em" class="mb-2 animate-spin" />
<p>Loading sessions...</p> <p>Loading sessions...</p>
</div> </div>
{:else if visible_session_obj_li.length > 0} {:else if visible_session_obj_li.length > 0}
<header <header
class="w-full flex flex-row gap-2 items-center justify-start mb-2 px-2" class="mb-2 flex w-full flex-row items-center justify-start gap-2 px-2">
> <h2 class="text-sm font-normal text-gray-500">Sessions:</h2>
<h2 class="text-sm text-gray-500 font-normal">Sessions:</h2>
<span <span
class="badge preset-tonal-success font-bold text-lg px-3 py-1" class="badge preset-tonal-success px-3 py-1 text-lg font-bold">
>
{visible_session_obj_li.length}<span {visible_session_obj_li.length}<span
class="text-gray-400 dark:text-gray-600">&times;</span class="text-gray-400 dark:text-gray-600">&times;</span>
>
</span> </span>
</header> </header>
<table class="table table-auto table-striped w-full"> <table class="table-striped table w-full table-auto">
<thead> <thead>
<tr class="bg-surface-100-900"> <tr class="bg-surface-100-900">
<th>Session</th> <th>Session</th>
@@ -112,8 +128,7 @@
<th <th
class:hidden={!$ae_loc.edit_mode || class:hidden={!$ae_loc.edit_mode ||
!$ae_loc.adv_mode || !$ae_loc.adv_mode ||
hide__admin}>Admin</th hide__admin}>Admin</th>
>
</tr> </tr>
</thead> </thead>
@@ -122,40 +137,34 @@
<tr <tr
class="relative transition-colors duration-200" class="relative transition-colors duration-200"
class:opacity-50={session_obj?.hide} class:opacity-50={session_obj?.hide}
class:preset-tonal-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}
<Comp_event_session_alert <Comp_event_session_alert
event_session_obj={session_obj} event_session_obj={session_obj}
{log_lvl} {log_lvl} />
/>
{/if} {/if}
<div class="flex flex-col gap-1"> <div class="flex flex-col gap-1">
<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 transition-colors duration-200" class="hover:text-primary-500 flex flex-row items-center gap-2 text-left text-lg font-bold transition-colors duration-200">
>
{#if session_obj?.hide} {#if session_obj?.hide}
<EyeOff <EyeOff
size="1em" size="1em"
class="text-gray-400 flex-none" class="flex-none text-gray-400" />
/>
{:else} {:else}
<Presentation <Presentation
size="1em" size="1em"
class="text-primary-500 flex-none" class="text-primary-500 flex-none" />
/>
{/if} {/if}
<span>{session_obj?.name}</span> <span>{session_obj?.name}</span>
{#if session_obj?.file_count_all} {#if session_obj?.file_count_all}
<span <span
class="badge preset-tonal-success flex items-center gap-1 text-xs py-0 px-1" class="badge preset-tonal-success flex items-center gap-1 px-1 py-0 text-xs">
>
<Check size="1em" /> <Check size="1em" />
{session_obj.file_count_all} {session_obj.file_count_all}
@@ -170,8 +179,7 @@
onclick={() => onclick={() =>
toggle_details( toggle_details(
session_obj.event_session_id session_obj.event_session_id
)} )}>
>
{#if show_details_kv[session_obj.event_session_id]} {#if show_details_kv[session_obj.event_session_id]}
<ChevronUp size="1.2em" /> <ChevronUp size="1.2em" />
{:else} {:else}
@@ -184,36 +192,31 @@
<!-- Mobile Schedule Summary --> <!-- Mobile Schedule Summary -->
<div <div
class="md:hidden text-xs text-surface-500 flex flex-wrap gap-x-3 gap-y-1" class="text-surface-500 flex flex-wrap gap-x-3 gap-y-1 text-xs md:hidden">
>
<span class="flex items-center gap-1" <span class="flex items-center gap-1"
><CalendarDays size="1em" /> ><CalendarDays size="1em" />
{ae_util.iso_datetime_formatter( {ae_util.iso_datetime_formatter(
session_obj?.start_datetime, session_obj?.start_datetime,
'date_short_month_day' 'date_short_month_day'
)}</span )}</span>
>
<span class="flex items-center gap-1" <span class="flex items-center gap-1"
><Clock size="1em" /> ><Clock size="1em" />
{ae_util.iso_datetime_formatter( {ae_util.iso_datetime_formatter(
session_obj?.start_datetime, session_obj?.start_datetime,
'time_12_short' 'time_12_short'
)}</span )}</span>
>
</div> </div>
{#if show_details_kv[session_obj.event_session_id]} {#if show_details_kv[session_obj.event_session_id]}
<div <div
class="p-2 bg-surface-500/5 rounded-lg border border-surface-500/10 mt-1" class="bg-surface-500/5 border-surface-500/10 mt-1 rounded-lg border p-2">
>
{#if show__session_presentations && $ae_loc.manager_access} {#if show__session_presentations && $ae_loc.manager_access}
<Comp_event_presenter_obj_li <Comp_event_presenter_obj_li
link_to_type={'event_session'} link_to_type={'event_session'}
link_to_id={session_obj?.event_session_id} link_to_id={session_obj?.event_session_id}
display_mode={'minimal'} display_mode={'minimal'}
{log_lvl} {log_lvl} />
/>
{/if} {/if}
{#if show__session_files && $ae_loc.manager_access} {#if show__session_files && $ae_loc.manager_access}
@@ -222,8 +225,7 @@
link_to_id={session_obj?.event_session_id} link_to_id={session_obj?.event_session_id}
allow_basic={true} allow_basic={true}
allow_moderator={true} allow_moderator={true}
display_mode={'minimal'} display_mode={'minimal'} />
/>
{/if} {/if}
</div> </div>
{/if} {/if}
@@ -236,8 +238,7 @@
>{ae_util.iso_datetime_formatter( >{ae_util.iso_datetime_formatter(
session_obj?.start_datetime, session_obj?.start_datetime,
'dddd, MMM D' 'dddd, MMM D'
)}</span )}</span>
>
<span class="text-surface-500 whitespace-nowrap" <span class="text-surface-500 whitespace-nowrap"
>{ae_util.iso_datetime_formatter( >{ae_util.iso_datetime_formatter(
@@ -246,17 +247,15 @@
)} {ae_util.iso_datetime_formatter( )} {ae_util.iso_datetime_formatter(
session_obj?.end_datetime, session_obj?.end_datetime,
'time_12_short' 'time_12_short'
)}</span )}</span>
>
</div> </div>
</td> </td>
<td class:hidden={hide__session_location}> <td class:hidden={hide__session_location}>
<div class="flex flex-col gap-1 min-w-32"> <div class="flex min-w-32 flex-col gap-1">
<span class="text-xs font-semibold" <span class="text-xs font-semibold"
>{session_obj?.event_location_name ?? >{session_obj?.event_location_name ??
'--'}</span '--'}</span>
>
<div class="flex gap-1"> <div class="flex gap-1">
{#if !hide__launcher_link} {#if !hide__launcher_link}
@@ -264,8 +263,7 @@
href="/events/{session_obj?.event_id}/launcher/{session_obj?.event_location_id}?session_id={session_obj?.event_session_id}" href="/events/{session_obj?.event_id}/launcher/{session_obj?.event_location_id}?session_id={session_obj?.event_session_id}"
class="btn btn-icon btn-xs preset-tonal-tertiary" class="btn btn-icon btn-xs preset-tonal-tertiary"
title="Svelte Launcher" title="Svelte Launcher"
><Rocket size="1em" /></a ><Rocket size="1em" /></a>
>
{/if} {/if}
{#if !hide__location_link} {#if !hide__location_link}
@@ -273,28 +271,25 @@
href="/events/{session_obj?.event_id}/location/{session_obj?.event_location_id}" href="/events/{session_obj?.event_id}/location/{session_obj?.event_location_id}"
class="btn btn-icon btn-xs preset-tonal-surface" class="btn btn-icon btn-xs preset-tonal-surface"
title="Location Details" title="Location Details"
><MapPin size="1em" /></a ><MapPin size="1em" /></a>
>
{/if} {/if}
</div> </div>
</div> </div>
</td> </td>
<td class:hidden={hide__session_poc}> <td class:hidden={hide__session_poc}>
<div class="flex flex-col text-xs min-w-32"> <div class="flex min-w-32 flex-col text-xs">
{#if session_obj?.poc_person_full_name} {#if session_obj?.poc_person_full_name}
<span <span
class="font-bold flex items-center gap-1" class="flex items-center gap-1 font-bold"
><User size="1em" /> ><User size="1em" />
{session_obj.poc_person_full_name}</span {session_obj.poc_person_full_name}</span>
>
{#if $ae_loc.trusted_access && session_obj?.poc_person_primary_email} {#if $ae_loc.trusted_access && session_obj?.poc_person_primary_email}
<a <a
href="mailto:{session_obj.poc_person_primary_email}" href="mailto:{session_obj.poc_person_primary_email}"
class="text-primary-500 hover:underline flex items-center gap-1" class="text-primary-500 flex items-center gap-1 hover:underline"
><Mail size="1em" /> Email</a ><Mail size="1em" /> Email</a>
>
{/if} {/if}
{:else} {:else}
<span class="opacity-30">--</span> <span class="opacity-30">--</span>
@@ -305,8 +300,7 @@
<td <td
class:hidden={!$ae_loc.edit_mode || class:hidden={!$ae_loc.edit_mode ||
!$ae_loc.adv_mode || !$ae_loc.adv_mode ||
hide__admin} hide__admin}>
>
<div class="flex gap-1"> <div class="flex gap-1">
<button <button
type="button" type="button"
@@ -321,15 +315,17 @@
fields: { hide: !session_obj.hide }, fields: { hide: !session_obj.hide },
log_lvl: 1 log_lvl: 1
}); });
events_func.load_ae_obj_id__event_session({ events_func.load_ae_obj_id__event_session(
api_cfg: $ae_api, {
event_session_id: session_obj.event_session_id api_cfg: $ae_api,
}); event_session_id:
}} session_obj.event_session_id
> }
);
}}>
{#if session_obj?.hide}<EyeOff {#if session_obj?.hide}<EyeOff
size="1.2em" size="1.2em" />{:else}<Eye
/>{:else}<Eye size="1.2em" />{/if} size="1.2em" />{/if}
</button> </button>
<button <button
@@ -342,18 +338,22 @@
api_cfg: $ae_api, api_cfg: $ae_api,
obj_type: 'event_session', obj_type: 'event_session',
obj_id: session_obj.event_session_id, obj_id: session_obj.event_session_id,
fields: { alert: !session_obj.alert }, fields: {
alert: !session_obj.alert
},
log_lvl: 1 log_lvl: 1
}); });
events_func.load_ae_obj_id__event_session({ events_func.load_ae_obj_id__event_session(
api_cfg: $ae_api, {
event_session_id: session_obj.event_session_id api_cfg: $ae_api,
}); event_session_id:
}} session_obj.event_session_id
> }
);
}}>
{#if session_obj?.alert}<Bell {#if session_obj?.alert}<Bell
size="1.2em" size="1.2em" />{:else}<BellOff
/>{:else}<BellOff size="1.2em" />{/if} size="1.2em" />{/if}
</button> </button>
</div> </div>
</td> </td>
@@ -363,9 +363,8 @@
</table> </table>
{:else} {:else}
<div <div
class="flex flex-col items-center justify-center p-20 opacity-50 text-center bg-surface-50 dark:bg-surface-900/50 rounded-lg border border-dashed border-surface-300" class="bg-surface-50 dark:bg-surface-900/50 border-surface-300 flex flex-col items-center justify-center rounded-lg border border-dashed p-20 text-center opacity-50">
> <FileSearch size="3em" class="mx-auto mb-2 opacity-20" />
<FileSearch size="3em" class="mb-2 opacity-20 mx-auto" />
<p class="text-xl">No sessions found matching your criteria.</p> <p class="text-xl">No sessions found matching your criteria.</p>

View File

@@ -1,59 +1,59 @@
<script lang="ts"> <script lang="ts">
interface Props { interface Props {
container_class_li?: string | Array<string>; container_class_li?: string | Array<string>;
lq__event_session_obj_li?: any; // New Shared Observable Pattern lq__event_session_obj_li?: any; // New Shared Observable Pattern
event_session_id_random_li?: Array<string>; event_session_id_random_li?: Array<string>;
link_to_type?: string; link_to_type?: string;
link_to_id?: string; link_to_id?: string;
hide__session_location?: boolean; hide__session_location?: boolean;
hide__session_poc?: boolean; hide__session_poc?: boolean;
hide__admin?: boolean; hide__admin?: boolean;
hide__launcher_link_legacy?: boolean; hide__launcher_link_legacy?: boolean;
hide__launcher_link?: boolean; hide__launcher_link?: boolean;
hide__location_link?: boolean; hide__location_link?: boolean;
log_lvl?: number; log_lvl?: number;
} }
let { let {
container_class_li = [], container_class_li = [],
lq__event_session_obj_li: lq__shared, lq__event_session_obj_li: lq__shared,
event_session_id_random_li = [], event_session_id_random_li = [],
link_to_type, link_to_type,
link_to_id, link_to_id,
hide__session_location = $bindable(false), hide__session_location = $bindable(false),
hide__session_poc = $bindable(false), hide__session_poc = $bindable(false),
hide__admin = $bindable(false), hide__admin = $bindable(false),
hide__launcher_link_legacy = $bindable(false), hide__launcher_link_legacy = $bindable(false),
hide__launcher_link = $bindable(false), hide__launcher_link = $bindable(false),
hide__location_link = $bindable(false), hide__location_link = $bindable(false),
log_lvl = 0 log_lvl = 0
}: Props = $props(); }: Props = $props();
// Imports // Imports
import Comp_event_session_obj_li from './ae_comp__event_session_obj_li.svelte'; import Comp_event_session_obj_li from './ae_comp__event_session_obj_li.svelte';
import { liveQuery } from 'dexie'; import { liveQuery } from 'dexie';
import { db_events } from '$lib/ae_events/db_events'; import { db_events } from '$lib/ae_events/db_events';
// Logic: Use shared observable if provided, otherwise create internal one // Logic: Use shared observable if provided, otherwise create internal one
let lq__event_session_obj_li = $derived.by(() => { let lq__event_session_obj_li = $derived.by(() => {
if (lq__shared) return lq__shared; if (lq__shared) return lq__shared;
return liveQuery(async () => { return liveQuery(async () => {
if (link_to_type && link_to_id) { if (link_to_type && link_to_id) {
const results = await db_events.session const results = await db_events.session
.where(`${link_to_type}_id_random`) .where(`${link_to_type}_id_random`)
.equals(link_to_id) .equals(link_to_id)
.sortBy('start_datetime'); .sortBy('start_datetime');
return results; return results;
} else if (event_session_id_random_li.length > 0) { } else if (event_session_id_random_li.length > 0) {
const results = await db_events.session.bulkGet( const results = await db_events.session.bulkGet(
event_session_id_random_li event_session_id_random_li
); );
return results.filter((item) => item !== undefined); return results.filter((item) => item !== undefined);
} }
return null; return null;
});
}); });
});
</script> </script>
<Comp_event_session_obj_li <Comp_event_session_obj_li
@@ -65,5 +65,4 @@
{hide__launcher_link_legacy} {hide__launcher_link_legacy}
{hide__launcher_link} {hide__launcher_link}
{hide__location_link} {hide__location_link}
{log_lvl} {log_lvl}></Comp_event_session_obj_li>
></Comp_event_session_obj_li>

View File

@@ -1,84 +1,81 @@
<script lang="ts"> <script lang="ts">
interface Props { interface Props {
container_class_li?: string | Array<string>; container_class_li?: string | Array<string>;
// export let event_session_id_random_li: Array<string>; // export let event_session_id_random_li: Array<string>;
lq__event_session_obj_li: any; lq__event_session_obj_li: any;
log_lvl?: number; log_lvl?: number;
show_location_fields?: boolean; show_location_fields?: boolean;
hide_session_code?: boolean; hide_session_code?: boolean;
}
let {
container_class_li = [],
lq__event_session_obj_li,
log_lvl = $bindable(0),
show_location_fields = true,
hide_session_code = false
}: Props = $props();
// Imports
// import type { key_val } from '$lib/ae_stores';
// import { liveQuery } from "dexie";
import { ae_util } from '$lib/ae_utils/ae_utils';
import { ListOrdered } from '@lucide/svelte';
// Imports - events specific
// import { events_loc, events_sess, events_slct, events_trigger, events_trig_kv } from '$lib/stores/ae_events_stores';
// import { db_events } from "$lib/db_events";
// export let display_mode: string = 'default'; // 'default', 'compact', 'minimal', 'launcher';
// export let link_to_type: string;
// export let link_to_id: string;
// *** Functions and Logic
$effect(() => {
if (log_lvl) {
console.log(
`container_class_li: ${container_class_li}; show_location_fields: ${show_location_fields}`
);
// console.log(`link_to_type: ${link_to_type}; link_to_id: ${link_to_id}`);
} }
});
let { let horiz_scroll_warning: boolean = $state(false);
container_class_li = [], let horiz_check_element: HTMLElement | null = $state(null);
lq__event_session_obj_li,
log_lvl = $bindable(0),
show_location_fields = true,
hide_session_code = false
}: Props = $props();
// Imports // Check if element is scrolling horizontally
// import type { key_val } from '$lib/ae_stores'; $effect(() => {
if (
// import { liveQuery } from "dexie"; horiz_check_element &&
import { ae_util } from '$lib/ae_utils/ae_utils'; horiz_check_element.scrollWidth > horiz_check_element.offsetWidth
import { ListOrdered } from '@lucide/svelte'; ) {
// Imports - events specific horiz_scroll_warning = true;
// import { events_loc, events_sess, events_slct, events_trigger, events_trig_kv } from '$lib/stores/ae_events_stores'; // console.log('Element is too wide for the container. Horizontal scrolling detected.');
// import { db_events } from "$lib/db_events"; } else {
horiz_scroll_warning = false;
// export let display_mode: string = 'default'; // 'default', 'compact', 'minimal', 'launcher'; // console.log('Element fits within the container. No horizontal scrolling.', horiz_check_element);
// export let link_to_type: string; }
// export let link_to_id: string; });
// *** Functions and Logic
$effect(() => {
if (log_lvl) {
console.log(
`container_class_li: ${container_class_li}; show_location_fields: ${show_location_fields}`
);
// console.log(`link_to_type: ${link_to_type}; link_to_id: ${link_to_id}`);
}
});
let horiz_scroll_warning: boolean = $state(false);
let horiz_check_element: HTMLElement | null = $state(null);
// Check if element is scrolling horizontally
$effect(() => {
if (
horiz_check_element &&
horiz_check_element.scrollWidth > horiz_check_element.offsetWidth
) {
horiz_scroll_warning = true;
// console.log('Element is too wide for the container. Horizontal scrolling detected.');
} else {
horiz_scroll_warning = false;
// console.log('Element fits within the container. No horizontal scrolling.', horiz_check_element);
}
});
</script> </script>
<section <section
class:border-r-2={horiz_scroll_warning} class:border-r-2={horiz_scroll_warning}
class:border-dashed={horiz_scroll_warning} class:border-dashed={horiz_scroll_warning}
class:border-warning-900-100={horiz_scroll_warning} class:border-warning-900-100={horiz_scroll_warning}
class="ae_comp event_session_obj_tbl {container_class_li} container overflow-auto max-w-screen" class="ae_comp event_session_obj_tbl {container_class_li} container max-w-screen overflow-auto">
>
{#if $lq__event_session_obj_li && $lq__event_session_obj_li?.length} {#if $lq__event_session_obj_li && $lq__event_session_obj_li?.length}
<div <div
bind:this={horiz_check_element} bind:this={horiz_check_element}
id="tbl_container" id="tbl_container"
class="space-y-2 pb-48" class="space-y-2 pb-48">
>
<h2 class="h3"> <h2 class="h3">
<span class="text-base"> Results: </span> <span class="text-base"> Results: </span>
{#if $lq__event_session_obj_li?.length} {#if $lq__event_session_obj_li?.length}
<span <span
class="text-3xl font-bold preset-filled-success-100-900 px-4 rounded-lg" class="preset-filled-success-100-900 rounded-lg px-4 text-3xl font-bold"
title="Count {$lq__event_session_obj_li.length ?? title="Count {$lq__event_session_obj_li.length ??
'None'}" 'None'}">
>
<ListOrdered size="1em" class="mx-4" /> <ListOrdered size="1em" class="mx-4" />
{$lq__event_session_obj_li.length ?? 'None'}&times; {$lq__event_session_obj_li.length ?? 'None'}&times;
</span> </span>
@@ -86,8 +83,7 @@
</h2> </h2>
<table <table
class="table table-auto table-striped w-full text-xs lg:text-sm" class="table-striped table w-full table-auto text-xs lg:text-sm">
>
<thead class=""> <thead class="">
<tr> <tr>
<th class="px-4 py-2">Name</th> <th class="px-4 py-2">Name</th>
@@ -105,8 +101,7 @@
<td class="px-4 py-2"> <td class="px-4 py-2">
<a <a
href="/events/{event_session_obj?.event_id}/session/{event_session_obj?.event_session_id}" href="/events/{event_session_obj?.event_id}/session/{event_session_obj?.event_session_id}"
class="text-blue-500 hover:text-blue-800 hover:underline" class="text-blue-500 hover:text-blue-800 hover:underline">
>
{event_session_obj?.name} {event_session_obj?.name}
</a> </a>
</td> </td>

View File

@@ -1,135 +1,135 @@
<script lang="ts"> <script lang="ts">
interface Props { interface Props {
// Exports // Exports
container_class_li?: string | Array<string>; container_class_li?: string | Array<string>;
// export let display_mode: string = 'default'; // 'default', 'compact', 'minimal', 'launcher' // export let display_mode: string = 'default'; // 'default', 'compact', 'minimal', 'launcher'
// event_session_id_random_li: Array<string>; // event_session_id_random_li: Array<string>;
event_session_obj_li?: Array<any>; event_session_obj_li?: Array<any>;
link_to_type: string; link_to_type: string;
link_to_id: string; link_to_id: string;
// export let lq__event_presentation_obj // export let lq__event_presentation_obj
log_lvl?: number; log_lvl?: number;
show_location_fields?: boolean; show_location_fields?: boolean;
hide_session_code?: boolean; hide_session_code?: boolean;
}
let {
container_class_li = [],
// event_session_id_random_li,
event_session_obj_li = $bindable(),
link_to_type,
link_to_id,
log_lvl = $bindable(0),
show_location_fields = true,
hide_session_code = false
}: Props = $props();
// Imports
import Comp_event_session_obj_tbl from './ae_comp__event_session_obj_tbl.svelte';
import { liveQuery } from 'dexie';
import {
events_loc,
events_sess,
events_slct,
events_trigger
} from '$lib/stores/ae_events_stores';
import { db_events } from '$lib/ae_events/db_events';
$effect(() => {
if (log_lvl) {
console.log(`link_to_type: ${link_to_type}; link_to_id: ${link_to_id}`);
} }
});
let { // Variables
container_class_li = [], // let ae_promises: key_val = {};
// event_session_id_random_li, // let ae_tmp: key_val = {};
event_session_obj_li = $bindable(), // let ae_triggers: key_val = {};
link_to_type,
link_to_id,
log_lvl = $bindable(0),
show_location_fields = true,
hide_session_code = false
}: Props = $props();
// Imports let event_session_id_random_li: Array<string> = $state([]);
import Comp_event_session_obj_tbl from './ae_comp__event_session_obj_tbl.svelte';
import { liveQuery } from 'dexie'; let dq__where_type_id_val = $derived(`${link_to_type}_id_random`);
import { let dq__where_eq_id_val = $derived(link_to_id);
events_loc,
events_sess,
events_slct,
events_trigger
} from '$lib/stores/ae_events_stores';
import { db_events } from '$lib/ae_events/db_events';
$effect(() => { // *** Functions and Logic
if (log_lvl) {
console.log(`link_to_type: ${link_to_type}; link_to_id: ${link_to_id}`); // OPTIMIZATION: If event_session_obj_li is provided (from API/reports),
// use it directly instead of fetching from IDB. The API returns fully
// enriched data with all joined fields (location names, file counts, etc.)
// that aren't available in the base IDB session table.
let lq__event_session_obj_li = $derived(
liveQuery(async () => {
let results: any;
// If data is already provided (from API reports with joins), use it directly
if (event_session_obj_li?.length) {
if (log_lvl) {
console.log(
`LQ - Using provided event_session_obj_li (${event_session_obj_li.length} sessions with API joins)`
);
}
return event_session_obj_li;
} }
});
// Variables // Otherwise fetch from IDB for link_to_type/link_to_id lookups
// let ae_promises: key_val = {}; if (link_to_type && link_to_id) {
// let ae_tmp: key_val = {}; if (log_lvl) {
// let ae_triggers: key_val = {}; console.log(
`LQ - Fetching from IDB where: ${dq__where_type_id_val} = ${dq__where_eq_id_val}`
let event_session_id_random_li: Array<string> = $state([]); );
let dq__where_type_id_val = $derived(`${link_to_type}_id_random`);
let dq__where_eq_id_val = $derived(link_to_id);
// *** Functions and Logic
// OPTIMIZATION: If event_session_obj_li is provided (from API/reports),
// use it directly instead of fetching from IDB. The API returns fully
// enriched data with all joined fields (location names, file counts, etc.)
// that aren't available in the base IDB session table.
let lq__event_session_obj_li = $derived(
liveQuery(async () => {
let results: any;
// If data is already provided (from API reports with joins), use it directly
if (event_session_obj_li?.length) {
if (log_lvl) {
console.log(
`LQ - Using provided event_session_obj_li (${event_session_obj_li.length} sessions with API joins)`
);
}
return event_session_obj_li;
} }
event_session_id_random_li = [];
results = await db_events.session
.where(dq__where_type_id_val)
.equals(dq__where_eq_id_val)
.sortBy('name');
} else {
event_session_id_random_li = [];
results = [];
}
// Otherwise fetch from IDB for link_to_type/link_to_id lookups // Check if results are different than the current session version stored under $events_slct
if (link_to_type && link_to_id) { if (
if (log_lvl) { $events_slct.event_session_obj_li &&
console.log( JSON.stringify($events_slct.event_session_obj_li) !==
`LQ - Fetching from IDB where: ${dq__where_type_id_val} = ${dq__where_eq_id_val}` JSON.stringify(results)
); ) {
} $events_slct.event_session_obj_li = [...results];
event_session_id_random_li = []; if (log_lvl) {
results = await db_events.session console.log(
.where(dq__where_type_id_val) `Session slct li stored version has changed for ID = ${$events_slct.journal_id}`,
.equals(dq__where_eq_id_val) $events_slct.event_session_obj_li
.sortBy('name'); );
} else {
event_session_id_random_li = [];
results = [];
} }
} else {
// Check if results are different than the current session version stored under $events_slct if (log_lvl > 1) {
if ( console.log(
$events_slct.event_session_obj_li && `Session slct li stored version has not changed for ID = ${$events_slct.journal_id}`
JSON.stringify($events_slct.event_session_obj_li) !== );
JSON.stringify(results)
) {
$events_slct.event_session_obj_li = [...results];
if (log_lvl) {
console.log(
`Session slct li stored version has changed for ID = ${$events_slct.journal_id}`,
$events_slct.event_session_obj_li
);
}
} else {
if (log_lvl > 1) {
console.log(
`Session slct li stored version has not changed for ID = ${$events_slct.journal_id}`
);
}
} }
}
return results; return results;
// if (event_session_id_random_li.length) { // if (event_session_id_random_li.length) {
// let results = await db_events.session // let results = await db_events.session
// .bulkGet(event_session_id_random_li); // .bulkGet(event_session_id_random_li);
// return results; // return results;
// } else if (link_to_type && link_to_id) { // } else if (link_to_type && link_to_id) {
// console.log(`Trying where: ${dq__where_type_id_val}; equals: ${dq__where_eq_id_val}`); // console.log(`Trying where: ${dq__where_type_id_val}; equals: ${dq__where_eq_id_val}`);
// let results = await db_events.session // let results = await db_events.session
// .where(dq__where_type_id_val) // .where(dq__where_type_id_val)
// .equals(dq__where_eq_id_val) // .equals(dq__where_eq_id_val)
// .sortBy('name') // .sortBy('name')
// return results; // return results;
// } else { // } else {
// return null; // return null;
// } // }
}) })
); );
</script> </script>
{#if event_session_obj_li && event_session_obj_li?.length} {#if event_session_obj_li && event_session_obj_li?.length}
@@ -138,10 +138,9 @@
{lq__event_session_obj_li} {lq__event_session_obj_li}
{show_location_fields} {show_location_fields}
{hide_session_code} {hide_session_code}
{log_lvl} {log_lvl}></Comp_event_session_obj_tbl>
></Comp_event_session_obj_tbl>
{:else} {:else}
<section class="grow px-1 md:px-2 pb-28 flex flex-col gap-1 items-center"> <section class="flex grow flex-col items-center gap-1 px-1 pb-28 md:px-2">
<p>No sessions available to show in table.</p> <p>No sessions available to show in table.</p>
</section> </section>
{/if} {/if}

View File

@@ -1,133 +1,134 @@
<script lang="ts"> <script lang="ts">
import { import {
ae_snip, ae_snip,
ae_loc, ae_loc,
ae_sess, ae_sess,
ae_api, ae_api,
ae_trig, ae_trig,
slct, slct,
slct_trigger slct_trigger
} from '$lib/stores/ae_stores'; } from '$lib/stores/ae_stores';
import { TrendingUp, MapPin, Wrench, Search, GraduationCap, Plane, Settings } from '@lucide/svelte'; import {
TrendingUp,
MapPin,
Wrench,
Search,
GraduationCap,
Plane,
Settings
} from '@lucide/svelte';
interface Props { interface Props {
// import { events_loc, events_sess, events_slct, events_trigger, events_trig_kv } from '$lib/stores/ae_events_stores'; // import { events_loc, events_sess, events_slct, events_trigger, events_trig_kv } from '$lib/stores/ae_events_stores';
hide?: boolean; hide?: boolean;
event_id?: null | string; event_id?: null | string;
ae_core?: boolean; ae_core?: boolean;
events__launcher_id?: null | string; // event_location_id events__launcher_id?: null | string; // event_location_id
events__location_id?: null | string; // event_location_id events__location_id?: null | string; // event_location_id
events__locations?: boolean; // event_id events__locations?: boolean; // event_id
// export let events__presenter_id: null|string = null; // event_presenter_id // export let events__presenter_id: null|string = null; // event_presenter_id
events__reports?: boolean; // event_id events__reports?: boolean; // event_id
events__settings?: boolean; // event_id events__settings?: boolean; // event_id
events__launcher_session_id?: null | string; // event_session_id to pass to launcher as ?session_id= events__launcher_session_id?: null | string; // event_session_id to pass to launcher as ?session_id=
events__session_id?: null | string; // event_session_id — used for "Back to Session" link only events__session_id?: null | string; // event_session_id — used for "Back to Session" link only
events__session_search?: boolean; // event_id events__session_search?: boolean; // event_id
events__launcher_extra_params?: string; // extra URL params appended to the launcher link (e.g. 'iframe=true&launcher_menu=hide') events__launcher_extra_params?: string; // extra URL params appended to the launcher link (e.g. 'iframe=true&launcher_menu=hide')
} }
let { let {
hide = true, hide = true,
event_id = null, event_id = null,
ae_core = false, ae_core = false,
events__launcher_id = null, events__launcher_id = null,
events__launcher_session_id = null, events__launcher_session_id = null,
events__launcher_extra_params = '', events__launcher_extra_params = '',
events__location_id = null, events__location_id = null,
events__locations = false, events__locations = false,
events__reports = false, events__reports = false,
events__settings = false, events__settings = false,
events__session_id = null, events__session_id = null,
events__session_search = false events__session_search = false
}: Props = $props(); }: Props = $props();
// Build launcher URL segments separately so the href template stays simple (avoids ternaries in href attr) // Build launcher URL segments separately so the href template stays simple (avoids ternaries in href attr)
let launcher_loc_seg = $derived(events__launcher_id ? `/${events__launcher_id}` : ''); let launcher_loc_seg = $derived(
let launcher_sess_qry = $derived.by(() => { events__launcher_id ? `/${events__launcher_id}` : ''
const parts: string[] = []; );
if (events__launcher_session_id) parts.push(`session_id=${events__launcher_session_id}`); let launcher_sess_qry = $derived.by(() => {
if (events__launcher_extra_params) parts.push(events__launcher_extra_params); const parts: string[] = [];
return parts.length ? `?${parts.join('&')}` : ''; if (events__launcher_session_id)
}); parts.push(`session_id=${events__launcher_session_id}`);
if (events__launcher_extra_params)
parts.push(events__launcher_extra_params);
return parts.length ? `?${parts.join('&')}` : '';
});
</script> </script>
<!-- This is for common navigation links. --> <!-- This is for common navigation links. -->
<div <div
class="ae_comp__events_menu_nav w-full flex flex-row flex-wrap gap-1.5 items-center justify-between" class="ae_comp__events_menu_nav flex w-full flex-row flex-wrap items-center justify-between gap-1.5"
class:hidden={hide} class:hidden={hide}>
>
<span <span
class="ae_menu__navigation_options flex flex-row flex-wrap gap-0.5 items-center justify-around" class="ae_menu__navigation_options flex flex-row flex-wrap items-center justify-around gap-0.5">
>
<a <a
href="/core" href="/core"
class="btn btn-sm mx-1 ae_btn_warning" class="btn btn-sm ae_btn_warning mx-1"
class:hidden={!ae_core} class:hidden={!ae_core}>
>
<Settings size="1em" aria-hidden="true" /> <Settings size="1em" aria-hidden="true" />
Æ Core Æ Core
</a> </a>
<a <a
href="/events/{event_id}/reports" href="/events/{event_id}/reports"
class="btn btn-sm mx-1 ae_btn_info" class="btn btn-sm ae_btn_info mx-1"
class:hidden={!events__reports} class:hidden={!events__reports}>
>
<TrendingUp size="1em" aria-hidden="true" /> <TrendingUp size="1em" aria-hidden="true" />
Pres Mgmt Reports Pres Mgmt Reports
</a> </a>
<a <a
href="/events/{event_id}/locations" href="/events/{event_id}/locations"
class="btn btn-sm mx-1 ae_btn_info" class="btn btn-sm ae_btn_info mx-1"
class:hidden={!events__locations} class:hidden={!events__locations}>
>
<MapPin size="1em" aria-hidden="true" /> <MapPin size="1em" aria-hidden="true" />
Locations Locations
</a> </a>
<a <a
href="/events/{event_id}/settings" href="/events/{event_id}/settings"
class="btn btn-sm mx-1 ae_btn_warning" class="btn btn-sm ae_btn_warning mx-1"
class:hidden={!events__settings} class:hidden={!events__settings}>
>
<Wrench size="1em" aria-hidden="true" /> <Wrench size="1em" aria-hidden="true" />
Admin Tools Admin Tools
</a> </a>
</span> </span>
<span <span
class="ae_menu__navigation_options flex flex-row flex-wrap gap-0.5 items-center justify-around" class="ae_menu__navigation_options flex flex-row flex-wrap items-center justify-around gap-0.5">
>
<a <a
href="/events/{event_id}/pres_mgmt" href="/events/{event_id}/pres_mgmt"
class="btn btn-sm mx-1 ae_btn_info" class="btn btn-sm ae_btn_info mx-1"
class:hidden={!events__session_search} class:hidden={!events__session_search}>
>
<Search size="1em" aria-hidden="true" /> <Search size="1em" aria-hidden="true" />
Session Search Session Search
</a> </a>
<a <a
href="/events/{event_id}/session/{events__session_id}" href="/events/{event_id}/session/{events__session_id}"
class="btn btn-sm mx-1 ae_btn_info" class="btn btn-sm ae_btn_info mx-1"
class:hidden={!events__session_id} class:hidden={!events__session_id}>
>
<GraduationCap size="1em" aria-hidden="true" /> <GraduationCap size="1em" aria-hidden="true" />
Back to Session Back to Session
</a> </a>
<!-- eslint-disable-next-line svelte/valid-compile --> <!-- eslint-disable-next-line svelte/valid-compile -->
<a <a
href="/events/{event_id}/launcher{launcher_loc_seg}{launcher_sess_qry}" href="/events/{event_id}/launcher{launcher_loc_seg}{launcher_sess_qry}"
class="btn btn-sm mx-1 ae_btn_info" class="btn btn-sm ae_btn_info mx-1"
class:hidden={!event_id} class:hidden={!event_id}>
>
<Plane size="1em" aria-hidden="true" /> <Plane size="1em" aria-hidden="true" />
Launcher Launcher
</a> </a>
<a <a
href="/events/{event_id}/location/{events__location_id}" href="/events/{event_id}/location/{events__location_id}"
class="btn btn-sm mx-1 ae_btn_info" class="btn btn-sm ae_btn_info mx-1"
class:hidden={!events__location_id} class:hidden={!events__location_id}>
>
<MapPin size="1em" aria-hidden="true" /> <MapPin size="1em" aria-hidden="true" />
Session Location Session Location
</a> </a>

View File

@@ -1,43 +1,62 @@
<script lang="ts"> <script lang="ts">
interface Props { interface Props {
hide?: boolean; hide?: boolean;
} }
let { hide = true }: Props = $props(); let { hide = true }: Props = $props();
import { import {
ae_snip, ae_snip,
ae_loc, ae_loc,
ae_sess, ae_sess,
ae_api, ae_api,
ae_trig, ae_trig,
slct, slct,
slct_trigger slct_trigger
} from '$lib/stores/ae_stores'; } from '$lib/stores/ae_stores';
import { import {
events_loc, events_loc,
events_sess, events_sess,
events_slct, events_slct,
events_trigger, events_trigger,
events_trig_kv events_trig_kv
} from '$lib/stores/ae_events_stores'; } from '$lib/stores/ae_events_stores';
import { Ban, Barcode, CheckCircle, ChevronDown, ChevronUp, Eye, EyeOff, List, MapPin, Pencil, Plane, QrCode, Save, Send, Sparkles, StickyNote, ToggleLeft, ToggleRight, UserRound, Wand2 } from '@lucide/svelte'; import {
Ban,
Barcode,
CheckCircle,
ChevronDown,
ChevronUp,
Eye,
EyeOff,
List,
MapPin,
Pencil,
Plane,
QrCode,
Save,
Send,
Sparkles,
StickyNote,
ToggleLeft,
ToggleRight,
UserRound,
Wand2
} from '@lucide/svelte';
</script> </script>
<!-- New standard events module menu 2025-06-20 --> <!-- New standard events module menu 2025-06-20 -->
<div <div
class="ae_comp__pres_mgmt_menu_opts w-full border-t border-gray-300 mt-1" class="ae_comp__pres_mgmt_menu_opts mt-1 w-full border-t border-gray-300"
class:hidden={hide} class:hidden={hide}>
> <h2 class="pb-1 text-center text-sm font-semibold">
<h2 class="text-sm font-semibold text-center pb-1">
<button <button
type="button" type="button"
onclick={() => { onclick={() => {
$events_loc.pres_mgmt.hide__menu_opts = $events_loc.pres_mgmt.hide__menu_opts =
!$events_loc.pres_mgmt.hide__menu_opts; !$events_loc.pres_mgmt.hide__menu_opts;
}} }}
class="btn btn-sm preset-tonal-info border border-info-500" class="btn btn-sm preset-tonal-info border-info-500 border">
>
{#if $events_loc.pres_mgmt.hide__menu_opts} {#if $events_loc.pres_mgmt.hide__menu_opts}
<ChevronUp size="1em" class="m-1" /> <ChevronUp size="1em" class="m-1" />
{:else} {:else}
@@ -53,28 +72,24 @@
</h2> </h2>
<div <div
class="flex flex-row flex-wrap gap-1 items-center justify-between" class="flex flex-row flex-wrap items-center justify-between gap-1"
class:hidden={$events_loc.pres_mgmt.hide__menu_opts} class:hidden={$events_loc.pres_mgmt.hide__menu_opts}>
>
{#if $ae_loc.authenticated_access} {#if $ae_loc.authenticated_access}
<div class="flex flex-col gap-1 items-end justify-center"> <div class="flex flex-col items-end justify-center gap-1">
<span class="flex flex-col gap-1 items-end justify-center"> <span class="flex flex-col items-end justify-center gap-1">
<!-- Max person select options --> <!-- Max person select options -->
<span <span
class="flex flex-row gap-1 items-center justify-around w-full" class="flex w-full flex-row items-center justify-around gap-1">
>
<label <label
class="text-sm w-32 text-right" class="w-32 text-right text-sm"
for="qry_limit__people" for="qry_limit__people">
>
Max people: Max people:
</label> </label>
<!-- Not using: $events_loc.pres_mgmt.qry_limit__people --> <!-- Not using: $events_loc.pres_mgmt.qry_limit__people -->
<select <select
id="qry_limit__people" id="qry_limit__people"
bind:value={$ae_loc.person.qry_limit__people} bind:value={$ae_loc.person.qry_limit__people}
class="select w-20 text-sm preset-tonal-surface px-1" class="select preset-tonal-surface w-20 px-1 text-sm">
>
<option value="">-- not set --</option> <option value="">-- not set --</option>
<option value={25}>25</option> <option value={25}>25</option>
<option value={50}>50</option> <option value={50}>50</option>
@@ -91,12 +106,10 @@
<!-- Max presenters select options --> <!-- Max presenters select options -->
<span <span
class="flex flex-row gap-1 items-center justify-around w-full" class="flex w-full flex-row items-center justify-around gap-1">
>
<label <label
class="text-sm w-32 text-right" class="w-32 text-right text-sm"
for="qry_limit__presenters" for="qry_limit__presenters">
>
Max presenters: Max presenters:
</label> </label>
<select <select
@@ -104,8 +117,7 @@
bind:value={ bind:value={
$events_loc.pres_mgmt.qry_limit__presenters $events_loc.pres_mgmt.qry_limit__presenters
} }
class="select w-20 text-sm preset-tonal-surface px-1" class="select preset-tonal-surface w-20 px-1 text-sm">
>
<option value="">-- not set --</option> <option value="">-- not set --</option>
<option value={25}>25</option> <option value={25}>25</option>
<option value={50}>50</option> <option value={50}>50</option>
@@ -123,12 +135,10 @@
<!-- Max sessions select options --> <!-- Max sessions select options -->
<span <span
class="flex flex-row gap-1 items-center justify-around w-full" class="flex w-full flex-row items-center justify-around gap-1">
>
<label <label
class="text-sm w-32 text-right" class="w-32 text-right text-sm"
for="qry_limit__sessions" for="qry_limit__sessions">
>
Max sessions: Max sessions:
</label> </label>
<select <select
@@ -136,8 +146,7 @@
bind:value={ bind:value={
$events_loc.pres_mgmt.qry_limit__sessions $events_loc.pres_mgmt.qry_limit__sessions
} }
class="select w-20 text-sm preset-tonal-surface px-1" class="select preset-tonal-surface w-20 px-1 text-sm">
>
<option value="">-- not set --</option> <option value="">-- not set --</option>
<option value={25}>25</option> <option value={25}>25</option>
<option value={50}>50</option> <option value={50}>50</option>
@@ -150,19 +159,16 @@
<!-- Max files select options --> <!-- Max files select options -->
<span <span
class="flex flex-row gap-1 items-center justify-around w-full" class="flex w-full flex-row items-center justify-around gap-1">
>
<label <label
class="text-sm w-32 text-right" class="w-32 text-right text-sm"
for="qry_limit__files" for="qry_limit__files">
>
Max files: Max files:
</label> </label>
<select <select
id="qry_limit__files" id="qry_limit__files"
bind:value={$events_loc.pres_mgmt.qry_limit__files} bind:value={$events_loc.pres_mgmt.qry_limit__files}
class="select w-20 text-sm preset-tonal-surface px-1" class="select preset-tonal-surface w-20 px-1 text-sm">
>
<option value="">-- not set --</option> <option value="">-- not set --</option>
<option value={25}>25</option> <option value={25}>25</option>
<option value={50}>50</option> <option value={50}>50</option>
@@ -174,7 +180,7 @@
</span> </span>
</span> </span>
<span class="flex flex-col gap-1 items-end justify-center"> <span class="flex flex-col items-end justify-center gap-1">
<!-- Toggle between the showing hidden sessions --> <!-- Toggle between the showing hidden sessions -->
<!-- qry_hidden = all, not_hidden, hidden --> <!-- qry_hidden = all, not_hidden, hidden -->
<button <button
@@ -186,10 +192,13 @@
$events_loc.pres_mgmt.qry_hidden = 'all'; $events_loc.pres_mgmt.qry_hidden = 'all';
} }
}} }}
class="btn btn-sm ae_btn_surface justify-between w-full text-center" class="btn btn-sm ae_btn_surface w-full justify-between text-center"
title="Toggle between showing hidden sessions" title="Toggle between showing hidden sessions">
> {#if $events_loc.pres_mgmt.qry_hidden == 'all'}<ToggleRight
{#if $events_loc.pres_mgmt.qry_hidden == 'all'}<ToggleRight size="1em" class="m-1" />{:else}<ToggleLeft size="1em" class="m-1" />{/if} size="1em"
class="m-1" />{:else}<ToggleLeft
size="1em"
class="m-1" />{/if}
{#if $events_loc.pres_mgmt.qry_hidden == 'all'} {#if $events_loc.pres_mgmt.qry_hidden == 'all'}
<span class="grow"> <span class="grow">
<EyeOff size="1em" class="m-1" /> <EyeOff size="1em" class="m-1" />
@@ -218,10 +227,13 @@
$events_loc.pres_mgmt.qry_enabled = 'all'; $events_loc.pres_mgmt.qry_enabled = 'all';
} }
}} }}
class="btn btn-sm ae_btn_surface justify-between w-full text-center" class="btn btn-sm ae_btn_surface w-full justify-between text-center"
title="Toggle between showing disabled sessions" title="Toggle between showing disabled sessions">
> {#if $events_loc.pres_mgmt.qry_enabled == 'all'}<ToggleRight
{#if $events_loc.pres_mgmt.qry_enabled == 'all'}<ToggleRight size="1em" class="m-1" />{:else}<ToggleLeft size="1em" class="m-1" />{/if} size="1em"
class="m-1" />{:else}<ToggleLeft
size="1em"
class="m-1" />{/if}
{#if $events_loc.pres_mgmt.qry_enabled == 'all'} {#if $events_loc.pres_mgmt.qry_enabled == 'all'}
<span class="grow"> <span class="grow">
<Ban size="1em" class="m-1" /> <Ban size="1em" class="m-1" />
@@ -239,19 +251,18 @@
</div> </div>
{/if} {/if}
<div class="flex flex-col flex-wrap gap-1 items-center justify-evenly"> <div class="flex flex-col flex-wrap items-center justify-evenly gap-1">
<button <button
type="button" type="button"
onclick={() => { onclick={() => {
$events_loc.pres_mgmt.save_search_text = $events_loc.pres_mgmt.save_search_text =
!$events_loc.pres_mgmt.save_search_text; !$events_loc.pres_mgmt.save_search_text;
}} }}
class="btn btn-sm justify-between w-full text-center" class="btn btn-sm w-full justify-between text-center"
class:ae_btn_surface={$events_loc.pres_mgmt.save_search_text} class:ae_btn_surface={$events_loc.pres_mgmt.save_search_text}
class:ae_btn_surface_outlined={!$events_loc.pres_mgmt class:ae_btn_surface_outlined={!$events_loc.pres_mgmt
.save_search_text} .save_search_text}
title="Save the search text for this session search?" title="Save the search text for this session search?">
>
{#if $events_loc.pres_mgmt.save_search_text} {#if $events_loc.pres_mgmt.save_search_text}
<ToggleRight size="1em" class="m-1" /> <ToggleRight size="1em" class="m-1" />
<span class="grow"> <span class="grow">
@@ -274,8 +285,7 @@
onclick={() => { onclick={() => {
$events_loc.pres_mgmt.hide__session_msg = true; $events_loc.pres_mgmt.hide__session_msg = true;
}} }}
class="btn btn-sm ae_btn_surface justify-between w-full text-center" class="btn btn-sm ae_btn_surface w-full justify-between text-center">
>
<ToggleRight size="1em" class="m-1" /> <ToggleRight size="1em" class="m-1" />
<span class="grow"> <span class="grow">
<StickyNote size="1em" class="m-1" /> <StickyNote size="1em" class="m-1" />
@@ -288,8 +298,7 @@
onclick={() => { onclick={() => {
$events_loc.pres_mgmt.hide__session_msg = false; $events_loc.pres_mgmt.hide__session_msg = false;
}} }}
class="btn btn-sm ae_btn_surface_outlined justify-between w-full text-center" class="btn btn-sm ae_btn_surface_outlined w-full justify-between text-center">
>
<ToggleLeft size="1em" class="m-1" /> <ToggleLeft size="1em" class="m-1" />
<span class="grow"> <span class="grow">
<StickyNote size="1em" class="m-1" /> <StickyNote size="1em" class="m-1" />
@@ -307,8 +316,7 @@
onclick={() => { onclick={() => {
$events_loc.pres_mgmt.hide__session_code = true; $events_loc.pres_mgmt.hide__session_code = true;
}} }}
class="btn btn-sm ae_btn_surface justify-between w-full text-center" class="btn btn-sm ae_btn_surface w-full justify-between text-center">
>
<ToggleRight size="1em" class="m-1" /> <ToggleRight size="1em" class="m-1" />
<span class="grow"> <span class="grow">
<Barcode size="1em" class="m-1" /> <Barcode size="1em" class="m-1" />
@@ -321,8 +329,7 @@
onclick={() => { onclick={() => {
$events_loc.pres_mgmt.hide__session_code = false; $events_loc.pres_mgmt.hide__session_code = false;
}} }}
class="btn btn-sm ae_btn_surface_outlined justify-between w-full text-center" class="btn btn-sm ae_btn_surface_outlined w-full justify-between text-center">
>
<ToggleLeft size="1em" class="m-1" /> <ToggleLeft size="1em" class="m-1" />
<span class="grow"> <span class="grow">
<Barcode size="1em" class="m-1" /> <Barcode size="1em" class="m-1" />
@@ -333,17 +340,16 @@
{/if} {/if}
</div> </div>
<div class="flex flex-col gap-1 items-center justify-center"> <div class="flex flex-col items-center justify-center gap-1">
{#if $ae_loc.trusted_access} {#if $ae_loc.trusted_access}
<div class="flex flex-col gap-1 items-center justify-center"> <div class="flex flex-col items-center justify-center gap-1">
{#if $events_loc.pres_mgmt.show__copy_access_link} {#if $events_loc.pres_mgmt.show__copy_access_link}
<button <button
type="button" type="button"
onclick={() => { onclick={() => {
$events_loc.pres_mgmt.show__copy_access_link = false; $events_loc.pres_mgmt.show__copy_access_link = false;
}} }}
class="btn btn-sm w-full ae_btn_surface" class="btn btn-sm ae_btn_surface w-full">
>
<ToggleRight size="1em" class="m-1" /> <ToggleRight size="1em" class="m-1" />
Showing Copy Access Link Showing Copy Access Link
</button> </button>
@@ -353,8 +359,7 @@
onclick={() => { onclick={() => {
$events_loc.pres_mgmt.show__copy_access_link = true; $events_loc.pres_mgmt.show__copy_access_link = true;
}} }}
class="btn btn-sm w-full ae_btn_surface_outlined" class="btn btn-sm ae_btn_surface_outlined w-full">
>
<ToggleLeft size="1em" class="m-1" /> <ToggleLeft size="1em" class="m-1" />
Show Copy Access Link? Show Copy Access Link?
</button> </button>
@@ -366,8 +371,7 @@
onclick={() => { onclick={() => {
$events_loc.pres_mgmt.show__email_access_link = false; $events_loc.pres_mgmt.show__email_access_link = false;
}} }}
class="btn btn-sm w-full ae_btn_surface" class="btn btn-sm ae_btn_surface w-full">
>
<ToggleRight size="1em" class="m-1" /> <ToggleRight size="1em" class="m-1" />
Showing Email Access Link Showing Email Access Link
</button> </button>
@@ -377,8 +381,7 @@
onclick={() => { onclick={() => {
$events_loc.pres_mgmt.show__email_access_link = true; $events_loc.pres_mgmt.show__email_access_link = true;
}} }}
class="btn btn-sm w-full ae_btn_surface_outlined" class="btn btn-sm ae_btn_surface_outlined w-full">
>
<ToggleLeft size="1em" class="m-1" /> <ToggleLeft size="1em" class="m-1" />
Show Email Access Link? Show Email Access Link?
</button> </button>
@@ -387,16 +390,15 @@
{/if} {/if}
{#if $ae_loc.authenticated_access} {#if $ae_loc.authenticated_access}
<div class="flex flex-col gap-1 items-end justify-center"> <div class="flex flex-col items-end justify-center gap-1">
{#if $events_loc.pres_mgmt.show_content__session_qr} {#if $events_loc.pres_mgmt.show_content__session_qr}
<button <button
type="button" type="button"
onclick={() => { onclick={() => {
$events_loc.pres_mgmt.show_content__session_qr = false; $events_loc.pres_mgmt.show_content__session_qr = false;
}} }}
class="btn btn-sm ae_btn_surface justify-between w-full text-center" class="btn btn-sm ae_btn_surface w-full justify-between text-center"
title="Showing Session QR Code" title="Showing Session QR Code">
>
<ToggleRight size="1em" class="m-1" /> <ToggleRight size="1em" class="m-1" />
<span class="grow"> <span class="grow">
<QrCode size="1em" class="m-1" /> <QrCode size="1em" class="m-1" />
@@ -409,9 +411,8 @@
onclick={() => { onclick={() => {
$events_loc.pres_mgmt.show_content__session_qr = true; $events_loc.pres_mgmt.show_content__session_qr = true;
}} }}
class="btn btn-sm ae_btn_surface_outlined justify-between w-full text-center" class="btn btn-sm ae_btn_surface_outlined w-full justify-between text-center"
title="Show Session QR Code" title="Show Session QR Code">
>
<ToggleLeft size="1em" class="m-1" /> <ToggleLeft size="1em" class="m-1" />
<span class="grow"> <span class="grow">
<QrCode size="1em" class="m-1" /> <QrCode size="1em" class="m-1" />
@@ -426,9 +427,8 @@
onclick={() => { onclick={() => {
$events_loc.pres_mgmt.show_content__presenter_qr = false; $events_loc.pres_mgmt.show_content__presenter_qr = false;
}} }}
class="btn btn-sm ae_btn_surface justify-between w-full text-center" class="btn btn-sm ae_btn_surface w-full justify-between text-center"
title="Showing Presenter QR Code" title="Showing Presenter QR Code">
>
<ToggleRight size="1em" class="m-1" /> <ToggleRight size="1em" class="m-1" />
<span class="grow"> <span class="grow">
<QrCode size="1em" class="m-1" /> <QrCode size="1em" class="m-1" />
@@ -441,9 +441,8 @@
onclick={() => { onclick={() => {
$events_loc.pres_mgmt.show_content__presenter_qr = true; $events_loc.pres_mgmt.show_content__presenter_qr = true;
}} }}
class="btn btn-sm ae_btn_surface_outlined justify-between w-full text-center" class="btn btn-sm ae_btn_surface_outlined w-full justify-between text-center"
title="Show Presenter QR Code" title="Show Presenter QR Code">
>
<ToggleLeft size="1em" class="m-1" /> <ToggleLeft size="1em" class="m-1" />
<span class="grow"> <span class="grow">
<QrCode size="1em" class="m-1" /> <QrCode size="1em" class="m-1" />
@@ -456,7 +455,7 @@
</div> </div>
{#if $ae_loc.authenticated_access} {#if $ae_loc.authenticated_access}
<div class="flex flex-col gap-1 items-center justify-center"> <div class="flex flex-col items-center justify-center gap-1">
<!-- Show/Hide launcher links (new version) --> <!-- Show/Hide launcher links (new version) -->
<button <button
type="button" type="button"
@@ -464,9 +463,12 @@
$events_loc.pres_mgmt.hide__launcher_link = $events_loc.pres_mgmt.hide__launcher_link =
!$events_loc.pres_mgmt.hide__launcher_link; !$events_loc.pres_mgmt.hide__launcher_link;
}} }}
class="btn btn-sm ae_btn_surface justify-between w-full text-center" class="btn btn-sm ae_btn_surface w-full justify-between text-center">
> {#if $events_loc.pres_mgmt.hide__launcher_link}<ToggleLeft
{#if $events_loc.pres_mgmt.hide__launcher_link}<ToggleLeft size="1em" class="m-1" />{:else}<ToggleRight size="1em" class="m-1" />{/if} size="1em"
class="m-1" />{:else}<ToggleRight
size="1em"
class="m-1" />{/if}
<span class="grow"> <span class="grow">
<Plane size="1em" class="m-1" /> <Plane size="1em" class="m-1" />
{$events_loc.pres_mgmt.hide__launcher_link {$events_loc.pres_mgmt.hide__launcher_link
@@ -482,9 +484,12 @@
$events_loc.pres_mgmt.hide__launcher_link_legacy = $events_loc.pres_mgmt.hide__launcher_link_legacy =
!$events_loc.pres_mgmt.hide__launcher_link_legacy; !$events_loc.pres_mgmt.hide__launcher_link_legacy;
}} }}
class="btn btn-sm ae_btn_surface justify-between w-full text-center" class="btn btn-sm ae_btn_surface w-full justify-between text-center">
> {#if $events_loc.pres_mgmt.hide__launcher_link_legacy}<ToggleLeft
{#if $events_loc.pres_mgmt.hide__launcher_link_legacy}<ToggleLeft size="1em" class="m-1" />{:else}<ToggleRight size="1em" class="m-1" />{/if} size="1em"
class="m-1" />{:else}<ToggleRight
size="1em"
class="m-1" />{/if}
<span class="grow"> <span class="grow">
<Send size="1em" class="m-1" /> <Send size="1em" class="m-1" />
{$events_loc.pres_mgmt.hide__launcher_link_legacy {$events_loc.pres_mgmt.hide__launcher_link_legacy
@@ -500,9 +505,12 @@
$events_loc.pres_mgmt.hide__location_link = $events_loc.pres_mgmt.hide__location_link =
!$events_loc.pres_mgmt.hide__location_link; !$events_loc.pres_mgmt.hide__location_link;
}} }}
class="btn btn-sm ae_btn_surface justify-between w-full text-center" class="btn btn-sm ae_btn_surface w-full justify-between text-center">
> {#if $events_loc.pres_mgmt.hide__location_link}<ToggleLeft
{#if $events_loc.pres_mgmt.hide__location_link}<ToggleLeft size="1em" class="m-1" />{:else}<ToggleRight size="1em" class="m-1" />{/if} size="1em"
class="m-1" />{:else}<ToggleRight
size="1em"
class="m-1" />{/if}
<span class="grow"> <span class="grow">
<MapPin size="1em" class="m-1" /> <MapPin size="1em" class="m-1" />
{$events_loc.pres_mgmt.hide__location_link {$events_loc.pres_mgmt.hide__location_link
@@ -519,10 +527,13 @@
!$events_loc.pres_mgmt !$events_loc.pres_mgmt
.hide__session_li_location_field; .hide__session_li_location_field;
}} }}
class="btn btn-sm ae_btn_surface justify-between w-full text-center" class="btn btn-sm ae_btn_surface w-full justify-between text-center"
title="Toggle showing the Location column in session lists and tables" title="Toggle showing the Location column in session lists and tables">
> {#if $events_loc.pres_mgmt.hide__session_li_location_field}<ToggleLeft
{#if $events_loc.pres_mgmt.hide__session_li_location_field}<ToggleLeft size="1em" class="m-1" />{:else}<ToggleRight size="1em" class="m-1" />{/if} size="1em"
class="m-1" />{:else}<ToggleRight
size="1em"
class="m-1" />{/if}
<span class="grow"> <span class="grow">
<!-- <span class="fas fa-door-open m-1"></span> --> <!-- <span class="fas fa-door-open m-1"></span> -->
{$events_loc.pres_mgmt.hide__session_li_location_field {$events_loc.pres_mgmt.hide__session_li_location_field
@@ -538,10 +549,13 @@
$events_loc.pres_mgmt.hide__session_li_poc_field = $events_loc.pres_mgmt.hide__session_li_poc_field =
!$events_loc.pres_mgmt.hide__session_li_poc_field; !$events_loc.pres_mgmt.hide__session_li_poc_field;
}} }}
class="btn btn-sm ae_btn_surface justify-between w-full text-center" class="btn btn-sm ae_btn_surface w-full justify-between text-center"
title="Toggle showing the POC column in session lists and tables" title="Toggle showing the POC column in session lists and tables">
> {#if $events_loc.pres_mgmt.hide__session_li_poc_field}<ToggleLeft
{#if $events_loc.pres_mgmt.hide__session_li_poc_field}<ToggleLeft size="1em" class="m-1" />{:else}<ToggleRight size="1em" class="m-1" />{/if} size="1em"
class="m-1" />{:else}<ToggleRight
size="1em"
class="m-1" />{/if}
<span class="grow"> <span class="grow">
<!-- <span class="fas fa-user-tie m-1"></span> --> <!-- <span class="fas fa-user-tie m-1"></span> -->
{$events_loc.pres_mgmt.hide__session_li_poc_field {$events_loc.pres_mgmt.hide__session_li_poc_field
@@ -552,8 +566,7 @@
<!-- These are related to more content showing in lists. --> <!-- These are related to more content showing in lists. -->
<span <span
class="flex flex-col flex-wrap gap-1 items-center justify-evenly" class="flex flex-col flex-wrap items-center justify-evenly gap-1">
>
{#if $events_loc.pres_mgmt.show_content__session_files} {#if $events_loc.pres_mgmt.show_content__session_files}
<button <button
type="button" type="button"
@@ -561,8 +574,7 @@
onclick={() => { onclick={() => {
$events_loc.pres_mgmt.show_content__session_files = false; $events_loc.pres_mgmt.show_content__session_files = false;
}} }}
class="btn btn-sm ae_btn_surface justify-between w-full text-center" class="btn btn-sm ae_btn_surface w-full justify-between text-center">
>
<ToggleRight size="1em" class="m-1" /> <ToggleRight size="1em" class="m-1" />
<span class="grow"> <span class="grow">
<List size="1em" class="m-1" /> <List size="1em" class="m-1" />
@@ -576,8 +588,7 @@
onclick={() => { onclick={() => {
$events_loc.pres_mgmt.show_content__session_files = true; $events_loc.pres_mgmt.show_content__session_files = true;
}} }}
class="btn btn-sm ae_btn_surface_outlined justify-between w-full text-center" class="btn btn-sm ae_btn_surface_outlined w-full justify-between text-center">
>
<ToggleLeft size="1em" class="m-1" /> <ToggleLeft size="1em" class="m-1" />
<span class="grow"> <span class="grow">
<List size="1em" class="m-1" /> <List size="1em" class="m-1" />
@@ -593,8 +604,7 @@
onclick={() => { onclick={() => {
$events_loc.pres_mgmt.show_content__session_presentations = false; $events_loc.pres_mgmt.show_content__session_presentations = false;
}} }}
class="btn btn-sm ae_btn_surface justify-between w-full text-center" class="btn btn-sm ae_btn_surface w-full justify-between text-center">
>
<ToggleRight size="1em" class="m-1" /> <ToggleRight size="1em" class="m-1" />
<span class="grow"> <span class="grow">
<List size="1em" class="m-1" /> <List size="1em" class="m-1" />
@@ -608,8 +618,7 @@
onclick={() => { onclick={() => {
$events_loc.pres_mgmt.show_content__session_presentations = true; $events_loc.pres_mgmt.show_content__session_presentations = true;
}} }}
class="btn btn-sm ae_btn_surface_outlined justify-between w-full text-center" class="btn btn-sm ae_btn_surface_outlined w-full justify-between text-center">
>
<ToggleLeft size="1em" class="m-1" /> <ToggleLeft size="1em" class="m-1" />
<span class="grow"> <span class="grow">
<List size="1em" class="m-1" /> <List size="1em" class="m-1" />
@@ -622,8 +631,7 @@
{/if} {/if}
<div <div
class="flex flex-row flex-wrap gap-1 items-center justify-evenly max-w-56" class="flex max-w-56 flex-row flex-wrap items-center justify-evenly gap-1">
>
{#if $ae_loc?.trusted_access} {#if $ae_loc?.trusted_access}
{#if $ae_loc?.edit_mode} {#if $ae_loc?.edit_mode}
<button <button
@@ -631,9 +639,8 @@
onclick={() => { onclick={() => {
$ae_loc.edit_mode = false; $ae_loc.edit_mode = false;
}} }}
class="btn btn-sm ae_btn_warning justify-between w-full text-center" class="btn btn-sm ae_btn_warning w-full justify-between text-center"
title="Turn off edit mode" title="Turn off edit mode">
>
<ToggleRight size="1em" class="m-1" /> <ToggleRight size="1em" class="m-1" />
<span class="grow"> <span class="grow">
<Pencil size="1em" class="m-1" /> <Pencil size="1em" class="m-1" />
@@ -646,9 +653,8 @@
onclick={() => { onclick={() => {
$ae_loc.edit_mode = true; $ae_loc.edit_mode = true;
}} }}
class="btn btn-sm ae_btn_warning_outlined justify-between w-full text-center" class="btn btn-sm ae_btn_warning_outlined w-full justify-between text-center"
title="Turn on edit mode" title="Turn on edit mode">
>
<ToggleLeft size="1em" class="m-1" /> <ToggleLeft size="1em" class="m-1" />
<span class="grow"> <span class="grow">
<UserRound size="1em" class="m-1" /> <UserRound size="1em" class="m-1" />
@@ -663,9 +669,8 @@
onclick={() => { onclick={() => {
$ae_loc.adv_mode = false; $ae_loc.adv_mode = false;
}} }}
class="btn btn-sm ae_btn_warning justify-between w-full text-center" class="btn btn-sm ae_btn_warning w-full justify-between text-center"
title="Turn off advanced mode" title="Turn off advanced mode">
>
<ToggleRight size="1em" class="m-1" /> <ToggleRight size="1em" class="m-1" />
<span class="grow"> <span class="grow">
<Sparkles size="1em" class="m-1" /> <Sparkles size="1em" class="m-1" />
@@ -678,9 +683,8 @@
onclick={() => { onclick={() => {
$ae_loc.adv_mode = true; $ae_loc.adv_mode = true;
}} }}
class="btn btn-sm ae_btn_warning_outlined justify-between w-full text-center" class="btn btn-sm ae_btn_warning_outlined w-full justify-between text-center"
title="Turn on advanced mode" title="Turn on advanced mode">
>
<ToggleLeft size="1em" class="m-1" /> <ToggleLeft size="1em" class="m-1" />
<span class="grow"> <span class="grow">
<Wand2 size="1em" class="m-1" /> <Wand2 size="1em" class="m-1" />