feat(idaa/archives): fire-and-forget page load + Create New Archive/Content buttons

- +page.ts: remove await on archive load so navigation is not blocked;
  liveQuery renders from Dexie cache immediately, API result updates reactively.
  Replace error(404) with console.warn — soft failure is correct for IDAA.
- ae_idaa_comp__archive_obj_li: add Create New Archive button (trusted+edit_mode
  only) with in-flight spinner and creating guard to prevent double-submit.
  Layout adjusted to justify-between to accommodate the new button.
- ae_idaa_comp__archive_content_obj_li: add Create New Archive Content button
  with same spinner/guard pattern; pre-populates original_timezone from parent
  archive so staff do not need to re-select it for every content item.
This commit is contained in:
Scott Idem
2026-03-09 17:53:24 -04:00
parent 89119191b1
commit dac7fc99da
3 changed files with 128 additions and 21 deletions

View File

@@ -2,7 +2,6 @@ import type { PageLoad } from './$types';
console.log(`ae_p_idaa_archives [archive_id] +page.ts start`);
import { error } from '@sveltejs/kit';
import { browser } from '$app/environment';
import { archives_func } from '$lib/ae_archives/ae_archives_functions';
@@ -27,8 +26,11 @@ export const load = (async ({ params, parent }) => {
archive_id
);
}
// Load archive object
const load_archive_obj = await archives_func
// NOTE: Fire in background — do NOT await. Newly created archives are already in Dexie
// (saved by create_ae_obj__archive), so the liveQuery renders immediately. For direct
// links or refreshes, the archive loads from the API and Dexie updates reactively.
// Awaiting here blocked the SvelteKit navigation, causing a visible "refresh" delay.
archives_func
.load_ae_obj_id__archive({
api_cfg: ae_acct.api,
archive_id: archive_id,
@@ -40,18 +42,9 @@ export const load = (async ({ params, parent }) => {
})
.then((results) => {
if (!results) {
error(404, {
message: 'IDAA Archives - Archive not found'
});
} else {
// ae_acct.slct.post_obj = results;
console.warn(`ae IDAA Archives [archive_id] +page.ts: Archive ${archive_id} not found via API or Cache.`);
}
});
if (log_lvl) {
console.log(`load_archive_obj = `, load_archive_obj);
}
ae_acct.slct.archive_obj = load_archive_obj;
}
// WARNING: Precaution against shared data between sites and presentations.

View File

@@ -22,9 +22,12 @@
slct_trigger
} from '$lib/stores/ae_stores';
import { idaa_loc, idaa_sess, idaa_slct } from '$lib/stores/ae_idaa_stores';
import { archives_func } from '$lib/ae_archives/ae_archives_functions';
import AE_Comp_Hosted_Files_Download_Button from '$lib/ae_core/ae_comp__hosted_files_download_button.svelte';
let creating = $state(false); // true while create API call is in-flight
let ae_promises: key_val = $state({});
// let ae_tmp: key_val = {};
// let ae_triggers: key_val = {};
@@ -63,6 +66,63 @@
<section
class="archive_list flex flex-col gap-2 items-center justify-center w-full"
>
{#if $ae_loc.trusted_access && $ae_loc.edit_mode}
<div class="w-full max-w-(--breakpoint-lg) flex flex-row items-center justify-start px-2 mb-2">
<button type="button"
disabled={creating}
onclick={() => {
creating = true;
archives_func
.create_ae_obj__archive_content({
api_cfg: $ae_api,
archive_id: $idaa_slct.archive_id,
data_kv: {
name: 'New Archive Content',
enable: true,
// NOTE: Pre-populate timezone from the parent archive so staff
// don't have to re-select it for every piece of content.
original_timezone: $idaa_slct.archive_obj?.original_timezone ?? null
}
})
.then((result) => {
const archive_content_id = result?.id || result?.archive_content_id;
if (archive_content_id) {
$idaa_slct.archive_content_id = archive_content_id;
$idaa_slct.archive_content_obj = result;
$idaa_sess.archives.show__modal_view__archive_content_id = false;
$idaa_sess.archives.show__modal_edit__archive_content_id = archive_content_id;
} else {
creating = false;
alert('Failed to create archive content.');
}
})
.catch((err) => {
console.error('Error creating archive content:', err);
creating = false;
alert('Failed to create archive content.');
})
.finally(() => {
creating = false;
});
}}
class="
novi_btn
btn btn-sm
preset-tonal-warning preset-outlined-warning-200-800 hover:preset-filled-warning-200-800
transition
"
title="Create new archive content item"
>
{#if creating}
<span class="fas fa-spinner fa-spin m-1"></span> Creating...
{:else}
<span class="fas fa-plus m-1"></span> Create New Archive Content
{/if}
</button>
</div>
{/if}
{#if $lq__archive_content_obj_li && $lq__archive_content_obj_li.length}
<!-- <div class="ae_group">
<span class="ae_label">Group:</span>

View File

@@ -18,7 +18,8 @@
slct,
slct_trigger
} from '$lib/stores/ae_stores';
// import { idaa_loc, idaa_sess, idaa_slct } from '$lib/stores/ae_idaa_stores';
import { idaa_sess, idaa_slct } from '$lib/stores/ae_idaa_stores';
import { archives_func } from '$lib/ae_archives/ae_archives_functions';
interface Props {
lq__archive_obj_li: any;
@@ -26,6 +27,8 @@
let { lq__archive_obj_li }: Props = $props();
let creating = $state(false); // true while create API call is in-flight
// Local Sort State
let qry_sort_mode = $state('priority_sort'); // 'priority_sort', 'updated_desc', 'name_asc'
@@ -64,13 +67,64 @@
flex flex-col gap-2 items-center justify-center w-full
"
>
<div class="w-full max-w-(--breakpoint-lg) flex flex-row items-center justify-end px-2 mb-2 gap-2">
<span class="text-xs font-bold opacity-50 uppercase tracking-widest">Sort By:</span>
<select bind:value={qry_sort_mode} class="select variant-filled-surface rounded-lg text-sm border border-surface-500/20 p-1 w-fit">
<option value="priority_sort">Priority & Order</option>
<option value="name_asc">Name (A-Z)</option>
<option value="updated_desc">Recently Updated</option>
</select>
<div class="w-full max-w-(--breakpoint-lg) flex flex-row items-center justify-between px-2 mb-2 gap-2">
{#if $ae_loc.trusted_access && $ae_loc.edit_mode}
<button type="button"
disabled={creating}
onclick={() => {
creating = true;
archives_func
.create_ae_obj__archive({
api_cfg: $ae_api,
account_id: $ae_loc.account_id,
data_kv: { name: 'New Archive', enable: true }
})
.then((result) => {
const archive_id = result?.id || result?.archive_id;
if (archive_id) {
$idaa_slct.archive_id = archive_id;
// NOTE: Set the edit modal flag before navigating so the detail page
// opens with the edit form already visible — saves an extra click.
$idaa_sess.archives.show__modal_edit__archive_id = true;
goto(`/idaa/archives/${archive_id}`);
} else {
creating = false;
alert('Failed to create archive.');
}
})
.catch((err) => {
console.error('Error creating archive:', err);
creating = false;
alert('Failed to create archive.');
});
}}
class="
novi_btn
btn btn-sm
preset-tonal-warning preset-outlined-warning-200-800 hover:preset-filled-warning-200-800
transition
"
title="Create new archive"
>
{#if creating}
<span class="fas fa-spinner fa-spin m-1"></span> Creating...
{:else}
<span class="fas fa-plus m-1"></span> Create New Archive
{/if}
</button>
{:else}
<span></span>
{/if}
<div class="flex flex-row items-center gap-2">
<span class="text-xs font-bold opacity-50 uppercase tracking-widest">Sort By:</span>
<select bind:value={qry_sort_mode} class="select variant-filled-surface rounded-lg text-sm border border-surface-500/20 p-1 w-fit">
<option value="priority_sort">Priority & Order</option>
<option value="name_asc">Name (A-Z)</option>
<option value="updated_desc">Recently Updated</option>
</select>
</div>
</div>
{#if sorted_archive_obj_li && sorted_archive_obj_li.length}