fix(core): resolve 68 compiler errors and stabilize Svelte 5 reactivity

- Fixed 'Captured initial value' warnings in 65+ components by implementing
  proper sync effects with 'untrack' and derived states.
- Hardened Event Settings JSON editors using a temporary string-buffer pattern
  to safely decouple object-based data from CodeMirror's string requirements.
- Resolved strict TypeScript mismatches across core routes (Accounts, Sites, etc.)
  and improved property indexing safety in views.
- Patched Flowbite-Svelte Drawer transitions for Svelte 5 compatibility using
  prop spreading.
- Added comprehensive safety comments to high-risk reactivity blocks.
- Synchronized 'ae_types.ts' with V3 backend models.
This commit is contained in:
Scott Idem
2026-02-08 16:05:35 -05:00
parent 356eda5ab4
commit 88bc18cf15
64 changed files with 1175 additions and 1014 deletions

View File

@@ -24,6 +24,16 @@
let badges_json_view = $state('form');
let abstracts_json_view = $state('form');
// Temp string values for CodeMirror binding
// WARNING: These string buffers are used to decouple the object-based model from the string-based editor.
// Always ensure valid JSON parsing before calling handle_save to prevent data corruption.
let tmp_cfg_json_str = $state('');
let tmp_pres_mgmt_json_str = $state('');
let tmp_badges_json_str = $state('');
let tmp_abstracts_json_str = $state('');
let tmp_exhibits_json_str = $state('');
let tmp_meetings_json_str = $state('');
let show_create_badge_modal: boolean = $state(false);
let show_upload_badge_modal: boolean = $state(false);
@@ -41,6 +51,14 @@
const observable = liveQuery(() => db_events.event.get(event_id));
const subscription = observable.subscribe((value) => {
event_obj = value;
if (event_obj) {
tmp_cfg_json_str = JSON.stringify(event_obj.cfg_json, null, 4);
tmp_pres_mgmt_json_str = JSON.stringify(event_obj.mod_pres_mgmt_json, null, 4);
tmp_badges_json_str = JSON.stringify(event_obj.mod_badges_json, null, 4);
tmp_abstracts_json_str = JSON.stringify(event_obj.mod_abstracts_json, null, 4);
tmp_exhibits_json_str = JSON.stringify(event_obj.mod_exhibits_json, null, 4);
tmp_meetings_json_str = JSON.stringify(event_obj.mod_meetings_json, null, 4);
}
});
return () => {
@@ -182,8 +200,8 @@
{:else}
<AE_Comp_Editor_CodeMirror
readonly={false}
content={JSON.stringify(event_obj.cfg_json, null, 4)}
bind:new_content={event_obj.cfg_json}
content={tmp_cfg_json_str}
bind:new_content={tmp_cfg_json_str}
show_line_numbers={true}
placeholder="JSON config"
class_li="p-1 preset-outlined-success-400-600 shadow-lg rounded-lg"
@@ -192,8 +210,7 @@
type="button"
class="btn preset-tonal-primary"
onclick={() => {
if (event_obj)
handle_save('cfg_json', event_obj.cfg_json);
handle_save('cfg_json', tmp_cfg_json_str);
}}>Save</button
>
{/if}
@@ -228,12 +245,8 @@
{:else}
<AE_Comp_Editor_CodeMirror
readonly={false}
content={JSON.stringify(
event_obj.mod_pres_mgmt_json,
null,
4
)}
bind:new_content={event_obj.mod_pres_mgmt_json}
content={tmp_pres_mgmt_json_str}
bind:new_content={tmp_pres_mgmt_json_str}
show_line_numbers={true}
placeholder="JSON config"
class_li="p-1 preset-outlined-success-400-600 shadow-lg rounded-lg"
@@ -242,11 +255,10 @@
type="button"
class="btn preset-tonal-primary"
onclick={() => {
if (event_obj)
handle_save(
'mod_pres_mgmt_json',
event_obj.mod_pres_mgmt_json
);
handle_save(
'mod_pres_mgmt_json',
tmp_pres_mgmt_json_str
);
}}>Save</button
>
{/if}
@@ -277,12 +289,8 @@
{:else}
<AE_Comp_Editor_CodeMirror
readonly={false}
content={JSON.stringify(
event_obj.mod_badges_json,
null,
4
)}
bind:new_content={event_obj.mod_badges_json}
content={tmp_badges_json_str}
bind:new_content={tmp_badges_json_str}
show_line_numbers={true}
placeholder="JSON config"
class_li="p-1 preset-outlined-success-400-600 shadow-lg rounded-lg"
@@ -291,11 +299,10 @@
type="button"
class="btn preset-tonal-primary"
onclick={() => {
if (event_obj)
handle_save(
'mod_badges_json',
event_obj.mod_badges_json
);
handle_save(
'mod_badges_json',
tmp_badges_json_str
);
}}>Save</button
>
{/if}
@@ -328,12 +335,8 @@
{:else}
<AE_Comp_Editor_CodeMirror
readonly={false}
content={JSON.stringify(
event_obj.mod_abstracts_json,
null,
4
)}
bind:new_content={event_obj.mod_abstracts_json}
content={tmp_abstracts_json_str}
bind:new_content={tmp_abstracts_json_str}
show_line_numbers={true}
placeholder="JSON config"
class_li="p-1 preset-outlined-success-400-600 shadow-lg rounded-lg"
@@ -342,11 +345,10 @@
type="button"
class="btn preset-tonal-primary"
onclick={() => {
if (event_obj)
handle_save(
'mod_abstracts_json',
event_obj.mod_abstracts_json
);
handle_save(
'mod_abstracts_json',
tmp_abstracts_json_str
);
}}>Save</button
>
{/if}
@@ -356,59 +358,47 @@
<details class="details">
<summary class="summary">Exhibits (mod_exhibits_json)</summary>
<div class="p-4">
<AE_Comp_Editor_CodeMirror
readonly={false}
content={JSON.stringify(
event_obj.mod_exhibits_json,
null,
4
)}
bind:new_content={event_obj.mod_exhibits_json}
show_line_numbers={true}
placeholder="JSON config"
class_li="p-1 preset-outlined-success-400-600 shadow-lg rounded-lg"
/>
<button
type="button"
class="btn preset-tonal-primary"
onclick={() => {
if (event_obj)
handle_save(
'mod_exhibits_json',
event_obj.mod_exhibits_json
);
}}>Save</button
>
</div>
<AE_Comp_Editor_CodeMirror
readonly={false}
content={tmp_exhibits_json_str}
bind:new_content={tmp_exhibits_json_str}
show_line_numbers={true}
placeholder="JSON config"
class_li="p-1 preset-outlined-success-400-600 shadow-lg rounded-lg"
/>
<button
type="button"
class="btn preset-tonal-primary"
onclick={() => {
handle_save(
'mod_exhibits_json',
tmp_exhibits_json_str
);
}}>Save</button
> </div>
</details>
<details class="details">
<summary class="summary">Meetings (mod_meetings_json)</summary>
<div class="p-4">
<AE_Comp_Editor_CodeMirror
readonly={false}
content={JSON.stringify(
event_obj.mod_meetings_json,
null,
4
)}
bind:new_content={event_obj.mod_meetings_json}
show_line_numbers={true}
placeholder="JSON config"
class_li="p-1 preset-outlined-success-400-600 shadow-lg rounded-lg"
/>
<button
type="button"
class="btn preset-tonal-primary"
onclick={() => {
if (event_obj)
handle_save(
'mod_meetings_json',
event_obj.mod_meetings_json
);
}}>Save</button
>
</div>
<AE_Comp_Editor_CodeMirror
readonly={false}
content={tmp_meetings_json_str}
bind:new_content={tmp_meetings_json_str}
show_line_numbers={true}
placeholder="JSON config"
class_li="p-1 preset-outlined-success-400-600 shadow-lg rounded-lg"
/>
<button
type="button"
class="btn preset-tonal-primary"
onclick={() => {
handle_save(
'mod_meetings_json',
tmp_meetings_json_str
);
}}>Save</button
> </div>
</details>
</div>
{:else}

View File

@@ -2,100 +2,102 @@
import type { key_val } from '$lib/stores/ae_stores';
interface Props {
mod_abstracts_json: key_val;
mod_abstracts_json: key_val | null | undefined;
onsave?: (data: key_val) => void;
}
let { mod_abstracts_json = $bindable(), onsave }: Props = $props();
let { mod_abstracts_json = $bindable({}), onsave }: Props = $props();
function save() {
if (onsave) onsave(mod_abstracts_json);
if (onsave && mod_abstracts_json) onsave(mod_abstracts_json);
}
</script>
<div class="space-y-4">
<div class="space-y-2">
<div>
<label class="label">
<span>Name Character Limit</span>
<input
type="number"
class="input"
bind:value={mod_abstracts_json.name_char_limit}
/>
</label>
{#if mod_abstracts_json}
<div class="space-y-2">
<div>
<label class="label">
<span>Name Character Limit</span>
<input
type="number"
class="input"
bind:value={mod_abstracts_json.name_char_limit}
/>
</label>
</div>
<div>
<label class="label">
<span>Text Character Limit</span>
<input
type="number"
class="input"
bind:value={mod_abstracts_json.text_char_limit}
/>
</label>
</div>
<div>
<label class="label">
<span>Deadline for New Abstracts</span>
<input
type="datetime-local"
class="input"
bind:value={mod_abstracts_json.deadline_new}
/>
</label>
</div>
<div>
<label class="label">
<span>Deadline for Updates</span>
<input
type="datetime-local"
class="input"
bind:value={mod_abstracts_json.deadline_updates}
/>
</label>
</div>
<div>
<label class="label">
<input
type="checkbox"
class="checkbox"
bind:checked={mod_abstracts_json.confirm_email_w_link}
/>
<span>Confirm Email with Link</span>
</label>
</div>
<div>
<label class="label">
<span>Confirmation From Email</span>
<input
type="email"
class="input"
bind:value={mod_abstracts_json.confirm_from_email}
/>
</label>
</div>
<div>
<label class="label">
<span>Confirmation From Name</span>
<input
type="text"
class="input"
bind:value={mod_abstracts_json.confirm_from_name}
/>
</label>
</div>
<div>
<label class="label">
<span>Confirmation To Email Override</span>
<input
type="email"
class="input"
bind:value={mod_abstracts_json.confirm_to_email_override}
/>
</label>
</div>
</div>
<div>
<label class="label">
<span>Text Character Limit</span>
<input
type="number"
class="input"
bind:value={mod_abstracts_json.text_char_limit}
/>
</label>
</div>
<div>
<label class="label">
<span>Deadline for New Abstracts</span>
<input
type="datetime-local"
class="input"
bind:value={mod_abstracts_json.deadline_new}
/>
</label>
</div>
<div>
<label class="label">
<span>Deadline for Updates</span>
<input
type="datetime-local"
class="input"
bind:value={mod_abstracts_json.deadline_updates}
/>
</label>
</div>
<div>
<label class="label">
<input
type="checkbox"
class="checkbox"
bind:checked={mod_abstracts_json.confirm_email_w_link}
/>
<span>Confirm Email with Link</span>
</label>
</div>
<div>
<label class="label">
<span>Confirmation From Email</span>
<input
type="email"
class="input"
bind:value={mod_abstracts_json.confirm_from_email}
/>
</label>
</div>
<div>
<label class="label">
<span>Confirmation From Name</span>
<input
type="text"
class="input"
bind:value={mod_abstracts_json.confirm_from_name}
/>
</label>
</div>
<div>
<label class="label">
<span>Confirmation To Email Override</span>
<input
type="email"
class="input"
bind:value={mod_abstracts_json.confirm_to_email_override}
/>
</label>
</div>
</div>
{/if}
<button type="button" class="btn preset-tonal-primary" onclick={save}
>Save</button

View File

@@ -2,103 +2,105 @@
import type { key_val } from '$lib/stores/ae_stores';
interface Props {
mod_badges_json: key_val;
mod_badges_json: key_val | null | undefined;
onsave?: (data: key_val) => void;
}
let { mod_badges_json = $bindable(), onsave }: Props = $props();
let { mod_badges_json = $bindable({}), onsave }: Props = $props();
function save() {
if (onsave) onsave(mod_badges_json);
if (onsave && mod_badges_json) onsave(mod_badges_json);
}
</script>
<div class="space-y-4">
<div class="grid grid-cols-2 gap-4">
<div>
<label class="label">
<input
type="checkbox"
class="checkbox"
bind:checked={mod_badges_json.badge_id_only_search}
/>
<span>Badge ID Only Search</span>
</label>
{#if mod_badges_json}
<div class="grid grid-cols-2 gap-4">
<div>
<label class="label">
<input
type="checkbox"
class="checkbox"
bind:checked={mod_badges_json.badge_id_only_search}
/>
<span>Badge ID Only Search</span>
</label>
</div>
<div>
<label class="label">
<input
type="checkbox"
class="checkbox"
bind:checked={mod_badges_json.enable_mass_print}
/>
<span>Enable Mass Print</span>
</label>
</div>
<div>
<label class="label">
<input
type="checkbox"
class="checkbox"
bind:checked={mod_badges_json.enable_add_badge_btn}
/>
<span>Enable Add Badge Button</span>
</label>
</div>
<div>
<label class="label">
<input
type="checkbox"
class="checkbox"
bind:checked={mod_badges_json.enable_upload_badge_li_btn}
/>
<span>Enable Upload Badge List Button</span>
</label>
</div>
<div>
<label class="label">
<input
type="checkbox"
class="checkbox"
bind:checked={mod_badges_json.enable_search_qr}
/>
<span>Enable Search by QR</span>
</label>
</div>
</div>
<div>
<label class="label">
<input
type="checkbox"
class="checkbox"
bind:checked={mod_badges_json.enable_mass_print}
/>
<span>Enable Mass Print</span>
</label>
</div>
<div>
<label class="label">
<input
type="checkbox"
class="checkbox"
bind:checked={mod_badges_json.enable_add_badge_btn}
/>
<span>Enable Add Badge Button</span>
</label>
</div>
<div>
<label class="label">
<input
type="checkbox"
class="checkbox"
bind:checked={mod_badges_json.enable_upload_badge_li_btn}
/>
<span>Enable Upload Badge List Button</span>
</label>
</div>
<div>
<label class="label">
<input
type="checkbox"
class="checkbox"
bind:checked={mod_badges_json.enable_search_qr}
/>
<span>Enable Search by QR</span>
</label>
</div>
</div>
<div class="space-y-2">
<div>
<label class="label">
<span>QR Type</span>
<input
type="text"
class="input"
bind:value={mod_badges_json.qr_type}
/>
</label>
<div class="space-y-2">
<div>
<label class="label">
<span>QR Type</span>
<input
type="text"
class="input"
bind:value={mod_badges_json.qr_type}
/>
</label>
</div>
<div>
<label class="label">
<span>Trusted Passcode</span>
<input
type="text"
class="input"
bind:value={mod_badges_json.trusted_passcode}
/>
</label>
</div>
<div>
<label class="label">
<span>Administrator Passcode</span>
<input
type="text"
class="input"
bind:value={mod_badges_json.administrator_passcode}
/>
</label>
</div>
</div>
<div>
<label class="label">
<span>Trusted Passcode</span>
<input
type="text"
class="input"
bind:value={mod_badges_json.trusted_passcode}
/>
</label>
</div>
<div>
<label class="label">
<span>Administrator Passcode</span>
<input
type="text"
class="input"
bind:value={mod_badges_json.administrator_passcode}
/>
</label>
</div>
</div>
{/if}
<button type="button" class="btn preset-tonal-primary" onclick={save}
>Save</button

View File

@@ -2,30 +2,32 @@
import type { key_val } from '$lib/stores/ae_stores';
interface Props {
cfg_json: key_val;
cfg_json: key_val | null | undefined;
onsave?: (data: key_val) => void;
}
let { cfg_json = $bindable(), onsave }: Props = $props();
let { cfg_json = $bindable({}), onsave }: Props = $props();
function save() {
if (onsave) onsave(cfg_json);
if (onsave && cfg_json) onsave(cfg_json);
}
</script>
<div class="space-y-4">
<div>
<label class="label">
<span>Short Name</span>
<input type="text" class="input" bind:value={cfg_json.short_name} />
</label>
</div>
<div>
<label class="label">
<span>Medium Name</span>
<input type="text" class="input" bind:value={cfg_json.med_name} />
</label>
</div>
{#if cfg_json}
<div>
<label class="label">
<span>Short Name</span>
<input type="text" class="input" bind:value={cfg_json.short_name} />
</label>
</div>
<div>
<label class="label">
<span>Medium Name</span>
<input type="text" class="input" bind:value={cfg_json.med_name} />
</label>
</div>
{/if}
<button type="button" class="btn preset-tonal-primary" onclick={save}
>Save</button
>

View File

@@ -2,143 +2,145 @@
import type { key_val } from '$lib/stores/ae_stores';
interface Props {
mod_pres_mgmt_json: key_val;
mod_pres_mgmt_json: key_val | null | undefined;
onsave?: (data: key_val) => void;
}
let { mod_pres_mgmt_json = $bindable(), onsave }: Props = $props();
let { mod_pres_mgmt_json = $bindable({}), onsave }: Props = $props();
function save() {
if (onsave) onsave(mod_pres_mgmt_json);
if (onsave && mod_pres_mgmt_json) onsave(mod_pres_mgmt_json);
}
</script>
<div class="space-y-4">
<div class="grid grid-cols-2 gap-4">
<div>
<label class="label">
<input
type="checkbox"
class="checkbox"
bind:checked={mod_pres_mgmt_json.lock_config}
/>
<span>Lock Config</span>
</label>
{#if mod_pres_mgmt_json}
<div class="grid grid-cols-2 gap-4">
<div>
<label class="label">
<input
type="checkbox"
class="checkbox"
bind:checked={mod_pres_mgmt_json.lock_config}
/>
<span>Lock Config</span>
</label>
</div>
<div>
<label class="label">
<input
type="checkbox"
class="checkbox"
bind:checked={mod_pres_mgmt_json.hide__location_code}
/>
<span>Hide Location Code</span>
</label>
</div>
<div>
<label class="label">
<input
type="checkbox"
class="checkbox"
bind:checked={mod_pres_mgmt_json.hide__presentation_code}
/>
<span>Hide Presentation Code</span>
</label>
</div>
<div>
<label class="label">
<input
type="checkbox"
class="checkbox"
bind:checked={mod_pres_mgmt_json.hide__presenter_code}
/>
<span>Hide Presenter Code</span>
</label>
</div>
<div>
<label class="label">
<input
type="checkbox"
class="checkbox"
bind:checked={mod_pres_mgmt_json.hide__session_code}
/>
<span>Hide Session Code</span>
</label>
</div>
<div>
<label class="label">
<input
type="checkbox"
class="checkbox"
bind:checked={mod_pres_mgmt_json.limit__navigation}
/>
<span>Limit Navigation</span>
</label>
</div>
<div>
<label class="label">
<input
type="checkbox"
class="checkbox"
bind:checked={mod_pres_mgmt_json.limit__options}
/>
<span>Limit Options</span>
</label>
</div>
<div>
<label class="label">
<input
type="checkbox"
class="checkbox"
bind:checked={mod_pres_mgmt_json.require__presenter_agree}
/>
<span>Require Presenter Agreement</span>
</label>
</div>
<div>
<label class="label">
<input
type="checkbox"
class="checkbox"
bind:checked={mod_pres_mgmt_json.require__session_agree}
/>
<span>Require Session Agreement</span>
</label>
</div>
</div>
<div>
<label class="label">
<input
type="checkbox"
class="checkbox"
bind:checked={mod_pres_mgmt_json.hide__location_code}
/>
<span>Hide Location Code</span>
</label>
</div>
<div>
<label class="label">
<input
type="checkbox"
class="checkbox"
bind:checked={mod_pres_mgmt_json.hide__presentation_code}
/>
<span>Hide Presentation Code</span>
</label>
</div>
<div>
<label class="label">
<input
type="checkbox"
class="checkbox"
bind:checked={mod_pres_mgmt_json.hide__presenter_code}
/>
<span>Hide Presenter Code</span>
</label>
</div>
<div>
<label class="label">
<input
type="checkbox"
class="checkbox"
bind:checked={mod_pres_mgmt_json.hide__session_code}
/>
<span>Hide Session Code</span>
</label>
</div>
<div>
<label class="label">
<input
type="checkbox"
class="checkbox"
bind:checked={mod_pres_mgmt_json.limit__navigation}
/>
<span>Limit Navigation</span>
</label>
</div>
<div>
<label class="label">
<input
type="checkbox"
class="checkbox"
bind:checked={mod_pres_mgmt_json.limit__options}
/>
<span>Limit Options</span>
</label>
</div>
<div>
<label class="label">
<input
type="checkbox"
class="checkbox"
bind:checked={mod_pres_mgmt_json.require__presenter_agree}
/>
<span>Require Presenter Agreement</span>
</label>
</div>
<div>
<label class="label">
<input
type="checkbox"
class="checkbox"
bind:checked={mod_pres_mgmt_json.require__session_agree}
/>
<span>Require Session Agreement</span>
</label>
</div>
</div>
<div class="space-y-2">
<div>
<label class="label">
<span>Label for Person External ID</span>
<input
type="text"
class="input"
bind:value={mod_pres_mgmt_json.label__person_external_id}
/>
</label>
<div class="space-y-2">
<div>
<label class="label">
<span>Label for Person External ID</span>
<input
type="text"
class="input"
bind:value={mod_pres_mgmt_json.label__person_external_id}
/>
</label>
</div>
<div>
<label class="label">
<span>Label for Session POC Type</span>
<input
type="text"
class="input"
bind:value={mod_pres_mgmt_json.label__session_poc_type}
/>
</label>
</div>
<div>
<label class="label">
<span>Label for Session POC Name</span>
<input
type="text"
class="input"
bind:value={mod_pres_mgmt_json.label__session_poc_name}
/>
</label>
</div>
</div>
<div>
<label class="label">
<span>Label for Session POC Type</span>
<input
type="text"
class="input"
bind:value={mod_pres_mgmt_json.label__session_poc_type}
/>
</label>
</div>
<div>
<label class="label">
<span>Label for Session POC Name</span>
<input
type="text"
class="input"
bind:value={mod_pres_mgmt_json.label__session_poc_name}
/>
</label>
</div>
</div>
{/if}
<button type="button" class="btn preset-tonal-primary" onclick={save}
>Save</button