refactor: harden type safety and modernize core forms for Svelte 5

- Standardize 'ae_BaseObj' and event types in 'ae_types.ts' to handle nullable fields from V3 API/Dexie.
- Modernize Person, Address, and Contact forms with Svelte 5 Runes and reactive synchronization.
- Refactor Event Settings and its sub-components to use the 'onsave' callback pattern, removing deprecated dispatchers.
- Hardened 'element_data_store_v2' with safe initialization and localStorage caching logic.
- Clean up unused 'element_data_store.svelte' (V1) and suppress Electron environment type errors in 'tmp_shell_handlers.ts'.
- Update documentation and workspace settings to reflect Phase 5 reactive patterns.
This commit is contained in:
Scott Idem
2026-01-28 14:40:20 -05:00
parent bc1d74f817
commit 55773a332d
24 changed files with 31006 additions and 1139 deletions

View File

@@ -12,24 +12,6 @@
import { ae_util } from '$lib/ae_utils/ae_utils';
import type { key_val } from '$lib/stores/ae_stores';
onMount(() => {
console.log(
`ae_e_data_store ${ds_code} account_id=${$ae_loc.account_id} for_type=${for_type} for_id=${for_id}`
);
});
// export let store: string = 'local';
let ae_promises: key_val = {};
// let ae_tmp: key_val = {};
// let ae_triggers: key_val = {};
let ds_get_results: Promise<any> | key_val = $state();
let ds_submit_results: Promise<any> | key_val = $state();
let val_json: key_val;
let val_html: key_val;
let val_md: key_val;
interface Props {
log_lvl?: number;
expire_minutes?: number;
@@ -73,6 +55,25 @@
ds_loading_status = $bindable('starting'),
val_sql = $bindable(null)
}: Props = $props();
onMount(() => {
console.log(
`ae_e_data_store ${ds_code} account_id=${$ae_loc.account_id} for_type=${for_type} for_id=${for_id}`
);
});
// export let store: string = 'local';
let ae_promises: key_val = {};
// let ae_tmp: key_val = {};
// let ae_triggers: key_val = {};
let ds_get_results: Promise<any> | key_val | undefined = $state();
let ds_submit_results: Promise<any> | key_val | undefined = $state();
let val_json: key_val;
let val_html: key_val;
let val_md: key_val;
let val_text: string;
let ds_code_obj = {
@@ -94,20 +95,27 @@
chk_account_id: null
};
let ae_ds_tmp: key_val = $state();
if (browser && try_cache && localStorage.getItem(`ae_ds__${ds_code}`)) {
if (log_lvl) {
console.log(`ae_e_data_store: Found cached data for ${ds_code}`);
let ae_ds_tmp: key_val | undefined = $state();
// Initialize ae_ds_tmp
if (browser && try_cache) {
const stored = localStorage.getItem(`ae_ds__${ds_code}`);
if (stored) {
if (log_lvl) {
console.log(`ae_e_data_store: Found cached data for ${ds_code}`);
}
ae_ds_tmp = JSON.parse(stored);
if (log_lvl) {
console.log(
`ae_e_data_store cached: ${ds_code} account_id=${$ae_loc.account_id}`,
ae_ds_tmp
);
}
}
ae_ds_tmp = JSON.parse(localStorage.getItem(`ae_ds__${ds_code}`) ?? '{}');
if (log_lvl) {
console.log(
`ae_e_data_store cached: ${ds_code} account_id=${$ae_loc.account_id}`,
ae_ds_tmp
);
}
} else {
ae_ds_tmp = ds_code_obj;
}
if (!ae_ds_tmp) {
ae_ds_tmp = { ...ds_code_obj };
ds_loading_status = 'loading';
}
@@ -200,36 +208,38 @@
ds_loaded = true;
ds_loading_status = 'loaded';
// Set the loaded_on datetime to the current time for reference later. This will be used to determine if the data store is stale.
ae_ds_tmp.loaded_on = new Date().toISOString();
// Set the chk_account_id as a backup check to make sure the data store belongs to the account for the current site. This should not be needed, but here we are...
ae_ds_tmp.chk_account_id = $ae_loc.account_id;
if (ae_ds_tmp) {
// Set the loaded_on datetime to the current time for reference later. This will be used to determine if the data store is stale.
ae_ds_tmp.loaded_on = new Date().toISOString();
// Set the chk_account_id as a backup check to make sure the data store belongs to the account for the current site. This should not be needed, but here we are...
ae_ds_tmp.chk_account_id = $ae_loc.account_id;
ae_ds_tmp.id = ds_results.data_store_id_random;
ae_ds_tmp.account_id = ds_results.account_id_random;
ae_ds_tmp.code = ds_results.code; // This will overwrite whatever was passed in.
ae_ds_tmp.name = ds_results.name;
ae_ds_tmp.type = ds_results.type; // This will overwrite whatever was passed in.
if (type == 'html') {
ae_ds_tmp.html = ds_results.text;
val_html = ds_results.text;
return ds_results.html;
} else if (type == 'json') {
ae_ds_tmp.json = ds_results.json;
val_json = ds_results.json;
return ds_results.json;
} else if (type == 'md') {
ae_ds_tmp.text = ds_results.text;
val_md = ds_results.text;
return ds_results.text;
} else if (type == 'sql') {
ae_ds_tmp.text = ds_results.text;
val_sql = ds_results.text;
return ds_results.text;
} else {
ae_ds_tmp.text = ds_results.text;
val_text = ds_results.text;
return ds_results.text;
ae_ds_tmp.id = ds_results.data_store_id_random;
ae_ds_tmp.account_id = ds_results.account_id_random;
ae_ds_tmp.code = ds_results.code; // This will overwrite whatever was passed in.
ae_ds_tmp.name = ds_results.name;
ae_ds_tmp.type = ds_results.type; // This will overwrite whatever was passed in.
if (type == 'html') {
ae_ds_tmp.html = ds_results.text;
val_html = ds_results.text;
return ds_results.html;
} else if (type == 'json') {
ae_ds_tmp.json = ds_results.json;
val_json = ds_results.json;
return ds_results.json;
} else if (type == 'md') {
ae_ds_tmp.text = ds_results.text;
val_md = ds_results.text;
return ds_results.text;
} else if (type == 'sql') {
ae_ds_tmp.text = ds_results.text;
val_sql = ds_results.text;
return ds_results.text;
} else {
ae_ds_tmp.text = ds_results.text;
val_text = ds_results.text;
return ds_results.text;
}
}
} else {
ds_loaded = false;
@@ -237,7 +247,7 @@
}
})
.then(function () {
if (browser && try_cache) {
if (browser && try_cache && ae_ds_tmp) {
if (log_lvl) {
console.log(`ae_e_data_store: Caching data store ${code} in localStorage.`);
}
@@ -290,7 +300,7 @@
}
// if (!$slct.data_store_id) {
if (!ae_ds_tmp.id) {
if (ae_ds_tmp && !ae_ds_tmp.id) {
data_store_do['account_id_random'] = $ae_loc.account_id;
}
@@ -372,7 +382,7 @@
console.log(`ae_ Data Store data out:`, data_store_do);
}
if (!ae_ds_tmp.id) {
if (ae_ds_tmp && !ae_ds_tmp.id) {
// Create
console.log(`ae_ Data Store Create:`, data_store_do);
ds_submit_results = handle_create__data_store({
@@ -381,18 +391,17 @@
})
.then(function (ds_results) {
console.log(`ae_ Data Store Create Results:`, ds_results);
if (ds_results) {
if (ds_results && ae_ds_tmp) {
ae_ds_tmp.id = ds_results.data_store_id_random;
ae_ds_tmp.updated_on = ds_results.updated_on;
}
return ds_results;
})
.finally(function (ds_val_result) {
console.log(`ae_ ds_val_result = `, ds_val_result);
.finally(function () {
trigger = 'load__ds__code';
$ae_sess.ds.submit_status = 'created';
});
} else {
} else if (ae_ds_tmp) {
// Update
console.log(`ae_ Data Store Update:`, data_store_do);
ds_submit_results = handle_update__data_store({
@@ -402,7 +411,7 @@
})
.then(function (ds_results) {
console.log(`ae_ Data Store Update Results:`, ds_results);
if (ds_results) {
if (ds_results && ae_ds_tmp) {
ae_ds_tmp.updated_on = ds_results.updated_on;
}
return ds_results;
@@ -453,9 +462,8 @@
console.log(error);
return false;
})
.finally(function (create__obj_result) {
.finally(function () {
$ae_sess.ds.create_status = 'finished';
return create__obj_result;
});
return ae_promises.api_create__data_store_obj;
@@ -496,9 +504,8 @@
console.log(error);
return false;
})
.finally(function (update__obj_result) {
.finally(function () {
$ae_sess.ds.update_status = 'finished';
return update__obj_result;
});
return ae_promises.update__data_store_obj;
@@ -518,7 +525,8 @@
type: ds_type,
for_type: for_type,
for_id: for_id,
try_cache: try_cache
try_cache: try_cache,
log_lvl: log_lvl
});
}
});
@@ -545,31 +553,21 @@
</pre>
{/if}
<!-- {#if show_edit} -->
<!-- <section class="edit z-50"> -->
<!-- Main modal -->
<Modal
title="{ae_ds_tmp.name} - {ae_ds_tmp.code}"
bind:open={show_edit}
autoclose={false}
size="xl"
placement="top-center"
class="
bg-white dark:bg-gray-800 text-gray-800 dark:text-gray-200 rounded-lg border-gray-200 dark:border-gray-700 divide-gray-200 dark:divide-gray-700 shadow-md
relative flex flex-col
mx-auto divide-y
w-full
max-w-6xl
"
class="bg-white dark:bg-gray-800 text-gray-800 dark:text-gray-200 rounded-lg border-gray-200 dark:border-gray-700 divide-gray-200 dark:divide-gray-700 shadow-md relative flex flex-col mx-auto divide-y w-full max-w-6xl"
>
<form class="flex flex-col gap-1" onsubmit={preventDefault(handle_submit_form)}>
<input type="hidden" name="ds_id_random" value={ae_ds_tmp.id} />
<div class="flex flex-row gap-1">
{#if $ae_loc.trusted_access}
<label for="ds_use_account_id" class="label text-xs inline"
>Use Account ID
<label for="ds_use_account_id" class="label text-xs inline">
Use Account ID
<input
type="checkbox"
name="ds_use_account_id"
@@ -659,13 +657,11 @@
/>
{:else}
Code: {ae_ds_tmp.code}
<!-- Name: {ae_ds_tmp.name} -->
Type: {ae_ds_tmp.type}
{/if}
</div>
<div class="">
<!-- Handle HTML type -->
{#if ae_ds_tmp.type == 'html' || ae_ds_tmp.type == null}
<textarea
name="ds_value"
@@ -673,11 +669,8 @@
cols="75"
rows="25"
placeholder="Enter the HTML here"
>{ae_ds_tmp.type == 'html' && ae_ds_tmp.html
? ae_ds_tmp.html
: ''}</textarea
>{ae_ds_tmp.type == 'html' && ae_ds_tmp.html ? ae_ds_tmp.html : ''}</textarea
>
<!-- Handle SQL type -->
{:else if ae_ds_tmp.type == 'sql'}
<textarea
name="ds_value"
@@ -685,12 +678,8 @@
cols="75"
rows="25"
placeholder="Enter the SQL here"
>{ae_ds_tmp.type == 'sql' && ae_ds_tmp.text
? ae_ds_tmp.text
: ''}</textarea
>{ae_ds_tmp.type == 'sql' && ae_ds_tmp.text ? ae_ds_tmp.text : ''}</textarea
>
<!-- Handle text type -->
{:else if ae_ds_tmp.type == 'text'}
<textarea
name="ds_value"
@@ -710,7 +699,6 @@
onclick={() => {
if (confirm('Are you sure you want to delete this data store?')) {
trigger = 'delete__ds__code';
// $slct_trigger = 'delete__ds__code';
}
show_edit = false;
show_view = true;
@@ -720,25 +708,12 @@
Delete
</button>
<!-- <button
type="button"
class="btn variant-soft-primary"
on:click={() => {
show_edit = false;
show_view = true;
}}
>
<span class="fas fa-times mx-1"></span>
Close
</button> -->
<button
type="submit"
class="btn preset-tonal-primary border border-primary-500"
disabled={ds_submit_results instanceof Promise && !ds_submit_results}
onclick={() => {
trigger = 'save__ds__code';
// $slct_trigger = 'save__ds__code';
}}
>
<span class="fas fa-save mx-1"></span>
@@ -772,7 +747,6 @@
<button
type="button"
onclick={() => {
console.log('Close modal edit data store.');
show_edit = false;
show_view = true;
}}
@@ -785,30 +759,11 @@
{/snippet}
</Modal>
<!-- </section> -->
<!-- {/if} -->
<!-- {#if mode == 'view'} -->
{#if !ae_ds_tmp.type && !ae_ds_tmp.html && !ae_ds_tmp.json && !ae_ds_tmp.md && !ae_ds_tmp.text}
{#if $ae_loc.manager_access}
<span class="preset-tonal-warning"
>No data found! Is the data store correct or new?</span
>
{:else}
<!-- <span class="variant-soft">loading</span> -->
{/if}
{/if}
{#if ae_ds_tmp.type == 'html' && ae_ds_tmp.html}
{@html ae_ds_tmp.html}
{:else if ae_ds_tmp.type == 'html'}
{#if $ae_loc.manager_access}
<span class="preset-tonal-warning"
>No HTML found! Is the data store type correct?</span
>
{:else}
<!-- <span class="variant-soft">loading</span> -->
<span class="preset-tonal-warning">No HTML found! Is the data store type correct?</span>
{/if}
{/if}
@@ -816,11 +771,7 @@
{ae_ds_tmp.text}
{:else if ae_ds_tmp.type == 'text'}
{#if $ae_loc.manager_access}
<span class="preset-tonal-warning"
>No text found! Is the data store type correct?</span
>
{:else}
<!-- <span class="variant-soft">loading</span> -->
<span class="preset-tonal-warning">No text found! Is the data store type correct?</span>
{/if}
{/if}
@@ -837,64 +788,23 @@
show_edit = true;
show_view = false;
}}
title="Double click to edit data store: {ds_code} with {ae_ds_tmp.account_id
? `account ID=${ae_ds_tmp.account_id}`
: 'no account ID'}"
title="Double click to edit data store: {ds_code}"
>
<span class="fas fa-edit mx-1"></span>
Edit
</button>
<!-- {/if} -->
{:else}
<!-- Nothing to see yet -->
{/if}
<!-- Text:
<pre>
{val_text}
</pre> -->
<!-- JSON:
<pre>
{val_json}
</pre> -->
{#await ds_get_results}
<div
class="modal-loading text-xs absolute bottom-0 left-0 opacity-30 hover:opacity-100 transition delay-700 hover:delay-200"
>
<div class="modal-loading text-xs absolute bottom-0 left-0 opacity-30 hover:opacity-100 transition delay-700 hover:delay-200">
<span class="fas fa-spinner fa-spin"></span>
<span class="loading-text"> Loading... </span>
</div>
{/await}
<!-- {ds_loading_status} -->
</div>
<style lang="postcss">
/* .ae_btn_edit__ds {
opacity: .5;
} */
/* The section.edit should be above the rest of the content and centered on the page */
/*
section.edit {
position: fixed;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
z-index: 100;
background-color: hsla(0, 0%, 100%, .95);
padding: 1rem;
border-radius: .5rem;
box-shadow: 0 0 1rem hsla(0, 0%, 0%, .5);
min-width: 80%;
}
*/
/* .hide {
display: none;
} */
</style>
.ae__elem__data_store {
/* Base styles */
}
</style>