Last round of prettier: npx prettier --write src/
This commit is contained in:
@@ -1,188 +1,191 @@
|
||||
<script lang="ts">
|
||||
// Imports
|
||||
// Import components and elements
|
||||
// import Element_input_files_tbl from '$lib/element_input_files_tbl.svelte';
|
||||
// Imports
|
||||
// Import components and elements
|
||||
// import Element_input_files_tbl from '$lib/element_input_files_tbl.svelte';
|
||||
|
||||
// Import storage, functions, and libraries
|
||||
import type { key_val } from '$lib/stores/ae_stores';
|
||||
// Import storage, functions, and libraries
|
||||
import type { key_val } from '$lib/stores/ae_stores';
|
||||
|
||||
import { api } from '$lib/api/api';
|
||||
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 { api } from '$lib/api/api';
|
||||
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 AE_Comp_Hosted_Files_Download_Button from '$lib/ae_core/ae_comp__hosted_files_download_button.svelte';
|
||||
import { Check, Download, LoaderCircle, MinusCircle, Scissors } from '@lucide/svelte';
|
||||
// Exports
|
||||
import AE_Comp_Hosted_Files_Download_Button from '$lib/ae_core/ae_comp__hosted_files_download_button.svelte';
|
||||
import {
|
||||
Check,
|
||||
Download,
|
||||
LoaderCircle,
|
||||
MinusCircle,
|
||||
Scissors
|
||||
} from '@lucide/svelte';
|
||||
// Exports
|
||||
|
||||
// export let input_name = 'file_list';
|
||||
// export let multiple: boolean = true;
|
||||
// export let required: boolean = true;
|
||||
// export let input_name = 'file_list';
|
||||
// export let multiple: boolean = true;
|
||||
// export let required: boolean = true;
|
||||
|
||||
// export let input_class_li: string[] = ['file_drop_area'];
|
||||
// export let input_class_li: string[] = ['file_drop_area'];
|
||||
|
||||
interface Props {
|
||||
log_lvl?: number;
|
||||
// Expecting these for link_to_type: 'event', 'event_location', 'archive_content', etc
|
||||
link_to_type: string;
|
||||
link_to_id: string;
|
||||
// export let accept: string = '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?: string;
|
||||
class_li?: string;
|
||||
// export let table_class_li: string[] = ['table', 'table-sm', 'table-striped', 'table-hover' , 'text-sm'];
|
||||
clip_complete?: boolean;
|
||||
// export let upload_complete: boolean = false;
|
||||
submit_status?: null | string;
|
||||
// hosted_file_id_li?: string[];
|
||||
// hosted_file_obj_li?: any[];
|
||||
hosted_file_obj_kv?: key_val;
|
||||
video_clip_file_kv?: key_val;
|
||||
}
|
||||
interface Props {
|
||||
log_lvl?: number;
|
||||
// Expecting these for link_to_type: 'event', 'event_location', 'archive_content', etc
|
||||
link_to_type: string;
|
||||
link_to_id: string;
|
||||
// export let accept: string = '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?: string;
|
||||
class_li?: string;
|
||||
// export let table_class_li: string[] = ['table', 'table-sm', 'table-striped', 'table-hover' , 'text-sm'];
|
||||
clip_complete?: boolean;
|
||||
// export let upload_complete: boolean = false;
|
||||
submit_status?: null | string;
|
||||
// hosted_file_id_li?: string[];
|
||||
// hosted_file_obj_li?: any[];
|
||||
hosted_file_obj_kv?: key_val;
|
||||
video_clip_file_kv?: key_val;
|
||||
}
|
||||
|
||||
let {
|
||||
log_lvl = $bindable(0),
|
||||
link_to_type = $bindable(),
|
||||
link_to_id = $bindable(),
|
||||
class_li_default = 'flex flex-col gap-1 items-center justify-center w-full max-w-2xl mx-auto my-1',
|
||||
class_li = $bindable(''),
|
||||
clip_complete = $bindable(false),
|
||||
submit_status = $bindable(null),
|
||||
// hosted_file_id_li = [],
|
||||
// hosted_file_obj_li = [],
|
||||
hosted_file_obj_kv = $bindable({}),
|
||||
video_clip_file_kv = $bindable({})
|
||||
}: Props = $props();
|
||||
let {
|
||||
log_lvl = $bindable(0),
|
||||
link_to_type = $bindable(),
|
||||
link_to_id = $bindable(),
|
||||
class_li_default = 'flex flex-col gap-1 items-center justify-center w-full max-w-2xl mx-auto my-1',
|
||||
class_li = $bindable(''),
|
||||
clip_complete = $bindable(false),
|
||||
submit_status = $bindable(null),
|
||||
// hosted_file_id_li = [],
|
||||
// hosted_file_obj_li = [],
|
||||
hosted_file_obj_kv = $bindable({}),
|
||||
video_clip_file_kv = $bindable({})
|
||||
}: Props = $props();
|
||||
|
||||
// Local Variables
|
||||
let task_id = link_to_id;
|
||||
// let input_file_list: any = null;
|
||||
let ae_promises: key_val = $state({});
|
||||
// let ae_promises_clipping: key_val = {};
|
||||
// let ae_triggers: key_val = {};
|
||||
// Local Variables
|
||||
let task_id = link_to_id;
|
||||
// let input_file_list: any = null;
|
||||
let ae_promises: key_val = $state({});
|
||||
// let ae_promises_clipping: key_val = {};
|
||||
// let ae_triggers: key_val = {};
|
||||
|
||||
// let input_element_id = 'ae_comp__hosted_files_upload__input';
|
||||
// let input_element_id = 'ae_comp__hosted_files_upload__input';
|
||||
|
||||
// let form_kv: key_val = {
|
||||
// start_time: null,
|
||||
// end_time: null,
|
||||
// reencode: null,
|
||||
// video_file: null,
|
||||
// };
|
||||
// let download_clip_src: string;
|
||||
// let download_clip_filename: string;
|
||||
// let form_kv: key_val = {
|
||||
// start_time: null,
|
||||
// end_time: null,
|
||||
// reencode: null,
|
||||
// video_file: null,
|
||||
// };
|
||||
// let download_clip_src: string;
|
||||
// let download_clip_filename: string;
|
||||
|
||||
$ae_sess.files.obj = {
|
||||
obj: null
|
||||
$ae_sess.files.obj = {
|
||||
obj: null
|
||||
};
|
||||
|
||||
// *** Functions and Logic
|
||||
function prevent_default<T extends Event>(fn: (event: T) => void) {
|
||||
return function (event: T) {
|
||||
event.preventDefault();
|
||||
fn(event);
|
||||
};
|
||||
}
|
||||
|
||||
function handle_clip_video(event: Event) {
|
||||
console.log('*** handle_clip_video() ***');
|
||||
|
||||
submit_status = 'clipping';
|
||||
clip_complete = false;
|
||||
|
||||
const form = event.target as HTMLFormElement;
|
||||
const formData = new FormData(form);
|
||||
|
||||
let hosted_file_id = formData.get('hosted_file_id') as string;
|
||||
let start_time = formData.get('start_time') as string;
|
||||
let end_time = formData.get('end_time') as string;
|
||||
let reencode = formData.get('reencode') as string;
|
||||
let scale_down = formData.get('scale_down') as string;
|
||||
let new_filename = formData.get('new_filename') as string;
|
||||
|
||||
$ae_sess.files.processed_file_kv[hosted_file_id] = {};
|
||||
$ae_sess.files.processed_file_kv[hosted_file_id].submit_status = 'clipping';
|
||||
$ae_sess.files.processed_file_kv[hosted_file_id].clip_complete = false;
|
||||
|
||||
// $ae_sess.files.disable_submit__hosted_file_obj = true;
|
||||
$ae_loc.files.processed_file_kv[hosted_file_id] = {};
|
||||
$ae_loc.files.processed_file_kv[hosted_file_id].submit_status = 'clipping';
|
||||
$ae_loc.files.processed_file_kv[hosted_file_id].start_time = start_time;
|
||||
$ae_loc.files.processed_file_kv[hosted_file_id].end_time = end_time;
|
||||
$ae_loc.files.processed_file_kv[hosted_file_id].reencode = reencode;
|
||||
$ae_loc.files.processed_file_kv[hosted_file_id].scale_down = scale_down;
|
||||
$ae_loc.files.processed_file_kv[hosted_file_id].new_filename = new_filename;
|
||||
$ae_loc.files.processed_file_kv[hosted_file_id].clip_complete = false;
|
||||
|
||||
let endpoint = `/hosted_file/${hosted_file_id}/clip_video`;
|
||||
|
||||
let params = {
|
||||
link_to_type: link_to_type,
|
||||
link_to_id: link_to_id,
|
||||
filename_no_ext: new_filename.replace('.mp4', ''),
|
||||
from_type: 'mp4', // Video file type being converted
|
||||
to_type: 'mp4', // Video file type to convert to
|
||||
start_time: start_time,
|
||||
end_time: end_time,
|
||||
reencode: reencode,
|
||||
scale_down: scale_down
|
||||
};
|
||||
|
||||
// *** Functions and Logic
|
||||
function prevent_default<T extends Event>(fn: (event: T) => void) {
|
||||
return function (event: T) {
|
||||
event.preventDefault();
|
||||
fn(event);
|
||||
};
|
||||
}
|
||||
ae_promises[hosted_file_id] = {};
|
||||
// .convert__hosted_file_obj
|
||||
ae_promises[hosted_file_id] = api
|
||||
.get_object({
|
||||
api_cfg: $ae_api,
|
||||
endpoint: endpoint,
|
||||
params: params,
|
||||
timeout: 300000, // 5 minutes
|
||||
// return_blob: true,
|
||||
// filename: event.target.new_filename.value,
|
||||
// auto_download: false,
|
||||
task_id: task_id,
|
||||
log_lvl: log_lvl
|
||||
})
|
||||
.then(function (result) {
|
||||
console.log(result);
|
||||
|
||||
function handle_clip_video(event: Event) {
|
||||
console.log('*** handle_clip_video() ***');
|
||||
video_clip_file_kv[result.hosted_file_id] = {};
|
||||
video_clip_file_kv[result.hosted_file_id] = result;
|
||||
|
||||
submit_status = 'clipping';
|
||||
clip_complete = false;
|
||||
// $ae_loc.files.video_clip_file_kv[result.hosted_file_id] = {};
|
||||
// $ae_loc.files.video_clip_file_kv[result.hosted_file_id] = result;
|
||||
|
||||
const form = event.target as HTMLFormElement;
|
||||
const formData = new FormData(form);
|
||||
$ae_sess.files.processed_file_kv[hosted_file_id].submit_status =
|
||||
'clipped';
|
||||
$ae_sess.files.processed_file_kv[hosted_file_id].clip_complete =
|
||||
true;
|
||||
|
||||
let hosted_file_id = formData.get('hosted_file_id') as string;
|
||||
let start_time = formData.get('start_time') as string;
|
||||
let end_time = formData.get('end_time') as string;
|
||||
let reencode = formData.get('reencode') as string;
|
||||
let scale_down = formData.get('scale_down') as string;
|
||||
let new_filename = formData.get('new_filename') as string;
|
||||
$ae_loc.files.processed_file_kv[hosted_file_id].submit_status =
|
||||
'clipped';
|
||||
$ae_loc.files.processed_file_kv[hosted_file_id].clip_complete =
|
||||
true;
|
||||
|
||||
$ae_sess.files.processed_file_kv[hosted_file_id] = {};
|
||||
$ae_sess.files.processed_file_kv[hosted_file_id].submit_status =
|
||||
'clipping';
|
||||
$ae_sess.files.processed_file_kv[hosted_file_id].clip_complete = false;
|
||||
submit_status = 'clipped';
|
||||
clip_complete = true;
|
||||
|
||||
// $ae_sess.files.disable_submit__hosted_file_obj = true;
|
||||
$ae_loc.files.processed_file_kv[hosted_file_id] = {};
|
||||
$ae_loc.files.processed_file_kv[hosted_file_id].submit_status =
|
||||
'clipping';
|
||||
$ae_loc.files.processed_file_kv[hosted_file_id].start_time = start_time;
|
||||
$ae_loc.files.processed_file_kv[hosted_file_id].end_time = end_time;
|
||||
$ae_loc.files.processed_file_kv[hosted_file_id].reencode = reencode;
|
||||
$ae_loc.files.processed_file_kv[hosted_file_id].scale_down = scale_down;
|
||||
$ae_loc.files.processed_file_kv[hosted_file_id].new_filename =
|
||||
new_filename;
|
||||
$ae_loc.files.processed_file_kv[hosted_file_id].clip_complete = false;
|
||||
// let file_blob = new Blob([result.data]);
|
||||
// // console.log(file_blob);
|
||||
// let file_obj_url = window.URL.createObjectURL(file_blob); // The img src
|
||||
// // const url = window.URL.createObjectURL(new Blob([result.data]));
|
||||
// download_clip_src = file_obj_url;
|
||||
// // download_filename = file_obj_url;
|
||||
|
||||
let endpoint = `/hosted_file/${hosted_file_id}/clip_video`;
|
||||
|
||||
let params = {
|
||||
link_to_type: link_to_type,
|
||||
link_to_id: link_to_id,
|
||||
filename_no_ext: new_filename.replace('.mp4', ''),
|
||||
from_type: 'mp4', // Video file type being converted
|
||||
to_type: 'mp4', // Video file type to convert to
|
||||
start_time: start_time,
|
||||
end_time: end_time,
|
||||
reencode: reencode,
|
||||
scale_down: scale_down
|
||||
};
|
||||
|
||||
ae_promises[hosted_file_id] = {};
|
||||
// .convert__hosted_file_obj
|
||||
ae_promises[hosted_file_id] = api
|
||||
.get_object({
|
||||
api_cfg: $ae_api,
|
||||
endpoint: endpoint,
|
||||
params: params,
|
||||
timeout: 300000, // 5 minutes
|
||||
// return_blob: true,
|
||||
// filename: event.target.new_filename.value,
|
||||
// auto_download: false,
|
||||
task_id: task_id,
|
||||
log_lvl: log_lvl
|
||||
})
|
||||
.then(function (result) {
|
||||
console.log(result);
|
||||
|
||||
video_clip_file_kv[result.hosted_file_id] = {};
|
||||
video_clip_file_kv[result.hosted_file_id] = result;
|
||||
|
||||
// $ae_loc.files.video_clip_file_kv[result.hosted_file_id] = {};
|
||||
// $ae_loc.files.video_clip_file_kv[result.hosted_file_id] = result;
|
||||
|
||||
$ae_sess.files.processed_file_kv[hosted_file_id].submit_status =
|
||||
'clipped';
|
||||
$ae_sess.files.processed_file_kv[hosted_file_id].clip_complete =
|
||||
true;
|
||||
|
||||
$ae_loc.files.processed_file_kv[hosted_file_id].submit_status =
|
||||
'clipped';
|
||||
$ae_loc.files.processed_file_kv[hosted_file_id].clip_complete =
|
||||
true;
|
||||
|
||||
submit_status = 'clipped';
|
||||
clip_complete = true;
|
||||
|
||||
// let file_blob = new Blob([result.data]);
|
||||
// // console.log(file_blob);
|
||||
// let file_obj_url = window.URL.createObjectURL(file_blob); // The img src
|
||||
// // const url = window.URL.createObjectURL(new Blob([result.data]));
|
||||
// download_clip_src = file_obj_url;
|
||||
// // download_filename = file_obj_url;
|
||||
|
||||
return true;
|
||||
});
|
||||
}
|
||||
return true;
|
||||
});
|
||||
}
|
||||
</script>
|
||||
|
||||
<section class="{class_li_default} {class_li}">
|
||||
@@ -191,11 +194,11 @@
|
||||
</h3>
|
||||
|
||||
{#each Object.entries(hosted_file_obj_kv) as [hosted_file_id, hosted_file_obj] (hosted_file_id)}
|
||||
<div class="border border-surface-500/20 rounded-lg p-2 m-2 preset-tonal-surface">
|
||||
<div
|
||||
class="border-surface-500/20 preset-tonal-surface m-2 rounded-lg border p-2">
|
||||
<!-- Download Button (Standardized) -->
|
||||
<div
|
||||
class="flex flex-row flex-wrap gap-1 justify-center items-center w-full"
|
||||
>
|
||||
class="flex w-full flex-row flex-wrap items-center justify-center gap-1">
|
||||
<!-- Remove from uploaded file kv list -->
|
||||
<button
|
||||
type="button"
|
||||
@@ -219,8 +222,7 @@
|
||||
);
|
||||
}}
|
||||
class="btn btn-sm preset-tonal-warning hover:preset-filled-warning-500"
|
||||
title={`Remove this file from list of videos:\n${hosted_file_obj.filename}\n[API] SHA256: ${hosted_file_obj?.hash_sha256?.slice(0, 10)}... Hosted ID: ${hosted_file_obj.hosted_file_id}`}
|
||||
>
|
||||
title={`Remove this file from list of videos:\n${hosted_file_obj.filename}\n[API] SHA256: ${hosted_file_obj?.hash_sha256?.slice(0, 10)}... Hosted ID: ${hosted_file_obj.hosted_file_id}`}>
|
||||
<MinusCircle size="1em" class="m-1" />
|
||||
<span class="">Remove</span>
|
||||
</button>
|
||||
@@ -234,60 +236,49 @@
|
||||
variant="tonal"
|
||||
classes="novi_btn btn-sm lg:btn-md min-w-72 lg:min-w-96 !justify-start"
|
||||
show_divider={true}
|
||||
max_filename={30}
|
||||
/>
|
||||
max_filename={30} />
|
||||
</div>
|
||||
<span
|
||||
>{ae_util.shorten_filename({
|
||||
filename: hosted_file_obj?.filename,
|
||||
max_length: 30
|
||||
})}</span
|
||||
>
|
||||
})}</span>
|
||||
<span>
|
||||
<span class="text-sm font-bold"> File ID: </span>
|
||||
{hosted_file_obj.hosted_file_id}</span
|
||||
>
|
||||
{hosted_file_obj.hosted_file_id}</span>
|
||||
<span>
|
||||
<span class="text-sm font-bold"> Type: </span>
|
||||
{hosted_file_obj.extension}</span
|
||||
>
|
||||
{hosted_file_obj.extension}</span>
|
||||
<!-- <span>{hosted_file_obj.filename}</span> -->
|
||||
</div>
|
||||
|
||||
<form
|
||||
onsubmit={prevent_default(handle_clip_video)}
|
||||
class="{class_li_default} {class_li}"
|
||||
>
|
||||
class="{class_li_default} {class_li}">
|
||||
<!-- {$ae_sess?.files[hosted_file_obj?.hosted_file_id ?? 'obj'].submit_status ?? 'not set'} -->
|
||||
|
||||
<input
|
||||
type="hidden"
|
||||
name="hosted_file_id"
|
||||
value={hosted_file_obj.hosted_file_id}
|
||||
/>
|
||||
value={hosted_file_obj.hosted_file_id} />
|
||||
|
||||
<div
|
||||
class="flex flex-row gap-1 justify-center items-center w-full"
|
||||
>
|
||||
<span class="text-xs font-bold w-32">New Filename:</span>
|
||||
class="flex w-full flex-row items-center justify-center gap-1">
|
||||
<span class="w-32 text-xs font-bold">New Filename:</span>
|
||||
<input
|
||||
type="text"
|
||||
class="input w-full text-sm variant-filled-surface"
|
||||
class="input variant-filled-surface w-full text-sm"
|
||||
name="new_filename"
|
||||
value={hosted_file_obj.filename}
|
||||
/>
|
||||
value={hosted_file_obj.filename} />
|
||||
</div>
|
||||
|
||||
<div
|
||||
class="max-w-(--breakpoint-sm) flex flex-row gap-1 justify-center items-center w-full"
|
||||
>
|
||||
class="flex w-full max-w-(--breakpoint-sm) flex-row items-center justify-center gap-1">
|
||||
<label
|
||||
class="label w-48"
|
||||
title="The start time of the clip. This is the time in the video where the clip will start. You may need to subtract a few seconds to get the exact start time."
|
||||
>
|
||||
title="The start time of the clip. This is the time in the video where the clip will start. You may need to subtract a few seconds to get the exact start time.">
|
||||
<span class="text-xs font-bold"
|
||||
>Start time (HH:MM:SS)</span
|
||||
>
|
||||
>Start time (HH:MM:SS)</span>
|
||||
<input
|
||||
type="text"
|
||||
name="start_time"
|
||||
@@ -300,17 +291,14 @@
|
||||
].start_time
|
||||
: '00:00:00'}
|
||||
placeholder="HH:MM:SS (00:01:30)"
|
||||
class="input w-32 variant-filled-surface"
|
||||
/>
|
||||
class="input variant-filled-surface w-32" />
|
||||
</label>
|
||||
|
||||
<label
|
||||
class="label w-48"
|
||||
title="The end time of the clip. This is the time in the video where the clip will end. You may need to add a few seconds to get the exact end time."
|
||||
>
|
||||
title="The end time of the clip. This is the time in the video where the clip will end. You may need to add a few seconds to get the exact end time.">
|
||||
<span class="text-xs font-bold"
|
||||
>End time (HH:MM:SS)</span
|
||||
>
|
||||
>End time (HH:MM:SS)</span>
|
||||
<input
|
||||
type="text"
|
||||
name="end_time"
|
||||
@@ -323,14 +311,12 @@
|
||||
].end_time
|
||||
: '00:45:59'}
|
||||
placeholder="HH:MM:SS (01:05:25)"
|
||||
class="input w-32 variant-filled-surface"
|
||||
/>
|
||||
class="input variant-filled-surface w-32" />
|
||||
</label>
|
||||
|
||||
<span
|
||||
class="flex flex-col gap-1 items-center justify-center"
|
||||
title="Re-encode the video file? This does cause some minor quality loss. Re-encoding is useful if the audio or video seems to be chopped off at the beginning or end of the clip. It can also help with partially corrupted files."
|
||||
>
|
||||
class="flex flex-col items-center justify-center gap-1"
|
||||
title="Re-encode the video file? This does cause some minor quality loss. Re-encoding is useful if the audio or video seems to be chopped off at the beginning or end of the clip. It can also help with partially corrupted files.">
|
||||
<span class="text-xs font-bold"> Re-encode? </span>
|
||||
<label class="inline-block">
|
||||
<input
|
||||
@@ -338,8 +324,7 @@
|
||||
name="reencode"
|
||||
value="true"
|
||||
class="radio"
|
||||
checked
|
||||
/>
|
||||
checked />
|
||||
True
|
||||
</label>
|
||||
<label class="inline-block">
|
||||
@@ -347,16 +332,14 @@
|
||||
type="radio"
|
||||
name="reencode"
|
||||
value="false"
|
||||
class="radio"
|
||||
/>
|
||||
class="radio" />
|
||||
False
|
||||
</label>
|
||||
</span>
|
||||
|
||||
<span
|
||||
class="flex flex-col gap-1 items-center justify-center"
|
||||
title="Scale the video file down to 1920x1080? This does cause some minor quality loss. Re-encoding is useful if the audio or video seems to be chopped off at the beginning or end of the clip. It can also help with partially corrupted files."
|
||||
>
|
||||
class="flex flex-col items-center justify-center gap-1"
|
||||
title="Scale the video file down to 1920x1080? This does cause some minor quality loss. Re-encoding is useful if the audio or video seems to be chopped off at the beginning or end of the clip. It can also help with partially corrupted files.">
|
||||
<span class="text-xs font-bold"> Scale down? </span>
|
||||
<label class="inline-block">
|
||||
<input
|
||||
@@ -364,8 +347,7 @@
|
||||
name="scale_down"
|
||||
value="true"
|
||||
class="radio"
|
||||
checked
|
||||
/>
|
||||
checked />
|
||||
True
|
||||
</label>
|
||||
<label class="inline-block">
|
||||
@@ -373,8 +355,7 @@
|
||||
type="radio"
|
||||
name="scale_down"
|
||||
value="false"
|
||||
class="radio"
|
||||
/>
|
||||
class="radio" />
|
||||
False
|
||||
</label>
|
||||
</span>
|
||||
@@ -382,9 +363,8 @@
|
||||
|
||||
<button
|
||||
type="submit"
|
||||
class="btn btn-lg btn-primary preset-tonal-primary border border-primary-500 hover:preset-filled-primary-500 transition-colors"
|
||||
disabled={submit_status == 'clipping'}
|
||||
>
|
||||
class="btn btn-lg btn-primary preset-tonal-primary border-primary-500 hover:preset-filled-primary-500 border transition-colors"
|
||||
disabled={submit_status == 'clipping'}>
|
||||
<!-- {#await ae_promises[hosted_file_id]} -->
|
||||
{#if $ae_loc.files.processed_file_kv[hosted_file_id] && $ae_loc.files.processed_file_kv[hosted_file_id].submit_status == 'clipping'}
|
||||
<LoaderCircle size="1em" class="m-1 animate-spin" />
|
||||
@@ -407,8 +387,7 @@
|
||||
{#await ae_promises[hosted_file_id]}
|
||||
<LoaderCircle size="1em" class="m-1 animate-spin" />
|
||||
<span class="highlight"
|
||||
>Processing... This may take a few minutes.</span
|
||||
>
|
||||
>Processing... This may take a few minutes.</span>
|
||||
{:then}
|
||||
{#if ae_promises[hosted_file_id]}
|
||||
<Download size="1em" /> Ready to download below!
|
||||
|
||||
@@ -1,48 +1,48 @@
|
||||
<script lang="ts">
|
||||
// Imports
|
||||
// Import components and elements
|
||||
import AE_Comp_Hosted_Files_Download_Button from '$lib/ae_core/ae_comp__hosted_files_download_button.svelte';
|
||||
// Imports
|
||||
// Import components and elements
|
||||
import AE_Comp_Hosted_Files_Download_Button from '$lib/ae_core/ae_comp__hosted_files_download_button.svelte';
|
||||
|
||||
// Import storage, functions, and libraries
|
||||
import type { key_val } from '$lib/stores/ae_stores';
|
||||
// Import storage, functions, and libraries
|
||||
import type { key_val } from '$lib/stores/ae_stores';
|
||||
|
||||
import { api } from '$lib/api/api';
|
||||
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 { api } from '$lib/api/api';
|
||||
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';
|
||||
|
||||
// Exports
|
||||
// Exports
|
||||
|
||||
// export let hosted_file_id_li: string[] = [];
|
||||
// export let hosted_file_obj_li: any[] = [];
|
||||
// export let hosted_file_id_li: string[] = [];
|
||||
// export let hosted_file_obj_li: any[] = [];
|
||||
|
||||
interface Props {
|
||||
log_lvl?: number;
|
||||
// export let hosted_file_obj_kv: key_val = {};
|
||||
video_clip_file_kv?: key_val;
|
||||
class_li_default?: string;
|
||||
class_li?: string;
|
||||
link_to_type: string;
|
||||
link_to_id: string;
|
||||
}
|
||||
interface Props {
|
||||
log_lvl?: number;
|
||||
// export let hosted_file_obj_kv: key_val = {};
|
||||
video_clip_file_kv?: key_val;
|
||||
class_li_default?: string;
|
||||
class_li?: string;
|
||||
link_to_type: string;
|
||||
link_to_id: string;
|
||||
}
|
||||
|
||||
let {
|
||||
log_lvl = 0,
|
||||
video_clip_file_kv = $bindable({}),
|
||||
class_li_default = 'flex flex-row flex-wrap gap-2 items-center justify-center w-full max-w-2xl p-2 mx-auto my-1 border border-surface-500/20 rounded-lg preset-tonal-surface',
|
||||
class_li = '',
|
||||
link_to_type,
|
||||
link_to_id
|
||||
}: Props = $props();
|
||||
let {
|
||||
log_lvl = 0,
|
||||
video_clip_file_kv = $bindable({}),
|
||||
class_li_default = 'flex flex-row flex-wrap gap-2 items-center justify-center w-full max-w-2xl p-2 mx-auto my-1 border border-surface-500/20 rounded-lg preset-tonal-surface',
|
||||
class_li = '',
|
||||
link_to_type,
|
||||
link_to_id
|
||||
}: Props = $props();
|
||||
|
||||
let ae_promises: key_val = $state({});
|
||||
let ae_promises: key_val = $state({});
|
||||
</script>
|
||||
|
||||
<h3 class="h3">{Object.entries(video_clip_file_kv).length}× files clipped</h3>
|
||||
@@ -54,7 +54,6 @@
|
||||
max_filename={30}
|
||||
classes="btn btn-sm lg:btn-md preset-tonal-primary hover:preset-filled-primary-500 min-w-72 lg:min-w-96"
|
||||
linked_to_type={link_to_type}
|
||||
linked_to_id={link_to_id}
|
||||
/>
|
||||
linked_to_id={link_to_id} />
|
||||
{/each}
|
||||
</div>
|
||||
|
||||
@@ -1,243 +1,275 @@
|
||||
<script lang="ts">
|
||||
// *** Import Svelte specific
|
||||
import * as Lucide from 'lucide-svelte';
|
||||
import { fade } from 'svelte/transition';
|
||||
// *** Import Svelte specific
|
||||
import * as Lucide from 'lucide-svelte';
|
||||
import { fade } from 'svelte/transition';
|
||||
|
||||
// *** Import Aether specific variables and functions
|
||||
import type { key_val } from '$lib/stores/ae_stores';
|
||||
import { ae_util } from '$lib/ae_utils/ae_utils';
|
||||
import { download_ae_obj_id__hosted_file } from '$lib/ae_core/core__hosted_files';
|
||||
import {
|
||||
ae_loc,
|
||||
ae_sess,
|
||||
ae_api
|
||||
} from '$lib/stores/ae_stores';
|
||||
// *** Import Aether specific variables and functions
|
||||
import type { key_val } from '$lib/stores/ae_stores';
|
||||
import { ae_util } from '$lib/ae_utils/ae_utils';
|
||||
import { download_ae_obj_id__hosted_file } from '$lib/ae_core/core__hosted_files';
|
||||
import { ae_loc, ae_sess, ae_api } from '$lib/stores/ae_stores';
|
||||
|
||||
interface Props {
|
||||
log_lvl?: number;
|
||||
hosted_file_id: null | string;
|
||||
hosted_file_obj: null | key_val;
|
||||
filename?: null | string;
|
||||
max_filename?: number;
|
||||
auto_download?: boolean;
|
||||
linked_to_type?: null | string;
|
||||
linked_to_id?: null | string;
|
||||
download_complete?: null | boolean;
|
||||
download_percent?: number;
|
||||
download_status_msg?: string;
|
||||
variant?: 'tonal' | 'filled' | 'outline' | 'ghost';
|
||||
color?: 'primary' | 'secondary' | 'tertiary' | 'success' | 'warning' | 'error' | 'surface';
|
||||
show_divider?: boolean;
|
||||
show_direct_download?: boolean;
|
||||
require_auth?: boolean;
|
||||
classes?: string;
|
||||
click?: () => void | Promise<any>;
|
||||
label?: import('svelte').Snippet;
|
||||
interface Props {
|
||||
log_lvl?: number;
|
||||
hosted_file_id: null | string;
|
||||
hosted_file_obj: null | key_val;
|
||||
filename?: null | string;
|
||||
max_filename?: number;
|
||||
auto_download?: boolean;
|
||||
linked_to_type?: null | string;
|
||||
linked_to_id?: null | string;
|
||||
download_complete?: null | boolean;
|
||||
download_percent?: number;
|
||||
download_status_msg?: string;
|
||||
variant?: 'tonal' | 'filled' | 'outline' | 'ghost';
|
||||
color?:
|
||||
| 'primary'
|
||||
| 'secondary'
|
||||
| 'tertiary'
|
||||
| 'success'
|
||||
| 'warning'
|
||||
| 'error'
|
||||
| 'surface';
|
||||
show_divider?: boolean;
|
||||
show_direct_download?: boolean;
|
||||
require_auth?: boolean;
|
||||
classes?: string;
|
||||
click?: () => void | Promise<any>;
|
||||
label?: import('svelte').Snippet;
|
||||
}
|
||||
|
||||
let {
|
||||
log_lvl = 0,
|
||||
hosted_file_id,
|
||||
hosted_file_obj,
|
||||
filename = $bindable(null),
|
||||
max_filename = $bindable(30),
|
||||
auto_download = true,
|
||||
linked_to_type = $bindable(null),
|
||||
linked_to_id = $bindable(null),
|
||||
download_complete = $bindable(),
|
||||
download_percent = $bindable(),
|
||||
download_status_msg = $bindable('Not started'),
|
||||
variant = 'tonal',
|
||||
color = 'primary',
|
||||
show_divider = true,
|
||||
show_direct_download = false,
|
||||
require_auth = true,
|
||||
classes = '',
|
||||
click,
|
||||
label
|
||||
}: Props = $props();
|
||||
|
||||
// Map variant/color to classes using literal strings so Tailwind can find them
|
||||
const color_map: Record<string, Record<string, string>> = {
|
||||
primary: {
|
||||
tonal: 'preset-tonal-primary border border-primary-500/30 hover:preset-filled-primary-500',
|
||||
filled: 'preset-filled-primary-500 hover:preset-filled-primary-600',
|
||||
outline: 'border border-primary-500 hover:preset-tonal-primary',
|
||||
ghost: 'hover:preset-tonal-primary'
|
||||
},
|
||||
secondary: {
|
||||
tonal: 'preset-tonal-secondary border border-secondary-500/30 hover:preset-filled-secondary-500',
|
||||
filled: 'preset-filled-secondary-500 hover:preset-filled-secondary-600',
|
||||
outline: 'border border-secondary-500 hover:preset-tonal-secondary',
|
||||
ghost: 'hover:preset-tonal-secondary'
|
||||
},
|
||||
tertiary: {
|
||||
tonal: 'preset-tonal-tertiary border border-tertiary-500/30 hover:preset-filled-tertiary-500',
|
||||
filled: 'preset-filled-tertiary-500 hover:preset-filled-tertiary-600',
|
||||
outline: 'border border-tertiary-500 hover:preset-tonal-tertiary',
|
||||
ghost: 'hover:preset-tonal-tertiary'
|
||||
},
|
||||
success: {
|
||||
tonal: 'preset-tonal-success border border-success-500/30 hover:preset-filled-success-500',
|
||||
filled: 'preset-filled-success-500 hover:preset-filled-success-600',
|
||||
outline: 'border border-success-500 hover:preset-tonal-success',
|
||||
ghost: 'hover:preset-tonal-success'
|
||||
},
|
||||
warning: {
|
||||
tonal: 'preset-tonal-warning border border-warning-500/30 hover:preset-filled-warning-500',
|
||||
filled: 'preset-filled-warning-500 hover:preset-filled-warning-600',
|
||||
outline: 'border border-warning-500 hover:preset-tonal-warning',
|
||||
ghost: 'hover:preset-tonal-warning'
|
||||
},
|
||||
error: {
|
||||
tonal: 'preset-tonal-error border border-error-500/30 hover:preset-filled-error-500',
|
||||
filled: 'preset-filled-error-500 hover:preset-filled-error-600',
|
||||
outline: 'border border-error-500 hover:preset-tonal-error',
|
||||
ghost: 'hover:preset-tonal-error'
|
||||
},
|
||||
surface: {
|
||||
tonal: 'preset-tonal-surface border border-surface-500/30 hover:preset-filled-surface-500',
|
||||
filled: 'preset-filled-surface-500 hover:preset-filled-surface-600',
|
||||
outline: 'border border-surface-500 hover:preset-tonal-surface',
|
||||
ghost: 'hover:preset-tonal-surface'
|
||||
}
|
||||
};
|
||||
|
||||
let variant_classes = $derived.by(() => {
|
||||
const base =
|
||||
'btn btn-sm lg:btn-md min-w-48 transition-all overflow-hidden px-3';
|
||||
const style = color_map[color]?.[variant] || color_map.primary.tonal;
|
||||
return `${base} ${style} ${classes}`.trim();
|
||||
});
|
||||
|
||||
let show_filename_view = $state(true);
|
||||
let status_interval: any;
|
||||
|
||||
$effect(() => {
|
||||
if (log_lvl) {
|
||||
console.log(
|
||||
`ae_comp__hosted_files_download_button.svelte hosted_file_id=${hosted_file_id}`,
|
||||
hosted_file_obj
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
let ae_promises: key_val = $state({});
|
||||
|
||||
$effect(() => {
|
||||
const file_id =
|
||||
hosted_file_obj?.id ||
|
||||
hosted_file_obj?.hosted_file_id ||
|
||||
hosted_file_id;
|
||||
if (file_id && $ae_sess?.api_download_kv[file_id]?.percent_completed) {
|
||||
download_percent = $ae_sess.api_download_kv[file_id].percent_completed;
|
||||
}
|
||||
});
|
||||
|
||||
// Reactive timer to alternate views during active download
|
||||
$effect(() => {
|
||||
const file_id =
|
||||
hosted_file_obj?.id ||
|
||||
hosted_file_obj?.hosted_file_id ||
|
||||
hosted_file_id;
|
||||
const is_actively_downloading =
|
||||
ae_promises[file_id] && download_complete === undefined;
|
||||
|
||||
if (is_actively_downloading) {
|
||||
if (!status_interval) {
|
||||
status_interval = setInterval(() => {
|
||||
show_filename_view = !show_filename_view;
|
||||
}, 3000);
|
||||
}
|
||||
} else {
|
||||
if (status_interval) {
|
||||
clearInterval(status_interval);
|
||||
status_interval = null;
|
||||
}
|
||||
show_filename_view = true; // Default view when not downloading
|
||||
}
|
||||
|
||||
let {
|
||||
log_lvl = 0,
|
||||
hosted_file_id,
|
||||
hosted_file_obj,
|
||||
filename = $bindable(null),
|
||||
max_filename = $bindable(30),
|
||||
auto_download = true,
|
||||
linked_to_type = $bindable(null),
|
||||
linked_to_id = $bindable(null),
|
||||
download_complete = $bindable(),
|
||||
download_percent = $bindable(),
|
||||
download_status_msg = $bindable('Not started'),
|
||||
variant = 'tonal',
|
||||
color = 'primary',
|
||||
show_divider = true,
|
||||
show_direct_download = false,
|
||||
require_auth = true,
|
||||
classes = '',
|
||||
click,
|
||||
label
|
||||
}: Props = $props();
|
||||
|
||||
// Map variant/color to classes using literal strings so Tailwind can find them
|
||||
const color_map: Record<string, Record<string, string>> = {
|
||||
primary: {
|
||||
tonal: 'preset-tonal-primary border border-primary-500/30 hover:preset-filled-primary-500',
|
||||
filled: 'preset-filled-primary-500 hover:preset-filled-primary-600',
|
||||
outline: 'border border-primary-500 hover:preset-tonal-primary',
|
||||
ghost: 'hover:preset-tonal-primary'
|
||||
},
|
||||
secondary: {
|
||||
tonal: 'preset-tonal-secondary border border-secondary-500/30 hover:preset-filled-secondary-500',
|
||||
filled: 'preset-filled-secondary-500 hover:preset-filled-secondary-600',
|
||||
outline: 'border border-secondary-500 hover:preset-tonal-secondary',
|
||||
ghost: 'hover:preset-tonal-secondary'
|
||||
},
|
||||
tertiary: {
|
||||
tonal: 'preset-tonal-tertiary border border-tertiary-500/30 hover:preset-filled-tertiary-500',
|
||||
filled: 'preset-filled-tertiary-500 hover:preset-filled-tertiary-600',
|
||||
outline: 'border border-tertiary-500 hover:preset-tonal-tertiary',
|
||||
ghost: 'hover:preset-tonal-tertiary'
|
||||
},
|
||||
success: {
|
||||
tonal: 'preset-tonal-success border border-success-500/30 hover:preset-filled-success-500',
|
||||
filled: 'preset-filled-success-500 hover:preset-filled-success-600',
|
||||
outline: 'border border-success-500 hover:preset-tonal-success',
|
||||
ghost: 'hover:preset-tonal-success'
|
||||
},
|
||||
warning: {
|
||||
tonal: 'preset-tonal-warning border border-warning-500/30 hover:preset-filled-warning-500',
|
||||
filled: 'preset-filled-warning-500 hover:preset-filled-warning-600',
|
||||
outline: 'border border-warning-500 hover:preset-tonal-warning',
|
||||
ghost: 'hover:preset-tonal-warning'
|
||||
},
|
||||
error: {
|
||||
tonal: 'preset-tonal-error border border-error-500/30 hover:preset-filled-error-500',
|
||||
filled: 'preset-filled-error-500 hover:preset-filled-error-600',
|
||||
outline: 'border border-error-500 hover:preset-tonal-error',
|
||||
ghost: 'hover:preset-tonal-error'
|
||||
},
|
||||
surface: {
|
||||
tonal: 'preset-tonal-surface border border-surface-500/30 hover:preset-filled-surface-500',
|
||||
filled: 'preset-filled-surface-500 hover:preset-filled-surface-600',
|
||||
outline: 'border border-surface-500 hover:preset-tonal-surface',
|
||||
ghost: 'hover:preset-tonal-surface'
|
||||
return () => {
|
||||
if (status_interval) {
|
||||
clearInterval(status_interval);
|
||||
status_interval = null;
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
let variant_classes = $derived.by(() => {
|
||||
const base = 'btn btn-sm lg:btn-md min-w-48 transition-all overflow-hidden px-3';
|
||||
const style = color_map[color]?.[variant] || color_map.primary.tonal;
|
||||
return `${base} ${style} ${classes}`.trim();
|
||||
});
|
||||
|
||||
let show_filename_view = $state(true);
|
||||
let status_interval: any;
|
||||
|
||||
$effect(() => {
|
||||
if (log_lvl) {
|
||||
console.log(
|
||||
`ae_comp__hosted_files_download_button.svelte hosted_file_id=${hosted_file_id}`,
|
||||
hosted_file_obj
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
let ae_promises: key_val = $state({});
|
||||
|
||||
$effect(() => {
|
||||
const file_id = hosted_file_obj?.id || hosted_file_obj?.hosted_file_id || hosted_file_id;
|
||||
if (file_id && $ae_sess?.api_download_kv[file_id]?.percent_completed) {
|
||||
download_percent =
|
||||
$ae_sess.api_download_kv[file_id].percent_completed;
|
||||
}
|
||||
});
|
||||
|
||||
// Reactive timer to alternate views during active download
|
||||
$effect(() => {
|
||||
const file_id = hosted_file_obj?.id || hosted_file_obj?.hosted_file_id || hosted_file_id;
|
||||
const is_actively_downloading = ae_promises[file_id] && download_complete === undefined;
|
||||
|
||||
if (is_actively_downloading) {
|
||||
if (!status_interval) {
|
||||
status_interval = setInterval(() => {
|
||||
show_filename_view = !show_filename_view;
|
||||
}, 3000);
|
||||
}
|
||||
} else {
|
||||
if (status_interval) {
|
||||
clearInterval(status_interval);
|
||||
status_interval = null;
|
||||
}
|
||||
show_filename_view = true; // Default view when not downloading
|
||||
}
|
||||
|
||||
return () => {
|
||||
if (status_interval) {
|
||||
clearInterval(status_interval);
|
||||
status_interval = null;
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
let final_filename = $derived(filename ?? hosted_file_obj?.filename ?? 'unknown');
|
||||
let shortened_filename = $derived(ae_util.shorten_filename({
|
||||
let final_filename = $derived(
|
||||
filename ?? hosted_file_obj?.filename ?? 'unknown'
|
||||
);
|
||||
let shortened_filename = $derived(
|
||||
ae_util.shorten_filename({
|
||||
filename: final_filename,
|
||||
max_length: max_filename
|
||||
}));
|
||||
})
|
||||
);
|
||||
|
||||
let direct_download_url = $derived.by(() => {
|
||||
if (!show_direct_download || !hosted_file_obj) return '';
|
||||
// IMPORTANT: For Direct Link Mode, we MUST use the V3 Action endpoint to support Random String IDs.
|
||||
// Legacy endpoints often expect integer IDs and will return 404 for string IDs.
|
||||
const file_id = hosted_file_obj.event_file_id || hosted_file_obj.hosted_file_id || hosted_file_id;
|
||||
const obj_type_path = hosted_file_obj.event_file_id ? 'event_file' : 'hosted_file';
|
||||
return `${$ae_api.base_url}/v3/action/${obj_type_path}/${file_id}/download?filename=${ae_util.clean_filename(final_filename)}&key=${$ae_api.account_id}`;
|
||||
});
|
||||
let direct_download_url = $derived.by(() => {
|
||||
if (!show_direct_download || !hosted_file_obj) return '';
|
||||
// IMPORTANT: For Direct Link Mode, we MUST use the V3 Action endpoint to support Random String IDs.
|
||||
// Legacy endpoints often expect integer IDs and will return 404 for string IDs.
|
||||
const file_id =
|
||||
hosted_file_obj.event_file_id ||
|
||||
hosted_file_obj.hosted_file_id ||
|
||||
hosted_file_id;
|
||||
const obj_type_path = hosted_file_obj.event_file_id
|
||||
? 'event_file'
|
||||
: 'hosted_file';
|
||||
return `${$ae_api.base_url}/v3/action/${obj_type_path}/${file_id}/download?filename=${ae_util.clean_filename(final_filename)}&key=${$ae_api.account_id}`;
|
||||
});
|
||||
|
||||
async function handle_click() {
|
||||
const file_id = hosted_file_obj?.id || hosted_file_obj?.hosted_file_id || hosted_file_id;
|
||||
download_complete = undefined;
|
||||
download_status_msg = 'Downloading...';
|
||||
async function handle_click() {
|
||||
const file_id =
|
||||
hosted_file_obj?.id ||
|
||||
hosted_file_obj?.hosted_file_id ||
|
||||
hosted_file_id;
|
||||
download_complete = undefined;
|
||||
download_status_msg = 'Downloading...';
|
||||
|
||||
if (click) {
|
||||
const result = click();
|
||||
// If the override returns a promise, track it so the UI shows progress
|
||||
if (result instanceof Promise) {
|
||||
ae_promises[file_id] = result;
|
||||
}
|
||||
return;
|
||||
if (click) {
|
||||
const result = click();
|
||||
// If the override returns a promise, track it so the UI shows progress
|
||||
if (result instanceof Promise) {
|
||||
ae_promises[file_id] = result;
|
||||
}
|
||||
|
||||
ae_promises[file_id] = download_ae_obj_id__hosted_file({
|
||||
api_cfg: $ae_api,
|
||||
hosted_file_id: file_id,
|
||||
return_file: true,
|
||||
filename: final_filename,
|
||||
auto_download: auto_download,
|
||||
log_lvl: log_lvl
|
||||
})
|
||||
.then((result) => {
|
||||
if (result === null) {
|
||||
console.log('File not found (404)');
|
||||
download_complete = null;
|
||||
download_status_msg = 'File not found';
|
||||
} else if (result === false) {
|
||||
console.log(
|
||||
'Possible error with API server (check network and server status)'
|
||||
);
|
||||
download_complete = false;
|
||||
download_status_msg = 'Failed to download';
|
||||
} else {
|
||||
// console.log('File found and downloaded');
|
||||
download_complete = true;
|
||||
download_status_msg = 'File downloaded';
|
||||
}
|
||||
return result;
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
ae_promises[file_id] = download_ae_obj_id__hosted_file({
|
||||
api_cfg: $ae_api,
|
||||
hosted_file_id: file_id,
|
||||
return_file: true,
|
||||
filename: final_filename,
|
||||
auto_download: auto_download,
|
||||
log_lvl: log_lvl
|
||||
}).then((result) => {
|
||||
if (result === null) {
|
||||
console.log('File not found (404)');
|
||||
download_complete = null;
|
||||
download_status_msg = 'File not found';
|
||||
} else if (result === false) {
|
||||
console.log(
|
||||
'Possible error with API server (check network and server status)'
|
||||
);
|
||||
download_complete = false;
|
||||
download_status_msg = 'Failed to download';
|
||||
} else {
|
||||
// console.log('File found and downloaded');
|
||||
download_complete = true;
|
||||
download_status_msg = 'File downloaded';
|
||||
}
|
||||
return result;
|
||||
});
|
||||
}
|
||||
</script>
|
||||
|
||||
{#snippet content()}
|
||||
{@const file_id = hosted_file_obj?.id || hosted_file_obj?.hosted_file_id || hosted_file_id}
|
||||
{@const file_id =
|
||||
hosted_file_obj?.id ||
|
||||
hosted_file_obj?.hosted_file_id ||
|
||||
hosted_file_id}
|
||||
{#await ae_promises[file_id]}
|
||||
<div class="flex items-center w-full min-h-[1.5rem]">
|
||||
<div class="flex min-h-[1.5rem] w-full items-center">
|
||||
<div
|
||||
class="flex items-center pr-2 shrink-0 {show_divider ? 'border-r border-surface-500/30 mr-2' : ''}"
|
||||
>
|
||||
class="flex shrink-0 items-center pr-2 {show_divider
|
||||
? 'border-surface-500/30 mr-2 border-r'
|
||||
: ''}">
|
||||
<Lucide.LoaderCircle class="animate-spin" size={18} />
|
||||
</div>
|
||||
<div class="grow relative text-left h-full">
|
||||
<div class="relative h-full grow text-left">
|
||||
{#if show_filename_view}
|
||||
<div in:fade={{ duration: 250 }} out:fade={{ duration: 250 }} class="flex items-center h-full">
|
||||
<div
|
||||
in:fade={{ duration: 250 }}
|
||||
out:fade={{ duration: 250 }}
|
||||
class="flex h-full items-center">
|
||||
<span class="truncate">
|
||||
{shortened_filename}
|
||||
</span>
|
||||
</div>
|
||||
{:else}
|
||||
<div in:fade={{ duration: 250 }} out:fade={{ duration: 250 }} class="absolute inset-0 flex items-center h-full">
|
||||
<div
|
||||
in:fade={{ duration: 250 }}
|
||||
out:fade={{ duration: 250 }}
|
||||
class="absolute inset-0 flex h-full items-center">
|
||||
<span class="font-bold whitespace-nowrap">
|
||||
Downloading:
|
||||
{#if $ae_sess.api_download_kv[file_id]}
|
||||
{$ae_sess.api_download_kv[file_id].percent_completed}%
|
||||
{$ae_sess.api_download_kv[file_id]
|
||||
.percent_completed}%
|
||||
{:else}
|
||||
...
|
||||
{/if}
|
||||
@@ -250,18 +282,22 @@
|
||||
{#if label}
|
||||
{@render label()}
|
||||
{:else}
|
||||
{@const IconComp = ae_util.file_extension_icon_lucide(hosted_file_obj?.extension)}
|
||||
<div class="flex items-center w-full">
|
||||
{@const IconComp = ae_util.file_extension_icon_lucide(
|
||||
hosted_file_obj?.extension
|
||||
)}
|
||||
<div class="flex w-full items-center">
|
||||
<div
|
||||
class="flex items-center pr-2 shrink-0 {show_divider ? 'border-r border-surface-500/30 mr-2' : ''}"
|
||||
>
|
||||
class="flex shrink-0 items-center pr-2 {show_divider
|
||||
? 'border-surface-500/30 mr-2 border-r'
|
||||
: ''}">
|
||||
<IconComp size={18} />
|
||||
</div>
|
||||
<span class="grow truncate text-left">
|
||||
{shortened_filename}
|
||||
</span>
|
||||
{#if hosted_file_obj?.file_purpose || hosted_file_obj?.group}
|
||||
<span class="badge preset-tonal-success ml-2 text-[10px] uppercase font-bold shrink-0">
|
||||
<span
|
||||
class="badge preset-tonal-success ml-2 shrink-0 text-[10px] font-bold uppercase">
|
||||
{hosted_file_obj.file_purpose || hosted_file_obj.group}
|
||||
</span>
|
||||
{/if}
|
||||
@@ -270,22 +306,25 @@
|
||||
{/await}
|
||||
|
||||
{#if download_complete === null}
|
||||
<span class="text-red-800 dark:text-red-200 ml-2 whitespace-nowrap">File not found</span>
|
||||
<span class="ml-2 whitespace-nowrap text-red-800 dark:text-red-200"
|
||||
>File not found</span>
|
||||
{:else if download_complete === false}
|
||||
<span class="text-red-800 dark:text-red-200 ml-2 whitespace-nowrap text-xs">Failed!</span>
|
||||
<span
|
||||
class="ml-2 text-xs whitespace-nowrap text-red-800 dark:text-red-200"
|
||||
>Failed!</span>
|
||||
{/if}
|
||||
{/snippet}
|
||||
|
||||
{#if hosted_file_id && hosted_file_obj}
|
||||
{@const file_id = hosted_file_obj.id || hosted_file_obj.hosted_file_id || hosted_file_id}
|
||||
{@const file_id =
|
||||
hosted_file_obj.id || hosted_file_obj.hosted_file_id || hosted_file_id}
|
||||
|
||||
{#if show_direct_download}
|
||||
<a
|
||||
href={direct_download_url}
|
||||
download={ae_util.clean_filename(final_filename)}
|
||||
class={variant_classes}
|
||||
title={`Direct download (V3 Action):\n${final_filename}\n[API] SHA256: ${hosted_file_obj?.hash_sha256?.slice(0, 10)}...\nHosted ID: ${file_id}`}
|
||||
>
|
||||
title={`Direct download (V3 Action):\n${final_filename}\n[API] SHA256: ${hosted_file_obj?.hash_sha256?.slice(0, 10)}...\nHosted ID: ${file_id}`}>
|
||||
{@render content()}
|
||||
</a>
|
||||
{:else}
|
||||
@@ -294,20 +333,24 @@
|
||||
disabled={require_auth && !$ae_loc.authenticated_access}
|
||||
class={variant_classes}
|
||||
onclick={handle_click}
|
||||
title={`Download this file:\n${final_filename}\n[API] SHA256: ${hosted_file_obj?.hash_sha256?.slice(0, 10)}...\nHosted ID: ${file_id}\n Linked to: ${linked_to_type} ID: ${linked_to_id}`}
|
||||
>
|
||||
title={`Download this file:\n${final_filename}\n[API] SHA256: ${hosted_file_obj?.hash_sha256?.slice(0, 10)}...\nHosted ID: ${file_id}\n Linked to: ${linked_to_type} ID: ${linked_to_id}`}>
|
||||
{@render content()}
|
||||
</button>
|
||||
{/if}
|
||||
{:else}
|
||||
<button type="button" disabled class={variant_classes} title="No file selected">
|
||||
<div class="flex items-center w-full">
|
||||
<button
|
||||
type="button"
|
||||
disabled
|
||||
class={variant_classes}
|
||||
title="No file selected">
|
||||
<div class="flex w-full items-center">
|
||||
<div
|
||||
class="flex items-center pr-2 shrink-0 {show_divider ? 'border-r border-surface-500/30 mr-2' : ''}"
|
||||
>
|
||||
class="flex shrink-0 items-center pr-2 {show_divider
|
||||
? 'border-surface-500/30 mr-2 border-r'
|
||||
: ''}">
|
||||
<Lucide.FileX size={18} />
|
||||
</div>
|
||||
<span class="grow text-left"> No file info </span>
|
||||
</div>
|
||||
</button>
|
||||
{/if}
|
||||
{/if}
|
||||
|
||||
@@ -1,281 +1,286 @@
|
||||
<script lang="ts">
|
||||
// untrack import removed — task_id sync now uses direct $effect (no untrack needed)
|
||||
// Imports
|
||||
// Import components and elements
|
||||
import * as Lucide from 'lucide-svelte';
|
||||
import Element_input_files_tbl from '$lib/elements/element_input_files_tbl.svelte';
|
||||
// untrack import removed — task_id sync now uses direct $effect (no untrack needed)
|
||||
// Imports
|
||||
// Import components and elements
|
||||
import * as Lucide 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 storage, functions, and libraries
|
||||
import type { key_val } from '$lib/stores/ae_stores';
|
||||
|
||||
import { api } from '$lib/api/api';
|
||||
import {
|
||||
ae_snip,
|
||||
ae_loc,
|
||||
ae_sess,
|
||||
ae_api,
|
||||
ae_trig,
|
||||
slct,
|
||||
slct_trigger
|
||||
} from '$lib/stores/ae_stores';
|
||||
import { api } from '$lib/api/api';
|
||||
import {
|
||||
ae_snip,
|
||||
ae_loc,
|
||||
ae_sess,
|
||||
ae_api,
|
||||
ae_trig,
|
||||
slct,
|
||||
slct_trigger
|
||||
} from '$lib/stores/ae_stores';
|
||||
|
||||
// Exports
|
||||
// Exports
|
||||
|
||||
interface Props {
|
||||
log_lvl?: number;
|
||||
// Expecting these for link_to_type: 'event', 'event_location', 'archive_content', etc
|
||||
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;
|
||||
hosted_file_id_li?: string[];
|
||||
hosted_file_obj_li?: any[];
|
||||
hosted_file_obj_kv?: key_val;
|
||||
label?: import('svelte').Snippet;
|
||||
interface Props {
|
||||
log_lvl?: number;
|
||||
// Expecting these for link_to_type: 'event', 'event_location', 'archive_content', etc
|
||||
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;
|
||||
hosted_file_id_li?: string[];
|
||||
hosted_file_obj_li?: any[];
|
||||
hosted_file_obj_kv?: key_val;
|
||||
label?: import('svelte').Snippet;
|
||||
}
|
||||
|
||||
let {
|
||||
log_lvl = 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),
|
||||
hosted_file_id_li = $bindable([]),
|
||||
hosted_file_obj_li = $bindable([]),
|
||||
hosted_file_obj_kv = $bindable({}),
|
||||
label
|
||||
}: Props = $props();
|
||||
|
||||
// Local Variables
|
||||
let task_id: string = $state('');
|
||||
let input_file_list: any = $state(null);
|
||||
let ae_promises: key_val = $state({}); // Promise<any>;
|
||||
let ae_triggers: key_val = {};
|
||||
|
||||
let input_element_id = 'ae_comp__hosted_files_upload__input';
|
||||
|
||||
$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(() => {
|
||||
// Sync task_id with link_to_id prop so it resets when navigating to a different object.
|
||||
task_id = link_to_id;
|
||||
});
|
||||
|
||||
// *** Functions and Logic
|
||||
async function handle_submit_form_files(event: SubmitEvent) {
|
||||
console.log('*** handle_submit_form() ***');
|
||||
event.preventDefault();
|
||||
|
||||
if (!event) {
|
||||
return;
|
||||
}
|
||||
|
||||
let {
|
||||
log_lvl = 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),
|
||||
hosted_file_id_li = $bindable([]),
|
||||
hosted_file_obj_li = $bindable([]),
|
||||
hosted_file_obj_kv = $bindable({}),
|
||||
label
|
||||
}: Props = $props();
|
||||
$ae_sess.files.disable_submit__hosted_file_obj = true;
|
||||
$ae_sess.files.submit_status = 'saving';
|
||||
submit_status = 'saving';
|
||||
upload_complete = false;
|
||||
|
||||
// Local Variables
|
||||
let task_id: string = $state('');
|
||||
let input_file_list: any = $state(null);
|
||||
let ae_promises: key_val = $state({}); // Promise<any>;
|
||||
let ae_triggers: key_val = {};
|
||||
hosted_file_id_li = [];
|
||||
hosted_file_obj_li = [];
|
||||
hosted_file_obj_kv = {};
|
||||
|
||||
let input_element_id = 'ae_comp__hosted_files_upload__input';
|
||||
let hosted_file_results;
|
||||
|
||||
$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}`);
|
||||
}
|
||||
});
|
||||
const target = event.currentTarget as HTMLFormElement;
|
||||
const file_input = target
|
||||
? (target[input_element_id] as HTMLInputElement)
|
||||
: null;
|
||||
|
||||
$effect(() => {
|
||||
// Sync task_id with link_to_id prop so it resets when navigating to a different object.
|
||||
task_id = link_to_id;
|
||||
});
|
||||
if (
|
||||
target &&
|
||||
file_input &&
|
||||
file_input.files &&
|
||||
file_input.files.length > 0
|
||||
) {
|
||||
task_id = link_to_id; // Ideally this should be the file hash, but we may be uploading multiple files at once. This should be done with a loop instead?
|
||||
|
||||
// *** Functions and Logic
|
||||
async function handle_submit_form_files(event: SubmitEvent) {
|
||||
console.log('*** handle_submit_form() ***');
|
||||
event.preventDefault();
|
||||
// Loop through each file and upload them individually in event.target[input_element_id].files
|
||||
// The task_id should be the file hash.
|
||||
// processed_file_list[i] has the file hash_sha256, hash_sha256_match, warnings, uploaded, uploaded_bytes, filename, and file_size_bytes.
|
||||
for (let i = 0; i < file_input.files.length; i++) {
|
||||
let tmp_file = file_input.files[i];
|
||||
|
||||
if (!event) {
|
||||
return;
|
||||
}
|
||||
task_id = $ae_sess.files.processed_file_list[i].hash_sha256;
|
||||
|
||||
$ae_sess.files.disable_submit__hosted_file_obj = true;
|
||||
$ae_sess.files.submit_status = 'saving';
|
||||
submit_status = 'saving';
|
||||
upload_complete = false;
|
||||
|
||||
hosted_file_id_li = [];
|
||||
hosted_file_obj_li = [];
|
||||
hosted_file_obj_kv = {};
|
||||
|
||||
let hosted_file_results;
|
||||
|
||||
const target = event.currentTarget as HTMLFormElement;
|
||||
const file_input = target ? (target[input_element_id] as HTMLInputElement) : null;
|
||||
|
||||
if (
|
||||
target &&
|
||||
file_input &&
|
||||
file_input.files &&
|
||||
file_input.files.length > 0
|
||||
) {
|
||||
task_id = link_to_id; // Ideally this should be the file hash, but we may be uploading multiple files at once. This should be done with a loop instead?
|
||||
|
||||
// Loop through each file and upload them individually in event.target[input_element_id].files
|
||||
// The task_id should be the file hash.
|
||||
// processed_file_list[i] has the file hash_sha256, hash_sha256_match, warnings, uploaded, uploaded_bytes, filename, and file_size_bytes.
|
||||
for (let i = 0; i < file_input.files.length; i++) {
|
||||
let tmp_file = file_input.files[i];
|
||||
|
||||
task_id = $ae_sess.files.processed_file_list[i].hash_sha256;
|
||||
|
||||
// hosted_file_results = await handle_input_upload_files([tmp_file], task_id);
|
||||
hosted_file_results = await handle_input_upload_files({
|
||||
input_upload_files: [tmp_file],
|
||||
task_id: task_id
|
||||
});
|
||||
|
||||
if (hosted_file_results) {
|
||||
console.log(`hosted_file_results:`, hosted_file_results);
|
||||
} else {
|
||||
console.log(`hosted_file_results:`, hosted_file_results);
|
||||
}
|
||||
}
|
||||
// hosted_file_results = await handle_input_upload_files(event.target[input_element_id].files, task_id);
|
||||
$ae_sess.files.processed_file_list = [];
|
||||
$ae_sess = $ae_sess; // Is this needed? 2025-03-17
|
||||
target.reset();
|
||||
// await tick();
|
||||
|
||||
if (log_lvl) {
|
||||
console.log(`hosted_file_id_li: ${hosted_file_id_li}`, hosted_file_id_li);
|
||||
} else if (log_lvl > 1) {
|
||||
console.log('hosted_file_results:', hosted_file_results);
|
||||
}
|
||||
}
|
||||
|
||||
$ae_sess.files.disable_submit__hosted_file_obj = false;
|
||||
$ae_sess.files.submit_status = 'saved';
|
||||
submit_status = 'saved';
|
||||
upload_complete = true;
|
||||
}
|
||||
|
||||
async function handle_input_upload_files({
|
||||
input_upload_files,
|
||||
task_id
|
||||
}: {
|
||||
input_upload_files: any[];
|
||||
task_id: string;
|
||||
}) {
|
||||
console.log('*** handle_input_upload_files() ***');
|
||||
|
||||
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);
|
||||
|
||||
for (let i = 0; i < input_upload_files.length; i++) {
|
||||
form_data.append(`file_list`, input_upload_files[i]);
|
||||
}
|
||||
|
||||
// hash_sha256, uploaded, uploaded_bytes
|
||||
// $ae_sess.files.processed_file_list[i] = {
|
||||
// ...$ae_sess.files.processed_file_list[i],
|
||||
// uploaded: $ae_sess.api_upload_kv[link_to_id].percent_completed,
|
||||
// uploaded_bytes: $ae_sess.api_upload_kv[link_to_id].uploaded_bytes,
|
||||
// };
|
||||
|
||||
let params = null;
|
||||
|
||||
let endpoint = '/hosted_file/upload_files';
|
||||
|
||||
console.log(form_data);
|
||||
|
||||
params = null;
|
||||
|
||||
// Uncomment and the post_promise is not seen by the "await" below
|
||||
// post_promise = await api.post_object({api_cfg: $cfg.api, endpoint: endpoint, params: params, data:form_data});
|
||||
// Uncomment so that the post_promise is not seen by the "await" below
|
||||
ae_promises.upload__hosted_file_obj = api
|
||||
.post_object({
|
||||
api_cfg: $ae_api,
|
||||
endpoint: endpoint,
|
||||
// params: params,
|
||||
form_data: form_data,
|
||||
task_id: task_id,
|
||||
log_lvl: log_lvl
|
||||
// retry_count: 1,
|
||||
})
|
||||
.then(async function (result) {
|
||||
// WARNING!!!! ONLY ONE FILE IS EXPECTED TO BE UPLOADED AT A TIME!!!
|
||||
// NOTE: The /hosted_file/upload_files endpoint will always return a list of successful files uploaded. In this case we are only uploading one file and expecting a list of one item.
|
||||
let x = 0;
|
||||
console.log(result[x]);
|
||||
let hosted_file_obj = result[x];
|
||||
|
||||
let hosted_file_id = hosted_file_obj.hosted_file_id;
|
||||
|
||||
hosted_file_id_li.push(hosted_file_id);
|
||||
hosted_file_obj_li.push(hosted_file_obj);
|
||||
|
||||
let hosted_file_data: key_val = {};
|
||||
hosted_file_data['id'] = hosted_file_id; // Same as the hosted_file_id
|
||||
hosted_file_data['hosted_file_id'] = hosted_file_id;
|
||||
hosted_file_data['for_type'] = link_to_type;
|
||||
hosted_file_data['for_id'] = link_to_id;
|
||||
hosted_file_data['hash_sha256'] = hosted_file_obj.hash_sha256;
|
||||
hosted_file_data['filename'] = hosted_file_obj.filename;
|
||||
hosted_file_data['extension'] = hosted_file_obj.extension;
|
||||
hosted_file_data['content_type'] = hosted_file_obj.content_type;
|
||||
hosted_file_data['size'] = hosted_file_obj.size;
|
||||
hosted_file_data['enable'] = true;
|
||||
hosted_file_data['created_on'] = hosted_file_obj.created_on;
|
||||
hosted_file_data['updated_on'] = hosted_file_obj.updated_on;
|
||||
console.log(hosted_file_data);
|
||||
|
||||
hosted_file_obj_kv[hosted_file_id] = hosted_file_data;
|
||||
|
||||
if (log_lvl) {
|
||||
console.log(`hosted_file_data:`, hosted_file_data);
|
||||
}
|
||||
|
||||
return hosted_file_data;
|
||||
|
||||
// $ae_sess.files.new_upload_list[i].uploaded_bytes = 10; // fake 10 bytes at least...
|
||||
|
||||
// let event_file_id = await events_func.create_hosted_file_obj_from_hosted_file_async({
|
||||
// api_cfg: $ae_api,
|
||||
// hosted_file_id: hosted_file_id,
|
||||
// data: event_file_data,
|
||||
// log_lvl: log_lvl
|
||||
// })
|
||||
// .then(function (create_result) {
|
||||
// console.log(create_result); // NOTE: This should be the event_file_id string
|
||||
// // let event_file_id = create_result;
|
||||
// return create_result;
|
||||
// });
|
||||
|
||||
// return event_file_id;
|
||||
})
|
||||
// .then(function (hosted_file_data) {
|
||||
// return hosted_file_data;
|
||||
// })
|
||||
.catch(function (error: any) {
|
||||
console.log('Something went wrong.');
|
||||
console.log(error);
|
||||
return false;
|
||||
})
|
||||
.finally(function () {
|
||||
$slct_trigger = 'load__hosted_file_obj_li';
|
||||
// hosted_file_results = await handle_input_upload_files([tmp_file], task_id);
|
||||
hosted_file_results = await handle_input_upload_files({
|
||||
input_upload_files: [tmp_file],
|
||||
task_id: task_id
|
||||
});
|
||||
|
||||
if (log_lvl) {
|
||||
console.log(`Waiting for upload__hosted_file_obj promise...`);
|
||||
if (hosted_file_results) {
|
||||
console.log(`hosted_file_results:`, hosted_file_results);
|
||||
} else {
|
||||
console.log(`hosted_file_results:`, hosted_file_results);
|
||||
}
|
||||
}
|
||||
let hosted_file_result = ae_promises.upload__hosted_file_obj;
|
||||
// hosted_file_results = await handle_input_upload_files(event.target[input_element_id].files, task_id);
|
||||
$ae_sess.files.processed_file_list = [];
|
||||
$ae_sess = $ae_sess; // Is this needed? 2025-03-17
|
||||
target.reset();
|
||||
// await tick();
|
||||
|
||||
return hosted_file_result;
|
||||
if (log_lvl) {
|
||||
console.log(
|
||||
`hosted_file_id_li: ${hosted_file_id_li}`,
|
||||
hosted_file_id_li
|
||||
);
|
||||
} else if (log_lvl > 1) {
|
||||
console.log('hosted_file_results:', hosted_file_results);
|
||||
}
|
||||
}
|
||||
|
||||
$ae_sess.files.disable_submit__hosted_file_obj = false;
|
||||
$ae_sess.files.submit_status = 'saved';
|
||||
submit_status = 'saved';
|
||||
upload_complete = true;
|
||||
}
|
||||
|
||||
async function handle_input_upload_files({
|
||||
input_upload_files,
|
||||
task_id
|
||||
}: {
|
||||
input_upload_files: any[];
|
||||
task_id: string;
|
||||
}) {
|
||||
console.log('*** handle_input_upload_files() ***');
|
||||
|
||||
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);
|
||||
|
||||
for (let i = 0; i < input_upload_files.length; i++) {
|
||||
form_data.append(`file_list`, input_upload_files[i]);
|
||||
}
|
||||
|
||||
// hash_sha256, uploaded, uploaded_bytes
|
||||
// $ae_sess.files.processed_file_list[i] = {
|
||||
// ...$ae_sess.files.processed_file_list[i],
|
||||
// uploaded: $ae_sess.api_upload_kv[link_to_id].percent_completed,
|
||||
// uploaded_bytes: $ae_sess.api_upload_kv[link_to_id].uploaded_bytes,
|
||||
// };
|
||||
|
||||
let params = null;
|
||||
|
||||
let endpoint = '/hosted_file/upload_files';
|
||||
|
||||
console.log(form_data);
|
||||
|
||||
params = null;
|
||||
|
||||
// Uncomment and the post_promise is not seen by the "await" below
|
||||
// post_promise = await api.post_object({api_cfg: $cfg.api, endpoint: endpoint, params: params, data:form_data});
|
||||
// Uncomment so that the post_promise is not seen by the "await" below
|
||||
ae_promises.upload__hosted_file_obj = api
|
||||
.post_object({
|
||||
api_cfg: $ae_api,
|
||||
endpoint: endpoint,
|
||||
// params: params,
|
||||
form_data: form_data,
|
||||
task_id: task_id,
|
||||
log_lvl: log_lvl
|
||||
// retry_count: 1,
|
||||
})
|
||||
.then(async function (result) {
|
||||
// WARNING!!!! ONLY ONE FILE IS EXPECTED TO BE UPLOADED AT A TIME!!!
|
||||
// NOTE: The /hosted_file/upload_files endpoint will always return a list of successful files uploaded. In this case we are only uploading one file and expecting a list of one item.
|
||||
let x = 0;
|
||||
console.log(result[x]);
|
||||
let hosted_file_obj = result[x];
|
||||
|
||||
let hosted_file_id = hosted_file_obj.hosted_file_id;
|
||||
|
||||
hosted_file_id_li.push(hosted_file_id);
|
||||
hosted_file_obj_li.push(hosted_file_obj);
|
||||
|
||||
let hosted_file_data: key_val = {};
|
||||
hosted_file_data['id'] = hosted_file_id; // Same as the hosted_file_id
|
||||
hosted_file_data['hosted_file_id'] = hosted_file_id;
|
||||
hosted_file_data['for_type'] = link_to_type;
|
||||
hosted_file_data['for_id'] = link_to_id;
|
||||
hosted_file_data['hash_sha256'] = hosted_file_obj.hash_sha256;
|
||||
hosted_file_data['filename'] = hosted_file_obj.filename;
|
||||
hosted_file_data['extension'] = hosted_file_obj.extension;
|
||||
hosted_file_data['content_type'] = hosted_file_obj.content_type;
|
||||
hosted_file_data['size'] = hosted_file_obj.size;
|
||||
hosted_file_data['enable'] = true;
|
||||
hosted_file_data['created_on'] = hosted_file_obj.created_on;
|
||||
hosted_file_data['updated_on'] = hosted_file_obj.updated_on;
|
||||
console.log(hosted_file_data);
|
||||
|
||||
hosted_file_obj_kv[hosted_file_id] = hosted_file_data;
|
||||
|
||||
if (log_lvl) {
|
||||
console.log(`hosted_file_data:`, hosted_file_data);
|
||||
}
|
||||
|
||||
return hosted_file_data;
|
||||
|
||||
// $ae_sess.files.new_upload_list[i].uploaded_bytes = 10; // fake 10 bytes at least...
|
||||
|
||||
// let event_file_id = await events_func.create_hosted_file_obj_from_hosted_file_async({
|
||||
// api_cfg: $ae_api,
|
||||
// hosted_file_id: hosted_file_id,
|
||||
// data: event_file_data,
|
||||
// log_lvl: log_lvl
|
||||
// })
|
||||
// .then(function (create_result) {
|
||||
// console.log(create_result); // NOTE: This should be the event_file_id string
|
||||
// // let event_file_id = create_result;
|
||||
// return create_result;
|
||||
// });
|
||||
|
||||
// return event_file_id;
|
||||
})
|
||||
// .then(function (hosted_file_data) {
|
||||
// return hosted_file_data;
|
||||
// })
|
||||
.catch(function (error: any) {
|
||||
console.log('Something went wrong.');
|
||||
console.log(error);
|
||||
return false;
|
||||
})
|
||||
.finally(function () {
|
||||
$slct_trigger = 'load__hosted_file_obj_li';
|
||||
});
|
||||
|
||||
if (log_lvl) {
|
||||
console.log(`Waiting for upload__hosted_file_obj promise...`);
|
||||
}
|
||||
let hosted_file_result = ae_promises.upload__hosted_file_obj;
|
||||
|
||||
return hosted_file_result;
|
||||
}
|
||||
</script>
|
||||
|
||||
<!-- class:hidden={!$ae_loc.trusted_access} -->
|
||||
<form onsubmit={handle_submit_form_files} 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">
|
||||
<Lucide.LoaderCircle class="animate-spin m-1" />
|
||||
<div class="flex flex-row items-center justify-center gap-1 text-lg">
|
||||
<Lucide.LoaderCircle class="m-1 animate-spin" />
|
||||
<span class="">
|
||||
Uploading
|
||||
{#if $ae_sess.api_upload_kv[task_id]}
|
||||
@@ -288,14 +293,14 @@
|
||||
<label
|
||||
for="ae_comp__hosted_files_upload__input"
|
||||
class="svelte_input_file_label text-center"
|
||||
class:hidden={$ae_sess.files.disable_submit__hosted_file_obj}
|
||||
>
|
||||
class:hidden={$ae_sess.files.disable_submit__hosted_file_obj}>
|
||||
{#if label}{@render label()}{:else}
|
||||
<div class="flex items-center justify-center gap-2 mb-2">
|
||||
<div class="mb-2 flex items-center justify-center gap-2">
|
||||
<Lucide.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>
|
||||
<span class="text-sm text-gray-600 dark:text-gray-400 italic">
|
||||
<span class="text-sm text-gray-600 italic dark:text-gray-400">
|
||||
<strong>Supported formats</strong><br />
|
||||
(PowerPoint, Keynote, PDF, Media, etc)
|
||||
</span>
|
||||
@@ -313,33 +318,30 @@
|
||||
class="
|
||||
svelte_input_file_element
|
||||
file-dropzone-input
|
||||
px-1
|
||||
block w-full text-lg
|
||||
preset-filled-surface-50-950
|
||||
text-surface-900 dark:text-surface-100
|
||||
border border-surface-300 dark:border-surface-700 rounded-lg
|
||||
cursor-pointer
|
||||
focus:outline-hidden focus:ring-2 focus:ring-primary-500
|
||||
text-surface-900 dark:text-surface-100 border-surface-300
|
||||
dark:border-surface-700
|
||||
focus:ring-primary-500 block
|
||||
w-full cursor-pointer rounded-lg border
|
||||
px-1
|
||||
text-lg focus:ring-2 focus:outline-hidden
|
||||
{input_class_li.join(' ')}
|
||||
"
|
||||
class:hidden={$ae_sess.files.disable_submit__hosted_file_obj}
|
||||
/>
|
||||
class:hidden={$ae_sess.files.disable_submit__hosted_file_obj} />
|
||||
|
||||
<Element_input_files_tbl
|
||||
bind:input_file_list
|
||||
bind:file_list_status={$ae_sess.files.status__file_list}
|
||||
bind:processed_file_list={$ae_sess.files.processed_file_list}
|
||||
{table_class_li}
|
||||
/>
|
||||
{table_class_li} />
|
||||
|
||||
<button
|
||||
type="submit"
|
||||
class="btn btn-lg btn-primary preset-tonal-primary border border-primary-500 hover:preset-tonal-success hover:border-success-500 w-54"
|
||||
class="btn btn-lg btn-primary preset-tonal-primary border-primary-500 hover:preset-tonal-success hover:border-success-500 w-54 border"
|
||||
disabled={$ae_sess.files.disable_submit__hosted_file_obj ||
|
||||
$ae_sess.files.status__file_list != 'ready'}
|
||||
>
|
||||
$ae_sess.files.status__file_list != 'ready'}>
|
||||
{#await ae_promises.upload__hosted_file_obj}
|
||||
<Lucide.LoaderCircle class="animate-spin m-1" />
|
||||
<Lucide.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}%
|
||||
@@ -350,9 +352,12 @@
|
||||
{:then}
|
||||
<Lucide.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 $ae_sess.files.processed_file_list?.length > 0}
|
||||
{$ae_sess.files.processed_file_list.length} { $ae_sess.files.processed_file_list.length === 1 ? 'file' : 'files' }
|
||||
{$ae_sess.files.processed_file_list.length}
|
||||
{$ae_sess.files.processed_file_list.length === 1
|
||||
? 'file'
|
||||
: 'files'}
|
||||
{:else}
|
||||
<span class="text-xs"> 0 </span>
|
||||
{/if}
|
||||
|
||||
@@ -1,235 +1,358 @@
|
||||
<script lang="ts">
|
||||
import { untrack } from 'svelte';
|
||||
/**
|
||||
* AE_Comp_Site_Config_Editor.svelte
|
||||
* Specialized UI for managing site.cfg_json settings.
|
||||
* Supports General, AI, Performance, and IDAA-specific configurations.
|
||||
*/
|
||||
import { Modal } from 'flowbite-svelte';
|
||||
import { Brain, CodeXml, ExternalLink, Globe, Mail, Minus, Palette, Plus, Save, ShieldCheck, Timer } from '@lucide/svelte';
|
||||
import AE_Comp_Editor_CodeMirror from '$lib/elements/element_editor_codemirror.svelte';
|
||||
import { ae_loc } from '$lib/stores/ae_stores';
|
||||
import { untrack } from 'svelte';
|
||||
/**
|
||||
* AE_Comp_Site_Config_Editor.svelte
|
||||
* Specialized UI for managing site.cfg_json settings.
|
||||
* Supports General, AI, Performance, and IDAA-specific configurations.
|
||||
*/
|
||||
import { Modal } from 'flowbite-svelte';
|
||||
import {
|
||||
Brain,
|
||||
CodeXml,
|
||||
ExternalLink,
|
||||
Globe,
|
||||
Mail,
|
||||
Minus,
|
||||
Palette,
|
||||
Plus,
|
||||
Save,
|
||||
ShieldCheck,
|
||||
Timer
|
||||
} from '@lucide/svelte';
|
||||
import AE_Comp_Editor_CodeMirror from '$lib/elements/element_editor_codemirror.svelte';
|
||||
import { ae_loc } from '$lib/stores/ae_stores';
|
||||
|
||||
interface Props {
|
||||
cfg_json: any;
|
||||
on_save?: () => void;
|
||||
}
|
||||
interface Props {
|
||||
cfg_json: any;
|
||||
on_save?: () => void;
|
||||
}
|
||||
|
||||
let { cfg_json = $bindable({}), on_save }: Props = $props();
|
||||
let { cfg_json = $bindable({}), on_save }: Props = $props();
|
||||
|
||||
// Ensure we have a valid object (handle strings/nulls)
|
||||
$effect(() => {
|
||||
if (typeof cfg_json === 'string') {
|
||||
try {
|
||||
cfg_json = JSON.parse(cfg_json);
|
||||
} catch (e) {
|
||||
cfg_json = {};
|
||||
}
|
||||
// Ensure we have a valid object (handle strings/nulls)
|
||||
$effect(() => {
|
||||
if (typeof cfg_json === 'string') {
|
||||
try {
|
||||
cfg_json = JSON.parse(cfg_json);
|
||||
} catch (e) {
|
||||
cfg_json = {};
|
||||
}
|
||||
if (!cfg_json) cfg_json = {};
|
||||
});
|
||||
|
||||
// Internal State
|
||||
let active_tab: 'visuals' | 'email' | 'ai' | 'refresh' | 'idaa' | 'raw' = $state('visuals');
|
||||
let raw_json_str = $state('');
|
||||
|
||||
// Ensure we have a valid object
|
||||
}
|
||||
if (!cfg_json) cfg_json = {};
|
||||
});
|
||||
|
||||
function add_to_list(key: string) {
|
||||
if (!cfg_json[key]) cfg_json[key] = [];
|
||||
const val = prompt('Enter Novi UUID:');
|
||||
if (val) cfg_json[key].push(val);
|
||||
// Internal State
|
||||
let active_tab: 'visuals' | 'email' | 'ai' | 'refresh' | 'idaa' | 'raw' =
|
||||
$state('visuals');
|
||||
let raw_json_str = $state('');
|
||||
|
||||
// Ensure we have a valid object
|
||||
if (!cfg_json) cfg_json = {};
|
||||
|
||||
function add_to_list(key: string) {
|
||||
if (!cfg_json[key]) cfg_json[key] = [];
|
||||
const val = prompt('Enter Novi UUID:');
|
||||
if (val) cfg_json[key].push(val);
|
||||
}
|
||||
|
||||
function remove_from_list(key: string, index: number) {
|
||||
cfg_json[key].splice(index, 1);
|
||||
}
|
||||
|
||||
// Sync Raw JSON string when entering the tab
|
||||
$effect(() => {
|
||||
if (active_tab === 'raw') {
|
||||
untrack(() => {
|
||||
raw_json_str = JSON.stringify(cfg_json, null, 2);
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
function remove_from_list(key: string, index: number) {
|
||||
cfg_json[key].splice(index, 1);
|
||||
// Update cfg_json when raw string changes
|
||||
$effect(() => {
|
||||
if (active_tab === 'raw' && raw_json_str) {
|
||||
try {
|
||||
const parsed = JSON.parse(raw_json_str);
|
||||
cfg_json = parsed;
|
||||
} catch (e) {
|
||||
// Ignore invalid JSON while typing
|
||||
}
|
||||
}
|
||||
|
||||
// Sync Raw JSON string when entering the tab
|
||||
$effect(() => {
|
||||
if (active_tab === 'raw') {
|
||||
untrack(() => {
|
||||
raw_json_str = JSON.stringify(cfg_json, null, 2);
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
// Update cfg_json when raw string changes
|
||||
$effect(() => {
|
||||
if (active_tab === 'raw' && raw_json_str) {
|
||||
try {
|
||||
const parsed = JSON.parse(raw_json_str);
|
||||
cfg_json = parsed;
|
||||
} catch (e) {
|
||||
// Ignore invalid JSON while typing
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
</script>
|
||||
|
||||
<div class="ae-site-config-editor flex flex-col h-full space-y-4">
|
||||
<div class="ae-site-config-editor flex h-full flex-col space-y-4">
|
||||
<!-- Tab Navigation -->
|
||||
<div class="flex flex-wrap gap-1 p-1 bg-surface-500/10 rounded-lg max-w-fit">
|
||||
<button class="btn btn-sm transition-all {active_tab === 'visuals' ? 'variant-filled-primary' : 'variant-soft-surface'}" onclick={() => active_tab = 'visuals'}>
|
||||
<div
|
||||
class="bg-surface-500/10 flex max-w-fit flex-wrap gap-1 rounded-lg p-1">
|
||||
<button
|
||||
class="btn btn-sm transition-all {active_tab === 'visuals'
|
||||
? 'variant-filled-primary'
|
||||
: 'variant-soft-surface'}"
|
||||
onclick={() => (active_tab = 'visuals')}>
|
||||
<Palette size="1.1em" class="mr-1" /> Visuals
|
||||
</button>
|
||||
<button class="btn btn-sm transition-all {active_tab === 'email' ? 'variant-filled-primary' : 'variant-soft-surface'}" onclick={() => active_tab = 'email'}>
|
||||
<button
|
||||
class="btn btn-sm transition-all {active_tab === 'email'
|
||||
? 'variant-filled-primary'
|
||||
: 'variant-soft-surface'}"
|
||||
onclick={() => (active_tab = 'email')}>
|
||||
<Mail size="1.1em" class="mr-1" /> Email
|
||||
</button>
|
||||
<button class="btn btn-sm transition-all {active_tab === 'ai' ? 'variant-filled-primary' : 'variant-soft-surface'}" onclick={() => active_tab = 'ai'}>
|
||||
<button
|
||||
class="btn btn-sm transition-all {active_tab === 'ai'
|
||||
? 'variant-filled-primary'
|
||||
: 'variant-soft-surface'}"
|
||||
onclick={() => (active_tab = 'ai')}>
|
||||
<Brain size="1.1em" class="mr-1" /> AI/LLM
|
||||
</button>
|
||||
<button class="btn btn-sm transition-all {active_tab === 'refresh' ? 'variant-filled-primary' : 'variant-soft-surface'}" onclick={() => active_tab = 'refresh'}>
|
||||
<button
|
||||
class="btn btn-sm transition-all {active_tab === 'refresh'
|
||||
? 'variant-filled-primary'
|
||||
: 'variant-soft-surface'}"
|
||||
onclick={() => (active_tab = 'refresh')}>
|
||||
<Timer size="1.1em" class="mr-1" /> Refresh
|
||||
</button>
|
||||
<button class="btn btn-sm transition-all {active_tab === 'idaa' ? 'variant-filled-primary' : 'variant-soft-surface'}" onclick={() => active_tab = 'idaa'}>
|
||||
<button
|
||||
class="btn btn-sm transition-all {active_tab === 'idaa'
|
||||
? 'variant-filled-primary'
|
||||
: 'variant-soft-surface'}"
|
||||
onclick={() => (active_tab = 'idaa')}>
|
||||
<ShieldCheck size="1.1em" class="mr-1" /> IDAA
|
||||
</button>
|
||||
<button class="btn btn-sm transition-all {active_tab === 'raw' ? 'variant-filled-primary' : 'variant-soft-surface'}" onclick={() => active_tab = 'raw'}>
|
||||
<button
|
||||
class="btn btn-sm transition-all {active_tab === 'raw'
|
||||
? 'variant-filled-primary'
|
||||
: 'variant-soft-surface'}"
|
||||
onclick={() => (active_tab = 'raw')}>
|
||||
<CodeXml size="1.1em" class="mr-1" /> Raw JSON
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<!-- Scrollable Content Area -->
|
||||
<div class="grow overflow-y-auto p-1 pr-2 space-y-6 max-h-[60vh]">
|
||||
|
||||
<div class="max-h-[60vh] grow space-y-6 overflow-y-auto p-1 pr-2">
|
||||
{#if active_tab === 'visuals'}
|
||||
<div class="grid grid-cols-1 md:grid-cols-2 gap-4 animate-in fade-in duration-200">
|
||||
<div
|
||||
class="animate-in fade-in grid grid-cols-1 gap-4 duration-200 md:grid-cols-2">
|
||||
<label class="label">
|
||||
<span class="text-xs font-bold uppercase opacity-50">Theme Name</span>
|
||||
<input type="text" bind:value={cfg_json.theme_name} class="input variant-form-material" placeholder="e.g. AE_OSIT_default" />
|
||||
<span class="text-xs font-bold uppercase opacity-50"
|
||||
>Theme Name</span>
|
||||
<input
|
||||
type="text"
|
||||
bind:value={cfg_json.theme_name}
|
||||
class="input variant-form-material"
|
||||
placeholder="e.g. AE_OSIT_default" />
|
||||
</label>
|
||||
<label class="label">
|
||||
<span class="text-xs font-bold uppercase opacity-50">Theme Mode</span>
|
||||
<select bind:value={cfg_json.theme_mode} class="select variant-form-material">
|
||||
<span class="text-xs font-bold uppercase opacity-50"
|
||||
>Theme Mode</span>
|
||||
<select
|
||||
bind:value={cfg_json.theme_mode}
|
||||
class="select variant-form-material">
|
||||
<option value="light">Light</option>
|
||||
<option value="dark">Dark</option>
|
||||
<option value="auto">Auto (System)</option>
|
||||
</select>
|
||||
</label>
|
||||
<label class="label md:col-span-2">
|
||||
<span class="text-xs font-bold uppercase opacity-50">Header Image Path (URL)</span>
|
||||
<span class="text-xs font-bold uppercase opacity-50"
|
||||
>Header Image Path (URL)</span>
|
||||
<div class="flex gap-2">
|
||||
<input type="text" bind:value={cfg_json.header_image_path} class="input variant-form-material grow" placeholder="https://..." />
|
||||
<input
|
||||
type="text"
|
||||
bind:value={cfg_json.header_image_path}
|
||||
class="input variant-form-material grow"
|
||||
placeholder="https://..." />
|
||||
{#if cfg_json.header_image_path}
|
||||
<a href={cfg_json.header_image_path} target="_blank" rel="noopener noreferrer" class="btn-icon variant-soft-surface"><ExternalLink size="1.2em" /></a>
|
||||
<a
|
||||
href={cfg_json.header_image_path}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
class="btn-icon variant-soft-surface"
|
||||
><ExternalLink size="1.2em" /></a>
|
||||
{/if}
|
||||
</div>
|
||||
</label>
|
||||
</div>
|
||||
|
||||
{:else if active_tab === 'email'}
|
||||
<div class="grid grid-cols-1 md:grid-cols-2 gap-4 animate-in fade-in duration-200">
|
||||
<section class="space-y-4 border-r border-surface-500/10 pr-4">
|
||||
<h4 class="text-sm font-black text-primary-500">Admin Contact</h4>
|
||||
<div
|
||||
class="animate-in fade-in grid grid-cols-1 gap-4 duration-200 md:grid-cols-2">
|
||||
<section class="border-surface-500/10 space-y-4 border-r pr-4">
|
||||
<h4 class="text-primary-500 text-sm font-black">
|
||||
Admin Contact
|
||||
</h4>
|
||||
<label class="label">
|
||||
<span class="text-xs font-bold uppercase opacity-50">Admin Name</span>
|
||||
<input type="text" bind:value={cfg_json.admin_name} class="input variant-form-material" />
|
||||
<span class="text-xs font-bold uppercase opacity-50"
|
||||
>Admin Name</span>
|
||||
<input
|
||||
type="text"
|
||||
bind:value={cfg_json.admin_name}
|
||||
class="input variant-form-material" />
|
||||
</label>
|
||||
<label class="label">
|
||||
<span class="text-xs font-bold uppercase opacity-50">Admin Email</span>
|
||||
<input type="email" bind:value={cfg_json.admin_email} class="input variant-form-material" />
|
||||
<span class="text-xs font-bold uppercase opacity-50"
|
||||
>Admin Email</span>
|
||||
<input
|
||||
type="email"
|
||||
bind:value={cfg_json.admin_email}
|
||||
class="input variant-form-material" />
|
||||
</label>
|
||||
</section>
|
||||
<section class="space-y-4">
|
||||
<h4 class="text-sm font-black text-secondary-500">System (No-Reply)</h4>
|
||||
<h4 class="text-secondary-500 text-sm font-black">
|
||||
System (No-Reply)
|
||||
</h4>
|
||||
<label class="label">
|
||||
<span class="text-xs font-bold uppercase opacity-50">No-Reply Name</span>
|
||||
<input type="text" bind:value={cfg_json.noreply_name} class="input variant-form-material" />
|
||||
<span class="text-xs font-bold uppercase opacity-50"
|
||||
>No-Reply Name</span>
|
||||
<input
|
||||
type="text"
|
||||
bind:value={cfg_json.noreply_name}
|
||||
class="input variant-form-material" />
|
||||
</label>
|
||||
<label class="label">
|
||||
<span class="text-xs font-bold uppercase opacity-50">No-Reply Email</span>
|
||||
<input type="email" bind:value={cfg_json.noreply_email} class="input variant-form-material" />
|
||||
<span class="text-xs font-bold uppercase opacity-50"
|
||||
>No-Reply Email</span>
|
||||
<input
|
||||
type="email"
|
||||
bind:value={cfg_json.noreply_email}
|
||||
class="input variant-form-material" />
|
||||
</label>
|
||||
</section>
|
||||
</div>
|
||||
|
||||
{:else if active_tab === 'ai'}
|
||||
<div class="space-y-4 animate-in fade-in duration-200">
|
||||
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
|
||||
<div class="animate-in fade-in space-y-4 duration-200">
|
||||
<div class="grid grid-cols-1 gap-4 md:grid-cols-2">
|
||||
<label class="label">
|
||||
<span class="text-xs font-bold uppercase opacity-50">LLM API Base URL</span>
|
||||
<input type="text" bind:value={cfg_json.llm__api_base_url} class="input variant-form-material" />
|
||||
<span class="text-xs font-bold uppercase opacity-50"
|
||||
>LLM API Base URL</span>
|
||||
<input
|
||||
type="text"
|
||||
bind:value={cfg_json.llm__api_base_url}
|
||||
class="input variant-form-material" />
|
||||
</label>
|
||||
<label class="label">
|
||||
<span class="text-xs font-bold uppercase opacity-50">LLM Model</span>
|
||||
<input type="text" bind:value={cfg_json.llm__api_model} class="input variant-form-material" />
|
||||
<span class="text-xs font-bold uppercase opacity-50"
|
||||
>LLM Model</span>
|
||||
<input
|
||||
type="text"
|
||||
bind:value={cfg_json.llm__api_model}
|
||||
class="input variant-form-material" />
|
||||
</label>
|
||||
</div>
|
||||
<label class="label">
|
||||
<span class="text-xs font-bold uppercase opacity-50">API Token</span>
|
||||
<input type="password" bind:value={cfg_json.llm__api_token} class="input variant-form-material font-mono" />
|
||||
<span class="text-xs font-bold uppercase opacity-50"
|
||||
>API Token</span>
|
||||
<input
|
||||
type="password"
|
||||
bind:value={cfg_json.llm__api_token}
|
||||
class="input variant-form-material font-mono" />
|
||||
</label>
|
||||
<label class="label">
|
||||
<span class="text-xs font-bold uppercase opacity-50">System Prompt</span>
|
||||
<textarea bind:value={cfg_json.llm__system_prompt} class="textarea variant-form-material h-24 text-sm"></textarea>
|
||||
<span class="text-xs font-bold uppercase opacity-50"
|
||||
>System Prompt</span>
|
||||
<textarea
|
||||
bind:value={cfg_json.llm__system_prompt}
|
||||
class="textarea variant-form-material h-24 text-sm"
|
||||
></textarea>
|
||||
</label>
|
||||
<label class="flex items-center space-x-2">
|
||||
<input type="checkbox" bind:checked={cfg_json.llm__api_dangerous_browser} class="checkbox" />
|
||||
<span class="text-xs font-bold uppercase opacity-50">Allow Browser Fetch (Dangerously)</span>
|
||||
<input
|
||||
type="checkbox"
|
||||
bind:checked={cfg_json.llm__api_dangerous_browser}
|
||||
class="checkbox" />
|
||||
<span class="text-xs font-bold uppercase opacity-50"
|
||||
>Allow Browser Fetch (Dangerously)</span>
|
||||
</label>
|
||||
</div>
|
||||
|
||||
{:else if active_tab === 'refresh'}
|
||||
<div class="grid grid-cols-1 md:grid-cols-2 gap-4 animate-in fade-in duration-200">
|
||||
<div
|
||||
class="animate-in fade-in grid grid-cols-1 gap-4 duration-200 md:grid-cols-2">
|
||||
<label class="label">
|
||||
<span class="text-xs font-bold uppercase opacity-50">Default (Minutes)</span>
|
||||
<input type="number" bind:value={cfg_json.default_refresh_minutes} class="input variant-form-material" />
|
||||
<span class="text-xs font-bold uppercase opacity-50"
|
||||
>Default (Minutes)</span>
|
||||
<input
|
||||
type="number"
|
||||
bind:value={cfg_json.default_refresh_minutes}
|
||||
class="input variant-form-material" />
|
||||
</label>
|
||||
<label class="label">
|
||||
<span class="text-xs font-bold uppercase opacity-50">Authenticated (Minutes)</span>
|
||||
<input type="number" bind:value={cfg_json.authenticated_refresh_time} class="input variant-form-material" />
|
||||
<span class="text-xs font-bold uppercase opacity-50"
|
||||
>Authenticated (Minutes)</span>
|
||||
<input
|
||||
type="number"
|
||||
bind:value={cfg_json.authenticated_refresh_time}
|
||||
class="input variant-form-material" />
|
||||
</label>
|
||||
<label class="label">
|
||||
<span class="text-xs font-bold uppercase opacity-50">Trusted (Minutes)</span>
|
||||
<input type="number" bind:value={cfg_json.trusted_refresh_minutes} class="input variant-form-material" />
|
||||
<span class="text-xs font-bold uppercase opacity-50"
|
||||
>Trusted (Minutes)</span>
|
||||
<input
|
||||
type="number"
|
||||
bind:value={cfg_json.trusted_refresh_minutes}
|
||||
class="input variant-form-material" />
|
||||
</label>
|
||||
<label class="label">
|
||||
<span class="text-xs font-bold uppercase opacity-50">Manager (Minutes)</span>
|
||||
<input type="number" bind:value={cfg_json.manager_refresh_minutes} class="input variant-form-material" />
|
||||
<span class="text-xs font-bold uppercase opacity-50"
|
||||
>Manager (Minutes)</span>
|
||||
<input
|
||||
type="number"
|
||||
bind:value={cfg_json.manager_refresh_minutes}
|
||||
class="input variant-form-material" />
|
||||
</label>
|
||||
</div>
|
||||
|
||||
{:else if active_tab === 'idaa'}
|
||||
<div class="space-y-6 animate-in fade-in duration-200">
|
||||
<div class="animate-in fade-in space-y-6 duration-200">
|
||||
<!-- Novi API -->
|
||||
<section class="space-y-4 p-4 bg-surface-500/5 rounded-xl border border-surface-500/10">
|
||||
<h4 class="text-sm font-black flex items-center gap-2">
|
||||
<section
|
||||
class="bg-surface-500/5 border-surface-500/10 space-y-4 rounded-xl border p-4">
|
||||
<h4 class="flex items-center gap-2 text-sm font-black">
|
||||
<Globe size="1.1em" class="text-primary-500" /> Novi API Connection
|
||||
</h4>
|
||||
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
|
||||
<div class="grid grid-cols-1 gap-4 md:grid-cols-2">
|
||||
<label class="label">
|
||||
<span class="text-xs font-bold uppercase opacity-50">Root URL</span>
|
||||
<input type="text" bind:value={cfg_json.novi_api_root_url} class="input variant-form-material" />
|
||||
<span class="text-xs font-bold uppercase opacity-50"
|
||||
>Root URL</span>
|
||||
<input
|
||||
type="text"
|
||||
bind:value={cfg_json.novi_api_root_url}
|
||||
class="input variant-form-material" />
|
||||
</label>
|
||||
<label class="label">
|
||||
<span class="text-xs font-bold uppercase opacity-50">API Key</span>
|
||||
<input type="password" bind:value={cfg_json.novi_idaa_api_key} class="input variant-form-material font-mono" />
|
||||
<span class="text-xs font-bold uppercase opacity-50"
|
||||
>API Key</span>
|
||||
<input
|
||||
type="password"
|
||||
bind:value={cfg_json.novi_idaa_api_key}
|
||||
class="input variant-form-material font-mono" />
|
||||
</label>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- UUID Lists -->
|
||||
<section class="grid grid-cols-1 md:grid-cols-2 gap-4">
|
||||
{#each [
|
||||
{ key: 'novi_admin_li', label: 'Novi Admins', color: 'text-error-500' },
|
||||
{ key: 'novi_trusted_li', label: 'Novi Trusted', color: 'text-warning-500' },
|
||||
{ key: 'novi_jitsi_mod_li', label: 'Jitsi Moderators', color: 'text-primary-500' },
|
||||
{ key: 'novi_idaa_group_guid_li', label: 'Member Group GUIDs', color: 'text-secondary-500' }
|
||||
] as list (list.key)}
|
||||
<div class="space-y-2 p-3 bg-surface-500/5 rounded-lg">
|
||||
<header class="flex justify-between items-center">
|
||||
<span class="text-[10px] font-black uppercase tracking-wider {list.color}">{list.label}</span>
|
||||
<button class="btn btn-icon btn-icon-sm variant-soft-primary" onclick={() => add_to_list(list.key)}>
|
||||
<section class="grid grid-cols-1 gap-4 md:grid-cols-2">
|
||||
{#each [{ key: 'novi_admin_li', label: 'Novi Admins', color: 'text-error-500' }, { key: 'novi_trusted_li', label: 'Novi Trusted', color: 'text-warning-500' }, { key: 'novi_jitsi_mod_li', label: 'Jitsi Moderators', color: 'text-primary-500' }, { key: 'novi_idaa_group_guid_li', label: 'Member Group GUIDs', color: 'text-secondary-500' }] as list (list.key)}
|
||||
<div class="bg-surface-500/5 space-y-2 rounded-lg p-3">
|
||||
<header class="flex items-center justify-between">
|
||||
<span
|
||||
class="text-[10px] font-black tracking-wider uppercase {list.color}"
|
||||
>{list.label}</span>
|
||||
<button
|
||||
class="btn btn-icon btn-icon-sm variant-soft-primary"
|
||||
onclick={() => add_to_list(list.key)}>
|
||||
<Plus size="12" />
|
||||
</button>
|
||||
</header>
|
||||
<div class="space-y-1">
|
||||
{#each cfg_json[list.key] ?? [] as uuid, i (uuid)}
|
||||
<div class="flex gap-1 items-center bg-surface-500/10 p-1 rounded font-mono text-[10px]">
|
||||
<span class="grow truncate">{uuid}</span>
|
||||
<button class="text-error-500 hover:scale-110 transition-transform" onclick={() => remove_from_list(list.key, i)}>
|
||||
<div
|
||||
class="bg-surface-500/10 flex items-center gap-1 rounded p-1 font-mono text-[10px]">
|
||||
<span class="grow truncate"
|
||||
>{uuid}</span>
|
||||
<button
|
||||
class="text-error-500 transition-transform hover:scale-110"
|
||||
onclick={() =>
|
||||
remove_from_list(list.key, i)}>
|
||||
<Minus size="12" />
|
||||
</button>
|
||||
</div>
|
||||
@@ -240,62 +363,96 @@
|
||||
</section>
|
||||
|
||||
<!-- Notifications -->
|
||||
<section class="grid grid-cols-1 md:grid-cols-2 gap-4 p-4 bg-surface-500/5 rounded-xl">
|
||||
<section
|
||||
class="bg-surface-500/5 grid grid-cols-1 gap-4 rounded-xl p-4 md:grid-cols-2">
|
||||
<div class="space-y-2">
|
||||
<h4 class="text-[10px] font-black uppercase opacity-50">Bulletin Board</h4>
|
||||
<h4 class="text-[10px] font-black uppercase opacity-50">
|
||||
Bulletin Board
|
||||
</h4>
|
||||
<label class="flex items-center space-x-2 text-xs">
|
||||
<input type="checkbox" bind:checked={cfg_json.bb_send_staff_new_email} class="checkbox checkbox-sm" />
|
||||
<input
|
||||
type="checkbox"
|
||||
bind:checked={cfg_json.bb_send_staff_new_email}
|
||||
class="checkbox checkbox-sm" />
|
||||
<span>Notify Staff (New)</span>
|
||||
</label>
|
||||
<label class="flex items-center space-x-2 text-xs">
|
||||
<input type="checkbox" bind:checked={cfg_json.bb_send_staff_update_email} class="checkbox checkbox-sm" />
|
||||
<input
|
||||
type="checkbox"
|
||||
bind:checked={
|
||||
cfg_json.bb_send_staff_update_email
|
||||
}
|
||||
class="checkbox checkbox-sm" />
|
||||
<span>Notify Staff (Update)</span>
|
||||
</label>
|
||||
<label class="flex items-center space-x-2 text-xs">
|
||||
<input type="checkbox" bind:checked={cfg_json.bb_send_poster_email} class="checkbox checkbox-sm" />
|
||||
<input
|
||||
type="checkbox"
|
||||
bind:checked={cfg_json.bb_send_poster_email}
|
||||
class="checkbox checkbox-sm" />
|
||||
<span>Notify Poster</span>
|
||||
</label>
|
||||
<label class="flex items-center space-x-2 text-xs">
|
||||
<input type="checkbox" bind:checked={cfg_json.bb_send_commenter_email} class="checkbox checkbox-sm" />
|
||||
<input
|
||||
type="checkbox"
|
||||
bind:checked={cfg_json.bb_send_commenter_email}
|
||||
class="checkbox checkbox-sm" />
|
||||
<span>Notify Commenters</span>
|
||||
</label>
|
||||
</div>
|
||||
<div class="space-y-2">
|
||||
<h4 class="text-[10px] font-black uppercase opacity-50">Recovery Meetings</h4>
|
||||
<h4 class="text-[10px] font-black uppercase opacity-50">
|
||||
Recovery Meetings
|
||||
</h4>
|
||||
<label class="flex items-center space-x-2 text-xs">
|
||||
<input type="checkbox" bind:checked={cfg_json.recovery_mtg_send_staff_new_email} class="checkbox checkbox-sm" />
|
||||
<input
|
||||
type="checkbox"
|
||||
bind:checked={
|
||||
cfg_json.recovery_mtg_send_staff_new_email
|
||||
}
|
||||
class="checkbox checkbox-sm" />
|
||||
<span>Notify Staff (New)</span>
|
||||
</label>
|
||||
<label class="flex items-center space-x-2 text-xs">
|
||||
<input type="checkbox" bind:checked={cfg_json.recovery_mtg_send_staff_update_email} class="checkbox checkbox-sm" />
|
||||
<input
|
||||
type="checkbox"
|
||||
bind:checked={
|
||||
cfg_json.recovery_mtg_send_staff_update_email
|
||||
}
|
||||
class="checkbox checkbox-sm" />
|
||||
<span>Notify Staff (Update)</span>
|
||||
</label>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
|
||||
{:else if active_tab === 'raw'}
|
||||
<div class="h-[50vh] animate-in fade-in duration-200">
|
||||
<div class="animate-in fade-in h-[50vh] duration-200">
|
||||
<AE_Comp_Editor_CodeMirror
|
||||
content={raw_json_str}
|
||||
bind:new_content={raw_json_str}
|
||||
language="json"
|
||||
theme_mode={$ae_loc.theme_mode}
|
||||
class_li="h-full border border-surface-500/20 rounded-lg shadow-inner"
|
||||
/>
|
||||
class_li="h-full border border-surface-500/20 rounded-lg shadow-inner" />
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
<!-- Action Bar -->
|
||||
<div class="flex justify-between items-center pt-4 border-t border-surface-500/10">
|
||||
<div
|
||||
class="border-surface-500/10 flex items-center justify-between border-t pt-4">
|
||||
<div class="flex items-center gap-2">
|
||||
<label class="flex items-center space-x-2 cursor-pointer">
|
||||
<input type="checkbox" bind:checked={cfg_json.test} class="checkbox" />
|
||||
<span class="text-xs font-bold uppercase text-warning-500">Test Mode</span>
|
||||
<label class="flex cursor-pointer items-center space-x-2">
|
||||
<input
|
||||
type="checkbox"
|
||||
bind:checked={cfg_json.test}
|
||||
class="checkbox" />
|
||||
<span class="text-warning-500 text-xs font-bold uppercase"
|
||||
>Test Mode</span>
|
||||
</label>
|
||||
</div>
|
||||
<button class="btn btn-sm variant-filled-primary font-bold shadow-lg" onclick={on_save}>
|
||||
<button
|
||||
class="btn btn-sm variant-filled-primary font-bold shadow-lg"
|
||||
onclick={on_save}>
|
||||
<Save size="1.1em" class="mr-2" /> Save Config
|
||||
</button>
|
||||
</div>
|
||||
|
||||
@@ -24,7 +24,9 @@ export async function load_ae_obj_id__account({
|
||||
log_lvl?: number;
|
||||
}): Promise<ae_Account | null> {
|
||||
if (log_lvl) {
|
||||
console.log(`*** load_ae_obj_id__account() *** account_id=${account_id}`);
|
||||
console.log(
|
||||
`*** load_ae_obj_id__account() *** account_id=${account_id}`
|
||||
);
|
||||
}
|
||||
|
||||
ae_promises.load__account_obj = await api
|
||||
@@ -39,10 +41,11 @@ export async function load_ae_obj_id__account({
|
||||
.then(async function (account_obj_get_result) {
|
||||
if (account_obj_get_result) {
|
||||
if (try_cache) {
|
||||
const processed_obj_li = await process_ae_obj__account_props({
|
||||
obj_li: [account_obj_get_result],
|
||||
log_lvl: log_lvl
|
||||
});
|
||||
const processed_obj_li =
|
||||
await process_ae_obj__account_props({
|
||||
obj_li: [account_obj_get_result],
|
||||
log_lvl: log_lvl
|
||||
});
|
||||
await db_save_ae_obj_li__ae_obj({
|
||||
db_instance: db_core,
|
||||
table_name: 'account',
|
||||
@@ -114,10 +117,11 @@ export async function load_ae_obj_li__account({
|
||||
.then(async function (account_obj_li_get_result) {
|
||||
if (account_obj_li_get_result) {
|
||||
if (try_cache) {
|
||||
const processed_obj_li = await process_ae_obj__account_props({
|
||||
obj_li: account_obj_li_get_result,
|
||||
log_lvl: log_lvl
|
||||
});
|
||||
const processed_obj_li =
|
||||
await process_ae_obj__account_props({
|
||||
obj_li: account_obj_li_get_result,
|
||||
log_lvl: log_lvl
|
||||
});
|
||||
await db_save_ae_obj_li__ae_obj({
|
||||
db_instance: db_core,
|
||||
table_name: 'account',
|
||||
@@ -164,10 +168,11 @@ export async function create_ae_obj__account({
|
||||
.then(async function (account_obj_create_result) {
|
||||
if (account_obj_create_result) {
|
||||
if (try_cache) {
|
||||
const processed_obj_li = await process_ae_obj__account_props({
|
||||
obj_li: [account_obj_create_result],
|
||||
log_lvl: log_lvl
|
||||
});
|
||||
const processed_obj_li =
|
||||
await process_ae_obj__account_props({
|
||||
obj_li: [account_obj_create_result],
|
||||
log_lvl: log_lvl
|
||||
});
|
||||
await db_save_ae_obj_li__ae_obj({
|
||||
db_instance: db_core,
|
||||
table_name: 'account',
|
||||
@@ -206,7 +211,10 @@ export async function update_ae_obj__account({
|
||||
log_lvl?: number;
|
||||
}): Promise<ae_Account | null> {
|
||||
if (log_lvl) {
|
||||
console.log(`*** update_ae_obj__account() *** account_id=${account_id}`, data_kv);
|
||||
console.log(
|
||||
`*** update_ae_obj__account() *** account_id=${account_id}`,
|
||||
data_kv
|
||||
);
|
||||
}
|
||||
|
||||
const result = await api.update_ae_obj({
|
||||
@@ -256,7 +264,9 @@ export async function delete_ae_obj_id__account({
|
||||
log_lvl?: number;
|
||||
}) {
|
||||
if (log_lvl) {
|
||||
console.log(`*** delete_ae_obj_id__account() *** account_id=${account_id}`);
|
||||
console.log(
|
||||
`*** delete_ae_obj_id__account() *** account_id=${account_id}`
|
||||
);
|
||||
}
|
||||
|
||||
ae_promises.delete__account_obj = await api
|
||||
@@ -337,11 +347,15 @@ async function _process_generic_props<T extends Record<string, any>>({
|
||||
const updated = processed_obj.updated_on ?? processed_obj.created_on;
|
||||
const name = processed_obj.name ?? '';
|
||||
|
||||
(processed_obj as any).tmp_sort_1 = `${group}_${priority}_${sort}_${updated}`;
|
||||
(processed_obj as any).tmp_sort_2 = `${group}_${priority}_${sort}_${name}_${updated}`;
|
||||
(processed_obj as any).tmp_sort_1 =
|
||||
`${group}_${priority}_${sort}_${updated}`;
|
||||
(processed_obj as any).tmp_sort_2 =
|
||||
`${group}_${priority}_${sort}_${name}_${updated}`;
|
||||
|
||||
if (specific_processor) {
|
||||
processed_obj = await Promise.resolve(specific_processor(processed_obj));
|
||||
processed_obj = await Promise.resolve(
|
||||
specific_processor(processed_obj)
|
||||
);
|
||||
}
|
||||
|
||||
processed_obj_li.push(processed_obj as T);
|
||||
|
||||
@@ -19,7 +19,9 @@ export async function load_ae_obj_id__activity_log({
|
||||
log_lvl?: number;
|
||||
}): Promise<ae_ActivityLog | null> {
|
||||
if (log_lvl) {
|
||||
console.log(`*** load_ae_obj_id__activity_log() *** activity_log_id=${activity_log_id}`);
|
||||
console.log(
|
||||
`*** load_ae_obj_id__activity_log() *** activity_log_id=${activity_log_id}`
|
||||
);
|
||||
}
|
||||
|
||||
ae_promises.load__activity_log_obj = await api.get_ae_obj({
|
||||
@@ -61,7 +63,9 @@ export async function load_ae_obj_li__activity_log({
|
||||
log_lvl?: number;
|
||||
}): Promise<ae_ActivityLog[]> {
|
||||
if (log_lvl) {
|
||||
console.log(`*** load_ae_obj_li__activity_log() *** for_obj_id=${for_obj_id}`);
|
||||
console.log(
|
||||
`*** load_ae_obj_li__activity_log() *** for_obj_id=${for_obj_id}`
|
||||
);
|
||||
}
|
||||
|
||||
ae_promises.load__activity_log_obj_li = await api.get_ae_obj_li({
|
||||
@@ -96,11 +100,15 @@ export async function create_ae_obj__activity_log({
|
||||
log_lvl?: number;
|
||||
}): Promise<ae_ActivityLog | null> {
|
||||
if (log_lvl) {
|
||||
console.log(`*** create_ae_obj__activity_log() *** account_id=${account_id}`);
|
||||
console.log(
|
||||
`*** create_ae_obj__activity_log() *** account_id=${account_id}`
|
||||
);
|
||||
}
|
||||
|
||||
if (!account_id) {
|
||||
console.log(`ERROR: Core - Activity Log - account_id required to create`);
|
||||
console.log(
|
||||
`ERROR: Core - Activity Log - account_id required to create`
|
||||
);
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -133,7 +141,9 @@ export async function update_ae_obj__activity_log({
|
||||
log_lvl?: number;
|
||||
}): Promise<ae_ActivityLog | null> {
|
||||
if (log_lvl) {
|
||||
console.log(`*** update_ae_obj__activity_log() *** activity_log_id=${activity_log_id}`);
|
||||
console.log(
|
||||
`*** update_ae_obj__activity_log() *** activity_log_id=${activity_log_id}`
|
||||
);
|
||||
}
|
||||
|
||||
ae_promises.update__activity_log_obj = await api.update_ae_obj({
|
||||
@@ -151,7 +161,6 @@ export async function update_ae_obj__activity_log({
|
||||
// Updated 2026-01-07
|
||||
|
||||
export async function qry__activity_log({
|
||||
|
||||
api_cfg,
|
||||
|
||||
account_id,
|
||||
@@ -173,9 +182,7 @@ export async function qry__activity_log({
|
||||
order_by_li = { created_on: 'DESC' },
|
||||
|
||||
log_lvl = 0
|
||||
|
||||
}: {
|
||||
|
||||
api_cfg: any;
|
||||
|
||||
account_id: string;
|
||||
@@ -197,49 +204,36 @@ export async function qry__activity_log({
|
||||
order_by_li?: Record<string, 'ASC' | 'DESC'>;
|
||||
|
||||
log_lvl?: number;
|
||||
|
||||
}): Promise<ae_ActivityLog[]> {
|
||||
|
||||
const search_query: any = {};
|
||||
|
||||
const filters: any[] = [];
|
||||
|
||||
|
||||
|
||||
if (account_id) {
|
||||
|
||||
filters.push({ field: 'account_id_random', op: 'eq', value: account_id });
|
||||
|
||||
filters.push({
|
||||
field: 'account_id_random',
|
||||
op: 'eq',
|
||||
value: account_id
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
|
||||
if (qry_person_id) {
|
||||
|
||||
filters.push({ field: 'person_id_random', op: 'eq', value: qry_person_id });
|
||||
|
||||
filters.push({
|
||||
field: 'person_id_random',
|
||||
op: 'eq',
|
||||
value: qry_person_id
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
|
||||
if (filters.length > 0) {
|
||||
|
||||
search_query.and = filters;
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
if (qry_str) {
|
||||
|
||||
search_query.q = qry_str;
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
ae_promises.load__activity_log_obj_li = await api.search_ae_obj({
|
||||
|
||||
api_cfg,
|
||||
|
||||
obj_type: 'activity_log',
|
||||
@@ -259,13 +253,9 @@ export async function qry__activity_log({
|
||||
order_by_li,
|
||||
|
||||
log_lvl
|
||||
|
||||
});
|
||||
|
||||
|
||||
|
||||
return ae_promises.load__activity_log_obj_li;
|
||||
|
||||
}
|
||||
|
||||
// Updated 2026-02-16
|
||||
@@ -338,14 +328,21 @@ async function _process_generic_props<T extends Record<string, any>>({
|
||||
const group = processed_obj.group ?? '0';
|
||||
const priority = processed_obj.priority ? 1 : 0;
|
||||
const sort = processed_obj.sort ?? '0';
|
||||
const updated = processed_obj.updated_on ?? processed_obj.created_on ?? new Date(0).toISOString();
|
||||
const updated =
|
||||
processed_obj.updated_on ??
|
||||
processed_obj.created_on ??
|
||||
new Date(0).toISOString();
|
||||
const name = processed_obj.name ?? '';
|
||||
|
||||
(processed_obj as any).tmp_sort_1 = `${group}_${priority}_${sort}_${updated}`;
|
||||
(processed_obj as any).tmp_sort_2 = `${group}_${priority}_${sort}_${name}_${updated}`;
|
||||
(processed_obj as any).tmp_sort_1 =
|
||||
`${group}_${priority}_${sort}_${updated}`;
|
||||
(processed_obj as any).tmp_sort_2 =
|
||||
`${group}_${priority}_${sort}_${name}_${updated}`;
|
||||
|
||||
if (specific_processor) {
|
||||
processed_obj = await Promise.resolve(specific_processor(processed_obj));
|
||||
processed_obj = await Promise.resolve(
|
||||
specific_processor(processed_obj)
|
||||
);
|
||||
}
|
||||
|
||||
processed_obj_li.push(processed_obj as T);
|
||||
@@ -367,5 +364,3 @@ export async function process_ae_obj__activity_log_props({
|
||||
log_lvl
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -23,29 +23,34 @@ export async function load_ae_obj_id__address({
|
||||
try_cache?: boolean;
|
||||
log_lvl?: number;
|
||||
}): Promise<ae_Address | null> {
|
||||
ae_promises.load__address_obj = await api.get_ae_obj({
|
||||
api_cfg,
|
||||
obj_type: 'address',
|
||||
obj_id: address_id,
|
||||
view,
|
||||
params,
|
||||
log_lvl
|
||||
}).then(async (result) => {
|
||||
if (result) {
|
||||
if (try_cache) {
|
||||
const processed = await process_ae_obj__address_props({ obj_li: [result], log_lvl });
|
||||
await db_save_ae_obj_li__ae_obj({
|
||||
db_instance: db_core,
|
||||
table_name: 'address',
|
||||
obj_li: processed,
|
||||
properties_to_save,
|
||||
log_lvl
|
||||
});
|
||||
ae_promises.load__address_obj = await api
|
||||
.get_ae_obj({
|
||||
api_cfg,
|
||||
obj_type: 'address',
|
||||
obj_id: address_id,
|
||||
view,
|
||||
params,
|
||||
log_lvl
|
||||
})
|
||||
.then(async (result) => {
|
||||
if (result) {
|
||||
if (try_cache) {
|
||||
const processed = await process_ae_obj__address_props({
|
||||
obj_li: [result],
|
||||
log_lvl
|
||||
});
|
||||
await db_save_ae_obj_li__ae_obj({
|
||||
db_instance: db_core,
|
||||
table_name: 'address',
|
||||
obj_li: processed,
|
||||
properties_to_save,
|
||||
log_lvl
|
||||
});
|
||||
}
|
||||
return result;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
return null;
|
||||
});
|
||||
return null;
|
||||
});
|
||||
return ae_promises.load__address_obj;
|
||||
}
|
||||
|
||||
@@ -77,34 +82,39 @@ export async function load_ae_obj_li__address({
|
||||
try_cache?: boolean;
|
||||
log_lvl?: number;
|
||||
}): Promise<ae_Address[]> {
|
||||
ae_promises.load__address_obj_li = await api.get_ae_obj_li({
|
||||
api_cfg,
|
||||
obj_type: 'address',
|
||||
for_obj_type,
|
||||
for_obj_id,
|
||||
enabled,
|
||||
hidden,
|
||||
view,
|
||||
limit,
|
||||
offset,
|
||||
order_by_li,
|
||||
log_lvl
|
||||
}).then(async (result) => {
|
||||
if (result && Array.isArray(result)) {
|
||||
if (try_cache) {
|
||||
const processed = await process_ae_obj__address_props({ obj_li: result, log_lvl });
|
||||
await db_save_ae_obj_li__ae_obj({
|
||||
db_instance: db_core,
|
||||
table_name: 'address',
|
||||
obj_li: processed,
|
||||
properties_to_save,
|
||||
log_lvl
|
||||
});
|
||||
ae_promises.load__address_obj_li = await api
|
||||
.get_ae_obj_li({
|
||||
api_cfg,
|
||||
obj_type: 'address',
|
||||
for_obj_type,
|
||||
for_obj_id,
|
||||
enabled,
|
||||
hidden,
|
||||
view,
|
||||
limit,
|
||||
offset,
|
||||
order_by_li,
|
||||
log_lvl
|
||||
})
|
||||
.then(async (result) => {
|
||||
if (result && Array.isArray(result)) {
|
||||
if (try_cache) {
|
||||
const processed = await process_ae_obj__address_props({
|
||||
obj_li: result,
|
||||
log_lvl
|
||||
});
|
||||
await db_save_ae_obj_li__ae_obj({
|
||||
db_instance: db_core,
|
||||
table_name: 'address',
|
||||
obj_li: processed,
|
||||
properties_to_save,
|
||||
log_lvl
|
||||
});
|
||||
}
|
||||
return result;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
return [];
|
||||
});
|
||||
return [];
|
||||
});
|
||||
return ae_promises.load__address_obj_li;
|
||||
}
|
||||
|
||||
@@ -136,7 +146,10 @@ export async function create_ae_obj__address({
|
||||
});
|
||||
|
||||
if (result && try_cache) {
|
||||
const processed = await process_ae_obj__address_props({ obj_li: [result], log_lvl });
|
||||
const processed = await process_ae_obj__address_props({
|
||||
obj_li: [result],
|
||||
log_lvl
|
||||
});
|
||||
await db_save_ae_obj_li__ae_obj({
|
||||
db_instance: db_core,
|
||||
table_name: 'address',
|
||||
@@ -174,7 +187,10 @@ export async function update_ae_obj__address({
|
||||
});
|
||||
|
||||
if (result && try_cache) {
|
||||
const processed = await process_ae_obj__address_props({ obj_li: [result], log_lvl });
|
||||
const processed = await process_ae_obj__address_props({
|
||||
obj_li: [result],
|
||||
log_lvl
|
||||
});
|
||||
await db_save_ae_obj_li__ae_obj({
|
||||
db_instance: db_core,
|
||||
table_name: 'address',
|
||||
@@ -269,7 +285,9 @@ async function _process_generic_props<T extends Record<string, any>>({
|
||||
}
|
||||
|
||||
if (specific_processor) {
|
||||
processed_obj = await Promise.resolve(specific_processor(processed_obj));
|
||||
processed_obj = await Promise.resolve(
|
||||
specific_processor(processed_obj)
|
||||
);
|
||||
}
|
||||
|
||||
processed_obj_li.push(processed_obj as T);
|
||||
@@ -290,4 +308,4 @@ export async function process_ae_obj__address_props({
|
||||
obj_type: 'address',
|
||||
log_lvl
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,29 +23,34 @@ export async function load_ae_obj_id__contact({
|
||||
try_cache?: boolean;
|
||||
log_lvl?: number;
|
||||
}): Promise<ae_Contact | null> {
|
||||
ae_promises.load__contact_obj = await api.get_ae_obj({
|
||||
api_cfg,
|
||||
obj_type: 'contact',
|
||||
obj_id: contact_id,
|
||||
view,
|
||||
params,
|
||||
log_lvl
|
||||
}).then(async (result) => {
|
||||
if (result) {
|
||||
if (try_cache) {
|
||||
const processed = await process_ae_obj__contact_props({ obj_li: [result], log_lvl });
|
||||
await db_save_ae_obj_li__ae_obj({
|
||||
db_instance: db_core,
|
||||
table_name: 'contact',
|
||||
obj_li: processed,
|
||||
properties_to_save,
|
||||
log_lvl
|
||||
});
|
||||
ae_promises.load__contact_obj = await api
|
||||
.get_ae_obj({
|
||||
api_cfg,
|
||||
obj_type: 'contact',
|
||||
obj_id: contact_id,
|
||||
view,
|
||||
params,
|
||||
log_lvl
|
||||
})
|
||||
.then(async (result) => {
|
||||
if (result) {
|
||||
if (try_cache) {
|
||||
const processed = await process_ae_obj__contact_props({
|
||||
obj_li: [result],
|
||||
log_lvl
|
||||
});
|
||||
await db_save_ae_obj_li__ae_obj({
|
||||
db_instance: db_core,
|
||||
table_name: 'contact',
|
||||
obj_li: processed,
|
||||
properties_to_save,
|
||||
log_lvl
|
||||
});
|
||||
}
|
||||
return result;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
return null;
|
||||
});
|
||||
return null;
|
||||
});
|
||||
return ae_promises.load__contact_obj;
|
||||
}
|
||||
|
||||
@@ -75,34 +80,39 @@ export async function load_ae_obj_li__contact({
|
||||
try_cache?: boolean;
|
||||
log_lvl?: number;
|
||||
}): Promise<ae_Contact[]> {
|
||||
ae_promises.load__contact_obj_li = await api.get_ae_obj_li({
|
||||
api_cfg,
|
||||
obj_type: 'contact',
|
||||
for_obj_type,
|
||||
for_obj_id,
|
||||
enabled,
|
||||
hidden,
|
||||
view,
|
||||
limit,
|
||||
offset,
|
||||
order_by_li,
|
||||
log_lvl
|
||||
}).then(async (result) => {
|
||||
if (result && Array.isArray(result)) {
|
||||
if (try_cache) {
|
||||
const processed = await process_ae_obj__contact_props({ obj_li: result, log_lvl });
|
||||
await db_save_ae_obj_li__ae_obj({
|
||||
db_instance: db_core,
|
||||
table_name: 'contact',
|
||||
obj_li: processed,
|
||||
properties_to_save,
|
||||
log_lvl
|
||||
});
|
||||
ae_promises.load__contact_obj_li = await api
|
||||
.get_ae_obj_li({
|
||||
api_cfg,
|
||||
obj_type: 'contact',
|
||||
for_obj_type,
|
||||
for_obj_id,
|
||||
enabled,
|
||||
hidden,
|
||||
view,
|
||||
limit,
|
||||
offset,
|
||||
order_by_li,
|
||||
log_lvl
|
||||
})
|
||||
.then(async (result) => {
|
||||
if (result && Array.isArray(result)) {
|
||||
if (try_cache) {
|
||||
const processed = await process_ae_obj__contact_props({
|
||||
obj_li: result,
|
||||
log_lvl
|
||||
});
|
||||
await db_save_ae_obj_li__ae_obj({
|
||||
db_instance: db_core,
|
||||
table_name: 'contact',
|
||||
obj_li: processed,
|
||||
properties_to_save,
|
||||
log_lvl
|
||||
});
|
||||
}
|
||||
return result;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
return [];
|
||||
});
|
||||
return [];
|
||||
});
|
||||
return ae_promises.load__contact_obj_li;
|
||||
}
|
||||
|
||||
@@ -134,7 +144,10 @@ export async function create_ae_obj__contact({
|
||||
});
|
||||
|
||||
if (result && try_cache) {
|
||||
const processed = await process_ae_obj__contact_props({ obj_li: [result], log_lvl });
|
||||
const processed = await process_ae_obj__contact_props({
|
||||
obj_li: [result],
|
||||
log_lvl
|
||||
});
|
||||
await db_save_ae_obj_li__ae_obj({
|
||||
db_instance: db_core,
|
||||
table_name: 'contact',
|
||||
@@ -172,7 +185,10 @@ export async function update_ae_obj__contact({
|
||||
});
|
||||
|
||||
if (result && try_cache) {
|
||||
const processed = await process_ae_obj__contact_props({ obj_li: [result], log_lvl });
|
||||
const processed = await process_ae_obj__contact_props({
|
||||
obj_li: [result],
|
||||
log_lvl
|
||||
});
|
||||
await db_save_ae_obj_li__ae_obj({
|
||||
db_instance: db_core,
|
||||
table_name: 'contact',
|
||||
@@ -267,7 +283,9 @@ async function _process_generic_props<T extends Record<string, any>>({
|
||||
}
|
||||
|
||||
if (specific_processor) {
|
||||
processed_obj = await Promise.resolve(specific_processor(processed_obj));
|
||||
processed_obj = await Promise.resolve(
|
||||
specific_processor(processed_obj)
|
||||
);
|
||||
}
|
||||
|
||||
processed_obj_li.push(processed_obj as T);
|
||||
@@ -288,4 +306,4 @@ export async function process_ae_obj__contact_props({
|
||||
obj_type: 'contact',
|
||||
log_lvl
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -39,10 +39,12 @@ export async function load_ae_obj_id__person({
|
||||
.then(async function (result) {
|
||||
if (result) {
|
||||
if (try_cache) {
|
||||
const processed_obj_li = await process_ae_obj__person_props({
|
||||
obj_li: [result],
|
||||
log_lvl
|
||||
});
|
||||
const processed_obj_li = await process_ae_obj__person_props(
|
||||
{
|
||||
obj_li: [result],
|
||||
log_lvl
|
||||
}
|
||||
);
|
||||
await db_save_ae_obj_li__ae_obj({
|
||||
db_instance: db_core,
|
||||
table_name: 'person',
|
||||
@@ -98,7 +100,9 @@ export async function load_ae_obj_li__person({
|
||||
log_lvl?: number;
|
||||
}): Promise<ae_Person[]> {
|
||||
if (log_lvl) {
|
||||
console.log(`*** load_ae_obj_li__person() *** for_obj_id=${for_obj_id}`);
|
||||
console.log(
|
||||
`*** load_ae_obj_li__person() *** for_obj_id=${for_obj_id}`
|
||||
);
|
||||
}
|
||||
|
||||
let promise;
|
||||
@@ -109,11 +113,19 @@ export async function load_ae_obj_li__person({
|
||||
};
|
||||
|
||||
if (qry_user_id) {
|
||||
search_query.and.push({ field: 'user_id_random', op: 'eq', value: qry_user_id });
|
||||
search_query.and.push({
|
||||
field: 'user_id_random',
|
||||
op: 'eq',
|
||||
value: qry_user_id
|
||||
});
|
||||
}
|
||||
|
||||
if (qry_email) {
|
||||
search_query.and.push({ field: 'primary_email', op: 'eq', value: qry_email });
|
||||
search_query.and.push({
|
||||
field: 'primary_email',
|
||||
op: 'eq',
|
||||
value: qry_email
|
||||
});
|
||||
}
|
||||
|
||||
if (for_obj_id) {
|
||||
@@ -161,26 +173,30 @@ export async function load_ae_obj_li__person({
|
||||
});
|
||||
}
|
||||
|
||||
ae_promises.load__person_obj_li = await promise.then(async function (result_li) {
|
||||
if (result_li) {
|
||||
if (try_cache) {
|
||||
const processed_obj_li = await process_ae_obj__person_props({
|
||||
obj_li: result_li,
|
||||
log_lvl
|
||||
});
|
||||
await db_save_ae_obj_li__ae_obj({
|
||||
db_instance: db_core,
|
||||
table_name: 'person',
|
||||
obj_li: processed_obj_li,
|
||||
properties_to_save: properties_to_save,
|
||||
log_lvl
|
||||
});
|
||||
ae_promises.load__person_obj_li = await promise.then(
|
||||
async function (result_li) {
|
||||
if (result_li) {
|
||||
if (try_cache) {
|
||||
const processed_obj_li = await process_ae_obj__person_props(
|
||||
{
|
||||
obj_li: result_li,
|
||||
log_lvl
|
||||
}
|
||||
);
|
||||
await db_save_ae_obj_li__ae_obj({
|
||||
db_instance: db_core,
|
||||
table_name: 'person',
|
||||
obj_li: processed_obj_li,
|
||||
properties_to_save: properties_to_save,
|
||||
log_lvl
|
||||
});
|
||||
}
|
||||
return result_li;
|
||||
} else {
|
||||
return [];
|
||||
}
|
||||
return result_li;
|
||||
} else {
|
||||
return [];
|
||||
}
|
||||
});
|
||||
);
|
||||
|
||||
return ae_promises.load__person_obj_li;
|
||||
}
|
||||
@@ -411,11 +427,15 @@ async function _process_generic_props<T extends Record<string, any>>({
|
||||
const updated = processed_obj.updated_on ?? processed_obj.created_on;
|
||||
const name = processed_obj.full_name ?? processed_obj.name ?? '';
|
||||
|
||||
(processed_obj as any).tmp_sort_1 = `${group}_${priority}_${sort}_${updated}`;
|
||||
(processed_obj as any).tmp_sort_2 = `${group}_${priority}_${sort}_${name}_${updated}`;
|
||||
(processed_obj as any).tmp_sort_1 =
|
||||
`${group}_${priority}_${sort}_${updated}`;
|
||||
(processed_obj as any).tmp_sort_2 =
|
||||
`${group}_${priority}_${sort}_${name}_${updated}`;
|
||||
|
||||
if (specific_processor) {
|
||||
processed_obj = await Promise.resolve(specific_processor(processed_obj));
|
||||
processed_obj = await Promise.resolve(
|
||||
specific_processor(processed_obj)
|
||||
);
|
||||
}
|
||||
|
||||
processed_obj_li.push(processed_obj as T);
|
||||
|
||||
@@ -113,10 +113,18 @@ export async function lookup_site_domain({
|
||||
try {
|
||||
cached = await db_core.site_domain.where('fqdn').equals(fqdn).first();
|
||||
if (cached) {
|
||||
if (log_lvl) console.log('BOOTSTRAP: Cache hit. Returning cached site domain immediately.');
|
||||
if (log_lvl)
|
||||
console.log(
|
||||
'BOOTSTRAP: Cache hit. Returning cached site domain immediately.'
|
||||
);
|
||||
|
||||
// Trigger background refresh to keep cache fresh, but don't await it
|
||||
_refresh_site_domain_background({ api_cfg, fqdn, view, log_lvl: 0 });
|
||||
_refresh_site_domain_background({
|
||||
api_cfg,
|
||||
fqdn,
|
||||
view,
|
||||
log_lvl: 0
|
||||
});
|
||||
|
||||
return cached as any;
|
||||
}
|
||||
@@ -125,13 +133,23 @@ export async function lookup_site_domain({
|
||||
}
|
||||
|
||||
// 2. SLOW PATH: Wait for API if cache is empty
|
||||
return await _refresh_site_domain_background({ api_cfg, fqdn, view, log_lvl });
|
||||
return await _refresh_site_domain_background({
|
||||
api_cfg,
|
||||
fqdn,
|
||||
view,
|
||||
log_lvl
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Internal helper to perform the actual API fetch and cache update
|
||||
*/
|
||||
async function _refresh_site_domain_background({ api_cfg, fqdn, view, log_lvl }: any) {
|
||||
async function _refresh_site_domain_background({
|
||||
api_cfg,
|
||||
fqdn,
|
||||
view,
|
||||
log_lvl
|
||||
}: any) {
|
||||
try {
|
||||
const guest_api_cfg = { ...api_cfg };
|
||||
guest_api_cfg.headers = { ...api_cfg.headers };
|
||||
@@ -144,7 +162,7 @@ async function _refresh_site_domain_background({ api_cfg, fqdn, view, log_lvl }:
|
||||
'JWT'
|
||||
];
|
||||
|
||||
auth_props.forEach(prop => {
|
||||
auth_props.forEach((prop) => {
|
||||
delete guest_api_cfg.headers[prop];
|
||||
delete guest_api_cfg.headers[prop.toLowerCase()];
|
||||
delete guest_api_cfg.headers[prop.replaceAll('-', '_')];
|
||||
@@ -481,11 +499,12 @@ export async function load_ae_obj_li__site_domain({
|
||||
.then(async function (domain_li) {
|
||||
if (domain_li) {
|
||||
if (try_cache) {
|
||||
const processed_obj_li = await process_ae_obj__site_domain_props({
|
||||
obj_li: domain_li,
|
||||
site_id,
|
||||
log_lvl
|
||||
});
|
||||
const processed_obj_li =
|
||||
await process_ae_obj__site_domain_props({
|
||||
obj_li: domain_li,
|
||||
site_id,
|
||||
log_lvl
|
||||
});
|
||||
await db_save_ae_obj_li__ae_obj({
|
||||
db_instance: db_core,
|
||||
table_name: 'site_domain',
|
||||
@@ -729,11 +748,15 @@ async function _process_generic_props<T extends Record<string, any>>({
|
||||
const updated = processed_obj.updated_on ?? processed_obj.created_on;
|
||||
const name = processed_obj.name ?? processed_obj.fqdn ?? '';
|
||||
|
||||
(processed_obj as any).tmp_sort_1 = `${group}_${priority}_${sort}_${updated}`;
|
||||
(processed_obj as any).tmp_sort_2 = `${group}_${priority}_${sort}_${name}_${updated}`;
|
||||
(processed_obj as any).tmp_sort_1 =
|
||||
`${group}_${priority}_${sort}_${updated}`;
|
||||
(processed_obj as any).tmp_sort_2 =
|
||||
`${group}_${priority}_${sort}_${name}_${updated}`;
|
||||
|
||||
if (specific_processor) {
|
||||
processed_obj = await Promise.resolve(specific_processor(processed_obj));
|
||||
processed_obj = await Promise.resolve(
|
||||
specific_processor(processed_obj)
|
||||
);
|
||||
}
|
||||
|
||||
processed_obj_li.push(processed_obj as T);
|
||||
|
||||
@@ -93,7 +93,9 @@ export async function load_ae_obj_li__user({
|
||||
log_lvl?: number;
|
||||
}): Promise<ae_User[]> {
|
||||
if (log_lvl) {
|
||||
console.log(`*** load_ae_obj_li__user() *** for_obj_id=${for_obj_id} include_global=${include_global} qry_str=${qry_str}`);
|
||||
console.log(
|
||||
`*** load_ae_obj_li__user() *** for_obj_id=${for_obj_id} include_global=${include_global} qry_str=${qry_str}`
|
||||
);
|
||||
}
|
||||
|
||||
// SCENARIO A: Text Search
|
||||
@@ -107,9 +109,17 @@ export async function load_ae_obj_li__user({
|
||||
]
|
||||
});
|
||||
} else if (for_obj_id) {
|
||||
search_query.and.push({ field: `account_id_random`, op: 'eq', value: for_obj_id });
|
||||
search_query.and.push({
|
||||
field: `account_id_random`,
|
||||
op: 'eq',
|
||||
value: for_obj_id
|
||||
});
|
||||
} else if (include_global) {
|
||||
search_query.and.push({ field: `account_id_random`, op: 'eq', value: null });
|
||||
search_query.and.push({
|
||||
field: `account_id_random`,
|
||||
op: 'eq',
|
||||
value: null
|
||||
});
|
||||
}
|
||||
|
||||
return await api.search_ae_obj({
|
||||
@@ -130,13 +140,33 @@ export async function load_ae_obj_li__user({
|
||||
if (for_obj_id && include_global) {
|
||||
if (log_lvl) console.log('Strategy: Multi-call (Account + Global)');
|
||||
const [acct_users, global_users] = await Promise.all([
|
||||
load_ae_obj_li__user({ api_cfg, for_obj_id, include_global: false, enabled, hidden, view, limit, log_lvl }),
|
||||
load_ae_obj_li__user({ api_cfg, for_obj_id: null, include_global: true, enabled, hidden, view, limit, log_lvl })
|
||||
load_ae_obj_li__user({
|
||||
api_cfg,
|
||||
for_obj_id,
|
||||
include_global: false,
|
||||
enabled,
|
||||
hidden,
|
||||
view,
|
||||
limit,
|
||||
log_lvl
|
||||
}),
|
||||
load_ae_obj_li__user({
|
||||
api_cfg,
|
||||
for_obj_id: null,
|
||||
include_global: true,
|
||||
enabled,
|
||||
hidden,
|
||||
view,
|
||||
limit,
|
||||
log_lvl
|
||||
})
|
||||
]);
|
||||
|
||||
// Merge and unique-ify by ID
|
||||
const merged = [...acct_users, ...global_users];
|
||||
const unique = Array.from(new Map(merged.map(u => [u.user_id_random, u])).values());
|
||||
const unique = Array.from(
|
||||
new Map(merged.map((u) => [u.user_id_random, u])).values()
|
||||
);
|
||||
return unique;
|
||||
}
|
||||
|
||||
@@ -162,7 +192,8 @@ export async function load_ae_obj_li__user({
|
||||
}
|
||||
|
||||
// SCENARIO D: Account Only or Everything (confirmed working List API)
|
||||
if (log_lvl) console.log(`Strategy: Standard List API (for_obj_id=${for_obj_id})`);
|
||||
if (log_lvl)
|
||||
console.log(`Strategy: Standard List API (for_obj_id=${for_obj_id})`);
|
||||
return await api.get_ae_obj_li({
|
||||
api_cfg,
|
||||
obj_type: 'user',
|
||||
@@ -383,7 +414,10 @@ export async function auth_ae_obj__username_password({
|
||||
});
|
||||
|
||||
if (log_lvl) {
|
||||
console.log('ae_promises.auth__username_password:', ae_promises.auth__username_password);
|
||||
console.log(
|
||||
'ae_promises.auth__username_password:',
|
||||
ae_promises.auth__username_password
|
||||
);
|
||||
}
|
||||
return ae_promises.auth__username_password;
|
||||
}
|
||||
@@ -450,7 +484,10 @@ export async function auth_ae_obj__user_id_user_auth_key({
|
||||
});
|
||||
|
||||
if (log_lvl) {
|
||||
console.log('ae_promises.auth__user_id_user_key:', ae_promises.auth__user_id_user_key);
|
||||
console.log(
|
||||
'ae_promises.auth__user_id_user_key:',
|
||||
ae_promises.auth__user_id_user_key
|
||||
);
|
||||
}
|
||||
return ae_promises.auth__user_id_user_key;
|
||||
}
|
||||
@@ -525,7 +562,9 @@ export async function qry_ae_obj_li__user_email({
|
||||
log_lvl?: number;
|
||||
}) {
|
||||
if (log_lvl) {
|
||||
console.log(`*** qry_ae_obj_li__user_email() *** account_id=${account_id} email=${email}`);
|
||||
console.log(
|
||||
`*** qry_ae_obj_li__user_email() *** account_id=${account_id} email=${email}`
|
||||
);
|
||||
}
|
||||
|
||||
const endpoint = '/user/lookup_email';
|
||||
@@ -676,11 +715,15 @@ async function _process_generic_props<T extends Record<string, any>>({
|
||||
const updated = processed_obj.updated_on ?? processed_obj.created_on;
|
||||
const name = processed_obj.username ?? processed_obj.name ?? '';
|
||||
|
||||
(processed_obj as any).tmp_sort_1 = `${group}_${priority}_${sort}_${updated}`;
|
||||
(processed_obj as any).tmp_sort_2 = `${group}_${priority}_${sort}_${name}_${updated}`;
|
||||
(processed_obj as any).tmp_sort_1 =
|
||||
`${group}_${priority}_${sort}_${updated}`;
|
||||
(processed_obj as any).tmp_sort_2 =
|
||||
`${group}_${priority}_${sort}_${name}_${updated}`;
|
||||
|
||||
if (specific_processor) {
|
||||
processed_obj = await Promise.resolve(specific_processor(processed_obj));
|
||||
processed_obj = await Promise.resolve(
|
||||
specific_processor(processed_obj)
|
||||
);
|
||||
}
|
||||
|
||||
processed_obj_li.push(processed_obj as T);
|
||||
@@ -701,4 +744,4 @@ export async function process_ae_obj__user_props({
|
||||
obj_type: 'user',
|
||||
log_lvl
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,7 +25,10 @@ import {
|
||||
auth_ae_obj__user_id_change_password
|
||||
} from '$lib/ae_core/ae_core__user';
|
||||
|
||||
import { generate_qr_code, js_generate_qr_code } from '$lib/ae_core/core__qr_code';
|
||||
import {
|
||||
generate_qr_code,
|
||||
js_generate_qr_code
|
||||
} from '$lib/ae_core/core__qr_code';
|
||||
|
||||
import { check_hosted_file_obj_w_hash } from '$lib/ae_core/core__check_hosted_file_obj_w_hash';
|
||||
|
||||
@@ -161,7 +164,9 @@ async function load_ae_obj_code__data_store({
|
||||
}
|
||||
|
||||
if (!get_ds_result.data_store_id_random) {
|
||||
console.log('*ae_func* Something went wrong? No data store ID found.');
|
||||
console.log(
|
||||
'*ae_func* Something went wrong? No data store ID found.'
|
||||
);
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -240,7 +245,10 @@ async function load_ae_obj_code__data_store({
|
||||
get_ds_result
|
||||
);
|
||||
}
|
||||
localStorage.setItem(`${key_prefix}${code}`, JSON.stringify(get_ds_result));
|
||||
localStorage.setItem(
|
||||
`${key_prefix}${code}`,
|
||||
JSON.stringify(get_ds_result)
|
||||
);
|
||||
} else {
|
||||
if (log_lvl) {
|
||||
console.log(
|
||||
@@ -491,7 +499,10 @@ async function download_export__obj_type({
|
||||
log_lvl: log_lvl
|
||||
});
|
||||
|
||||
console.log('ae_promises.download__export_file:', ae_promises.download__export_file);
|
||||
console.log(
|
||||
'ae_promises.download__export_file:',
|
||||
ae_promises.download__export_file
|
||||
);
|
||||
return ae_promises.download__export_file;
|
||||
}
|
||||
|
||||
|
||||
@@ -13,12 +13,17 @@ export function add_url_params({
|
||||
log_lvl?: number;
|
||||
}) {
|
||||
if (log_lvl) {
|
||||
console.log(`*** add_url_params() *** base_url=${base_url} endpoint=${endpoint}`, params);
|
||||
console.log(
|
||||
`*** add_url_params() *** base_url=${base_url} endpoint=${endpoint}`,
|
||||
params
|
||||
);
|
||||
}
|
||||
|
||||
const url_obj = new URL(endpoint, base_url);
|
||||
|
||||
Object.keys(params).forEach((key) => url_obj.searchParams.append(key, params[key]));
|
||||
Object.keys(params).forEach((key) =>
|
||||
url_obj.searchParams.append(key, params[key])
|
||||
);
|
||||
|
||||
if (log_lvl) {
|
||||
console.log('New URL:', url_obj.toString());
|
||||
@@ -30,7 +35,13 @@ export function add_url_params({
|
||||
|
||||
// This is used to clean the header property names. Not underscores allowed in the header names.
|
||||
// Updated 2025-01-28
|
||||
export function clean_headers({ headers, log_lvl = 0 }: { headers: any; log_lvl?: number }) {
|
||||
export function clean_headers({
|
||||
headers,
|
||||
log_lvl = 0
|
||||
}: {
|
||||
headers: any;
|
||||
log_lvl?: number;
|
||||
}) {
|
||||
if (log_lvl) {
|
||||
console.log(`*** clean_headers() ***`, headers);
|
||||
}
|
||||
|
||||
@@ -33,8 +33,14 @@ async function _refresh_lu_country_background({
|
||||
if (result?.length) {
|
||||
await db_lookups.lu_country.clear();
|
||||
await db_lookups.lu_country.bulkPut(result);
|
||||
await db_lookups.lu_cache_meta.put({ lu_type: 'country', refreshed_at: Date.now() });
|
||||
if (log_lvl) console.log(`lu_country: saved ${result.length} records to IDB`);
|
||||
await db_lookups.lu_cache_meta.put({
|
||||
lu_type: 'country',
|
||||
refreshed_at: Date.now()
|
||||
});
|
||||
if (log_lvl)
|
||||
console.log(
|
||||
`lu_country: saved ${result.length} records to IDB`
|
||||
);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('lu_country refresh failed:', error);
|
||||
@@ -59,6 +65,8 @@ export async function load_ae_obj_li__country({
|
||||
// Fire-and-forget — liveQuery subscribers receive updates when IDB is written
|
||||
_refresh_lu_country_background({ api_cfg, log_lvl });
|
||||
} else if (log_lvl) {
|
||||
console.log(`lu_country: IDB fresh (${count} records), skipping refresh`);
|
||||
console.log(
|
||||
`lu_country: IDB fresh (${count} records), skipping refresh`
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,7 +19,8 @@ async function _refresh_lu_country_subdivision_background({
|
||||
api_cfg: any;
|
||||
log_lvl?: number;
|
||||
}) {
|
||||
if (log_lvl) console.log('*** _refresh_lu_country_subdivision_background() ***');
|
||||
if (log_lvl)
|
||||
console.log('*** _refresh_lu_country_subdivision_background() ***');
|
||||
try {
|
||||
const result = await api.get_ae_obj_li_for_lu({
|
||||
api_cfg,
|
||||
@@ -37,7 +38,9 @@ async function _refresh_lu_country_subdivision_background({
|
||||
refreshed_at: Date.now()
|
||||
});
|
||||
if (log_lvl)
|
||||
console.log(`lu_country_subdivision: saved ${result.length} records to IDB`);
|
||||
console.log(
|
||||
`lu_country_subdivision: saved ${result.length} records to IDB`
|
||||
);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('lu_country_subdivision refresh failed:', error);
|
||||
@@ -61,6 +64,8 @@ export async function load_ae_obj_li__country_subdivision({
|
||||
if (count === 0 || is_stale) {
|
||||
_refresh_lu_country_subdivision_background({ api_cfg, log_lvl });
|
||||
} else if (log_lvl) {
|
||||
console.log(`lu_country_subdivision: IDB fresh (${count} records), skipping refresh`);
|
||||
console.log(
|
||||
`lu_country_subdivision: IDB fresh (${count} records), skipping refresh`
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -50,16 +50,24 @@ export async function load_ae_obj_by_code__data_store({
|
||||
return null;
|
||||
}
|
||||
|
||||
const ds_id = get_ds_result.data_store_id_random || get_ds_result.id_random;
|
||||
const ds_id =
|
||||
get_ds_result.data_store_id_random || get_ds_result.id_random;
|
||||
|
||||
if (!ds_id) {
|
||||
if (log_lvl) console.log('*ae_func* Something went wrong? No data store ID found.');
|
||||
if (log_lvl)
|
||||
console.log(
|
||||
'*ae_func* Something went wrong? No data store ID found.'
|
||||
);
|
||||
return null;
|
||||
}
|
||||
|
||||
// Map content fields for convenience
|
||||
const text_val = get_ds_result.text || '';
|
||||
const json_val = get_ds_result.json || (get_ds_result.json_str ? JSON.parse(get_ds_result.json_str) : null);
|
||||
const json_val =
|
||||
get_ds_result.json ||
|
||||
(get_ds_result.json_str
|
||||
? JSON.parse(get_ds_result.json_str)
|
||||
: null);
|
||||
|
||||
const mapped_ds: ae_DataStore = {
|
||||
...get_ds_result,
|
||||
@@ -77,7 +85,6 @@ export async function load_ae_obj_by_code__data_store({
|
||||
if (data_type === 'html') return mapped_ds.html;
|
||||
if (data_type === 'json') return mapped_ds.json;
|
||||
return mapped_ds.text;
|
||||
|
||||
} catch (error) {
|
||||
if (log_lvl) console.error('*ae_func* Fetch failed.', error);
|
||||
return null;
|
||||
|
||||
@@ -22,7 +22,9 @@ export async function load_ae_obj_id__hosted_file({
|
||||
log_lvl?: number;
|
||||
}): Promise<ae_HostedFile | null> {
|
||||
if (log_lvl) {
|
||||
console.log(`*** load_ae_obj_id__hosted_file() *** [V3] id=${hosted_file_id}`);
|
||||
console.log(
|
||||
`*** load_ae_obj_id__hosted_file() *** [V3] id=${hosted_file_id}`
|
||||
);
|
||||
}
|
||||
|
||||
try {
|
||||
@@ -36,10 +38,11 @@ export async function load_ae_obj_id__hosted_file({
|
||||
|
||||
if (ae_promises.load__hosted_file_obj) {
|
||||
if (try_cache) {
|
||||
const processed_obj_li = await process_ae_obj__hosted_file_props({
|
||||
obj_li: [ae_promises.load__hosted_file_obj],
|
||||
log_lvl
|
||||
});
|
||||
const processed_obj_li =
|
||||
await process_ae_obj__hosted_file_props({
|
||||
obj_li: [ae_promises.load__hosted_file_obj],
|
||||
log_lvl
|
||||
});
|
||||
await db_save_ae_obj_li__ae_obj({
|
||||
db_instance: db_core,
|
||||
table_name: 'file',
|
||||
@@ -49,12 +52,14 @@ export async function load_ae_obj_id__hosted_file({
|
||||
});
|
||||
}
|
||||
} else if (try_cache) {
|
||||
ae_promises.load__hosted_file_obj = await db_core.file.get(hosted_file_id);
|
||||
ae_promises.load__hosted_file_obj =
|
||||
await db_core.file.get(hosted_file_id);
|
||||
}
|
||||
} catch (error: any) {
|
||||
console.log('V3 Request failed.', error);
|
||||
if (try_cache) {
|
||||
ae_promises.load__hosted_file_obj = await db_core.file.get(hosted_file_id);
|
||||
ae_promises.load__hosted_file_obj =
|
||||
await db_core.file.get(hosted_file_id);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -90,7 +95,9 @@ export async function load_ae_obj_li__hosted_file({
|
||||
log_lvl?: number;
|
||||
}): Promise<ae_HostedFile[]> {
|
||||
if (log_lvl) {
|
||||
console.log(`*** load_ae_obj_li__hosted_file() *** [V3] for=${for_obj_type}:${for_obj_id}`);
|
||||
console.log(
|
||||
`*** load_ae_obj_li__hosted_file() *** [V3] for=${for_obj_type}:${for_obj_id}`
|
||||
);
|
||||
}
|
||||
|
||||
try {
|
||||
@@ -109,10 +116,11 @@ export async function load_ae_obj_li__hosted_file({
|
||||
|
||||
if (ae_promises.load__hosted_file_obj_li) {
|
||||
if (try_cache) {
|
||||
const processed_obj_li = await process_ae_obj__hosted_file_props({
|
||||
obj_li: ae_promises.load__hosted_file_obj_li,
|
||||
log_lvl
|
||||
});
|
||||
const processed_obj_li =
|
||||
await process_ae_obj__hosted_file_props({
|
||||
obj_li: ae_promises.load__hosted_file_obj_li,
|
||||
log_lvl
|
||||
});
|
||||
await db_save_ae_obj_li__ae_obj({
|
||||
db_instance: db_core,
|
||||
table_name: 'file',
|
||||
@@ -123,14 +131,16 @@ export async function load_ae_obj_li__hosted_file({
|
||||
}
|
||||
} else if (try_cache) {
|
||||
ae_promises.load__hosted_file_obj_li = await db_core.file
|
||||
.where('for_id').equals(for_obj_id)
|
||||
.where('for_id')
|
||||
.equals(for_obj_id)
|
||||
.toArray();
|
||||
}
|
||||
} catch (error: any) {
|
||||
console.log('V3 List Request failed.', error);
|
||||
if (try_cache) {
|
||||
ae_promises.load__hosted_file_obj_li = await db_core.file
|
||||
.where('for_id').equals(for_obj_id)
|
||||
.where('for_id')
|
||||
.equals(for_obj_id)
|
||||
.toArray();
|
||||
}
|
||||
}
|
||||
@@ -159,7 +169,9 @@ export async function delete_ae_obj_id__hosted_file({
|
||||
log_lvl?: number;
|
||||
}) {
|
||||
if (log_lvl) {
|
||||
console.log(`*** delete_ae_obj_id__hosted_file() *** [Special] id=${hosted_file_id}`);
|
||||
console.log(
|
||||
`*** delete_ae_obj_id__hosted_file() *** [Special] id=${hosted_file_id}`
|
||||
);
|
||||
}
|
||||
|
||||
// Use the specialized hosted file delete endpoint
|
||||
@@ -203,7 +215,9 @@ export async function download_ae_obj_id__hosted_file({
|
||||
log_lvl?: number;
|
||||
}) {
|
||||
if (log_lvl) {
|
||||
console.log(`*** download_ae_obj_id__hosted_file() *** id=${hosted_file_id}`);
|
||||
console.log(
|
||||
`*** download_ae_obj_id__hosted_file() *** id=${hosted_file_id}`
|
||||
);
|
||||
}
|
||||
|
||||
const task_id = hosted_file_id;
|
||||
@@ -290,11 +304,15 @@ async function _process_generic_props<T extends Record<string, any>>({
|
||||
const updated = processed_obj.updated_on ?? processed_obj.created_on;
|
||||
const name = processed_obj.name ?? '';
|
||||
|
||||
(processed_obj as any).tmp_sort_1 = `${group}_${priority}_${sort}_${updated}`;
|
||||
(processed_obj as any).tmp_sort_2 = `${group}_${priority}_${sort}_${name}_${updated}`;
|
||||
(processed_obj as any).tmp_sort_1 =
|
||||
`${group}_${priority}_${sort}_${updated}`;
|
||||
(processed_obj as any).tmp_sort_2 =
|
||||
`${group}_${priority}_${sort}_${name}_${updated}`;
|
||||
|
||||
if (specific_processor) {
|
||||
processed_obj = await Promise.resolve(specific_processor(processed_obj));
|
||||
processed_obj = await Promise.resolve(
|
||||
specific_processor(processed_obj)
|
||||
);
|
||||
}
|
||||
|
||||
processed_obj_li.push(processed_obj as T);
|
||||
@@ -315,4 +333,4 @@ export async function process_ae_obj__hosted_file_props({
|
||||
obj_type: 'hosted_file',
|
||||
log_lvl
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,9 +14,9 @@ function find_object_id(
|
||||
log_lvl: number
|
||||
): string | number | undefined {
|
||||
const potential_keys = [
|
||||
'id',
|
||||
'id_random',
|
||||
`${table_name}_id`,
|
||||
'id',
|
||||
'id_random',
|
||||
`${table_name}_id`,
|
||||
`${table_name}_id_random`,
|
||||
`event_${table_name}_id`,
|
||||
`event_${table_name}_id_random`
|
||||
@@ -115,7 +115,9 @@ export async function db_save_ae_obj_li__ae_obj<T extends Record<string, any>>({
|
||||
|
||||
if (data_to_save.length === 0) {
|
||||
if (log_lvl > 0) {
|
||||
console.warn('All objects were skipped, likely due to missing IDs.');
|
||||
console.warn(
|
||||
'All objects were skipped, likely due to missing IDs.'
|
||||
);
|
||||
}
|
||||
return [];
|
||||
}
|
||||
@@ -124,7 +126,9 @@ export async function db_save_ae_obj_li__ae_obj<T extends Record<string, any>>({
|
||||
// bulkPut efficiently handles both inserts and updates.
|
||||
const keys = await db_table.bulkPut(data_to_save);
|
||||
if (log_lvl > 0) {
|
||||
console.log(`Successfully saved ${data_to_save.length} objects to "${table_name}".`);
|
||||
console.log(
|
||||
`Successfully saved ${data_to_save.length} objects to "${table_name}".`
|
||||
);
|
||||
}
|
||||
return keys;
|
||||
} catch (error) {
|
||||
|
||||
@@ -49,7 +49,8 @@ export async function generate_qr_code({
|
||||
|
||||
if (qr_type == 'vcard') {
|
||||
if (qr_data.informal_name) {
|
||||
params['n'] = `${qr_data.family_name};${qr_data.given_name};${qr_data.informal_name}`;
|
||||
params['n'] =
|
||||
`${qr_data.family_name};${qr_data.given_name};${qr_data.informal_name}`;
|
||||
} else {
|
||||
params['n'] = `${qr_data.family_name};${qr_data.given_name}`;
|
||||
}
|
||||
@@ -99,7 +100,8 @@ export async function generate_qr_code({
|
||||
|
||||
// If return_blob is true, ensure we return an object URL for use in <img src=...>
|
||||
if (return_blob) {
|
||||
const data = ae_promises.generate_qr_code.data ?? ae_promises.generate_qr_code;
|
||||
const data =
|
||||
ae_promises.generate_qr_code.data ?? ae_promises.generate_qr_code;
|
||||
|
||||
// If already a Blob, use it directly
|
||||
if (data instanceof Blob) {
|
||||
@@ -133,7 +135,8 @@ export async function generate_qr_code({
|
||||
const blob = new Blob([data as BlobPart], { type: 'image/png' });
|
||||
return URL.createObjectURL(blob);
|
||||
} catch (e) {
|
||||
if (log_lvl) console.error('Could not create QR code image blob:', e, data);
|
||||
if (log_lvl)
|
||||
console.error('Could not create QR code image blob:', e, data);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -155,7 +158,10 @@ export async function generate_qr_code({
|
||||
* @returns {Promise<string>} A promise that resolves to a Base64 data URL of the QR code image.
|
||||
* @throws {Error} If the qr_type is unknown or data is missing.
|
||||
*/
|
||||
export async function js_generate_qr_code(qr_type: string, params: key_val = {}) {
|
||||
export async function js_generate_qr_code(
|
||||
qr_type: string,
|
||||
params: key_val = {}
|
||||
) {
|
||||
const {
|
||||
n = '',
|
||||
fn = '',
|
||||
@@ -179,7 +185,8 @@ export async function js_generate_qr_code(qr_type: string, params: key_val = {})
|
||||
log_lvl = 0
|
||||
} = params;
|
||||
|
||||
if (log_lvl >= 2) console.log(`*** js_generate_qr_code() *** qr_type=${qr_type}`, params);
|
||||
if (log_lvl >= 2)
|
||||
console.log(`*** js_generate_qr_code() *** qr_type=${qr_type}`, params);
|
||||
|
||||
let qr_data: string | null = null;
|
||||
|
||||
@@ -211,13 +218,15 @@ export async function js_generate_qr_code(qr_type: string, params: key_val = {})
|
||||
|
||||
case 'obj':
|
||||
// Custom format: OBJ:ot:obj_type,oi:obj_id
|
||||
if (!obj_type || !obj_id) throw new Error('Missing obj_type or obj_id for type "obj".');
|
||||
if (!obj_type || !obj_id)
|
||||
throw new Error('Missing obj_type or obj_id for type "obj".');
|
||||
qr_data = `OBJ:ot:${obj_type},oi:${obj_id}`;
|
||||
break;
|
||||
|
||||
case 'kv':
|
||||
// Custom format: KV:k:"key",v:"val"
|
||||
if (!key || !val) throw new Error('Missing key or val for type "kv".');
|
||||
if (!key || !val)
|
||||
throw new Error('Missing key or val for type "kv".');
|
||||
qr_data = `KV:k:"${key}",v:"${val}"`;
|
||||
break;
|
||||
|
||||
@@ -229,7 +238,8 @@ export async function js_generate_qr_code(qr_type: string, params: key_val = {})
|
||||
|
||||
case 'str':
|
||||
// Raw string data
|
||||
if (!str) throw new Error('Missing raw string data for type "str".');
|
||||
if (!str)
|
||||
throw new Error('Missing raw string data for type "str".');
|
||||
qr_data = str;
|
||||
break;
|
||||
|
||||
|
||||
@@ -27,7 +27,7 @@ async function _refresh_lu_time_zone_background({
|
||||
for_lu_type: 'time_zone',
|
||||
enabled: 'enabled',
|
||||
hidden: 'not_hidden',
|
||||
only_priority: true, // ~72 priority timezone records
|
||||
only_priority: true, // ~72 priority timezone records
|
||||
limit: 1800,
|
||||
log_lvl
|
||||
});
|
||||
@@ -38,7 +38,10 @@ async function _refresh_lu_time_zone_background({
|
||||
lu_type: 'time_zone',
|
||||
refreshed_at: Date.now()
|
||||
});
|
||||
if (log_lvl) console.log(`lu_time_zone: saved ${result.length} records to IDB`);
|
||||
if (log_lvl)
|
||||
console.log(
|
||||
`lu_time_zone: saved ${result.length} records to IDB`
|
||||
);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('lu_time_zone refresh failed:', error);
|
||||
@@ -62,6 +65,8 @@ export async function load_ae_obj_li__time_zone({
|
||||
if (count === 0 || is_stale) {
|
||||
_refresh_lu_time_zone_background({ api_cfg, log_lvl });
|
||||
} else if (log_lvl) {
|
||||
console.log(`lu_time_zone: IDB fresh (${count} records), skipping refresh`);
|
||||
console.log(
|
||||
`lu_time_zone: IDB fresh (${count} records), skipping refresh`
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,7 +12,7 @@ import Dexie, { type Table } from 'dexie';
|
||||
|
||||
export interface LuCountry {
|
||||
id: number;
|
||||
group: string; // dedup key = alpha_2_code (e.g. "US")
|
||||
group: string; // dedup key = alpha_2_code (e.g. "US")
|
||||
alpha_2_code: string;
|
||||
name: string;
|
||||
english_short_name?: string;
|
||||
@@ -22,12 +22,12 @@ export interface LuCountry {
|
||||
priority?: number;
|
||||
sort?: number;
|
||||
account_id?: number | null;
|
||||
[key: string]: unknown; // allow extra fields from API without TS errors
|
||||
[key: string]: unknown; // allow extra fields from API without TS errors
|
||||
}
|
||||
|
||||
export interface LuCountrySubdivision {
|
||||
id: number;
|
||||
group: string; // dedup key = code (e.g. "US-NY")
|
||||
group: string; // dedup key = code (e.g. "US-NY")
|
||||
code: string;
|
||||
name: string;
|
||||
country_alpha_2_code?: string;
|
||||
@@ -42,9 +42,9 @@ export interface LuCountrySubdivision {
|
||||
|
||||
export interface LuTimeZone {
|
||||
id: number;
|
||||
group: string; // dedup key = name (IANA identifier, e.g. "US/Eastern")
|
||||
group: string; // dedup key = name (IANA identifier, e.g. "US/Eastern")
|
||||
name: string;
|
||||
name_override?: string; // display label override; prefer this over name when set
|
||||
name_override?: string; // display label override; prefer this over name when set
|
||||
enable?: number;
|
||||
hide?: number;
|
||||
priority?: number;
|
||||
@@ -55,7 +55,7 @@ export interface LuTimeZone {
|
||||
|
||||
export interface LuCacheMeta {
|
||||
lu_type: 'country' | 'country_subdivision' | 'time_zone';
|
||||
refreshed_at: number; // Unix timestamp ms — used for 24h TTL check
|
||||
refreshed_at: number; // Unix timestamp ms — used for 24h TTL check
|
||||
}
|
||||
|
||||
class LookupsDexie extends Dexie {
|
||||
@@ -67,10 +67,10 @@ class LookupsDexie extends Dexie {
|
||||
constructor() {
|
||||
super('ae_lookups_db');
|
||||
this.version(1).stores({
|
||||
lu_country: 'id, alpha_2_code, group',
|
||||
lu_country: 'id, alpha_2_code, group',
|
||||
lu_country_subdivision: 'id, code, country_alpha_2_code, group',
|
||||
lu_time_zone: 'id, name, group',
|
||||
lu_cache_meta: 'lu_type'
|
||||
lu_time_zone: 'id, name, group',
|
||||
lu_cache_meta: 'lu_type'
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user