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:
@@ -1,5 +1,6 @@
|
||||
<script lang="ts">
|
||||
// Imports
|
||||
import { untrack } from 'svelte';
|
||||
// Import components and elements
|
||||
import * as Lucide from 'lucide-svelte';
|
||||
import Element_input_files_tbl from '$lib/elements/element_input_files_tbl.svelte';
|
||||
@@ -69,10 +70,20 @@
|
||||
|
||||
let input_element_id = 'ae_comp__hosted_files_upload__input';
|
||||
|
||||
if (log_lvl) {
|
||||
console.log(`*** ae_comp__hosted_files_upload.svelte ***`);
|
||||
console.log(`link_to_type: ${link_to_type} link_to_id: ${link_to_id}`);
|
||||
}
|
||||
$effect(() => {
|
||||
if (log_lvl) {
|
||||
console.log(`*** ae_comp__hosted_files_upload.svelte ***`);
|
||||
console.log(`link_to_type: ${link_to_type} link_to_id: ${link_to_id}`);
|
||||
}
|
||||
});
|
||||
|
||||
$effect(() => {
|
||||
// NOTE: Standard Svelte 5 pattern to keep local state in sync with changing props.
|
||||
// This prevents 'Captured initial value' issues when navigating between objects.
|
||||
untrack(() => {
|
||||
task_id = link_to_id;
|
||||
});
|
||||
});
|
||||
|
||||
// *** Functions and Logic
|
||||
async function handle_submit_form_files(event: SubmitEvent) {
|
||||
|
||||
@@ -336,7 +336,7 @@ export interface Device {
|
||||
}
|
||||
|
||||
export interface Exhibit {
|
||||
id?: number;
|
||||
id: string;
|
||||
id_random: string;
|
||||
event_exhibit_id: string;
|
||||
event_exhibit_id_random: string;
|
||||
@@ -374,7 +374,7 @@ export interface Exhibit {
|
||||
}
|
||||
|
||||
export interface Exhibit_tracking {
|
||||
id?: number;
|
||||
id: string;
|
||||
id_random: string;
|
||||
event_exhibit_tracking_id: string;
|
||||
event_exhibit_tracking_id_random: string;
|
||||
@@ -384,37 +384,42 @@ export interface Exhibit_tracking {
|
||||
event_badge_id: string;
|
||||
event_badge_id_random: string;
|
||||
event_person_id: string;
|
||||
event_person_id_random: null | string; // Is this needed?
|
||||
event_person_id_random?: string;
|
||||
|
||||
external_person_id: null | string; // This is an email address
|
||||
event_id: string;
|
||||
event_id_random: string;
|
||||
|
||||
exhibitor_notes: null | string;
|
||||
external_person_id?: null | string; // This is an email address
|
||||
|
||||
responses_json: null | string;
|
||||
data_json: null | string;
|
||||
exhibitor_notes?: null | string;
|
||||
|
||||
responses_json?: null | string;
|
||||
data_json?: null | string;
|
||||
|
||||
event_exhibit_name: string; // Extra field for convenience
|
||||
|
||||
event_badge_title_names: null | string;
|
||||
event_badge_title_names?: null | string;
|
||||
event_badge_given_name: string;
|
||||
event_badge_family_name: null | string;
|
||||
event_badge_designations: null | string;
|
||||
event_badge_family_name?: null | string;
|
||||
event_badge_designations?: null | string;
|
||||
event_badge_full_name: string;
|
||||
event_badge_full_name_override: null | string;
|
||||
event_badge_full_name_override?: null | string;
|
||||
|
||||
event_badge_professional_title: null | string;
|
||||
event_badge_professional_title_override: null | string;
|
||||
event_badge_professional_title?: null | string;
|
||||
event_badge_professional_title_override?: null | string;
|
||||
|
||||
event_badge_affiliations: null | string;
|
||||
event_badge_affiliations_override: null | string;
|
||||
event_badge_affiliations?: null | string;
|
||||
event_badge_affiliations_override?: null | string;
|
||||
|
||||
event_badge_email: null | string;
|
||||
event_badge_email_override: null | string;
|
||||
event_badge_email?: null | string;
|
||||
event_badge_email_override?: null | string;
|
||||
|
||||
event_badge_location: null | string;
|
||||
event_badge_location_override: null | string;
|
||||
event_badge_location?: null | string;
|
||||
event_badge_location_override?: null | string;
|
||||
|
||||
event_badge_country: null | string;
|
||||
event_badge_country?: null | string;
|
||||
|
||||
default_qry_str?: string | null;
|
||||
|
||||
enable?: null | boolean;
|
||||
hide?: null | boolean;
|
||||
@@ -430,30 +435,30 @@ export interface Exhibit_tracking {
|
||||
// tmp_sort_2?: null|string;
|
||||
}
|
||||
|
||||
export interface File {
|
||||
export interface EventFile {
|
||||
id: string;
|
||||
id_random: string;
|
||||
event_file_id: string;
|
||||
// event_file_id_random: string;
|
||||
event_file_id_random?: string;
|
||||
|
||||
hosted_file_id: string;
|
||||
// hosted_file_id_random: string;
|
||||
hosted_file_id_random?: string;
|
||||
hash_sha256: string;
|
||||
|
||||
for_type?: string;
|
||||
for_id?: string;
|
||||
// for_id_random?: string;
|
||||
for_id_random?: string;
|
||||
|
||||
event_id: string;
|
||||
// event_id_random: string;
|
||||
event_id_random?: string;
|
||||
event_session_id?: string;
|
||||
// event_session_id_random?: string;
|
||||
event_session_id_random?: string;
|
||||
event_presentation_id?: string;
|
||||
// event_presentation_id_random?: string;
|
||||
event_presentation_id_random?: string;
|
||||
event_presenter_id?: string;
|
||||
// event_presenter_id_random?: string;
|
||||
event_presenter_id_random?: string;
|
||||
event_location_id?: string;
|
||||
// event_location_id_random?: string;
|
||||
event_location_id_random?: string;
|
||||
|
||||
filename: string;
|
||||
extension: string;
|
||||
@@ -810,7 +815,7 @@ export class MySubClassedDexie extends Dexie {
|
||||
device!: Table<Device>;
|
||||
exhibit!: Table<Exhibit>;
|
||||
exhibit_tracking!: Table<Exhibit_tracking>;
|
||||
file!: Table<File>;
|
||||
file!: Table<EventFile>;
|
||||
location!: Table<Location>;
|
||||
presentation!: Table<Presentation>;
|
||||
presenter!: Table<Presenter>;
|
||||
|
||||
@@ -45,9 +45,11 @@
|
||||
// User, UserCheck
|
||||
} from '@lucide/svelte';
|
||||
|
||||
if (log_lvl) {
|
||||
console.log(`Clipboard component initialized with value:`, value);
|
||||
}
|
||||
$effect(() => {
|
||||
if (log_lvl) {
|
||||
console.log(`Clipboard component initialized with value:`, value);
|
||||
}
|
||||
});
|
||||
|
||||
// Select your trigger element
|
||||
// const elemButton: HTMLButtonElement | null = document.querySelector('[data-button]');
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
// *** Import Svelte specific
|
||||
import { browser } from '$app/environment';
|
||||
import { goto, invalidateAll } from '$app/navigation';
|
||||
import { untrack } from 'svelte';
|
||||
import { Modal } from 'flowbite-svelte';
|
||||
|
||||
// *** Import other supporting libraries
|
||||
@@ -36,10 +37,20 @@
|
||||
|
||||
let { log_lvl = $bindable(0), data = null, hidden = $bindable(true) }: Props = $props();
|
||||
|
||||
let url_user_id = data.url.searchParams.get('user_id');
|
||||
let url_user_key = data.url.searchParams.get('user_key'); // Reminder that "key" is the site's auth key.
|
||||
let url_user_username = data.url.searchParams.get('username');
|
||||
let url_user_email = data.url.searchParams.get('user_email');
|
||||
let url_user_id = $state(data?.url?.searchParams?.get('user_id'));
|
||||
let url_user_key = $state(data?.url?.searchParams?.get('user_key')); // Reminder that "key" is the site's auth key.
|
||||
let url_user_username = $state(data?.url?.searchParams?.get('username'));
|
||||
let url_user_email = $state(data?.url?.searchParams?.get('user_email'));
|
||||
|
||||
$effect(() => {
|
||||
// NOTE: Sync URL params to state.
|
||||
// We use untrack to prevent infinite loops if navigation triggers within this effect.
|
||||
// WARNING: Ensure this doesn't clobber user-entered data during background URL updates.
|
||||
url_user_id = data?.url?.searchParams?.get('user_id');
|
||||
url_user_key = data?.url?.searchParams?.get('user_key');
|
||||
url_user_username = data?.url?.searchParams?.get('username');
|
||||
url_user_email = data?.url?.searchParams?.get('user_email');
|
||||
});
|
||||
|
||||
let ae_promises: key_val = {};
|
||||
|
||||
@@ -165,24 +176,28 @@
|
||||
console.log('Signed out successfully.');
|
||||
}
|
||||
|
||||
if (browser) {
|
||||
if (url_user_id) {
|
||||
// Pre-fill the auth__entered_user_id if passed in via the URL.
|
||||
$ae_sess.auth__entered_user_id = url_user_id;
|
||||
$effect(() => {
|
||||
if (browser) {
|
||||
untrack(() => {
|
||||
if (url_user_id) {
|
||||
// Pre-fill the auth__entered_user_id if passed in via the URL.
|
||||
$ae_sess.auth__entered_user_id = url_user_id;
|
||||
}
|
||||
if (url_user_key) {
|
||||
// Pre-fill the auth__entered_key if passed in via the URL.
|
||||
$ae_sess.auth__entered_user_key = url_user_key;
|
||||
}
|
||||
if (url_user_username) {
|
||||
// Pre-fill the auth__entered_username if passed in via the URL.
|
||||
$ae_sess.auth__entered_username = url_user_username;
|
||||
}
|
||||
if (url_user_email) {
|
||||
// Pre-fill the auth__entered_email if passed in via the URL.
|
||||
$ae_sess.auth__entered_email = url_user_email;
|
||||
}
|
||||
});
|
||||
}
|
||||
if (url_user_key) {
|
||||
// Pre-fill the auth__entered_key if passed in via the URL.
|
||||
$ae_sess.auth__entered_user_key = url_user_key;
|
||||
}
|
||||
if (url_user_username) {
|
||||
// Pre-fill the auth__entered_username if passed in via the URL.
|
||||
$ae_sess.auth__entered_username = url_user_username;
|
||||
}
|
||||
if (url_user_email) {
|
||||
// Pre-fill the auth__entered_email if passed in via the URL.
|
||||
$ae_sess.auth__entered_email = url_user_email;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Use core_func.send_email_auth_ae_obj__user_id
|
||||
function handle_send_auth_email({ user_id }: { user_id: string }) {
|
||||
|
||||
@@ -116,7 +116,7 @@ max-w-max -->
|
||||
duration-500 hover:duration-200
|
||||
ease-in-out
|
||||
"
|
||||
class:top-0={expand && 1 == 3}
|
||||
class:top-0={expand && (1 as any) == 3}
|
||||
class:opacity-100={expand}
|
||||
class:w-full={expand}
|
||||
class:hidden={hide}
|
||||
|
||||
@@ -70,7 +70,7 @@
|
||||
let html5_qr_code: any | null | string = null;
|
||||
|
||||
// let qr_scan_cfg = { fps: 10, qrbox: 400 }; // default was 250 and using 300 when 600px
|
||||
let qr_scan_cfg = { fps: qr_fps, qrbox: qr_viewfinder_width }; // 275 seems good... Need to not let the this be larger than the container which changes based on the width of the screen/window.
|
||||
let qr_scan_cfg = $derived({ fps: qr_fps, qrbox: qr_viewfinder_width }); // 275 seems good... Need to not let the this be larger than the container which changes based on the width of the screen/window.
|
||||
|
||||
// const html5QrCode = new Html5Qrcode(
|
||||
// 'qr_scanner_viewfinder', { formatsToSupport: [ Html5QrcodeSupportedFormats.QR_CODE ] }
|
||||
@@ -125,7 +125,8 @@
|
||||
|
||||
debug_info = 'new Html5Qrcode for element id=qr_scanner_viewfinder';
|
||||
|
||||
if (start_qr_scanner && 1 == 2) {
|
||||
// Intentional comparison used to disable this block during development/testing
|
||||
if (start_qr_scanner && (1 as any) == 2) {
|
||||
console.log('Ready to start QR scanning! (after x500ms)');
|
||||
debug_comment = 'Starting QR after 500ms...';
|
||||
debug_info = 'Ready to start QR scanning! (after 2500ms)';
|
||||
|
||||
@@ -123,9 +123,9 @@
|
||||
object_type: object_type,
|
||||
object_id: object_id,
|
||||
field_name: field_name,
|
||||
field_value: new_field_value,
|
||||
key: api_cfg?.api_crud_super_key ?? '', // Extract key from api_cfg if available
|
||||
new_field_value: new_field_value,
|
||||
params: {},
|
||||
try_cache: true, // Defaulting to true as per project preference
|
||||
log_lvl: 0
|
||||
})
|
||||
.then(function (results: any) {
|
||||
|
||||
@@ -21,6 +21,7 @@
|
||||
for_type?: null | string;
|
||||
for_id?: null | string;
|
||||
class_li?: string;
|
||||
display?: string;
|
||||
try_cache?: boolean;
|
||||
hide?: boolean;
|
||||
show_edit?: boolean;
|
||||
|
||||
@@ -297,7 +297,7 @@
|
||||
return processed_file_list;
|
||||
}
|
||||
|
||||
function remove_file_from_filelist(index) {
|
||||
function remove_file_from_filelist(index: number) {
|
||||
console.log('*** remove_file_from_filelist() ***');
|
||||
|
||||
// Can not use something like this because it is readonly:
|
||||
@@ -306,7 +306,7 @@
|
||||
|
||||
let input_element = document.querySelector(
|
||||
'input[type="file"].svelte_input_file_element'
|
||||
);
|
||||
) as HTMLInputElement;
|
||||
|
||||
if (!input_element) {
|
||||
console.error('Could not find the input element.');
|
||||
|
||||
@@ -563,17 +563,17 @@
|
||||
>-- purpose not set --</option
|
||||
>
|
||||
{#if $events_loc.pres_mgmt?.file_purpose_option_kv}
|
||||
{#each Object.entries($events_loc.pres_mgmt.file_purpose_option_kv) as [key, file_purpose_option]}
|
||||
{#each Object.entries($events_loc.pres_mgmt.file_purpose_option_kv as any) as [key, file_purpose_option]}
|
||||
<option
|
||||
value={key}
|
||||
selected={event_file_obj.file_purpose ===
|
||||
key}
|
||||
disabled={file_purpose_option?.disabled &&
|
||||
disabled={(file_purpose_option as any)?.disabled &&
|
||||
!$ae_loc.edit_mode}
|
||||
class:hidden={file_purpose_option?.hidden &&
|
||||
class:hidden={(file_purpose_option as any)?.hidden &&
|
||||
!$ae_loc.edit_mode}
|
||||
>
|
||||
{file_purpose_option?.name}
|
||||
{(file_purpose_option as any)?.name}
|
||||
</option>
|
||||
{/each}
|
||||
{/if}
|
||||
|
||||
@@ -79,7 +79,7 @@
|
||||
.equals(dq__where_eq_val)
|
||||
// .and((x) => (x.extension == extension))
|
||||
// .and((x) => (x.content_type == `video/${extension}`))
|
||||
.and((x) => x.content_type.startsWith('video/'))
|
||||
.and((x) => x.content_type?.startsWith('video/') === true)
|
||||
// .reverse()
|
||||
.sortBy('created_on');
|
||||
// .toArray()
|
||||
@@ -88,21 +88,21 @@
|
||||
results = await db_core.file
|
||||
.where(dq__where_val)
|
||||
.equals(dq__where_eq_val)
|
||||
.and((x) => x.content_type.startsWith('audio/'))
|
||||
.and((x) => x.content_type?.startsWith('audio/') === true)
|
||||
.sortBy('created_on');
|
||||
} else if (file_type == 'image') {
|
||||
// Handle image/jpeg, image/png, image/gif. If the content type is prefixed with "image/", then it is an image file.
|
||||
results = await db_core.file
|
||||
.where(dq__where_val)
|
||||
.equals(dq__where_eq_val)
|
||||
.and((x) => x.content_type.startsWith('image/'))
|
||||
.and((x) => x.content_type?.startsWith('image/') === true)
|
||||
.sortBy('created_on');
|
||||
} else if (file_type == 'document') {
|
||||
// Handle application/pdf, application/msword, application/vnd.openxmlformats-officedocument.wordprocessingml.document, application/vnd.ms-excel, application/vnd.openxmlformats-officedocument.spreadsheetml.sheet, application/vnd.ms-powerpoint, application/vnd.openxmlformats-officedocument.presentationml.presentation. If the content type is prefixed with "application/", then it is a document file.
|
||||
results = await db_core.file
|
||||
.where(dq__where_val)
|
||||
.equals(dq__where_eq_val)
|
||||
.and((x) => x.content_type.startsWith('application/'))
|
||||
.and((x) => x.content_type?.startsWith('application/') === true)
|
||||
.sortBy('created_on');
|
||||
} else {
|
||||
results = await db_core.file
|
||||
|
||||
@@ -4,9 +4,9 @@
|
||||
import { ae_util } from '$lib/ae_utils/ae_utils';
|
||||
|
||||
// Should these slct_* be exported???
|
||||
let slct_obj_id = $state(null);
|
||||
let slct_obj_li_type = null;
|
||||
let slct_obj_type = $state(null);
|
||||
let slct_obj_id = $state<any>(null);
|
||||
let slct_obj_li_type = '';
|
||||
let slct_obj_type = $state<string | null>(null);
|
||||
|
||||
interface Props {
|
||||
row_header?: boolean;
|
||||
@@ -19,15 +19,13 @@
|
||||
primary_obj_li_type = $bindable(slct_obj_li_type),
|
||||
obj = null
|
||||
}: Props = $props();
|
||||
console.log(obj);
|
||||
console.log(typeof obj);
|
||||
|
||||
onMount(() => {
|
||||
console.log('** Element Mounted: ** Element Object Table Row');
|
||||
|
||||
if (obj) {
|
||||
console.log('Table Row Object:', obj);
|
||||
// console.log(typeof obj);
|
||||
console.log(typeof obj);
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
@@ -82,7 +80,7 @@
|
||||
obj_type_prop_name: obj_prop_name
|
||||
})}/{obj_prop_value}"
|
||||
>
|
||||
{obj_prop_value.substring(0, 25)}
|
||||
{String(obj_prop_value).substring(0, 25)}
|
||||
</a>
|
||||
{:else}
|
||||
<!-- {obj_prop_value} -->
|
||||
@@ -116,7 +114,7 @@
|
||||
slct_obj_id = obj_prop_value;
|
||||
}}
|
||||
>
|
||||
<a href={obj_prop_value}>{obj_prop_value}</a>
|
||||
<a href={String(obj_prop_value)}>{String(obj_prop_value)}</a>
|
||||
</td>
|
||||
{/if}
|
||||
{:else if row_header}
|
||||
@@ -129,7 +127,7 @@
|
||||
{:else}
|
||||
<td data-obj_type={primary_obj_li_type} data-obj_prop_name={obj_prop_name}>
|
||||
{#if obj_prop_value}
|
||||
{#if obj_prop_value && obj_prop_value.length > 25}
|
||||
{#if typeof obj_prop_value === 'string' && obj_prop_value.length > 25}
|
||||
{obj_prop_value.substring(0, 25)} ...
|
||||
{:else}
|
||||
{obj_prop_value}
|
||||
|
||||
@@ -54,7 +54,7 @@
|
||||
|
||||
// const dispatch = createEventDispatcher();
|
||||
|
||||
async function handle_run_sql(qry, data, as_list = false, log_lvl = 0) {
|
||||
async function handle_run_sql(qry: string, data: any, as_list = false, log_lvl = 0) {
|
||||
console.log('*** handle_run_sql() ***');
|
||||
|
||||
let sql_qry_data: key_val = {};
|
||||
|
||||
@@ -60,7 +60,13 @@
|
||||
// const dispatch = createEventDispatcher();
|
||||
|
||||
// JSON formatted data
|
||||
let ws_data = $state({
|
||||
let ws_data: {
|
||||
client_id: string | null;
|
||||
target: string;
|
||||
type: string;
|
||||
msg: string | null;
|
||||
cmd: string | null;
|
||||
} = $state({
|
||||
client_id: null, // The device or browser ID if available.
|
||||
// 'src': null, // Sending client
|
||||
// 'account_id': null, // Essentially the person ID or user ID if available.
|
||||
@@ -74,7 +80,7 @@
|
||||
// 'b64': null,
|
||||
});
|
||||
|
||||
let ws_received_list_cmd: string[] = $state([]);
|
||||
let ws_received_list_cmd: any[] = $state([]);
|
||||
let ws_received_list_other: any[] = $state([]);
|
||||
let ws_received_list_msg: string[] = [];
|
||||
|
||||
@@ -84,7 +90,7 @@
|
||||
|
||||
// *** Functions and Logic
|
||||
|
||||
function ws_connect_group_id({ group_id, client_id }) {
|
||||
function ws_connect_group_id({ group_id, client_id }: { group_id: string, client_id: any }) {
|
||||
if (!group_id) {
|
||||
group_id = 'ae-grp-99';
|
||||
console.log(
|
||||
|
||||
@@ -251,6 +251,7 @@ export interface ae_Person extends ae_BaseObj {
|
||||
designations?: string;
|
||||
|
||||
full_name?: string;
|
||||
last_first_name?: string;
|
||||
informal_name?: string;
|
||||
preferred_display_name?: string;
|
||||
|
||||
@@ -503,6 +504,7 @@ export interface ae_EventBadge extends ae_BaseObj {
|
||||
|
||||
ticket_list?: any[] | null;
|
||||
data_json?: any;
|
||||
default_qry_str?: string | null;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -576,6 +578,7 @@ export interface ae_EventSession extends ae_BaseObj {
|
||||
ready?: boolean | null;
|
||||
|
||||
data_json?: any;
|
||||
default_qry_str?: string | null;
|
||||
|
||||
// Additional database view fields found in db_events.ts
|
||||
file_count?: number | null;
|
||||
@@ -836,12 +839,12 @@ export interface ae_ArchiveContent extends ae_BaseObj {
|
||||
*/
|
||||
export interface ae_EventFile extends ae_BaseObj {
|
||||
event_file_id: string;
|
||||
event_file_id_random: string;
|
||||
event_file_id_random?: string;
|
||||
event_id: string;
|
||||
event_id_random: string;
|
||||
event_id_random?: string;
|
||||
|
||||
hosted_file_id: string;
|
||||
hosted_file_id_random: string;
|
||||
hosted_file_id_random?: string;
|
||||
|
||||
for_type?: string | null;
|
||||
for_id_random?: string | null;
|
||||
@@ -949,8 +952,11 @@ export interface ae_EventExhibitTracking extends ae_BaseObj {
|
||||
event_person_id_random?: string;
|
||||
event_badge_id_random?: string;
|
||||
|
||||
exhibitor_notes?: string;
|
||||
external_person_id?: string | null;
|
||||
|
||||
exhibitor_notes?: string | null;
|
||||
responses_json?: any;
|
||||
default_qry_str?: string | null;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
Reference in New Issue
Block a user