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">
// 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 +page data:`, data);
// 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 { db_events } from '$lib/ae_events/db_events';
import {
ae_loc,
ae_sess,
ae_api,
slct,
slct_trigger
} from '$lib/stores/ae_stores';
// import {
// events_loc,
// events_slct,
// events_trigger
// } from '$lib/stores/ae_events_stores';
import { ae_util } from '$lib/ae_utils/ae_utils';
import { CalendarDays, LoaderCircle, TriangleAlert } from '@lucide/svelte';
import { page } from '$app/stores';
import { liveQuery } from 'dexie';
import { db_events } from '$lib/ae_events/db_events';
import {
ae_loc,
ae_sess,
ae_api,
slct,
slct_trigger
} from '$lib/stores/ae_stores';
// import {
// events_loc,
// events_slct,
// events_trigger
// } from '$lib/stores/ae_events_stores';
import { ae_util } from '$lib/ae_utils/ae_utils';
import { CalendarDays, LoaderCircle, TriangleAlert } from '@lucide/svelte';
import { page } from '$app/stores';
interface Props {
data: any;
interface Props {
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();
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++;
}
function prev_page() {
if (current_page > 1) {
current_page--;
}
}
function prev_page() {
if (current_page > 1) {
current_page--;
}
}
onMount(() => {
// console.log('Events - Presentation Management: +page.svelte');
});
onMount(() => {
// console.log('Events - Presentation Management: +page.svelte');
});
</script>
<h2 class="h3">
@@ -119,12 +119,11 @@
{#each $lq__event_obj_li as event_obj (event_obj.event_id)}
<li class:dim={event_obj?.hide}>
<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. -->
{#if new Date(event_obj.start_datetime ?? '').getTime() > new Date().getTime() - 1000 * 60 * 60 * 24 * 30 * 8 || $ae_loc.trusted_access}
<span>
<CalendarDays size="1em" class="inline mx-1" />
<CalendarDays size="1em" class="mx-1 inline" />
{ae_util.iso_datetime_formatter(
event_obj.start_datetime,
'date_long'
@@ -135,7 +134,7 @@
</strong>
{:else}
<span>
<CalendarDays size="1em" class="inline mx-1" />
<CalendarDays size="1em" class="mx-1 inline" />
{ae_util.iso_datetime_formatter(
event_obj.start_datetime,
'date_long'
@@ -148,36 +147,31 @@
</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}
<a
data-sveltekit-reload
href="/events/{event_obj.event_id}"
class="btn btn-sm preset-tonal-secondary border border-secondary-500 hover:preset-filled-secondary-500"
title="Presentation Management for {event_obj.name}"
>
class="btn btn-sm preset-tonal-secondary border-secondary-500 hover:preset-filled-secondary-500 border"
title="Presentation Management for {event_obj.name}">
Pres Mgmt
</a>
<a
href="/events/{event_obj.event_id}/badges"
class="btn btn-sm preset-tonal-secondary border border-secondary-500 hover:preset-filled-secondary-500"
title="Badge Management for {event_obj.name}"
>
class="btn btn-sm preset-tonal-secondary border-secondary-500 hover:preset-filled-secondary-500 border"
title="Badge Management for {event_obj.name}">
Badges
</a>
<a
href="/events/{event_obj.event_id}/leads"
class="btn btn-sm preset-tonal-secondary border border-secondary-500 hover:preset-filled-secondary-500"
title="Exhibitor Leads for {event_obj.name}"
>
class="btn btn-sm preset-tonal-secondary border-secondary-500 hover:preset-filled-secondary-500 border"
title="Exhibitor Leads for {event_obj.name}">
Leads
</a>
<a
href="/events/{event_obj.event_id}/launcher"
class="btn btn-sm preset-tonal-secondary border border-secondary-500 hover:preset-filled-secondary-500"
title="Event Launcher for {event_obj.name}"
>
class="btn btn-sm preset-tonal-secondary border-secondary-500 hover:preset-filled-secondary-500 border"
title="Event Launcher for {event_obj.name}">
Launcher
</a>
{/if}
@@ -185,9 +179,8 @@
<a
data-sveltekit-reload
href="/event/{event_obj.event_id}"
class="btn btn-sm preset-tonal-warning border border-warning-500 hover:preset-filled-warning-500"
title="Legacy Presentation Management System (Flask/Svelte) for {event_obj.name}"
>
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}">
Legacy Pres Mgmt
</a>
{/if}
@@ -195,31 +188,29 @@
</li>
{/each}
</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
type="button"
class="btn btn-sm"
onclick={prev_page}
disabled={current_page === 1}>Previous</button
>
disabled={current_page === 1}>Previous</button>
<span>Page {current_page} of {total_pages}</span>
<button
type="button"
class="btn btn-sm"
onclick={next_page}
disabled={current_page === total_pages}>Next</button
>
disabled={current_page === total_pages}>Next</button>
</div>
{:else}
<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>
<TriangleAlert size="1em" class="inline text-error-500 mx-1" />
<TriangleAlert size="1em" class="text-error-500 mx-1 inline" />
</div>
{/if}
{:else}
<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>
</div>
{/if}

View File

@@ -1,314 +1,328 @@
<script lang="ts">
interface Props {
// Exports
container_class_li?: string | Array<string>;
event_file_id_random_li?: Array<string>;
lq__event_file_obj_li: any;
allow_basic?: boolean;
allow_moderator?: boolean;
// export let max_records: number = 100;
show_direct_download?: boolean;
show_location_fields?: boolean;
show_presentation_fields?: boolean;
// export let show_presenter_fields: boolean = false;
show_session_fields?: boolean;
hide_session_code?: boolean;
log_lvl?: number;
}
interface Props {
// Exports
container_class_li?: string | Array<string>;
event_file_id_random_li?: Array<string>;
lq__event_file_obj_li: any;
allow_basic?: boolean;
allow_moderator?: boolean;
// export let max_records: number = 100;
show_direct_download?: boolean;
show_location_fields?: boolean;
show_presentation_fields?: boolean;
// export let show_presenter_fields: boolean = false;
show_session_fields?: boolean;
hide_session_code?: boolean;
log_lvl?: number;
}
let {
container_class_li = [],
event_file_id_random_li = [],
lq__event_file_obj_li,
allow_basic = false,
allow_moderator = false,
show_direct_download = $bindable(false),
show_location_fields = false,
show_presentation_fields = false,
show_session_fields = false,
hide_session_code = false,
log_lvl = $bindable(0)
}: Props = $props();
let {
container_class_li = [],
event_file_id_random_li = [],
lq__event_file_obj_li,
allow_basic = false,
allow_moderator = false,
show_direct_download = $bindable(false),
show_location_fields = false,
show_presentation_fields = false,
show_session_fields = false,
hide_session_code = false,
log_lvl = $bindable(0)
}: Props = $props();
// Imports
// import { liveQuery } from 'dexie';
// Imports
// import { liveQuery } from 'dexie';
import type { key_val } from '$lib/stores/ae_stores';
import { ae_util } from '$lib/ae_utils/ae_utils';
// import { api } from '$lib/api/api';
import type { key_val } from '$lib/stores/ae_stores';
import { ae_util } from '$lib/ae_utils/ae_utils';
// import { api } from '$lib/api/api';
// import { db_events } from '$lib/ae_events/db_events';
import {
ae_snip,
ae_loc,
ae_sess,
ae_api,
ae_trig,
slct,
slct_trigger
} 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_func } from '$lib/ae_events/ae_events_functions';
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 { Check, Download, FileImage, FileSpreadsheet, ListOrdered, LoaderCircle, Presentation, ToggleLeft, ToggleRight, TriangleAlert, User } from '@lucide/svelte';
// export let display_mode: string = 'default'; // 'default', 'compact', 'minimal', 'launcher'
// import { db_events } from '$lib/ae_events/db_events';
import {
ae_snip,
ae_loc,
ae_sess,
ae_api,
ae_trig,
slct,
slct_trigger
} 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_func } from '$lib/ae_events/ae_events_functions';
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 {
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
let ae_promises: key_val = $state({});
// Variables
let ae_promises: key_val = $state({});
// *** Functions and Logic
// *** Functions and Logic
// Define the list of unacceptable characters if not using the default.
// const unacceptable_chars = /[ <>:"/\\|?*]/g;
// Define the list of unacceptable characters if not using the default.
// const unacceptable_chars = /[ <>:"/\\|?*]/g;
let horiz_scroll_warning: boolean = $state(false);
let horiz_check_element: HTMLElement | null = $state(null);
let horiz_scroll_warning: boolean = $state(false);
let horiz_check_element: HTMLElement | null = $state(null);
// 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.
// 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.
// 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.
type ConvertStatus = 'idle' | 'converting' | 'done' | 'error';
let convert_status_kv: Record<string, ConvertStatus> = $state({});
let convert_result_kv: Record<string, any> = $state({});
// 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.
// 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.
// 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.
type ConvertStatus = 'idle' | 'converting' | 'done' | 'error';
let convert_status_kv: Record<string, ConvertStatus> = $state({});
let convert_result_kv: Record<string, any> = $state({});
async function handle_convert_pdf_to_image(event_file_obj: any) {
const file_id = event_file_obj.event_file_id;
convert_status_kv[file_id] = 'converting';
try {
// Link the new image to the most specific parent available
const link_to_type = event_file_obj.event_session_id ? 'event_session'
: event_file_obj.event_presentation_id ? 'event_presentation'
: 'event';
const link_to_id = event_file_obj.event_session_id
|| event_file_obj.event_presentation_id
|| event_file_obj.event_id;
const filename_no_ext = (event_file_obj.filename ?? 'poster_image').replace(/\.pdf$/i, '');
const url = `${$ae_api.base_url}/v3/hosted_file/${event_file_obj.hosted_file_id}/convert_file`
+ `?link_to_type=${encodeURIComponent(link_to_type)}`
+ `&link_to_id=${encodeURIComponent(link_to_id)}`
+ `&filename_no_ext=${encodeURIComponent(filename_no_ext)}`
+ `&to_type=webp`;
const resp = await fetch(url, {
headers: {
'x-aether-api-key': $ae_api.api_secret_key,
'x-account-id': String($ae_api.account_id ?? '')
}
});
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';
async function handle_convert_pdf_to_image(event_file_obj: any) {
const file_id = event_file_obj.event_file_id;
convert_status_kv[file_id] = 'converting';
try {
// Link the new image to the most specific parent available
const link_to_type = event_file_obj.event_session_id
? 'event_session'
: event_file_obj.event_presentation_id
? 'event_presentation'
: 'event';
const link_to_id =
event_file_obj.event_session_id ||
event_file_obj.event_presentation_id ||
event_file_obj.event_id;
const filename_no_ext = (
event_file_obj.filename ?? 'poster_image'
).replace(/\.pdf$/i, '');
const url =
`${$ae_api.base_url}/v3/hosted_file/${event_file_obj.hosted_file_id}/convert_file` +
`?link_to_type=${encodeURIComponent(link_to_type)}` +
`&link_to_id=${encodeURIComponent(link_to_id)}` +
`&filename_no_ext=${encodeURIComponent(filename_no_ext)}` +
`&to_type=webp`;
const resp = await fetch(url, {
headers: {
'x-aether-api-key': $ae_api.api_secret_key,
'x-account-id': String($ae_api.account_id ?? '')
}
} 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';
}
} 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
$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);
}
console.log('CSV Data:', csv_data);
let csv_content_str = '';
csv_data.forEach(function (row) {
csv_content_str += row.join(';');
csv_content_str += '\n';
});
function generate_file_export_csv(ae_obj_li: any[]) {
console.log(`*** generate_file_export_csv() ***`, ae_obj_li);
const blob = new Blob([csv_content_str], {
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.
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);
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';
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);
}
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;
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;
}
</script>
<section
class:border-r-2={horiz_scroll_warning}
class:border-dashed={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 $lq__event_file_obj_li && $lq__event_file_obj_li?.length}
<div
bind:this={horiz_check_element}
id="tbl_container"
class="space-y-2 pb-8"
>
class="space-y-2 pb-8">
<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">
<span class="text-base"> Results: </span>
{#if $lq__event_file_obj_li.length}
<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 ??
'None'}"
>
'None'}">
<ListOrdered size="1em" class="mx-4" />
{$lq__event_file_obj_li.length ?? 'None'}&times;
</span>
@@ -316,12 +330,11 @@
</h2>
<div
class="flex flex-row flex-wrap gap-1 items-center justify-end"
class:hidden={!$ae_loc.edit_mode}
>
class="flex flex-row flex-wrap items-center justify-end gap-1"
class:hidden={!$ae_loc.edit_mode}>
<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={() => {
if (
!confirm(
@@ -335,8 +348,7 @@
$lq__event_file_obj_li
);
console.log('CSV Data:', csv_data);
}}
>
}}>
<FileSpreadsheet size="1em" class="mx-1" />
Export Files CSV
</button>
@@ -351,8 +363,7 @@
class="btn btn-sm {show_session_fields
? 'ae_btn_surface'
: '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" />
Showing Session Fields
</button>
@@ -365,8 +376,7 @@
class="btn btn-sm {show_session_fields
? 'ae_btn_surface'
: '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" />
Show Session Fields
</button>
@@ -380,8 +390,7 @@
hide_session_code = true;
}}
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" />
Showing Session Code
</button>
@@ -392,8 +401,7 @@
hide_session_code = false;
}}
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" />
Show Session Code
</button>
@@ -409,8 +417,7 @@
class="btn btn-sm {show_presentation_fields
? 'ae_btn_surface'
: '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" />
Showing Presentation Fields
</button>
@@ -424,8 +431,7 @@
class="btn btn-sm {show_presentation_fields
? 'ae_btn_surface'
: '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" />
Show Presentation Fields
</button>
@@ -434,8 +440,7 @@
</header>
<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="">
<tr>
<th class="px-4 py-2">
@@ -444,8 +449,7 @@
</th>
<th
class="px-4 py-2"
class:hidden={!show_direct_download}
>
class:hidden={!show_direct_download}>
Link
</th>
<th class="px-4 py-2">Size</th>
@@ -456,8 +460,7 @@
{#if show_session_fields}
<th
class="px-4 py-2"
class:hidden={hide_session_code}
>
class:hidden={hide_session_code}>
Code
</th>
<th class="px-4 py-2"> Session </th>
@@ -482,41 +485,58 @@
show_divider={true}
{show_direct_download}
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 -->
{#if $ae_loc.edit_mode && event_file_obj?.extension === 'pdf' && event_file_obj?.event_session_type_code === 'poster'}
<div class="mt-1">
{#if !convert_status_kv[event_file_obj.event_file_id] || convert_status_kv[event_file_obj.event_file_id] === 'idle'}
<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."
onclick={() => handle_convert_pdf_to_image(event_file_obj)}
>
<FileImage size="1em" class="mx-1" />
onclick={() =>
handle_convert_pdf_to_image(
event_file_obj
)}>
<FileImage
size="1em"
class="mx-1" />
Convert PDF → Image
</button>
{:else if convert_status_kv[event_file_obj.event_file_id] === 'converting'}
<span class="btn btn-sm preset-tonal-surface opacity-60 cursor-wait">
<LoaderCircle size="1em" class="mx-1 animate-spin" />
<span
class="btn btn-sm preset-tonal-surface cursor-wait opacity-60">
<LoaderCircle
size="1em"
class="mx-1 animate-spin" />
Converting…
</span>
{: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 ?? ''}">
<Check size="1em" class="mx-1" />
Done — {convert_result_kv[event_file_obj.event_file_id]?.filename ?? 'image created'}
<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 ?? ''}">
<Check
size="1em"
class="mx-1" />
Done — {convert_result_kv[
event_file_obj.event_file_id
]?.filename ?? 'image created'}
</span>
{:else if convert_status_kv[event_file_obj.event_file_id] === 'error'}
<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."
onclick={() => {
convert_status_kv[event_file_obj.event_file_id] = 'idle';
}}
>
<TriangleAlert size="1em" class="mx-1" />
convert_status_kv[
event_file_obj.event_file_id
] = 'idle';
}}>
<TriangleAlert
size="1em"
class="mx-1" />
Failed — Retry?
</button>
{/if}
@@ -524,23 +544,20 @@
{/if}
</td>
<td
class="px-4 py-2 flex flex-col gap-0.5"
class:hidden={!show_direct_download}
>
class="flex flex-col gap-0.5 px-4 py-2"
class:hidden={!show_direct_download}>
<div
class:hidden={!show_direct_download}
class="flex flex-row gap-0.5"
>
<span class="text-xs text-gray-500 w-32">
class="flex flex-row gap-0.5">
<span class="w-32 text-xs text-gray-500">
Original:
</span>
<a
href="{$ae_api.base_url}/event/file/{event_file_obj?.event_file_id}/download?filename={ae_util.clean_filename(
event_file_obj?.filename
)}&key={$ae_api.account_id}"
class="btn btn-sm p-1 preset-tonal-secondary *:hover:inline lg:text-xs underline"
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}`}
>
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}`}>
<Download size="1em" class="mx-1" />
<span class="hidden"> Download </span>
</a>
@@ -560,9 +577,8 @@
<div
class="flex flex-row gap-0.5"
class:hidden={!show_direct_download}
>
<span class="text-xs text-gray-500 w-32">
class:hidden={!show_direct_download}>
<span class="w-32 text-xs text-gray-500">
Session Name:
</span>
<a
@@ -576,9 +592,8 @@
)}-{ae_util.clean_filename(
event_file_obj?.event_presenter_full_name
)}.{event_file_obj?.extension}&key={$ae_api.account_id}"
class="btn btn-sm p-1 preset-tonal-secondary *:hover:inline lg:text-xs underline"
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}`}
>
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}`}>
<Download size="1em" class="mx-1" />
<span class="hidden"> Renamed </span>
</a>
@@ -595,9 +610,8 @@
<div
class:hidden={!show_direct_download}
class="flex flex-row gap-0.5"
>
<span class="text-xs text-gray-500 w-32">
class="flex flex-row gap-0.5">
<span class="w-32 text-xs text-gray-500">
Presentation Name:
</span>
<a
@@ -611,9 +625,8 @@
)}-{ae_util.clean_filename(
event_file_obj?.event_presenter_full_name
)}.{event_file_obj?.extension}&key=${$ae_api.account_id}"
class="btn btn-sm p-1 preset-tonal-secondary *:hover:inline lg:text-xs underline"
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}`}
>
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}`}>
<Download size="1em" class="mx-1" />
<span class="hidden"> Renamed </span>
</a>
@@ -630,9 +643,9 @@
</td>
<td class="px-4 py-2"
>{ae_util.format_bytes(
event_file_obj?.file_size || event_file_obj?.hosted_file_size
)}</td
>
event_file_obj?.file_size ||
event_file_obj?.hosted_file_size
)}</td>
<td class="px-4 py-2">
<div>
<span>
@@ -669,8 +682,7 @@
event_file_obj?.created_on,
minutes: 2880
}
)}
>
)}>
{ae_util.iso_datetime_formatter(
event_file_obj?.created_on,
'time_12_short'
@@ -691,8 +703,7 @@
{#if show_session_fields}
<td
class="px-4 py-2 lg:text-xs"
class:hidden={hide_session_code}
>
class:hidden={hide_session_code}>
{event_file_obj?.event_session_code ??
'-- not set --'}
</td>
@@ -700,8 +711,7 @@
<Presentation size="1em" />
<a
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}
</a>
</td>
@@ -709,8 +719,7 @@
>{ae_util.iso_datetime_formatter(
event_file_obj?.event_session_start_datetime,
'datetime_iso_12_no_seconds'
)}</td
>
)}</td>
{/if}
{#if show_presentation_fields}
<td class="px-4 py-2 lg:text-xs">
@@ -736,8 +745,7 @@
<User size="1em" />
<a
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}
</a>
{:else}
@@ -745,7 +753,6 @@
{@html ae_snip.html__not_set}
{/if}
</td>
</tr>
{/each}
</tbody>
@@ -757,8 +764,8 @@
</section>
<style>
.dim {
opacity: 0.5;
color: #999;
}
.dim {
opacity: 0.5;
color: #999;
}
</style>

View File

@@ -1,124 +1,124 @@
<script lang="ts">
interface Props {
// Exports
container_class_li?: string | Array<string>;
event_file_obj_li?: Array<any>;
link_to_type?: string;
link_to_id?: string;
allow_basic?: boolean;
allow_moderator?: boolean;
show_direct_download?: boolean;
show_location_fields?: boolean;
show_presentation_fields?: boolean;
show_session_fields?: boolean;
hide_session_code?: boolean;
log_lvl?: number;
interface Props {
// Exports
container_class_li?: string | Array<string>;
event_file_obj_li?: Array<any>;
link_to_type?: string;
link_to_id?: string;
allow_basic?: boolean;
allow_moderator?: boolean;
show_direct_download?: boolean;
show_location_fields?: boolean;
show_presentation_fields?: boolean;
show_session_fields?: boolean;
hide_session_code?: boolean;
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 {
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();
// Variables
// let ae_promises: key_val = {};
// let ae_tmp: key_val = {};
// let ae_triggers: key_val = {};
// Imports
import Comp_event_file_obj_tbl from './ae_comp__event_file_obj_tbl.svelte';
let event_file_id_random_li: Array<string> = $state([]);
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';
let dq__where_type_id_val = $derived(`${link_to_type}_id_random`);
let dq__where_eq_id_val = $derived(link_to_id ?? '');
$effect(() => {
if (log_lvl) {
console.log(`link_to_type: ${link_to_type}; link_to_id: ${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;
}
});
// Variables
// let ae_promises: key_val = {};
// let ae_tmp: key_val = {};
// let ae_triggers: key_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;
// Otherwise fetch from IDB for link_to_type/link_to_id lookups
if (link_to_type && link_to_id) {
if (log_lvl) {
console.log(
`LQ - Fetching from IDB where: ${dq__where_type_id_val} = ${dq__where_eq_id_val}`
);
}
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
if (link_to_type && link_to_id) {
if (log_lvl) {
console.log(
`LQ - Fetching from IDB where: ${dq__where_type_id_val} = ${dq__where_eq_id_val}`
);
}
results = await db_events.file
.where(dq__where_type_id_val)
.equals(dq__where_eq_id_val)
.sortBy('name');
} else {
results = [];
// Check if results are different than the current session version stored under $events_slct
if (
$events_slct.event_file_obj_li &&
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
);
}
// Check if results are different than the current session version stored under $events_slct
if (
$events_slct.event_file_obj_li &&
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}`
);
}
} 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>
{#if event_file_obj_li && event_file_obj_li?.length}
@@ -132,10 +132,9 @@
{show_presentation_fields}
{show_session_fields}
{hide_session_code}
{log_lvl}
></Comp_event_file_obj_tbl>
{log_lvl}></Comp_event_file_obj_tbl>
{: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>
</section>
{/if}

View File

@@ -1,206 +1,203 @@
<script lang="ts">
/**
* src/routes/events/ae_comp__event_files_upload.svelte
* Specialized component for uploading files and automatically linking them to Event objects.
*/
import { LoaderCircle, Upload, UploadCloud } from '@lucide/svelte';
import Element_input_files_tbl from '$lib/elements/element_input_files_tbl.svelte';
/**
* src/routes/events/ae_comp__event_files_upload.svelte
* Specialized component for uploading files and automatically linking them to Event objects.
*/
import { LoaderCircle, Upload, UploadCloud } from '@lucide/svelte';
import Element_input_files_tbl from '$lib/elements/element_input_files_tbl.svelte';
// Import storage, functions, and libraries
import type { key_val } from '$lib/stores/ae_stores';
import { api } from '$lib/api/api';
import {
ae_loc,
ae_sess,
ae_api,
ae_trig,
slct,
slct_trigger
} from '$lib/stores/ae_stores';
import {
events_loc,
events_sess,
events_slct,
events_trig
} from '$lib/stores/ae_events_stores';
import { events_func } from '$lib/ae_events/ae_events_functions';
import { db_events } from '$lib/ae_events/db_events';
// Import storage, functions, and libraries
import type { key_val } from '$lib/stores/ae_stores';
import { api } from '$lib/api/api';
import {
ae_loc,
ae_sess,
ae_api,
ae_trig,
slct,
slct_trigger
} from '$lib/stores/ae_stores';
import {
events_loc,
events_sess,
events_slct,
events_trig
} from '$lib/stores/ae_events_stores';
import { events_func } from '$lib/ae_events/ae_events_functions';
import { db_events } from '$lib/ae_events/db_events';
interface Props {
log_lvl?: number;
// Expecting these for link_to_type: 'event', 'event_location', 'event_presentation', 'event_presenter', 'event_session'
link_to_type: string;
link_to_id: string;
input_name?: string;
multiple?: boolean;
required?: boolean;
accept?: string;
class_li_default?: string;
class_li?: string;
input_class_li?: string[];
table_class_li?: string[];
upload_complete?: boolean;
submit_status?: null | string;
label?: import('svelte').Snippet;
interface Props {
log_lvl?: number;
// Expecting these for link_to_type: 'event', 'event_location', 'event_presentation', 'event_presenter', 'event_session'
link_to_type: string;
link_to_id: string;
input_name?: string;
multiple?: boolean;
required?: boolean;
accept?: string;
class_li_default?: string;
class_li?: string;
input_class_li?: string[];
table_class_li?: string[];
upload_complete?: boolean;
submit_status?: null | string;
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 {
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();
function prevent_default<T extends Event>(fn: (event: T) => void) {
return function (event: T) {
event.preventDefault();
fn(event);
};
}
// 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';
async function handle_submit_form_files(event: SubmitEvent) {
if (log_lvl) console.log('*** handle_submit_form() ***');
// 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;
}
});
$events_sess.files.disable_submit__event_file_obj = true;
$events_sess.files.submit_status = 'saving';
submit_status = 'saving';
upload_complete = false;
function prevent_default<T extends Event>(fn: (event: T) => void) {
return function (event: T) {
event.preventDefault();
fn(event);
};
}
const target = event.currentTarget as HTMLFormElement;
const file_input = target
? (target[input_element_id] as HTMLInputElement)
: null;
async function handle_submit_form_files(event: SubmitEvent) {
if (log_lvl) console.log('*** handle_submit_form() ***');
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;
$events_sess.files.disable_submit__event_file_obj = true;
$events_sess.files.submit_status = 'saving';
submit_status = 'saving';
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
await handle_input_upload_files({
input_upload_files: [tmp_file],
task_id: task_id
});
}
$events_sess.files.disable_submit__event_file_obj = false;
$events_sess.files.submit_status = 'saved';
submit_status = 'saved';
upload_complete = true;
// 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
});
}
async function handle_input_upload_files({
input_upload_files,
task_id
}: {
input_upload_files: any[];
task_id: string;
}) {
if (log_lvl)
console.log(
`*** handle_input_upload_files() *** task_id = ${task_id}`
);
$events_sess.files.disable_submit__event_file_obj = false;
$events_sess.files.submit_status = 'saved';
submit_status = 'saved';
upload_complete = true;
}
const form_data = new FormData();
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);
async function handle_input_upload_files({
input_upload_files,
task_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++) {
form_data.append(`file_list`, input_upload_files[i]);
}
const form_data = new FormData();
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
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;
for (let i = 0; i < input_upload_files.length; i++) {
form_data.append(`file_list`, input_upload_files[i]);
}
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,
for_type: link_to_type,
for_id: link_to_id,
filename: hosted_file_obj.filename,
extension: hosted_file_obj.extension,
enable: true
};
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';
});
// 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,
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;
}
return ae_promises.upload__hosted_file_obj;
}
</script>
<form
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}
<div class="text-lg flex flex-row gap-1 items-center justify-center">
<LoaderCircle class="animate-spin m-1" />
<div class="flex flex-row items-center justify-center gap-1 text-lg">
<LoaderCircle class="m-1 animate-spin" />
<span class="">
Uploading
{#if $ae_sess.api_upload_kv[task_id]}
@@ -212,31 +209,27 @@
<div
class="
border-2 hover:border-2 border-dashed border-primary-500
p-1 w-full rounded-lg
preset-filled-primary-50-950 hover:border-primary-800
cursor-pointer transition-colors
"
>
border-primary-500 preset-filled-primary-50-950 hover:border-primary-800 w-full
cursor-pointer rounded-lg border-2
border-dashed p-1
transition-colors hover:border-2
">
<label
for={input_element_id}
class="
svelte_input_file_label
text-center
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}
<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" />
<strong class="preset-tonal-primary px-3 py-1 rounded-full"
>Select Files</strong
>
<strong class="preset-tonal-primary rounded-full px-3 py-1"
>Select Files</strong>
</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 />
(PPTX, Keynote, PDF, MP4, etc)
</div>
@@ -254,39 +247,36 @@
class:hidden={$events_sess.files.disable_submit__event_file_obj}
class="
svelte_input_file_element file-dropzone-input
block w-full text-lg text-center
rounded-lg
preset-tonal-primary preset-outlined-primary-200-800 hover:preset-filled-success-200-800 block
w-full
cursor-pointer
p-1
preset-tonal-primary preset-outlined-primary-200-800 hover:preset-filled-success-200-800
rounded-lg
p-1 text-center text-lg
transition-all
{input_class_li.join(' ')}
"
/>
" />
</div>
<Element_input_files_tbl
bind:input_file_list
bind:file_list_status={$events_sess.files.status__file_list}
bind:processed_file_list={$events_sess.files.processed_file_list}
{table_class_li}
/>
{table_class_li} />
<button
type="submit"
class="
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
preset-tonal-primary border border-primary-500 hover:preset-tonal-success hover:border-success-500
flex items-center justify-center
w-54
transition-all
"
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}
<LoaderCircle class="animate-spin m-1" />
<LoaderCircle class="m-1 animate-spin" />
<span class="">
{#if $ae_sess.api_upload_kv[task_id]}
{$ae_sess.api_upload_kv[task_id].percent_completed}%
@@ -297,7 +287,7 @@
{:then}
<UploadCloud class="m-1" size={20} />
<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}
{$events_sess.files.processed_file_list.length}
{$events_sess.files.processed_file_list.length === 1

View File

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

View File

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

View File

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

View File

@@ -1,84 +1,81 @@
<script lang="ts">
interface Props {
container_class_li?: string | Array<string>;
// export let event_session_id_random_li: Array<string>;
lq__event_session_obj_li: any;
log_lvl?: number;
show_location_fields?: boolean;
hide_session_code?: boolean;
interface Props {
container_class_li?: string | Array<string>;
// export let event_session_id_random_li: Array<string>;
lq__event_session_obj_li: any;
log_lvl?: number;
show_location_fields?: 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 {
container_class_li = [],
lq__event_session_obj_li,
log_lvl = $bindable(0),
show_location_fields = true,
hide_session_code = false
}: Props = $props();
let horiz_scroll_warning: boolean = $state(false);
let horiz_check_element: HTMLElement | null = $state(null);
// 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 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);
}
});
// 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>
<section
class:border-r-2={horiz_scroll_warning}
class:border-dashed={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}
<div
bind:this={horiz_check_element}
id="tbl_container"
class="space-y-2 pb-48"
>
class="space-y-2 pb-48">
<h2 class="h3">
<span class="text-base"> Results: </span>
{#if $lq__event_session_obj_li?.length}
<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 ??
'None'}"
>
'None'}">
<ListOrdered size="1em" class="mx-4" />
{$lq__event_session_obj_li.length ?? 'None'}&times;
</span>
@@ -86,8 +83,7 @@
</h2>
<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="">
<tr>
<th class="px-4 py-2">Name</th>
@@ -105,8 +101,7 @@
<td class="px-4 py-2">
<a
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}
</a>
</td>

View File

@@ -1,135 +1,135 @@
<script lang="ts">
interface Props {
// Exports
container_class_li?: string | Array<string>;
// export let display_mode: string = 'default'; // 'default', 'compact', 'minimal', 'launcher'
// event_session_id_random_li: Array<string>;
event_session_obj_li?: Array<any>;
link_to_type: string;
link_to_id: string;
// export let lq__event_presentation_obj
log_lvl?: number;
show_location_fields?: boolean;
hide_session_code?: boolean;
interface Props {
// Exports
container_class_li?: string | Array<string>;
// export let display_mode: string = 'default'; // 'default', 'compact', 'minimal', 'launcher'
// event_session_id_random_li: Array<string>;
event_session_obj_li?: Array<any>;
link_to_type: string;
link_to_id: string;
// export let lq__event_presentation_obj
log_lvl?: number;
show_location_fields?: 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 {
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();
// Variables
// let ae_promises: key_val = {};
// let ae_tmp: key_val = {};
// let ae_triggers: key_val = {};
// Imports
import Comp_event_session_obj_tbl from './ae_comp__event_session_obj_tbl.svelte';
let event_session_id_random_li: Array<string> = $state([]);
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';
let dq__where_type_id_val = $derived(`${link_to_type}_id_random`);
let dq__where_eq_id_val = $derived(link_to_id);
$effect(() => {
if (log_lvl) {
console.log(`link_to_type: ${link_to_type}; link_to_id: ${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;
}
});
// Variables
// let ae_promises: key_val = {};
// let ae_tmp: key_val = {};
// let ae_triggers: key_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;
// Otherwise fetch from IDB for link_to_type/link_to_id lookups
if (link_to_type && link_to_id) {
if (log_lvl) {
console.log(
`LQ - Fetching from IDB where: ${dq__where_type_id_val} = ${dq__where_eq_id_val}`
);
}
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
if (link_to_type && link_to_id) {
if (log_lvl) {
console.log(
`LQ - Fetching from IDB where: ${dq__where_type_id_val} = ${dq__where_eq_id_val}`
);
}
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 = [];
// Check if results are different than the current session version stored under $events_slct
if (
$events_slct.event_session_obj_li &&
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
);
}
// Check if results are different than the current session version stored under $events_slct
if (
$events_slct.event_session_obj_li &&
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}`
);
}
} 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) {
// let results = await db_events.session
// .bulkGet(event_session_id_random_li);
// if (event_session_id_random_li.length) {
// let results = await db_events.session
// .bulkGet(event_session_id_random_li);
// return results;
// } else if (link_to_type && link_to_id) {
// console.log(`Trying where: ${dq__where_type_id_val}; equals: ${dq__where_eq_id_val}`);
// let results = await db_events.session
// .where(dq__where_type_id_val)
// .equals(dq__where_eq_id_val)
// .sortBy('name')
// return results;
// } else if (link_to_type && link_to_id) {
// console.log(`Trying where: ${dq__where_type_id_val}; equals: ${dq__where_eq_id_val}`);
// let results = await db_events.session
// .where(dq__where_type_id_val)
// .equals(dq__where_eq_id_val)
// .sortBy('name')
// return results;
// } else {
// return null;
// }
})
);
// return results;
// } else {
// return null;
// }
})
);
</script>
{#if event_session_obj_li && event_session_obj_li?.length}
@@ -138,10 +138,9 @@
{lq__event_session_obj_li}
{show_location_fields}
{hide_session_code}
{log_lvl}
></Comp_event_session_obj_tbl>
{log_lvl}></Comp_event_session_obj_tbl>
{: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>
</section>
{/if}

View File

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

View File

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