Last round of prettier: npx prettier --write src/

This commit is contained in:
Scott Idem
2026-03-24 13:27:40 -04:00
parent 23d25bf65a
commit a8f3c29b9f
146 changed files with 13201 additions and 9277 deletions

View File

@@ -43,7 +43,9 @@ export const delete_object = async function delete_object({
// Construct the URL with query parameters
const url = new URL(endpoint, api_cfg['base_url']);
if (params) {
Object.keys(params).forEach((key) => url.searchParams.append(key, params[key]));
Object.keys(params).forEach((key) =>
url.searchParams.append(key, params[key])
);
}
// Clean and merge headers without mutating the original api_cfg
@@ -70,8 +72,13 @@ export const delete_object = async function delete_object({
}
// Auto-inject Authorization header if JWT is present but header is missing
const jwt = headers_cleaned['jwt'] || headers_cleaned['JWT'] || api_cfg['jwt'];
if (jwt && !headers_cleaned['Authorization'] && !headers_cleaned['authorization']) {
const jwt =
headers_cleaned['jwt'] || headers_cleaned['JWT'] || api_cfg['jwt'];
if (
jwt &&
!headers_cleaned['Authorization'] &&
!headers_cleaned['authorization']
) {
headers_cleaned['Authorization'] = `Bearer ${jwt}`;
}
@@ -93,20 +100,26 @@ export const delete_object = async function delete_object({
try {
const controller = new AbortController();
const timeoutId = setTimeout(() => {
console.error(`API DELETE request timed out after ${timeout}ms.`);
console.error(
`API DELETE request timed out after ${timeout}ms.`
);
controller.abort();
}, timeout);
const fetchOptions: RequestInit = {
method: 'DELETE',
headers: headers_cleaned,
body: Object.keys(data).length > 0 ? JSON.stringify(data) : undefined,
body:
Object.keys(data).length > 0
? JSON.stringify(data)
: undefined,
signal: controller.signal
};
const response = await fetch_method(url.toString(), fetchOptions).catch(function (
error: any
) {
const response = await fetch_method(
url.toString(),
fetchOptions
).catch(function (error: any) {
console.log(
'API DELETE Object *fetch* request was aborted or failed in an unexpected way.',
error
@@ -121,7 +134,9 @@ export const delete_object = async function delete_object({
}
if (log_lvl) {
console.log(`Response: status=${response.status} attempt=${attempt}`);
console.log(
`Response: status=${response.status} attempt=${attempt}`
);
}
if (!response.ok) {
@@ -129,15 +144,20 @@ export const delete_object = async function delete_object({
console.warn('404 Not Found. Returning null.');
return null;
}
const errorBody = await response.text();
console.error(`HTTP error! status: ${response.status}`, errorBody);
console.error(
`HTTP error! status: ${response.status}`,
errorBody
);
if (response.status >= 400 && response.status < 404) {
return false;
}
throw new Error(`HTTP error! status: ${response.status} - ${errorBody}`);
throw new Error(
`HTTP error! status: ${response.status} - ${errorBody}`
);
}
const json = await response.json();
@@ -148,7 +168,11 @@ export const delete_object = async function delete_object({
// Return the response data or metadata
// Robustly handle V3 response envelopes
return return_meta ? json : (json.data !== undefined ? json.data : json);
return return_meta
? json
: json.data !== undefined
? json.data
: json;
} catch (error) {
console.error(`API DELETE error on attempt ${attempt}:`, error);

View File

@@ -44,7 +44,9 @@ export async function get_ae_obj_id_crud({
log_lvl?: number;
}) {
if (log_lvl) {
console.log(`*** get_ae_obj_id_crud() *** Type: ${obj_type} ID: ${obj_id}`);
console.log(
`*** get_ae_obj_id_crud() *** Type: ${obj_type} ID: ${obj_id}`
);
}
// V3 Standard: Unified endpoint for all objects
@@ -77,7 +79,10 @@ export async function get_ae_obj_id_crud({
log_lvl: log_lvl,
return_meta: return_meta
}).catch(function (error: any) {
console.error(`API GET CRUD object ID request failed for ${obj_type}/${obj_id}`, error);
console.error(
`API GET CRUD object ID request failed for ${obj_type}/${obj_id}`,
error
);
return false;
});
@@ -86,4 +91,4 @@ export async function get_ae_obj_id_crud({
}
return result;
}
}

View File

@@ -95,7 +95,10 @@ interface GetAeObjLiV3Params {
view?: string;
limit?: number;
offset?: number;
order_by_li?: Record<string, 'ASC' | 'DESC'> | Record<string, 'ASC' | 'DESC'>[] | null;
order_by_li?:
| Record<string, 'ASC' | 'DESC'>
| Record<string, 'ASC' | 'DESC'>[]
| null;
delay_ms?: number;
params?: key_val;
headers?: key_val;
@@ -162,7 +165,10 @@ interface GetNestedObjLiV3Params {
view?: string;
limit?: number;
offset?: number;
order_by_li?: Record<string, 'ASC' | 'DESC'> | Record<string, 'ASC' | 'DESC'>[] | null;
order_by_li?:
| Record<string, 'ASC' | 'DESC'>
| Record<string, 'ASC' | 'DESC'>[]
| null;
delay_ms?: number;
log_lvl?: number;
}
@@ -206,4 +212,4 @@ export async function get_nested_obj_li({
params,
log_lvl
});
}
}

View File

@@ -24,7 +24,9 @@ export async function get_data_store({
log_lvl = 0
}: GetDataStoreV3Params): Promise<any> {
if (log_lvl) {
console.log(`*** get_data_store() *** code=${code} no_account_id=${no_account_id}`);
console.log(
`*** get_data_store() *** code=${code} no_account_id=${no_account_id}`
);
}
const endpoint = `/v3/data_store/code/${code}`;

View File

@@ -41,7 +41,9 @@ export const get_object = async function get_object({
retry_count?: number;
}) {
if (log_lvl) {
console.log(`*** get_object() *** Endpoint: ${endpoint} AE Task ID: ${task_id}`);
console.log(
`*** get_object() *** Endpoint: ${endpoint} AE Task ID: ${task_id}`
);
console.log('Params:', params);
if (log_lvl > 1) {
console.log('Data:', data);
@@ -55,7 +57,10 @@ export const get_object = async function get_object({
// FAIL FAST: Check if we are explicitly offline to avoid long browser timeouts
if (typeof navigator !== 'undefined' && !navigator.onLine) {
if (log_lvl) console.log('get_object: Browser is offline. Failing fast to allow cache fallback.');
if (log_lvl)
console.log(
'get_object: Browser is offline. Failing fast to allow cache fallback.'
);
return false;
}
@@ -64,7 +69,9 @@ export const get_object = async function get_object({
}
const url = new URL(endpoint, api_cfg['base_url']);
Object.keys(params).forEach((key) => url.searchParams.append(key, params[key]));
Object.keys(params).forEach((key) =>
url.searchParams.append(key, params[key])
);
const controller = new AbortController();
const timeoutId = setTimeout(() => controller.abort(), timeout);
@@ -96,14 +103,19 @@ export const get_object = async function get_object({
}
// Handle "Bootstrap Paradox" for unauthenticated requests
const bypass_val = merged_headers['x-no-account-id'] || merged_headers['x_no_account_id'];
const is_valid_bypass = bypass_val === 'bypass' ||
bypass_val === 'Nothing to See Here' ||
params['key'] ||
bypass_val === 'direct-download';
const bypass_val =
merged_headers['x-no-account-id'] || merged_headers['x_no_account_id'];
const is_valid_bypass =
bypass_val === 'bypass' ||
bypass_val === 'Nothing to See Here' ||
params['key'] ||
bypass_val === 'direct-download';
if (is_valid_bypass) {
if (log_lvl > 1) console.log('api_get_object: Valid bypass detected. Stripping account ID context.');
if (log_lvl > 1)
console.log(
'api_get_object: Valid bypass detected. Stripping account ID context.'
);
delete merged_headers['x-account-id'];
delete merged_headers['x_account_id'];
} else {
@@ -126,11 +138,12 @@ export const get_object = async function get_object({
}
// Auto-inject Authorization header if JWT is present but header is missing
let jwt = headers_cleaned['jwt'] ||
headers_cleaned['JWT'] ||
api_cfg['jwt'] ||
api_cfg['headers']?.['jwt'] ||
api_cfg['headers']?.['JWT'];
let jwt =
headers_cleaned['jwt'] ||
headers_cleaned['JWT'] ||
api_cfg['jwt'] ||
api_cfg['headers']?.['jwt'] ||
api_cfg['headers']?.['JWT'];
// Final Fallback: Direct check of primary ae_loc key
if (!jwt && typeof localStorage !== 'undefined') {
@@ -145,7 +158,11 @@ export const get_object = async function get_object({
}
}
if (jwt && !headers_cleaned['Authorization'] && !headers_cleaned['authorization']) {
if (
jwt &&
!headers_cleaned['Authorization'] &&
!headers_cleaned['authorization']
) {
headers_cleaned['Authorization'] = `Bearer ${jwt}`;
}
@@ -174,18 +191,29 @@ export const get_object = async function get_object({
for (let attempt = 1; attempt <= retry_count; attempt++) {
// FAIL FAST: Check if we are explicitly offline to avoid long browser timeouts
if (typeof navigator !== 'undefined' && !navigator.onLine) {
if (log_lvl) console.log(`get_object: Browser is offline (attempt ${attempt}). Failing fast to allow cache fallback.`);
if (log_lvl)
console.log(
`get_object: Browser is offline (attempt ${attempt}). Failing fast to allow cache fallback.`
);
return false;
}
try {
const response = await fetch_method(url.toString(), fetchOptions).catch(function (
error: any
) {
const response = await fetch_method(
url.toString(),
fetchOptions
).catch(function (error: any) {
// SILENCE NOISE: Aborted requests (common in SWR/Background loads) shouldn't spam logs
if (error.name === 'AbortError' || error.message?.includes('aborted') || error.name === 'TypeError') {
if (
error.name === 'AbortError' ||
error.message?.includes('aborted') ||
error.name === 'TypeError'
) {
if (log_lvl > 1) {
console.log('API GET: Request was aborted or terminated by browser. This is expected during navigation.', error);
console.log(
'API GET: Request was aborted or terminated by browser. This is expected during navigation.',
error
);
}
return error; // Return error to be handled below
}
@@ -199,11 +227,19 @@ export const get_object = async function get_object({
clearTimeout(timeoutId);
// Check if we should stop due to abort or network failure
if (response instanceof Error || (response && (response.name === 'TypeError' || response.name === 'AbortError'))) {
if (
response instanceof Error ||
(response &&
(response.name === 'TypeError' ||
response.name === 'AbortError'))
) {
// If it was an explicit abort, definitely stop
if (response.name === 'AbortError') return false;
if (log_lvl > 1) console.log('API GET Object: Detected NetworkError or TypeError. Failing fast.');
if (log_lvl > 1)
console.log(
'API GET Object: Detected NetworkError or TypeError. Failing fast.'
);
return false;
}
@@ -231,24 +267,45 @@ export const get_object = async function get_object({
if (!response.ok) {
if (response.status === 404) {
if (log_lvl) {
console.log('The response was a 404 not found "error". Returning null.');
console.log(
'The response was a 404 not found "error". Returning null.'
);
}
return null;
}
// FAIL FAST (Section 2D): Do not retry on Auth or Client errors (400, 401, 403, 422)
if (response.status === 400 || response.status === 401 || response.status === 403 || response.status === 422) {
if (log_lvl) console.error(`API Client Failure (${response.status}). Failing fast.`);
if (
response.status === 400 ||
response.status === 401 ||
response.status === 403 ||
response.status === 422
) {
if (log_lvl)
console.error(
`API Client Failure (${response.status}). Failing fast.`
);
if (response.status === 401 || response.status === 403) {
console.warn(`AUTH DIAGNOSTICS: Headers sent for ${endpoint}:`, {
has_auth: !!headers_cleaned['Authorization'],
has_api_key: !!headers_cleaned['x-aether-api-key'],
has_account_id: !!headers_cleaned['x-account-id'],
jwt_preview: jwt ? `${jwt.slice(0, 8)}...` : 'MISSING'
});
console.warn(
`AUTH DIAGNOSTICS: Headers sent for ${endpoint}:`,
{
has_auth: !!headers_cleaned['Authorization'],
has_api_key:
!!headers_cleaned['x-aether-api-key'],
has_account_id:
!!headers_cleaned['x-account-id'],
jwt_preview: jwt
? `${jwt.slice(0, 8)}...`
: 'MISSING'
}
);
// Signal the root layout to show the session-expired banner.
if (browser) ae_auth_error.set({ type: 'expired', ts: Date.now() });
if (browser)
ae_auth_error.set({
type: 'expired',
ts: Date.now()
});
}
// Structured Error Handling (V3): Attempt to get rich error metadata
@@ -259,7 +316,11 @@ export const get_object = async function get_object({
// Not JSON
}
if (log_lvl) console.log('The response was not ok. Structured Error Check:', error_json);
if (log_lvl)
console.log(
'The response was not ok. Structured Error Check:',
error_json
);
if (error_json?.meta?.details) {
return error_json;
@@ -273,7 +334,10 @@ export const get_object = async function get_object({
status_code: response.status,
details: {
category: 'validation',
message: typeof error_json.detail === 'string' ? error_json.detail : JSON.stringify(error_json.detail),
message:
typeof error_json.detail === 'string'
? error_json.detail
: JSON.stringify(error_json.detail),
raw: error_json.detail
}
}
@@ -307,7 +371,9 @@ export const get_object = async function get_object({
chunks.push(value);
receivedLength += value.length;
const percent_completed = Math.round((receivedLength * 100) / contentLength);
const percent_completed = Math.round(
(receivedLength * 100) / contentLength
);
if (log_lvl > 1) {
console.log(
'GET Blob Progress:',
@@ -359,7 +425,10 @@ export const get_object = async function get_object({
}
}
} catch (error) {
console.log(`API GET object request *fetch* error on attempt ${attempt}:`, error);
console.log(
`API GET object request *fetch* error on attempt ${attempt}:`,
error
);
if (attempt === retry_count) {
console.log('Max retry attempts reached. Returning false.');

View File

@@ -45,7 +45,9 @@ export const patch_object = async function patch_object({
// Construct the URL with query parameters
const url = new URL(endpoint, api_cfg['base_url']);
if (params) {
Object.keys(params).forEach((key) => url.searchParams.append(key, params[key]));
Object.keys(params).forEach((key) =>
url.searchParams.append(key, params[key])
);
}
// Clean and merge headers without mutating the original api_cfg
@@ -75,14 +77,19 @@ export const patch_object = async function patch_object({
}
// Handle "Bootstrap Paradox" for unauthenticated requests
const bypass_val = merged_headers['x-no-account-id'] || merged_headers['x_no_account_id'];
const is_valid_bypass = bypass_val === 'bypass' ||
bypass_val === 'Nothing to See Here' ||
params['key'] ||
bypass_val === 'direct-download';
const bypass_val =
merged_headers['x-no-account-id'] || merged_headers['x_no_account_id'];
const is_valid_bypass =
bypass_val === 'bypass' ||
bypass_val === 'Nothing to See Here' ||
params['key'] ||
bypass_val === 'direct-download';
if (is_valid_bypass) {
if (log_lvl > 1) console.log('api_patch_object: Valid bypass detected. Stripping account ID context.');
if (log_lvl > 1)
console.log(
'api_patch_object: Valid bypass detected. Stripping account ID context.'
);
delete merged_headers['x-account-id'];
delete merged_headers['x_account_id'];
} else {
@@ -104,11 +111,12 @@ export const patch_object = async function patch_object({
}
// Auto-inject Authorization header if JWT is present but header is missing
let jwt = headers_cleaned['jwt'] ||
headers_cleaned['JWT'] ||
api_cfg['jwt'] ||
api_cfg['headers']?.['jwt'] ||
api_cfg['headers']?.['JWT'];
let jwt =
headers_cleaned['jwt'] ||
headers_cleaned['JWT'] ||
api_cfg['jwt'] ||
api_cfg['headers']?.['jwt'] ||
api_cfg['headers']?.['JWT'];
// Final Fallback: Direct check of primary ae_loc key
if (!jwt && typeof localStorage !== 'undefined') {
@@ -123,7 +131,11 @@ export const patch_object = async function patch_object({
}
}
if (jwt && !headers_cleaned['Authorization'] && !headers_cleaned['authorization']) {
if (
jwt &&
!headers_cleaned['Authorization'] &&
!headers_cleaned['authorization']
) {
headers_cleaned['Authorization'] = `Bearer ${jwt}`;
}
@@ -145,7 +157,9 @@ export const patch_object = async function patch_object({
try {
const controller = new AbortController();
const timeoutId = setTimeout(() => {
console.error(`API PATCH request timed out after ${timeout}ms.`);
console.error(
`API PATCH request timed out after ${timeout}ms.`
);
controller.abort();
}, timeout);
@@ -156,9 +170,10 @@ export const patch_object = async function patch_object({
signal: controller.signal
};
const response = await fetch_method(url.toString(), fetchOptions).catch(function (
error: any
) {
const response = await fetch_method(
url.toString(),
fetchOptions
).catch(function (error: any) {
console.log(
'API PATCH Object *fetch* request was aborted or failed in an unexpected way.',
error
@@ -173,30 +188,53 @@ export const patch_object = async function patch_object({
}
if (log_lvl) {
console.log(`Response: status=${response.status} attempt=${attempt}`);
console.log(
`Response: status=${response.status} attempt=${attempt}`
);
}
if (!response.ok) {
if (response.status === 404) {
if (log_lvl) {
console.log('The response was a 404 not found "error". Returning null.');
console.log(
'The response was a 404 not found "error". Returning null.'
);
}
return null;
}
// FAIL FAST (Section 2D): Do not retry on Auth or Client errors (400, 401, 403, 422)
if (response.status === 400 || response.status === 401 || response.status === 403 || response.status === 422) {
if (log_lvl) console.error(`API Client Failure (${response.status}). Failing fast.`);
if (
response.status === 400 ||
response.status === 401 ||
response.status === 403 ||
response.status === 422
) {
if (log_lvl)
console.error(
`API Client Failure (${response.status}). Failing fast.`
);
if (response.status === 401 || response.status === 403) {
console.warn(`AUTH DIAGNOSTICS (PATCH): Headers sent for ${endpoint}:`, {
has_auth: !!headers_cleaned['Authorization'],
has_api_key: !!headers_cleaned['x-aether-api-key'],
has_account_id: !!headers_cleaned['x-account-id'],
jwt_preview: jwt ? `${jwt.slice(0, 8)}...` : 'MISSING'
});
console.warn(
`AUTH DIAGNOSTICS (PATCH): Headers sent for ${endpoint}:`,
{
has_auth: !!headers_cleaned['Authorization'],
has_api_key:
!!headers_cleaned['x-aether-api-key'],
has_account_id:
!!headers_cleaned['x-account-id'],
jwt_preview: jwt
? `${jwt.slice(0, 8)}...`
: 'MISSING'
}
);
// Signal the root layout to show the session-expired banner.
if (browser) ae_auth_error.set({ type: 'expired', ts: Date.now() });
if (browser)
ae_auth_error.set({
type: 'expired',
ts: Date.now()
});
}
// Structured Error Handling (V3): Attempt to get rich error metadata
@@ -207,7 +245,11 @@ export const patch_object = async function patch_object({
// Not JSON
}
if (log_lvl) console.log('The response was not ok. Structured Error Check:', error_json);
if (log_lvl)
console.log(
'The response was not ok. Structured Error Check:',
error_json
);
if (error_json?.meta?.details) {
return error_json;
@@ -221,7 +263,10 @@ export const patch_object = async function patch_object({
status_code: response.status,
details: {
category: 'validation',
message: typeof error_json.detail === 'string' ? error_json.detail : JSON.stringify(error_json.detail),
message:
typeof error_json.detail === 'string'
? error_json.detail
: JSON.stringify(error_json.detail),
raw: error_json.detail
}
}
@@ -242,7 +287,11 @@ export const patch_object = async function patch_object({
// Return the response data or metadata
// Robustly handle V3 response envelopes
return return_meta ? json : (json.data !== undefined ? json.data : json);
return return_meta
? json
: json.data !== undefined
? json.data
: json;
} catch (error) {
console.error(`API PATCH error on attempt ${attempt}:`, error);

View File

@@ -33,7 +33,11 @@ export async function create_ae_obj({
// Standard Aether Pattern: Auto-serialize any key ending in _json
const cleaned_fields = { ...fields };
for (const key in cleaned_fields) {
if (key.endsWith('_json') && cleaned_fields[key] !== null && typeof cleaned_fields[key] === 'object') {
if (
key.endsWith('_json') &&
cleaned_fields[key] !== null &&
typeof cleaned_fields[key] === 'object'
) {
if (log_lvl) console.log(`Auto-serializing field: ${key}`);
cleaned_fields[key] = JSON.stringify(cleaned_fields[key]);
}
@@ -94,7 +98,11 @@ export async function create_nested_obj({
// Standard Aether Pattern: Auto-serialize any key ending in _json
const cleaned_fields = { ...fields };
for (const key in cleaned_fields) {
if (key.endsWith('_json') && cleaned_fields[key] !== null && typeof cleaned_fields[key] === 'object') {
if (
key.endsWith('_json') &&
cleaned_fields[key] !== null &&
typeof cleaned_fields[key] === 'object'
) {
cleaned_fields[key] = JSON.stringify(cleaned_fields[key]);
}
}
@@ -140,7 +148,11 @@ export async function update_ae_obj({
// Standard Aether Pattern: Auto-serialize any key ending in _json
const cleaned_fields = { ...fields };
for (const key in cleaned_fields) {
if (key.endsWith('_json') && cleaned_fields[key] !== null && typeof cleaned_fields[key] === 'object') {
if (
key.endsWith('_json') &&
cleaned_fields[key] !== null &&
typeof cleaned_fields[key] === 'object'
) {
if (log_lvl > 1) console.log(`Auto-serializing field: ${key}`);
cleaned_fields[key] = JSON.stringify(cleaned_fields[key]);
}
@@ -202,7 +214,11 @@ export async function update_nested_obj({
// Standard Aether Pattern: Auto-serialize any key ending in _json
const cleaned_fields = { ...fields };
for (const key in cleaned_fields) {
if (key.endsWith('_json') && cleaned_fields[key] !== null && typeof cleaned_fields[key] === 'object') {
if (
key.endsWith('_json') &&
cleaned_fields[key] !== null &&
typeof cleaned_fields[key] === 'object'
) {
cleaned_fields[key] = JSON.stringify(cleaned_fields[key]);
}
}

View File

@@ -10,7 +10,10 @@ interface SearchAeObjV3Params {
view?: string;
for_obj_type?: string;
for_obj_id?: string;
order_by_li?: Record<string, 'ASC' | 'DESC'> | Record<string, 'ASC' | 'DESC'>[] | null;
order_by_li?:
| Record<string, 'ASC' | 'DESC'>
| Record<string, 'ASC' | 'DESC'>[]
| null;
limit?: number;
offset?: number;
delay_ms?: number;
@@ -55,7 +58,10 @@ export async function search_ae_obj({
// Serialize any complex objects in the query params (e.g. ft_qry, lk_qry)
for (const key in query_params) {
if (typeof query_params[key] === 'object' && query_params[key] !== null) {
if (
typeof query_params[key] === 'object' &&
query_params[key] !== null
) {
query_params[key] = JSON.stringify(query_params[key]);
}
}
@@ -76,4 +82,4 @@ export async function search_ae_obj({
data: search_query,
log_lvl
});
}
}

View File

@@ -41,7 +41,9 @@ export const post_object = async function post_object({
retry_count?: number;
}) {
if (log_lvl) {
console.log(`*** post_object() *** Endpoint: ${endpoint} Task ID: ${task_id}`);
console.log(
`*** post_object() *** Endpoint: ${endpoint} Task ID: ${task_id}`
);
console.log('Params:', params);
if (log_lvl > 1) {
console.log('Data:', data);
@@ -65,7 +67,9 @@ export const post_object = async function post_object({
// Construct the URL with query parameters
const url = new URL(endpoint, api_cfg['base_url']);
if (params) {
Object.keys(params).forEach((key) => url.searchParams.append(key, params[key]));
Object.keys(params).forEach((key) =>
url.searchParams.append(key, params[key])
);
}
// Clean and merge headers
@@ -95,14 +99,19 @@ export const post_object = async function post_object({
}
// Handle "Bootstrap Paradox" for unauthenticated requests
const bypass_val = merged_headers['x-no-account-id'] || merged_headers['x_no_account_id'];
const is_valid_bypass = bypass_val === 'bypass' ||
bypass_val === 'Nothing to See Here' ||
params['key'] ||
bypass_val === 'direct-download';
const bypass_val =
merged_headers['x-no-account-id'] || merged_headers['x_no_account_id'];
const is_valid_bypass =
bypass_val === 'bypass' ||
bypass_val === 'Nothing to See Here' ||
params['key'] ||
bypass_val === 'direct-download';
if (is_valid_bypass) {
if (log_lvl > 1) console.log('api_post_object: Valid bypass detected. Stripping account ID context.');
if (log_lvl > 1)
console.log(
'api_post_object: Valid bypass detected. Stripping account ID context.'
);
delete merged_headers['x-account-id'];
delete merged_headers['x_account_id'];
} else {
@@ -124,11 +133,12 @@ export const post_object = async function post_object({
}
// Auto-inject Authorization header if JWT is present but header is missing
let jwt = headers_cleaned['jwt'] ||
headers_cleaned['JWT'] ||
api_cfg['jwt'] ||
api_cfg['headers']?.['jwt'] ||
api_cfg['headers']?.['JWT'];
let jwt =
headers_cleaned['jwt'] ||
headers_cleaned['JWT'] ||
api_cfg['jwt'] ||
api_cfg['headers']?.['jwt'] ||
api_cfg['headers']?.['JWT'];
// Final Fallback: Direct check of primary ae_loc key
if (!jwt && typeof localStorage !== 'undefined') {
@@ -143,7 +153,11 @@ export const post_object = async function post_object({
}
}
if (jwt && !headers_cleaned['Authorization'] && !headers_cleaned['authorization']) {
if (
jwt &&
!headers_cleaned['Authorization'] &&
!headers_cleaned['authorization']
) {
headers_cleaned['Authorization'] = `Bearer ${jwt}`;
}
@@ -186,13 +200,21 @@ export const post_object = async function post_object({
console.log('Fetch Options:', fetchOptions);
}
const response = await fetch_method(url.toString(), fetchOptions).catch(function (
error: any
) {
const response = await fetch_method(
url.toString(),
fetchOptions
).catch(function (error: any) {
// SILENCE NOISE: Aborted requests shouldn't spam logs at log_lvl 0
if (error.name === 'AbortError' || error.message?.includes('aborted') || error.name === 'TypeError') {
if (
error.name === 'AbortError' ||
error.message?.includes('aborted') ||
error.name === 'TypeError'
) {
if (log_lvl > 1) {
console.log('API POST: Request was aborted or terminated by browser. Expected during navigation.', error);
console.log(
'API POST: Request was aborted or terminated by browser. Expected during navigation.',
error
);
}
return error;
}
@@ -206,9 +228,17 @@ export const post_object = async function post_object({
clearTimeout(timeoutId);
// Check if we should stop due to abort or network failure
if (response instanceof Error || (response && (response.name === 'TypeError' || response.name === 'AbortError'))) {
if (
response instanceof Error ||
(response &&
(response.name === 'TypeError' ||
response.name === 'AbortError'))
) {
if (response.name === 'AbortError') return false;
if (log_lvl > 1) console.log('API POST Object: Detected NetworkError or TypeError. Failing fast.');
if (log_lvl > 1)
console.log(
'API POST Object: Detected NetworkError or TypeError. Failing fast.'
);
return false;
}
@@ -219,30 +249,53 @@ export const post_object = async function post_object({
}
if (log_lvl) {
console.log(`Response: status=${response.status} attempt=${attempt}`);
console.log(
`Response: status=${response.status} attempt=${attempt}`
);
}
if (!response.ok) {
if (response.status === 404) {
if (log_lvl) {
console.log('The response was a 404 not found "error". Returning null.');
console.log(
'The response was a 404 not found "error". Returning null.'
);
}
return null;
}
// FAIL FAST (Section 2D): Do not retry on Auth or Client errors (400, 401, 403, 422)
if (response.status === 400 || response.status === 401 || response.status === 403 || response.status === 422) {
if (log_lvl) console.error(`API Client Failure (${response.status}). Failing fast.`);
if (
response.status === 400 ||
response.status === 401 ||
response.status === 403 ||
response.status === 422
) {
if (log_lvl)
console.error(
`API Client Failure (${response.status}). Failing fast.`
);
if (response.status === 401 || response.status === 403) {
console.warn(`AUTH DIAGNOSTICS (POST): Headers sent for ${endpoint}:`, {
has_auth: !!headers_cleaned['Authorization'],
has_api_key: !!headers_cleaned['x-aether-api-key'],
has_account_id: !!headers_cleaned['x-account-id'],
jwt_preview: jwt ? `${jwt.slice(0, 8)}...` : 'MISSING'
});
console.warn(
`AUTH DIAGNOSTICS (POST): Headers sent for ${endpoint}:`,
{
has_auth: !!headers_cleaned['Authorization'],
has_api_key:
!!headers_cleaned['x-aether-api-key'],
has_account_id:
!!headers_cleaned['x-account-id'],
jwt_preview: jwt
? `${jwt.slice(0, 8)}...`
: 'MISSING'
}
);
// Signal the root layout to show the session-expired banner.
if (browser) ae_auth_error.set({ type: 'expired', ts: Date.now() });
if (browser)
ae_auth_error.set({
type: 'expired',
ts: Date.now()
});
}
// Structured Error Handling (V3): Attempt to get rich error metadata
@@ -253,7 +306,11 @@ export const post_object = async function post_object({
// Not JSON
}
if (log_lvl) console.log('The response was not ok. Structured Error Check:', error_json);
if (log_lvl)
console.log(
'The response was not ok. Structured Error Check:',
error_json
);
if (error_json?.meta?.details) {
return error_json;
@@ -267,7 +324,10 @@ export const post_object = async function post_object({
status_code: response.status,
details: {
category: 'validation',
message: typeof error_json.detail === 'string' ? error_json.detail : JSON.stringify(error_json.detail),
message:
typeof error_json.detail === 'string'
? error_json.detail
: JSON.stringify(error_json.detail),
raw: error_json.detail
}
}
@@ -311,7 +371,11 @@ export const post_object = async function post_object({
// Return the response data or metadata
// Robustly handle V3 response envelopes
return return_meta ? json : (json.data !== undefined ? json.data : json);
return return_meta
? json
: json.data !== undefined
? json.data
: json;
} else {
const blob = await response.blob();

View File

@@ -36,7 +36,9 @@ export async function load_ae_obj_id__archive({
log_lvl?: number;
}): Promise<ae_Archive | null> {
if (log_lvl) {
console.log(`*** load_ae_obj_id__archive() *** archive_id=${archive_id}`);
console.log(
`*** load_ae_obj_id__archive() *** archive_id=${archive_id}`
);
}
ae_promises.load__archive_obj = await api
@@ -52,10 +54,11 @@ export async function load_ae_obj_id__archive({
.then(async function (archive_obj_get_result) {
if (archive_obj_get_result) {
if (try_cache) {
const processed_obj_li = await process_ae_obj__archive_props({
obj_li: [archive_obj_get_result],
log_lvl: log_lvl
});
const processed_obj_li =
await process_ae_obj__archive_props({
obj_li: [archive_obj_get_result],
log_lvl: log_lvl
});
await db_save_ae_obj_li__ae_obj({
db_instance: db_archives,
table_name: 'archive',
@@ -76,19 +79,21 @@ export async function load_ae_obj_id__archive({
if (inc_content_li && ae_promises.load__archive_obj) {
// Load the contents for the archive
const load_archive_content_obj_li = await load_ae_obj_li__archive_content({
api_cfg: api_cfg,
for_obj_type: 'archive',
for_obj_id: archive_id,
enabled: enabled,
hidden: hidden,
limit: limit,
offset: offset,
params: params,
try_cache: try_cache,
log_lvl: log_lvl
});
ae_promises.load__archive_obj.archive_content_li = load_archive_content_obj_li;
const load_archive_content_obj_li =
await load_ae_obj_li__archive_content({
api_cfg: api_cfg,
for_obj_type: 'archive',
for_obj_id: archive_id,
enabled: enabled,
hidden: hidden,
limit: limit,
offset: offset,
params: params,
try_cache: try_cache,
log_lvl: log_lvl
});
ae_promises.load__archive_obj.archive_content_li =
load_archive_content_obj_li;
}
return ae_promises.load__archive_obj;
@@ -125,7 +130,9 @@ export async function load_ae_obj_li__archive({
view?: string;
limit?: number;
offset?: number;
order_by_li?: Record<string, 'ASC' | 'DESC'> | Record<string, 'ASC' | 'DESC'>[];
order_by_li?:
| Record<string, 'ASC' | 'DESC'>
| Record<string, 'ASC' | 'DESC'>[];
params?: key_val;
try_cache?: boolean;
log_lvl?: number;
@@ -138,7 +145,9 @@ export async function load_ae_obj_li__archive({
// DEBUG: Trace massive content loads
if (inc_content_li) {
console.warn(`load_ae_obj_li__archive: Loading content for ALL archives in list! Limit: ${limit}`);
console.warn(
`load_ae_obj_li__archive: Loading content for ALL archives in list! Limit: ${limit}`
);
// console.trace();
}
@@ -159,10 +168,11 @@ export async function load_ae_obj_li__archive({
.then(async function (archive_obj_li_get_result) {
if (archive_obj_li_get_result) {
if (try_cache) {
const processed_obj_li = await process_ae_obj__archive_props({
obj_li: archive_obj_li_get_result,
log_lvl: log_lvl
});
const processed_obj_li =
await process_ae_obj__archive_props({
obj_li: archive_obj_li_get_result,
log_lvl: log_lvl
});
await db_save_ae_obj_li__ae_obj({
db_instance: db_archives,
table_name: 'archive',
@@ -220,7 +230,9 @@ export async function create_ae_obj__archive({
log_lvl?: number;
}): Promise<ae_Archive | null> {
if (log_lvl) {
console.log(`*** create_ae_obj__archive() *** account_id=${account_id}`);
console.log(
`*** create_ae_obj__archive() *** account_id=${account_id}`
);
}
const result = await api.create_ae_obj({
@@ -268,7 +280,9 @@ export async function delete_ae_obj_id__archive({
log_lvl?: number;
}) {
if (log_lvl) {
console.log(`*** delete_ae_obj_id__archive() *** archive_id=${archive_id}`);
console.log(
`*** delete_ae_obj_id__archive() *** archive_id=${archive_id}`
);
}
const result = await api.delete_ae_obj({
@@ -304,7 +318,10 @@ export async function update_ae_obj__archive({
log_lvl?: number;
}): Promise<ae_Archive | null> {
if (log_lvl) {
console.log(`*** update_ae_obj__archive() *** archive_id=${archive_id}`, data_kv);
console.log(
`*** update_ae_obj__archive() *** archive_id=${archive_id}`,
data_kv
);
}
const result = await api.update_ae_obj({
@@ -366,7 +383,11 @@ export async function qry__archive({
const search_query: any = { and: [] };
if (account_id) {
search_query.and.push({ field: 'account_id_random', op: 'eq', value: account_id });
search_query.and.push({
field: 'account_id_random',
op: 'eq',
value: account_id
});
}
if (qry_str) {
@@ -452,11 +473,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);
@@ -489,4 +514,4 @@ export async function process_ae_obj__archive_props({
return obj;
}
});
}
}

View File

@@ -41,10 +41,11 @@ export async function load_ae_obj_id__archive_content({
.then(async function (archive_content_obj_get_result) {
if (archive_content_obj_get_result) {
if (try_cache) {
const processed_obj_li = await process_ae_obj__archive_content_props({
obj_li: [archive_content_obj_get_result],
log_lvl: log_lvl
});
const processed_obj_li =
await process_ae_obj__archive_content_props({
obj_li: [archive_content_obj_get_result],
log_lvl: log_lvl
});
await db_save_ae_obj_li__ae_obj({
db_instance: db_archives,
table_name: 'content',
@@ -96,7 +97,9 @@ export async function load_ae_obj_li__archive_content({
view?: string;
limit?: number;
offset?: number;
order_by_li?: Record<string, 'ASC' | 'DESC'> | Record<string, 'ASC' | 'DESC'>[];
order_by_li?:
| Record<string, 'ASC' | 'DESC'>
| Record<string, 'ASC' | 'DESC'>[];
params?: key_val;
try_cache?: boolean;
log_lvl?: number;
@@ -124,10 +127,11 @@ export async function load_ae_obj_li__archive_content({
.then(async function (archive_content_obj_li_get_result) {
if (archive_content_obj_li_get_result) {
if (try_cache) {
const processed_obj_li = await process_ae_obj__archive_content_props({
obj_li: archive_content_obj_li_get_result,
log_lvl: log_lvl
});
const processed_obj_li =
await process_ae_obj__archive_content_props({
obj_li: archive_content_obj_li_get_result,
log_lvl: log_lvl
});
await db_save_ae_obj_li__ae_obj({
db_instance: db_archives,
table_name: 'content',
@@ -162,11 +166,15 @@ export async function create_ae_obj__archive_content({
log_lvl?: number;
}): Promise<ae_ArchiveContent | null> {
if (log_lvl) {
console.log(`*** create_ae_obj__archive_content() *** archive_id=${archive_id}`);
console.log(
`*** create_ae_obj__archive_content() *** archive_id=${archive_id}`
);
}
if (!archive_id) {
console.log(`ERROR: Archives - Content - archive_id required to create`);
console.log(
`ERROR: Archives - Content - archive_id required to create`
);
return null;
}
@@ -357,11 +365,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);
@@ -399,4 +411,4 @@ export async function process_ae_obj__archive_content_props({
return obj;
}
});
}
}

View File

@@ -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!

View File

@@ -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>

View File

@@ -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}

View File

@@ -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}

View File

@@ -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>

View File

@@ -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);

View File

@@ -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
});
}

View File

@@ -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
});
}
}

View File

@@ -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
});
}
}

View File

@@ -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);

View File

@@ -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);

View File

@@ -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
});
}
}

View File

@@ -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;
}

View 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);
}

View File

@@ -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`
);
}
}

View File

@@ -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`
);
}
}

View File

@@ -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;

View File

@@ -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
});
}
}

View File

@@ -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) {

View File

@@ -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;

View File

@@ -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`
);
}
}

View File

@@ -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'
});
}
}

View File

@@ -1,125 +1,140 @@
<script lang="ts">
/**
* AE_AITools.svelte
* GENERIC Aether AI Toolset (Runes/Svelte 5)
* Extracted logic from Journals module to be system-wide.
*/
import OpenAI from 'openai';
import { Modal } from 'flowbite-svelte';
import {
Bot, BotMessageSquare, Loader, FileText,
Save, FilePenLine, RotateCcw, Settings,
RefreshCcw, Globe, Copy
} from '@lucide/svelte';
import { ae_loc, ae_api } from '$lib/stores/ae_stores';
import AE_Comp_Editor_CodeMirror from '$lib/elements/element_editor_codemirror.svelte';
/**
* AE_AITools.svelte
* GENERIC Aether AI Toolset (Runes/Svelte 5)
* Extracted logic from Journals module to be system-wide.
*/
import OpenAI from 'openai';
import { Modal } from 'flowbite-svelte';
import {
Bot,
BotMessageSquare,
Loader,
FileText,
Save,
FilePenLine,
RotateCcw,
Settings,
RefreshCcw,
Globe,
Copy
} from '@lucide/svelte';
import { ae_loc, ae_api } from '$lib/stores/ae_stores';
import AE_Comp_Editor_CodeMirror from '$lib/elements/element_editor_codemirror.svelte';
interface Props {
// Core Props
content: string; // The text to summarize/analyze
summary: string; // The result (bindable)
// Configuration (Bindable for global settings persistence)
model?: string;
baseUrl?: string;
token?: string;
systemPrompt?: string;
maxTokens?: number;
temperature?: number;
// Callbacks
onSave?: (newSummary: string) => void;
onSyncConfig?: () => void; // Optional: callback to sync from global site config
// UI Customization
buttonClass?: string;
log_lvl?: number;
interface Props {
// Core Props
content: string; // The text to summarize/analyze
summary: string; // The result (bindable)
// Configuration (Bindable for global settings persistence)
model?: string;
baseUrl?: string;
token?: string;
systemPrompt?: string;
maxTokens?: number;
temperature?: number;
// Callbacks
onSave?: (newSummary: string) => void;
onSyncConfig?: () => void; // Optional: callback to sync from global site config
// UI Customization
buttonClass?: string;
log_lvl?: number;
}
let {
content,
summary = $bindable(),
model = $bindable(),
baseUrl = $bindable(),
token = $bindable(),
systemPrompt = $bindable(),
maxTokens = $bindable(),
temperature = $bindable(),
onSave,
onSyncConfig,
buttonClass = 'btn btn-sm preset-tonal-primary shadow-lg hover:scale-105 transition-all',
log_lvl = 0
}: Props = $props();
// Apply defaults if undefined (Safe for Svelte 5 Runes)
if (model === undefined) model = 'dgrzone-deepseek-8b-quick';
if (baseUrl === undefined) baseUrl = 'https://ai.dgrzone.com/api';
if (token === undefined) token = '';
if (systemPrompt === undefined) systemPrompt = 'You are a helpful assistant.';
if (maxTokens === undefined) maxTokens = 512;
if (temperature === undefined) temperature = 0.7;
// Internal State
let ae_promises: any = $state(null);
let show_modal = $state(false);
let active_tab: 'result' | 'settings' = $state('result');
let tmp_summary = $state('');
async function generate_ai_result() {
if (!content) {
alert('No content available to analyze.');
return;
}
let {
content,
summary = $bindable(),
model = $bindable(),
baseUrl = $bindable(),
token = $bindable(),
systemPrompt = $bindable(),
maxTokens = $bindable(),
temperature = $bindable(),
onSave,
onSyncConfig,
buttonClass = "btn btn-sm preset-tonal-primary shadow-lg hover:scale-105 transition-all",
log_lvl = 0
}: Props = $props();
active_tab = 'result';
// Apply defaults if undefined (Safe for Svelte 5 Runes)
if (model === undefined) model = 'dgrzone-deepseek-8b-quick';
if (baseUrl === undefined) baseUrl = 'https://ai.dgrzone.com/api';
if (token === undefined) token = '';
if (systemPrompt === undefined) systemPrompt = 'You are a helpful assistant.';
if (maxTokens === undefined) maxTokens = 512;
if (temperature === undefined) temperature = 0.7;
// Internal State
let ae_promises: any = $state(null);
let show_modal = $state(false);
let active_tab: 'result' | 'settings' = $state('result');
let tmp_summary = $state('');
async function generate_ai_result() {
if (!content) {
alert('No content available to analyze.');
return;
}
active_tab = 'result';
// If no token is provided, trigger a "Demo Mode" placeholder after a fake delay
if (!token || token === '') {
console.log('AE_AITools: No token provided. Entering Demo Mode.');
ae_promises = new Promise((resolve) => {
setTimeout(() => {
tmp_summary = `### AI Summary (DEMO MODE)\n\nThis is a placeholder summary because no API token was provided in the settings. \n\n**Original Content Length:** ${content.length} characters.\n\n**System Prompt:** ${systemPrompt}\n\n**Model:** ${model}`;
show_modal = true;
resolve(true);
}, 1500);
});
return;
}
const ai_client = new OpenAI({
apiKey: token,
baseURL: baseUrl,
dangerouslyAllowBrowser: true
// If no token is provided, trigger a "Demo Mode" placeholder after a fake delay
if (!token || token === '') {
console.log('AE_AITools: No token provided. Entering Demo Mode.');
ae_promises = new Promise((resolve) => {
setTimeout(() => {
tmp_summary = `### AI Summary (DEMO MODE)\n\nThis is a placeholder summary because no API token was provided in the settings. \n\n**Original Content Length:** ${content.length} characters.\n\n**System Prompt:** ${systemPrompt}\n\n**Model:** ${model}`;
show_modal = true;
resolve(true);
}, 1500);
});
return;
}
try {
ae_promises = ai_client.chat.completions.create({
const ai_client = new OpenAI({
apiKey: token,
baseURL: baseUrl,
dangerouslyAllowBrowser: true
});
try {
ae_promises = ai_client.chat.completions
.create({
model: model || 'dgrzone-deepseek-8b-quick',
max_tokens: maxTokens,
temperature: temperature,
messages: [
{ role: 'system', content: systemPrompt || 'You are a helpful assistant.' },
{
role: 'system',
content: systemPrompt || 'You are a helpful assistant.'
},
{ role: 'user', content: content }
]
}).then((resp) => {
const result = resp?.choices?.[0]?.message?.content || 'No result generated.';
})
.then((resp) => {
const result =
resp?.choices?.[0]?.message?.content ||
'No result generated.';
tmp_summary = result;
show_modal = true;
});
} catch (err: any) {
console.error('AE_AITools: AI Error:', err);
// Even on error, show the modal with the error message so the UI can be inspected
tmp_summary = `### AI Error\n\nFailed to connect to the AI service.\n\n**Error:** ${err.message}\n\nCheck your Settings tab for Base URL and Token configuration.`;
show_modal = true;
ae_promises = Promise.resolve();
}
} catch (err: any) {
console.error('AE_AITools: AI Error:', err);
// Even on error, show the modal with the error message so the UI can be inspected
tmp_summary = `### AI Error\n\nFailed to connect to the AI service.\n\n**Error:** ${err.message}\n\nCheck your Settings tab for Base URL and Token configuration.`;
show_modal = true;
ae_promises = Promise.resolve();
}
}
function handle_save() {
summary = tmp_summary;
if (onSave) onSave(tmp_summary);
show_modal = false;
}
function handle_save() {
summary = tmp_summary;
if (onSave) onSave(tmp_summary);
show_modal = false;
}
</script>
<div class="ae-ai-tools-wrapper inline-flex items-center gap-1">
@@ -128,13 +143,12 @@
type="button"
onclick={generate_ai_result}
class={buttonClass}
title="Generate AI summary/analysis"
>
title="Generate AI summary/analysis">
{#await ae_promises}
<Loader class="inline-block mr-1 animate-spin" size="1.2em" />
<Loader class="mr-1 inline-block animate-spin" size="1.2em" />
<span class="text-sm">Processing...</span>
{:then}
<BotMessageSquare class="inline-block mr-1" size="1.2em" />
<BotMessageSquare class="mr-1 inline-block" size="1.2em" />
<span class="text-sm">Summarize</span>
{:catch}
<span class="text-sm text-red-500">Error</span>
@@ -149,8 +163,7 @@
show_modal = true;
}}
class="btn btn-sm variant-soft-surface shadow-md"
title="AI Settings"
>
title="AI Settings">
<Settings size="1.2em" />
</button>
@@ -160,32 +173,37 @@
title="Aether AI Assistant"
bind:open={show_modal}
size="lg"
class="bg-white dark:bg-gray-800"
>
class="bg-white dark:bg-gray-800">
<div class="space-y-4 p-2">
<!-- Tab Navigation -->
<div class="flex gap-1 border-b border-surface-500/20 pb-2">
<button
class="btn btn-sm {active_tab === 'result' ? 'variant-filled-primary' : 'variant-soft-surface'}"
onclick={() => active_tab = 'result'}
>
<div class="border-surface-500/20 flex gap-1 border-b pb-2">
<button
class="btn btn-sm {active_tab === 'result'
? 'variant-filled-primary'
: 'variant-soft-surface'}"
onclick={() => (active_tab = 'result')}>
<Bot size="1.1em" class="mr-1" /> Result
</button>
<button
class="btn btn-sm {active_tab === 'settings' ? 'variant-filled-secondary' : 'variant-soft-surface'}"
onclick={() => active_tab = 'settings'}
>
<button
class="btn btn-sm {active_tab === 'settings'
? 'variant-filled-secondary'
: 'variant-soft-surface'}"
onclick={() => (active_tab = 'settings')}>
<Settings size="1.1em" class="mr-1" /> Settings
</button>
</div>
{#if active_tab === 'result'}
<div class="space-y-4 animate-in fade-in duration-200">
<div class="flex gap-2 justify-start">
<button class="btn btn-sm variant-filled-success" onclick={handle_save}>
<div class="animate-in fade-in space-y-4 duration-200">
<div class="flex justify-start gap-2">
<button
class="btn btn-sm variant-filled-success"
onclick={handle_save}>
<Save size="1.1em" class="mr-1" /> Save Result
</button>
<button class="btn btn-sm variant-ghost-primary" onclick={generate_ai_result}>
<button
class="btn btn-sm variant-ghost-primary"
onclick={generate_ai_result}>
<RotateCcw size="1.1em" class="mr-1" /> Re-run
</button>
</div>
@@ -195,57 +213,84 @@
bind:new_content={tmp_summary}
theme_mode={$ae_loc.theme_mode}
placeholder="AI Result will appear here..."
class_li="p-2 border rounded-lg h-96 shadow-inner bg-surface-500/5"
/>
class_li="p-2 border rounded-lg h-96 shadow-inner bg-surface-500/5" />
</div>
{:else}
<div class="space-y-6 animate-in slide-in-from-left-4 duration-200">
<div
class="animate-in slide-in-from-left-4 space-y-6 duration-200">
<!-- Connection Settings -->
<div class="space-y-4">
<h3 class="text-sm font-bold uppercase tracking-widest text-surface-500 flex items-center gap-2">
<h3
class="text-surface-500 flex items-center gap-2 text-sm font-bold tracking-widest uppercase">
<Globe size="1.1em" /> API Connection
</h3>
{#if onSyncConfig}
<button class="btn btn-sm variant-soft-primary" onclick={onSyncConfig}>
<Copy size="1.1em" class="mr-1" /> Sync Global Defaults
<button
class="btn btn-sm variant-soft-primary"
onclick={onSyncConfig}>
<Copy size="1.1em" class="mr-1" /> Sync Global
Defaults
</button>
{/if}
<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>Base URL</span>
<input type="text" bind:value={baseUrl} class="input input-sm" />
<input
type="text"
bind:value={baseUrl}
class="input input-sm" />
</label>
<label class="label">
<span>Model</span>
<input type="text" bind:value={model} class="input input-sm" />
<input
type="text"
bind:value={model}
class="input input-sm" />
</label>
</div>
<label class="label">
<span>API Token</span>
<input type="password" bind:value={token} class="input input-sm font-mono" />
<input
type="password"
bind:value={token}
class="input input-sm font-mono" />
</label>
</div>
<!-- Model Parameters -->
<div class="space-y-4 pt-4 border-t border-surface-500/10">
<h3 class="text-sm font-bold uppercase tracking-widest text-surface-500 flex items-center gap-2">
<div
class="border-surface-500/10 space-y-4 border-t pt-4">
<h3
class="text-surface-500 flex items-center gap-2 text-sm font-bold tracking-widest uppercase">
<FilePenLine size="1.1em" /> Inference Parameters
</h3>
<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>Temperature ({temperature})</span>
<input type="range" bind:value={temperature} min="0" max="1" step="0.1" class="range" />
<input
type="range"
bind:value={temperature}
min="0"
max="1"
step="0.1"
class="range" />
</label>
<label class="label">
<span>Max Tokens</span>
<input type="number" bind:value={maxTokens} class="input input-sm" />
<input
type="number"
bind:value={maxTokens}
class="input input-sm" />
</label>
</div>
<label class="label">
<span>System Prompt</span>
<textarea bind:value={systemPrompt} class="textarea h-24 text-xs font-mono"></textarea>
<textarea
bind:value={systemPrompt}
class="textarea h-24 font-mono text-xs"
></textarea>
</label>
</div>
</div>
@@ -253,4 +298,4 @@
</div>
</Modal>
{/if}
</div>
</div>

View File

@@ -1,58 +1,64 @@
<script lang="ts">
/**
* AE_ObjectFlags.svelte
* GENERIC Aether Object Flags & Visibility Toggles
* Manages: alert, private, public, personal, professional, template
*/
import {
Siren, MessageSquareWarning, Fingerprint,
Globe, BookHeart, BriefcaseBusiness, NotepadTextDashed,
Settings
} from '@lucide/svelte';
import { ae_loc } from '$lib/stores/ae_stores';
/**
* AE_ObjectFlags.svelte
* GENERIC Aether Object Flags & Visibility Toggles
* Manages: alert, private, public, personal, professional, template
*/
import {
Siren,
MessageSquareWarning,
Fingerprint,
Globe,
BookHeart,
BriefcaseBusiness,
NotepadTextDashed,
Settings
} from '@lucide/svelte';
import { ae_loc } from '$lib/stores/ae_stores';
interface Props {
// The object containing the flags (bindable)
obj: any;
interface Props {
// The object containing the flags (bindable)
obj: any;
// Visibility configuration (optional overrides)
show_labels?: boolean;
hide_alert?: boolean;
hide_private?: boolean;
hide_public?: boolean;
hide_personal?: boolean;
hide_professional?: boolean;
hide_template?: boolean;
// Visibility configuration (optional overrides)
show_labels?: boolean;
hide_alert?: boolean;
hide_private?: boolean;
hide_public?: boolean;
hide_personal?: boolean;
hide_professional?: boolean;
hide_template?: boolean;
// Callbacks
on_toggle?: (prop: string, newValue: boolean) => void;
// Callbacks
on_toggle?: (prop: string, newValue: boolean) => void;
// Styling
container_class?: string;
}
// Styling
container_class?: string;
}
let {
obj = $bindable(),
show_labels = true,
hide_alert: hide_alert = false,
hide_private: hide_private = false,
hide_public: hide_public = false,
hide_personal: hide_personal = false,
hide_professional: hide_professional = false,
hide_template: hide_template = false,
on_toggle: onToggle,
container_class = "flex flex-row flex-wrap gap-1 items-center justify-evenly py-2 border-y border-surface-500/10"
}: Props = $props();
let {
obj = $bindable(),
show_labels = true,
hide_alert: hide_alert = false,
hide_private: hide_private = false,
hide_public: hide_public = false,
hide_personal: hide_personal = false,
hide_professional: hide_professional = false,
hide_template: hide_template = false,
on_toggle: onToggle,
container_class = 'flex flex-row flex-wrap gap-1 items-center justify-evenly py-2 border-y border-surface-500/10'
}: Props = $props();
function handle_toggle(prop: string) {
obj[prop] = !obj[prop];
if (onToggle) onToggle(prop, obj[prop]);
}
function handle_toggle(prop: string) {
obj[prop] = !obj[prop];
if (onToggle) onToggle(prop, obj[prop]);
}
</script>
<div class={container_class}>
{#if show_labels}
<span class="text-xs text-surface-500 flex items-center gap-1 uppercase font-bold tracking-wider mr-2">
<span
class="text-surface-500 mr-2 flex items-center gap-1 text-xs font-bold tracking-wider uppercase">
<Settings size="1.1em" /> Flags:
</span>
{/if}
@@ -63,9 +69,10 @@
type="button"
onclick={() => handle_toggle('alert')}
class="btn-icon btn-icon-sm preset-tonal-secondary hover:preset-filled-secondary-500 transition"
title="Toggle Alert Status"
>
<Siren size="1.2em" class={obj?.alert ? 'text-error-500' : 'opacity-40'} />
title="Toggle Alert Status">
<Siren
size="1.2em"
class={obj?.alert ? 'text-error-500' : 'opacity-40'} />
</button>
{/if}
@@ -75,9 +82,10 @@
type="button"
onclick={() => handle_toggle('private')}
class="btn-icon btn-icon-sm preset-tonal-secondary hover:preset-filled-secondary-500 transition"
title="Toggle Private/Encrypted"
>
<Fingerprint size="1.2em" class={obj?.private ? 'text-success-500' : 'opacity-40'} />
title="Toggle Private/Encrypted">
<Fingerprint
size="1.2em"
class={obj?.private ? 'text-success-500' : 'opacity-40'} />
</button>
{/if}
@@ -87,9 +95,10 @@
type="button"
onclick={() => handle_toggle('public')}
class="btn-icon btn-icon-sm preset-tonal-secondary hover:preset-filled-secondary-500 transition"
title="Toggle Public Visibility"
>
<Globe size="1.2em" class={obj?.public ? 'text-success-500' : 'opacity-40'} />
title="Toggle Public Visibility">
<Globe
size="1.2em"
class={obj?.public ? 'text-success-500' : 'opacity-40'} />
</button>
{/if}
@@ -99,9 +108,10 @@
type="button"
onclick={() => handle_toggle('personal')}
class="btn-icon btn-icon-sm preset-tonal-secondary hover:preset-filled-secondary-500 transition"
title="Toggle Personal Scope"
>
<BookHeart size="1.2em" class={obj?.personal ? 'text-success-500' : 'opacity-40'} />
title="Toggle Personal Scope">
<BookHeart
size="1.2em"
class={obj?.personal ? 'text-success-500' : 'opacity-40'} />
</button>
{/if}
@@ -111,9 +121,10 @@
type="button"
onclick={() => handle_toggle('professional')}
class="btn-icon btn-icon-sm preset-tonal-secondary hover:preset-filled-secondary-500 transition"
title="Toggle Professional Scope"
>
<BriefcaseBusiness size="1.2em" class={obj?.professional ? 'text-success-500' : 'opacity-40'} />
title="Toggle Professional Scope">
<BriefcaseBusiness
size="1.2em"
class={obj?.professional ? 'text-success-500' : 'opacity-40'} />
</button>
{/if}
@@ -123,9 +134,10 @@
type="button"
onclick={() => handle_toggle('template')}
class="btn-icon btn-icon-sm preset-tonal-secondary hover:preset-filled-secondary-500 transition"
title="Toggle Template Mode"
>
<NotepadTextDashed size="1.2em" class={obj?.template ? 'text-success-500' : 'opacity-40'} />
title="Toggle Template Mode">
<NotepadTextDashed
size="1.2em"
class={obj?.template ? 'text-success-500' : 'opacity-40'} />
</button>
{/if}
</div>

View File

@@ -1,95 +1,96 @@
<script lang="ts">
/**
* AE_Record_Controls.svelte
* GENERIC Aether Record Management Controls
* Manages: priority, hide, enable, alert, delete/disable
*
* Emits events — NO API calls. Parent is responsible for:
* 1. Calling the API (update_ae_obj, delete_ae_obj_id__*)
* 2. Refreshing the object from cache/API
* 3. Navigating away on delete
*
* Usage:
* <AE_Record_Controls
* obj={$lq__event_session_obj}
* obj_label="session"
* allow_delete={$ae_loc.manager_access}
* allow_disable={$ae_loc.administrator_access}
* on_toggle={(field, val) => { ... }}
* on_delete={(method) => { ... }}
* />
*/
import {
Star,
Eye,
EyeOff,
ToggleLeft,
ToggleRight,
Bell,
BellOff,
Trash2,
CircleMinus,
Settings
} from '@lucide/svelte';
/**
* AE_Record_Controls.svelte
* GENERIC Aether Record Management Controls
* Manages: priority, hide, enable, alert, delete/disable
*
* Emits events — NO API calls. Parent is responsible for:
* 1. Calling the API (update_ae_obj, delete_ae_obj_id__*)
* 2. Refreshing the object from cache/API
* 3. Navigating away on delete
*
* Usage:
* <AE_Record_Controls
* obj={$lq__event_session_obj}
* obj_label="session"
* allow_delete={$ae_loc.manager_access}
* allow_disable={$ae_loc.administrator_access}
* on_toggle={(field, val) => { ... }}
* on_delete={(method) => { ... }}
* />
*/
import {
Star,
Eye,
EyeOff,
ToggleLeft,
ToggleRight,
Bell,
BellOff,
Trash2,
CircleMinus,
Settings
} from '@lucide/svelte';
interface Props {
// The object whose flags are being displayed (read-only — parent owns state)
obj: any;
interface Props {
// The object whose flags are being displayed (read-only — parent owns state)
obj: any;
// Human-readable label for confirm dialogs ("session", "presenter", "location", etc.)
obj_label?: string;
// Human-readable label for confirm dialogs ("session", "presenter", "location", etc.)
obj_label?: string;
// Visibility — hide any control that doesn't apply for this object type
show_alert?: boolean;
show_priority?: boolean;
show_enable?: boolean;
show_hide?: boolean;
show_labels?: boolean;
// Visibility — hide any control that doesn't apply for this object type
show_alert?: boolean;
show_priority?: boolean;
show_enable?: boolean;
show_hide?: boolean;
show_labels?: boolean;
// Permission gates — parent passes booleans derived from $ae_loc
allow_delete?: boolean; // Hard permanent delete (manager+)
allow_disable?: boolean; // Soft disable/remove (administrator+)
// Permission gates — parent passes booleans derived from $ae_loc
allow_delete?: boolean; // Hard permanent delete (manager+)
allow_disable?: boolean; // Soft disable/remove (administrator+)
// Callbacks — parent handles API + refresh + navigation
on_toggle?: (field: string, new_val: boolean) => void;
on_delete?: (method: 'delete' | 'disable') => void;
// Callbacks — parent handles API + refresh + navigation
on_toggle?: (field: string, new_val: boolean) => void;
on_delete?: (method: 'delete' | 'disable') => void;
// Styling
container_class?: string;
}
// Styling
container_class?: string;
}
let {
obj,
obj_label = 'record',
show_alert = true,
show_priority = true,
show_enable = true,
show_hide = true,
show_labels = true,
allow_delete = false,
allow_disable = false,
on_toggle,
on_delete,
container_class = 'flex flex-row flex-wrap gap-1 items-center justify-evenly py-2 border-y border-surface-500/10'
}: Props = $props();
let {
obj,
obj_label = 'record',
show_alert = true,
show_priority = true,
show_enable = true,
show_hide = true,
show_labels = true,
allow_delete = false,
allow_disable = false,
on_toggle,
on_delete,
container_class = 'flex flex-row flex-wrap gap-1 items-center justify-evenly py-2 border-y border-surface-500/10'
}: Props = $props();
function toggle(field: string) {
if (on_toggle) on_toggle(field, !obj?.[field]);
}
function toggle(field: string) {
if (on_toggle) on_toggle(field, !obj?.[field]);
}
function handle_delete(method: 'delete' | 'disable') {
const msg =
method === 'delete'
? `Permanently delete this ${obj_label}? This cannot be undone.`
: `Remove (disable) this ${obj_label}?`;
if (!confirm(msg)) return;
if (on_delete) on_delete(method);
}
function handle_delete(method: 'delete' | 'disable') {
const msg =
method === 'delete'
? `Permanently delete this ${obj_label}? This cannot be undone.`
: `Remove (disable) this ${obj_label}?`;
if (!confirm(msg)) return;
if (on_delete) on_delete(method);
}
</script>
<div class={container_class}>
{#if show_labels}
<span class="text-xs text-surface-500 flex items-center gap-1 uppercase font-bold tracking-wider mr-2">
<span
class="text-surface-500 mr-2 flex items-center gap-1 text-xs font-bold tracking-wider uppercase">
<Settings size="1.1em" /> Controls:
</span>
{/if}
@@ -103,12 +104,10 @@
class:preset-filled-warning-500={obj?.priority}
class:preset-tonal-secondary={!obj?.priority}
class:hover:preset-filled-warning-500={!obj?.priority}
title={obj?.priority ? 'Remove priority flag' : 'Mark as priority'}
>
title={obj?.priority ? 'Remove priority flag' : 'Mark as priority'}>
<Star
size="1.2em"
class={obj?.priority ? 'fill-current' : 'opacity-50'}
/>
class={obj?.priority ? 'fill-current' : 'opacity-50'} />
</button>
{/if}
@@ -121,8 +120,7 @@
class:preset-filled-warning-500={obj?.hide}
class:preset-tonal-secondary={!obj?.hide}
class:hover:preset-filled-warning-500={!obj?.hide}
title={obj?.hide ? 'Unhide this record' : 'Hide this record'}
>
title={obj?.hide ? 'Unhide this record' : 'Hide this record'}>
{#if obj?.hide}
<EyeOff size="1.2em" class="text-warning-500" />
{:else}
@@ -140,8 +138,7 @@
class:preset-filled-success-500={obj?.enable}
class:preset-filled-error-500={!obj?.enable}
class:hover:preset-filled-success-500={!obj?.enable}
title={obj?.enable ? 'Disable this record' : 'Enable this record'}
>
title={obj?.enable ? 'Disable this record' : 'Enable this record'}>
{#if obj?.enable}
<ToggleRight size="1.2em" class="text-success-300" />
{:else}
@@ -159,8 +156,7 @@
class:preset-filled-error-500={obj?.alert}
class:preset-tonal-secondary={!obj?.alert}
class:hover:preset-filled-error-500={!obj?.alert}
title={obj?.alert ? 'Remove alert status' : 'Mark as alert'}
>
title={obj?.alert ? 'Remove alert status' : 'Mark as alert'}>
{#if obj?.alert}
<Bell size="1.2em" class="text-error-300" />
{:else}
@@ -175,8 +171,7 @@
type="button"
onclick={() => handle_delete('delete')}
class="btn-icon btn-icon-sm preset-filled-error-500 hover:preset-filled-error-600 transition"
title="Permanently delete this {obj_label}"
>
title="Permanently delete this {obj_label}">
<Trash2 size="1.2em" />
</button>
{:else if allow_disable}
@@ -184,8 +179,7 @@
type="button"
onclick={() => handle_delete('disable')}
class="btn-icon btn-icon-sm preset-filled-warning-500 hover:preset-filled-warning-600 transition"
title="Disable / soft-remove this {obj_label}"
>
title="Disable / soft-remove this {obj_label}">
<CircleMinus size="1.2em" />
</button>
{/if}

View File

@@ -46,7 +46,9 @@ export async function load_ae_obj_id__event({
log_lvl?: number;
}): Promise<ae_Event | null> {
if (log_lvl) {
console.log(`*** load_ae_obj_id__event() *** event_id=${event_id} (SWR Optimization)`);
console.log(
`*** load_ae_obj_id__event() *** event_id=${event_id} (SWR Optimization)`
);
}
// Hierarchy Enforcement: Pulling presentations/presenters requires pulling sessions first
@@ -57,20 +59,43 @@ export async function load_ae_obj_id__event({
try {
const cached_event = await db_events.event.get(event_id);
if (cached_event) {
if (log_lvl) console.log('EVENT LOAD: Cache hit. Returning stale data immediately.');
if (log_lvl)
console.log(
'EVENT LOAD: Cache hit. Returning stale data immediately.'
);
// Trigger background refresh
_refresh_event_background({
api_cfg, event_id, view, try_cache,
inc_device_li, inc_file_li, inc_location_li, inc_session_li, inc_presentation_li, inc_presenter_li, inc_template_li,
enabled, hidden,
api_cfg,
event_id,
view,
try_cache,
inc_device_li,
inc_file_li,
inc_location_li,
inc_session_li,
inc_presentation_li,
inc_presenter_li,
inc_template_li,
enabled,
hidden,
log_lvl: 0
});
// Still handle nested loads for the cached version to ensure UI richness
return await _handle_nested_loads(cached_event, {
api_cfg, inc_device_li, inc_file_li, inc_location_li, inc_session_li, inc_presentation_li, inc_presenter_li, inc_template_li,
enabled, hidden, try_cache, log_lvl
api_cfg,
inc_device_li,
inc_file_li,
inc_location_li,
inc_session_li,
inc_presentation_li,
inc_presenter_li,
inc_template_li,
enabled,
hidden,
try_cache,
log_lvl
});
}
} catch (e) {
@@ -80,9 +105,19 @@ export async function load_ae_obj_id__event({
// 2. SLOW PATH: Wait for API if cache is empty or try_cache is false
return await _refresh_event_background({
api_cfg, event_id, view, try_cache,
inc_device_li, inc_file_li, inc_location_li, inc_session_li, inc_presentation_li, inc_presenter_li, inc_template_li,
enabled, hidden,
api_cfg,
event_id,
view,
try_cache,
inc_device_li,
inc_file_li,
inc_location_li,
inc_session_li,
inc_presentation_li,
inc_presenter_li,
inc_template_li,
enabled,
hidden,
log_lvl
});
}
@@ -91,9 +126,19 @@ export async function load_ae_obj_id__event({
* Internal helper to perform the actual API fetch and cache update for events
*/
async function _refresh_event_background({
api_cfg, event_id, view, try_cache,
inc_device_li, inc_file_li, inc_location_li, inc_session_li, inc_presentation_li, inc_presenter_li, inc_template_li,
enabled, hidden,
api_cfg,
event_id,
view,
try_cache,
inc_device_li,
inc_file_li,
inc_location_li,
inc_session_li,
inc_presentation_li,
inc_presenter_li,
inc_template_li,
enabled,
hidden,
log_lvl
}: any) {
// Check if offline
@@ -129,8 +174,18 @@ async function _refresh_event_background({
}
return await _handle_nested_loads(processed_obj, {
api_cfg, inc_device_li, inc_file_li, inc_location_li, inc_session_li, inc_presentation_li, inc_presenter_li, inc_template_li,
enabled, hidden, try_cache: false, log_lvl
api_cfg,
inc_device_li,
inc_file_li,
inc_location_li,
inc_session_li,
inc_presentation_li,
inc_presenter_li,
inc_template_li,
enabled,
hidden,
try_cache: false,
log_lvl
});
}
} catch (error: any) {
@@ -142,8 +197,27 @@ async function _refresh_event_background({
/**
* Shared logic for loading nested child collections
*/
async function _handle_nested_loads(event_obj: any, { api_cfg, inc_device_li, inc_file_li, inc_location_li, inc_session_li, inc_presentation_li, inc_presenter_li, inc_template_li, enabled, hidden, try_cache, log_lvl }: any) {
if (log_lvl) console.log(`Loading nested collections for event: ${event_obj.event_id} (Devices: ${inc_device_li}, Files: ${inc_file_li}, Locations: ${inc_location_li}, Sessions: ${inc_session_li}, Presentations: ${inc_presentation_li}, Presenters: ${inc_presenter_li}, Templates: ${inc_template_li})`);
async function _handle_nested_loads(
event_obj: any,
{
api_cfg,
inc_device_li,
inc_file_li,
inc_location_li,
inc_session_li,
inc_presentation_li,
inc_presenter_li,
inc_template_li,
enabled,
hidden,
try_cache,
log_lvl
}: any
) {
if (log_lvl)
console.log(
`Loading nested collections for event: ${event_obj.event_id} (Devices: ${inc_device_li}, Files: ${inc_file_li}, Locations: ${inc_location_li}, Sessions: ${inc_session_li}, Presentations: ${inc_presentation_li}, Presenters: ${inc_presenter_li}, Templates: ${inc_template_li})`
);
// String-Only ID Vision: the '_id' field IS the string ID
const current_event_id = event_obj.id || event_obj.event_id;
@@ -151,56 +225,66 @@ async function _handle_nested_loads(event_obj: any, { api_cfg, inc_device_li, in
const tasks = [];
if (inc_device_li) {
tasks.push(load_ae_obj_li__event_device({
api_cfg,
for_obj_type: 'event',
for_obj_id: current_event_id,
try_cache,
log_lvl
}).then(res => event_obj.event_device_obj_li = res));
tasks.push(
load_ae_obj_li__event_device({
api_cfg,
for_obj_type: 'event',
for_obj_id: current_event_id,
try_cache,
log_lvl
}).then((res) => (event_obj.event_device_obj_li = res))
);
}
if (inc_file_li) {
tasks.push(load_ae_obj_li__event_file({
api_cfg,
for_obj_type: 'event',
for_obj_id: current_event_id,
enabled: 'all',
limit: 100,
try_cache,
log_lvl
}).then(res => event_obj.event_file_li = res));
tasks.push(
load_ae_obj_li__event_file({
api_cfg,
for_obj_type: 'event',
for_obj_id: current_event_id,
enabled: 'all',
limit: 100,
try_cache,
log_lvl
}).then((res) => (event_obj.event_file_li = res))
);
}
if (inc_location_li) {
tasks.push(load_ae_obj_li__event_location({
api_cfg,
for_obj_type: 'event',
for_obj_id: current_event_id,
enabled,
hidden,
try_cache,
log_lvl
}).then(res => event_obj.event_location_obj_li = res));
tasks.push(
load_ae_obj_li__event_location({
api_cfg,
for_obj_type: 'event',
for_obj_id: current_event_id,
enabled,
hidden,
try_cache,
log_lvl
}).then((res) => (event_obj.event_location_obj_li = res))
);
}
if (inc_session_li) {
tasks.push(load_ae_obj_li__event_session({
api_cfg,
for_obj_type: 'event',
for_obj_id: current_event_id,
inc_presentation_li,
inc_presenter_li,
enabled,
hidden,
try_cache,
log_lvl
}).then(res => event_obj.event_session_obj_li = res));
tasks.push(
load_ae_obj_li__event_session({
api_cfg,
for_obj_type: 'event',
for_obj_id: current_event_id,
inc_presentation_li,
inc_presenter_li,
enabled,
hidden,
try_cache,
log_lvl
}).then((res) => (event_obj.event_session_obj_li = res))
);
}
if (inc_template_li) {
tasks.push(load_ae_obj_li__event_badge_template({
api_cfg,
event_id: current_event_id,
try_cache,
log_lvl
}).then(res => event_obj.event_badge_template_obj_li = res));
tasks.push(
load_ae_obj_li__event_badge_template({
api_cfg,
event_id: current_event_id,
try_cache,
log_lvl
}).then((res) => (event_obj.event_badge_template_obj_li = res))
);
}
if (tasks.length > 0) await Promise.all(tasks);
@@ -238,20 +322,25 @@ export async function load_ae_obj_li__event({
inc_presenter_li?: boolean;
limit?: number;
offset?: number;
order_by_li?: Record<string, 'ASC' | 'DESC'> | Record<string, 'ASC' | 'DESC'>[];
order_by_li?:
| Record<string, 'ASC' | 'DESC'>
| Record<string, 'ASC' | 'DESC'>[];
params?: key_val;
try_cache?: boolean;
log_lvl?: number;
}): Promise<ae_Event[]> {
// Hierarchy Enforcement: Pulling presentations/presenters requires pulling sessions first
if (inc_presenter_li || inc_presentation_li) inc_session_li = true;
// Check if offline
if (typeof navigator !== 'undefined' && !navigator.onLine) {
if (log_lvl) console.log('Browser is offline. Skipping API and attempting cache load.');
if (log_lvl)
console.log(
'Browser is offline. Skipping API and attempting cache load.'
);
ae_promises.load__event_obj_li = await db_events.event
.where('account_id').equals(for_obj_id)
.where('account_id')
.equals(for_obj_id)
.toArray();
return ae_promises.load__event_obj_li || [];
}
@@ -264,7 +353,11 @@ export async function load_ae_obj_li__event({
};
if (for_obj_id) {
search_query.and.push({ field: `${for_obj_type}_id`, op: 'eq', value: for_obj_id });
search_query.and.push({
field: `${for_obj_type}_id`,
op: 'eq',
value: for_obj_id
});
}
promise = api.search_ae_obj({
@@ -319,9 +412,11 @@ export async function load_ae_obj_li__event({
} else {
console.log('No results returned from API.');
if (try_cache) {
if (log_lvl) console.log('Attempting to load from local cache...');
if (log_lvl)
console.log('Attempting to load from local cache...');
ae_promises.load__event_obj_li = await db_events.event
.where('account_id').equals(for_obj_id)
.where('account_id')
.equals(for_obj_id)
.toArray();
} else {
ae_promises.load__event_obj_li = [];
@@ -330,9 +425,13 @@ export async function load_ae_obj_li__event({
} catch (error: any) {
console.log('API request failed.', error);
if (try_cache) {
if (log_lvl) console.log('Attempting to load from local cache after error...');
if (log_lvl)
console.log(
'Attempting to load from local cache after error...'
);
ae_promises.load__event_obj_li = await db_events.event
.where('account_id').equals(for_obj_id)
.where('account_id')
.equals(for_obj_id)
.toArray();
} else {
ae_promises.load__event_obj_li = [];
@@ -340,18 +439,20 @@ export async function load_ae_obj_li__event({
}
if (inc_session_li && ae_promises.load__event_obj_li) {
const session_tasks = ae_promises.load__event_obj_li.map((event_obj: any) => {
const current_event_id = event_obj.id || event_obj.event_id;
return load_ae_obj_li__event_session({
api_cfg,
for_obj_type: 'event',
for_obj_id: current_event_id,
inc_presentation_li,
inc_presenter_li,
try_cache,
log_lvl
}).then((res) => (event_obj.event_session_obj_li = res));
});
const session_tasks = ae_promises.load__event_obj_li.map(
(event_obj: any) => {
const current_event_id = event_obj.id || event_obj.event_id;
return load_ae_obj_li__event_session({
api_cfg,
for_obj_type: 'event',
for_obj_id: current_event_id,
inc_presentation_li,
inc_presenter_li,
try_cache,
log_lvl
}).then((res) => (event_obj.event_session_obj_li = res));
}
);
await Promise.all(session_tasks);
}
@@ -543,13 +644,21 @@ export async function search__event({
};
const params: key_val = {};
search_query.and.push({ field: 'default_qry_str', op: 'like', value: `%${qry_str.trim()}%` });
params['lk_qry'] = { 'default_qry_str': qry_str.trim() };
search_query.and.push({
field: 'default_qry_str',
op: 'like',
value: `%${qry_str.trim()}%`
});
params['lk_qry'] = { default_qry_str: qry_str.trim() };
if (for_obj_id) {
// V3 Standard: Use random string ID for body filters.
// The API resolves this to the integer column automatically.
search_query.and.push({ field: `${for_obj_type}_id_random`, op: 'eq', value: for_obj_id });
search_query.and.push({
field: `${for_obj_type}_id_random`,
op: 'eq',
value: for_obj_id
});
}
// NOTE: We do NOT push 'physical' and 'virtual' to the server-side query here.
@@ -593,7 +702,11 @@ export async function search__event({
let valid_result_li: ae_Event[] = [];
if (Array.isArray(result_li)) {
valid_result_li = result_li;
} else if (result_li && typeof result_li === 'object' && Array.isArray((result_li as any).data)) {
} else if (
result_li &&
typeof result_li === 'object' &&
Array.isArray((result_li as any).data)
) {
valid_result_li = (result_li as any).data;
} else {
return [];
@@ -641,13 +754,12 @@ export async function search__event({
// Handle person ID filter
if (qry_person_id) {
const match = (
const match =
ev.external_person_id === qry_person_id ||
ev.poc_person_id === qry_person_id ||
ev.poc_person_id_random === qry_person_id ||
ev.poc_event_person_id === qry_person_id ||
ev.poc_event_person_id_random === qry_person_id
);
ev.poc_event_person_id_random === qry_person_id;
if (!match) return false;
}
@@ -655,7 +767,9 @@ export async function search__event({
});
if (log_lvl) {
console.log(`Filter results (Hybrid): Input=${processed_obj_li.length}, Output=${filtered_obj_li.length}`);
console.log(
`Filter results (Hybrid): Input=${processed_obj_li.length}, Output=${filtered_obj_li.length}`
);
}
return filtered_obj_li.slice(0, limit);
@@ -663,7 +777,6 @@ export async function search__event({
export const qry_ae_obj_li__event = search__event;
// Updated 2026-03-10
export const properties_to_save = [
'id',
@@ -780,14 +893,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);
@@ -849,7 +969,8 @@ export function sync_config__event_pres_mgmt({
pres_mgmt_cfg_remote?.label__session_poc_name_short ?? 'POC';
pres_mgmt_cfg_local.label__session_poc_name =
pres_mgmt_cfg_remote?.label__session_poc_name ?? 'Point of Contact';
pres_mgmt_cfg_local.hide__session_poc = pres_mgmt_cfg_remote?.hide__session_poc ?? false;
pres_mgmt_cfg_local.hide__session_poc =
pres_mgmt_cfg_remote?.hide__session_poc ?? false;
pres_mgmt_cfg_local.require__presenter_agree =
pres_mgmt_cfg_remote?.require__presenter_agree ?? false;
pres_mgmt_cfg_local.require__session_agree =
@@ -874,27 +995,31 @@ export function sync_config__event_pres_mgmt({
pres_mgmt_cfg_local.hide__presentation_datetime =
pres_mgmt_cfg_remote?.hide__presentation_datetime ?? false;
pres_mgmt_cfg_local.show_content__presentation_description =
pres_mgmt_cfg_remote?.show_content__presentation_description ?? false;
pres_mgmt_cfg_remote?.show_content__presentation_description ??
false;
pres_mgmt_cfg_local.hide__presenter_code =
pres_mgmt_cfg_remote?.hide__presenter_code ?? false;
pres_mgmt_cfg_local.hide__presenter_biography =
pres_mgmt_cfg_remote?.hide__presenter_biography ?? false;
pres_mgmt_cfg_local.hide__session_code = pres_mgmt_cfg_remote?.hide__session_code ?? false;
pres_mgmt_cfg_local.hide__session_code =
pres_mgmt_cfg_remote?.hide__session_code ?? false;
pres_mgmt_cfg_local.hide__session_description =
pres_mgmt_cfg_remote?.hide__session_description ?? false;
pres_mgmt_cfg_local.hide__session_location =
pres_mgmt_cfg_remote?.hide__session_location ?? false;
pres_mgmt_cfg_local.hide__session_msg = pres_mgmt_cfg_remote?.hide__session_msg ?? false;
pres_mgmt_cfg_local.hide__session_msg =
pres_mgmt_cfg_remote?.hide__session_msg ?? false;
pres_mgmt_cfg_local.hide__session_poc_profile =
pres_mgmt_cfg_remote?.hide__session_poc_profile ?? false;
pres_mgmt_cfg_local.hide__session_poc_biography =
pres_mgmt_cfg_remote?.hide__session_poc_biography ?? false;
pres_mgmt_cfg_local.hide__session_poc_profile_pic =
pres_mgmt_cfg_remote?.hide__session_poc_profile_pic ?? false;
pres_mgmt_cfg_local.hide_launcher_link = pres_mgmt_cfg_remote?.hide_launcher_link ?? false;
pres_mgmt_cfg_local.hide_launcher_link =
pres_mgmt_cfg_remote?.hide_launcher_link ?? false;
pres_mgmt_cfg_local.hide_launcher_link_legacy =
pres_mgmt_cfg_remote?.hide_launcher_link_legacy ?? false;
}
return pres_mgmt_cfg_local;
}
}

View File

@@ -9,9 +9,15 @@ vi.mock('$lib/api/api', () => ({
search_ae_obj: vi.fn()
}
}));
vi.mock('$lib/ae_core/core__idb_dexie', () => ({ db_save_ae_obj_li__ae_obj: vi.fn() }));
vi.mock('$lib/ae_events/db_events', () => ({ db_events: { badge: { get: vi.fn(), delete: vi.fn() } } }));
vi.mock('$lib/ae_events/ae_events__event_badge_template', () => ({ load_ae_obj_id__event_badge_template: vi.fn() }));
vi.mock('$lib/ae_core/core__idb_dexie', () => ({
db_save_ae_obj_li__ae_obj: vi.fn()
}));
vi.mock('$lib/ae_events/db_events', () => ({
db_events: { badge: { get: vi.fn(), delete: vi.fn() } }
}));
vi.mock('$lib/ae_events/ae_events__event_badge_template', () => ({
load_ae_obj_id__event_badge_template: vi.fn()
}));
import { create_ae_obj__event_badge } from './ae_events__event_badge';
@@ -25,9 +31,17 @@ describe('create_ae_obj__event_badge', () => {
const api_cfg = { base_url: 'http://localhost', headers: {} };
const event_id = 'evt1';
const data_kv = { full_name_override: 'Test User', email: 't@example.com' };
const data_kv = {
full_name_override: 'Test User',
email: 't@example.com'
};
const result = await create_ae_obj__event_badge({ api_cfg, event_id, data_kv, try_cache: false });
const result = await create_ae_obj__event_badge({
api_cfg,
event_id,
data_kv,
try_cache: false
});
expect(mockCreateNested).toHaveBeenCalled();
expect(mockCreateNested).toHaveBeenCalledWith({
@@ -51,9 +65,16 @@ describe('update_ae_obj__event_badge', () => {
const fakeResult = { event_badge_id: 'eb999', full_name: 'Updated' };
mockUpdate.mockResolvedValue(fakeResult);
const { update_ae_obj__event_badge } = await import('./ae_events__event_badge');
const { update_ae_obj__event_badge } =
await import('./ae_events__event_badge');
const api_cfg = { base_url: 'http://localhost', headers: {} };
const res = await update_ae_obj__event_badge({ api_cfg, event_id: 'evt1', event_badge_id: 'eb999', data_kv: { full_name_override: 'Updated' }, try_cache: false });
const res = await update_ae_obj__event_badge({
api_cfg,
event_id: 'evt1',
event_badge_id: 'eb999',
data_kv: { full_name_override: 'Updated' },
try_cache: false
});
expect(mockUpdate).toHaveBeenCalled();
expect(mockUpdate).toHaveBeenCalledWith({
@@ -79,10 +100,16 @@ describe('delete_ae_obj_id__event_badge', () => {
const db = await import('$lib/ae_events/db_events');
const dbDelete = db.db_events.badge.delete as any;
const { delete_ae_obj_id__event_badge } = await import('./ae_events__event_badge');
const { delete_ae_obj_id__event_badge } =
await import('./ae_events__event_badge');
const api_cfg = { base_url: 'http://localhost', headers: {} };
const res = await delete_ae_obj_id__event_badge({ api_cfg, event_id: 'evt1', event_badge_id: 'ebDel', try_cache: true });
const res = await delete_ae_obj_id__event_badge({
api_cfg,
event_id: 'evt1',
event_badge_id: 'ebDel',
try_cache: true
});
expect(mockDelete).toHaveBeenCalled();
expect(dbDelete).toHaveBeenCalledWith('ebDel');
@@ -97,9 +124,15 @@ describe('search__event_badge', () => {
const fakeList = [{ event_badge_id: 'eb1' }, { event_badge_id: 'eb2' }];
mockSearch.mockResolvedValue({ data: fakeList });
const { search__event_badge } = await import('./ae_events__event_badge');
const { search__event_badge } =
await import('./ae_events__event_badge');
const api_cfg = { base_url: 'http://localhost', headers: {} };
const res = await search__event_badge({ api_cfg, event_id: 'evt1', fulltext_search_qry_str: 'Test', try_cache: false });
const res = await search__event_badge({
api_cfg,
event_id: 'evt1',
fulltext_search_qry_str: 'Test',
try_cache: false
});
expect(mockSearch).toHaveBeenCalled();
expect(Array.isArray(res)).toBe(true);

View File

@@ -35,29 +35,36 @@ export async function load_ae_obj_id__event_badge({
log_lvl?: number;
}): Promise<ae_EventBadge | null> {
if (log_lvl) {
console.log(`*** load_ae_obj_id__event_badge() *** event_badge_id=${event_badge_id}`);
console.log(
`*** load_ae_obj_id__event_badge() *** event_badge_id=${event_badge_id}`
);
}
try {
ae_promises.load__event_badge_obj = await api
.get_ae_obj({
api_cfg,
obj_type: 'event_badge',
obj_id: event_badge_id,
view,
log_lvl
});
ae_promises.load__event_badge_obj = await api.get_ae_obj({
api_cfg,
obj_type: 'event_badge',
obj_id: event_badge_id,
view,
log_lvl
});
if (ae_promises.load__event_badge_obj) {
if (try_cache) {
// In theory we should be able to use the event_id found in the Badge load object. 2026-02-04
// This keeps coming up as undefined: ae_promises.load__event_badge_obj.event_id
if (log_lvl) console.log(`Saving to local cache... Event ID: ${event_id} or ${ae_promises.load__event_badge_obj.event_id}`);
const processed_obj_li = await process_ae_obj__event_badge_props({
obj_li: [ae_promises.load__event_badge_obj],
event_id: event_id || ae_promises.load__event_badge_obj.event_id,
log_lvl
});
if (log_lvl)
console.log(
`Saving to local cache... Event ID: ${event_id} or ${ae_promises.load__event_badge_obj.event_id}`
);
const processed_obj_li =
await process_ae_obj__event_badge_props({
obj_li: [ae_promises.load__event_badge_obj],
event_id:
event_id ||
ae_promises.load__event_badge_obj.event_id,
log_lvl
});
await db_save_ae_obj_li__ae_obj({
db_instance: db_events,
table_name: 'badge',
@@ -71,15 +78,21 @@ export async function load_ae_obj_id__event_badge({
} else {
console.log('No results returned from API.');
if (try_cache) {
if (log_lvl) console.log('Attempting to load from local cache...');
ae_promises.load__event_badge_obj = await db_events.badge.get(event_badge_id);
if (log_lvl)
console.log('Attempting to load from local cache...');
ae_promises.load__event_badge_obj =
await db_events.badge.get(event_badge_id);
}
}
} catch (error: any) {
console.log('API request failed.', error);
if (try_cache) {
if (log_lvl) console.log('Attempting to load from local cache after error...');
ae_promises.load__event_badge_obj = await db_events.badge.get(event_badge_id);
if (log_lvl)
console.log(
'Attempting to load from local cache after error...'
);
ae_promises.load__event_badge_obj =
await db_events.badge.get(event_badge_id);
} else {
ae_promises.load__event_badge_obj = null;
}
@@ -87,14 +100,16 @@ export async function load_ae_obj_id__event_badge({
if (inc_template && ae_promises.load__event_badge_obj) {
// Load the templates for the event badge
const current_template_id = ae_promises.load__event_badge_obj.event_badge_template_id;
const current_template_id =
ae_promises.load__event_badge_obj.event_badge_template_id;
if (current_template_id) {
ae_promises.load__event_badge_obj.event_badge_template = await load_ae_obj_id__event_badge_template({
api_cfg: api_cfg,
event_badge_template_id: current_template_id,
try_cache: try_cache,
log_lvl: log_lvl
});
ae_promises.load__event_badge_obj.event_badge_template =
await load_ae_obj_id__event_badge_template({
api_cfg: api_cfg,
event_badge_template_id: current_template_id,
try_cache: try_cache,
log_lvl: log_lvl
});
}
}
@@ -111,7 +126,12 @@ export async function load_ae_obj_li__event_badge({
view = 'default',
limit = 99,
offset = 0,
order_by_li = { priority: 'DESC', sort: 'DESC', updated_on: 'DESC', created_on: 'DESC' },
order_by_li = {
priority: 'DESC',
sort: 'DESC',
updated_on: 'DESC',
created_on: 'DESC'
},
params = {},
try_cache = true,
log_lvl = 0
@@ -130,32 +150,34 @@ export async function load_ae_obj_li__event_badge({
log_lvl?: number;
}): Promise<ae_EventBadge[]> {
if (log_lvl) {
console.log(`*** load_ae_obj_li__event_badge() *** event_id=${event_id}`);
console.log(
`*** load_ae_obj_li__event_badge() *** event_id=${event_id}`
);
}
try {
ae_promises.load__event_badge_obj_li = await api
.get_ae_obj_li({
api_cfg,
obj_type: 'event_badge',
for_obj_type: 'event',
for_obj_id: event_id,
enabled: enabled,
hidden: hidden,
view: view,
order_by_li: order_by_li,
limit: limit,
offset: offset,
log_lvl: log_lvl
});
ae_promises.load__event_badge_obj_li = await api.get_ae_obj_li({
api_cfg,
obj_type: 'event_badge',
for_obj_type: 'event',
for_obj_id: event_id,
enabled: enabled,
hidden: hidden,
view: view,
order_by_li: order_by_li,
limit: limit,
offset: offset,
log_lvl: log_lvl
});
if (ae_promises.load__event_badge_obj_li) {
if (try_cache) {
const processed_obj_li = await process_ae_obj__event_badge_props({
obj_li: ae_promises.load__event_badge_obj_li,
event_id,
log_lvl
});
const processed_obj_li =
await process_ae_obj__event_badge_props({
obj_li: ae_promises.load__event_badge_obj_li,
event_id,
log_lvl
});
await db_save_ae_obj_li__ae_obj({
db_instance: db_events,
table_name: 'badge',
@@ -169,9 +191,11 @@ export async function load_ae_obj_li__event_badge({
} else {
console.log('No results returned from API.');
if (try_cache) {
if (log_lvl) console.log('Attempting to load from local cache...');
if (log_lvl)
console.log('Attempting to load from local cache...');
ae_promises.load__event_badge_obj_li = await db_events.badge
.where('event_id').equals(event_id)
.where('event_id')
.equals(event_id)
.toArray();
} else {
ae_promises.load__event_badge_obj_li = [];
@@ -180,9 +204,13 @@ export async function load_ae_obj_li__event_badge({
} catch (error: any) {
console.log('API request failed.', error);
if (try_cache) {
if (log_lvl) console.log('Attempting to load from local cache after error...');
if (log_lvl)
console.log(
'Attempting to load from local cache after error...'
);
ae_promises.load__event_badge_obj_li = await db_events.badge
.where('event_id').equals(event_id)
.where('event_id')
.equals(event_id)
.toArray();
} else {
ae_promises.load__event_badge_obj_li = [];
@@ -193,12 +221,13 @@ export async function load_ae_obj_li__event_badge({
for (const badge_obj of ae_promises.load__event_badge_obj_li) {
const current_template_id = badge_obj.event_badge_template_id;
if (current_template_id) {
badge_obj.event_badge_template = await load_ae_obj_id__event_badge_template({
api_cfg: api_cfg,
event_badge_template_id: current_template_id,
try_cache: try_cache,
log_lvl: log_lvl
});
badge_obj.event_badge_template =
await load_ae_obj_id__event_badge_template({
api_cfg: api_cfg,
event_badge_template_id: current_template_id,
try_cache: try_cache,
log_lvl: log_lvl
});
}
}
}
@@ -223,7 +252,9 @@ export async function create_ae_obj__event_badge({
log_lvl?: number;
}): Promise<ae_EventBadge | null> {
if (log_lvl) {
console.log(`*** create_ae_obj__event_badge() *** event_id=${event_id}`);
console.log(
`*** create_ae_obj__event_badge() *** event_id=${event_id}`
);
}
const result = await api.create_nested_obj({
@@ -275,7 +306,9 @@ export async function delete_ae_obj_id__event_badge({
log_lvl?: number;
}) {
if (log_lvl) {
console.log(`*** delete_ae_obj_id__event_badge() *** event_badge_id=${event_badge_id}`);
console.log(
`*** delete_ae_obj_id__event_badge() *** event_badge_id=${event_badge_id}`
);
}
const result = await api.delete_nested_ae_obj({
@@ -315,7 +348,9 @@ export async function update_ae_obj__event_badge({
log_lvl?: number;
}): Promise<ae_EventBadge | null> {
if (log_lvl) {
console.log(`*** update_ae_obj__event_badge() *** event_badge_id=${event_badge_id}`);
console.log(
`*** update_ae_obj__event_badge() *** event_badge_id=${event_badge_id}`
);
}
const result = await api.update_nested_obj({
@@ -391,7 +426,9 @@ export async function search__event_badge({
log_lvl?: number;
}): Promise<ae_EventBadge[] | false> {
if (log_lvl) {
console.log(`*** search__event_badge() *** event_id=${event_id} ft=${fulltext_search_qry_str}`);
console.log(
`*** search__event_badge() *** event_id=${event_id} ft=${fulltext_search_qry_str}`
);
}
const search_query: any = {
@@ -406,15 +443,23 @@ export async function search__event_badge({
// Multi-word support: Split query by spaces and search for all words (AND logic)
if (fulltext_search_qry_str && fulltext_search_qry_str.trim().length > 0) {
const qry = fulltext_search_qry_str.trim();
const words = qry.split(/\s+/).filter(w => w.length > 0);
const words = qry.split(/\s+/).filter((w) => w.length > 0);
if (words.length === 1) {
// Single word: use simple LIKE
search_query.and.push({ field: 'default_qry_str', op: 'like', value: `%${words[0]}%` });
search_query.and.push({
field: 'default_qry_str',
op: 'like',
value: `%${words[0]}%`
});
} else if (words.length > 1) {
// Multiple words: each word must match (AND logic)
for (const word of words) {
search_query.and.push({ field: 'default_qry_str', op: 'like', value: `%${word}%` });
search_query.and.push({
field: 'default_qry_str',
op: 'like',
value: `%${word}%`
});
}
}
@@ -424,7 +469,11 @@ export async function search__event_badge({
if (affiliations_qry_str && affiliations_qry_str.trim().length > 0) {
const qry = affiliations_qry_str.trim();
search_query.and.push({ field: 'affiliations', op: 'like', value: `%${qry}%` });
search_query.and.push({
field: 'affiliations',
op: 'like',
value: `%${qry}%`
});
params['lk_qry'] = params['lk_qry'] || {};
params['lk_qry']['affiliations'] = qry;
}
@@ -435,11 +484,19 @@ export async function search__event_badge({
}
if (external_event_id) {
search_query.and.push({ field: 'external_event_id', op: 'eq', value: external_event_id });
search_query.and.push({
field: 'external_event_id',
op: 'eq',
value: external_event_id
});
}
if (type_code) {
search_query.and.push({ field: 'badge_type_code', op: 'eq', value: type_code });
search_query.and.push({
field: 'badge_type_code',
op: 'eq',
value: type_code
});
}
if (printed_status === 'printed') {
@@ -448,11 +505,15 @@ export async function search__event_badge({
search_query.and.push({ field: 'print_count', op: 'eq', value: 0 });
}
if (enabled === 'enabled') search_query.and.push({ field: 'enable', op: 'eq', value: true });
else if (enabled === 'not_enabled') search_query.and.push({ field: 'enable', op: 'eq', value: false });
if (enabled === 'enabled')
search_query.and.push({ field: 'enable', op: 'eq', value: true });
else if (enabled === 'not_enabled')
search_query.and.push({ field: 'enable', op: 'eq', value: false });
if (hidden === 'hidden') search_query.and.push({ field: 'hide', op: 'eq', value: true });
else if (hidden === 'not_hidden') search_query.and.push({ field: 'hide', op: 'eq', value: false });
if (hidden === 'hidden')
search_query.and.push({ field: 'hide', op: 'eq', value: true });
else if (hidden === 'not_hidden')
search_query.and.push({ field: 'hide', op: 'eq', value: false });
ae_promises.search__event_badge_obj_li = await api
.search_ae_obj({
@@ -473,17 +534,21 @@ export async function search__event_badge({
let result_li: ae_EventBadge[] = [];
if (Array.isArray(badge_obj_li_get_result)) {
result_li = badge_obj_li_get_result;
} else if (badge_obj_li_get_result?.data && Array.isArray(badge_obj_li_get_result.data)) {
} else if (
badge_obj_li_get_result?.data &&
Array.isArray(badge_obj_li_get_result.data)
) {
result_li = badge_obj_li_get_result.data;
}
if (result_li.length > 0) {
if (try_cache) {
const processed_obj_li = await process_ae_obj__event_badge_props({
obj_li: result_li,
event_id,
log_lvl
});
const processed_obj_li =
await process_ae_obj__event_badge_props({
obj_li: result_li,
event_id,
log_lvl
});
await db_save_ae_obj_li__ae_obj({
db_instance: db_events,
table_name: 'badge',
@@ -631,14 +696,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);
@@ -663,7 +735,9 @@ export async function process_ae_obj__event_badge_props({
log_lvl,
specific_processor: (obj) => {
if (log_lvl) {
console.log(`*** process_ae_obj__event_badge_props() *** event_id=${event_id}`);
console.log(
`*** process_ae_obj__event_badge_props() *** event_id=${event_id}`
);
}
if (event_id) {
if (!obj.event_id) obj.event_id = event_id;
@@ -672,4 +746,4 @@ export async function process_ae_obj__event_badge_props({
return obj;
}
});
}
}

View File

@@ -87,11 +87,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);
@@ -129,7 +133,9 @@ export async function load_ae_obj_id__event_badge_template({
log_lvl?: number;
}) {
if (log_lvl) {
console.log(`*** load_ae_obj_id__event_badge_template() *** [V3] id=${event_badge_template_id}`);
console.log(
`*** load_ae_obj_id__event_badge_template() *** [V3] id=${event_badge_template_id}`
);
}
try {
@@ -156,11 +162,13 @@ export async function load_ae_obj_id__event_badge_template({
});
}
} else if (try_cache) {
ae_promises.load__event_badge_template_obj = await db_events.badge_template.get(event_badge_template_id);
ae_promises.load__event_badge_template_obj =
await db_events.badge_template.get(event_badge_template_id);
}
} catch (error: any) {
if (try_cache) {
ae_promises.load__event_badge_template_obj = await db_events.badge_template.get(event_badge_template_id);
ae_promises.load__event_badge_template_obj =
await db_events.badge_template.get(event_badge_template_id);
}
}
@@ -197,19 +205,21 @@ export async function load_ae_obj_li__event_badge_template({
log_lvl?: number;
}) {
try {
ae_promises.load__event_badge_template_obj_li = await api.get_ae_obj_li({
api_cfg,
obj_type: 'event_badge_template',
for_obj_type: 'event',
for_obj_id: event_id,
enabled,
hidden,
view,
limit,
offset,
order_by_li,
log_lvl
});
ae_promises.load__event_badge_template_obj_li = await api.get_ae_obj_li(
{
api_cfg,
obj_type: 'event_badge_template',
for_obj_type: 'event',
for_obj_id: event_id,
enabled,
hidden,
view,
limit,
offset,
order_by_li,
log_lvl
}
);
if (ae_promises.load__event_badge_template_obj_li) {
if (try_cache) {
@@ -226,15 +236,19 @@ export async function load_ae_obj_li__event_badge_template({
});
}
} else if (try_cache) {
ae_promises.load__event_badge_template_obj_li = await db_events.badge_template
.where('event_id').equals(event_id)
.toArray();
ae_promises.load__event_badge_template_obj_li =
await db_events.badge_template
.where('event_id')
.equals(event_id)
.toArray();
}
} catch (error: any) {
if (try_cache) {
ae_promises.load__event_badge_template_obj_li = await db_events.badge_template
.where('event_id').equals(event_id)
.toArray();
ae_promises.load__event_badge_template_obj_li =
await db_events.badge_template
.where('event_id')
.equals(event_id)
.toArray();
}
}
@@ -243,115 +257,115 @@ export async function load_ae_obj_li__event_badge_template({
// Updated 2026-01-20 to V3
export async function create_ae_obj__event_badge_template({
api_cfg,
event_id,
data_kv,
try_cache = true,
log_lvl = 0
api_cfg,
event_id,
data_kv,
try_cache = true,
log_lvl = 0
}: {
api_cfg: any;
event_id: string;
data_kv: key_val;
try_cache?: boolean;
log_lvl?: number;
api_cfg: any;
event_id: string;
data_kv: key_val;
try_cache?: boolean;
log_lvl?: number;
}) {
const result = await api.create_nested_obj({
api_cfg,
for_obj_type: 'event',
for_obj_id: event_id,
obj_type: 'event_badge_template',
fields: { ...data_kv },
log_lvl
});
const result = await api.create_nested_obj({
api_cfg,
for_obj_type: 'event',
for_obj_id: event_id,
obj_type: 'event_badge_template',
fields: { ...data_kv },
log_lvl
});
if (result && try_cache) {
const processed_obj_li = await process_ae_badge_template_props({
obj_li: [result],
log_lvl
});
await db_save_ae_obj_li__ae_obj({
db_instance: db_events,
table_name: 'badge_template',
obj_li: processed_obj_li,
properties_to_save,
log_lvl
});
}
return result;
if (result && try_cache) {
const processed_obj_li = await process_ae_badge_template_props({
obj_li: [result],
log_lvl
});
await db_save_ae_obj_li__ae_obj({
db_instance: db_events,
table_name: 'badge_template',
obj_li: processed_obj_li,
properties_to_save,
log_lvl
});
}
return result;
}
// Updated 2026-01-20 to V3
export async function delete_ae_obj_id__event_badge_template({
api_cfg,
event_id,
event_badge_template_id,
method = 'delete',
try_cache = true,
log_lvl = 0
api_cfg,
event_id,
event_badge_template_id,
method = 'delete',
try_cache = true,
log_lvl = 0
}: {
api_cfg: any;
event_id: string;
event_badge_template_id: string;
method?: 'delete' | 'soft_delete' | 'disable' | 'hide';
try_cache?: boolean;
log_lvl?: number;
api_cfg: any;
event_id: string;
event_badge_template_id: string;
method?: 'delete' | 'soft_delete' | 'disable' | 'hide';
try_cache?: boolean;
log_lvl?: number;
}) {
const result = await api.delete_nested_ae_obj({
api_cfg,
for_obj_type: 'event',
for_obj_id: event_id,
obj_type: 'event_badge_template',
obj_id: event_badge_template_id,
method,
log_lvl
});
const result = await api.delete_nested_ae_obj({
api_cfg,
for_obj_type: 'event',
for_obj_id: event_id,
obj_type: 'event_badge_template',
obj_id: event_badge_template_id,
method,
log_lvl
});
if (try_cache) {
await db_events.badge_template.delete(event_badge_template_id);
}
return result;
if (try_cache) {
await db_events.badge_template.delete(event_badge_template_id);
}
return result;
}
// Updated 2026-01-20 to V3
export async function update_ae_obj__event_badge_template({
api_cfg,
event_id,
event_badge_template_id,
data_kv,
try_cache = true,
log_lvl = 0
api_cfg,
event_id,
event_badge_template_id,
data_kv,
try_cache = true,
log_lvl = 0
}: {
api_cfg: any;
event_id: string;
event_badge_template_id: string;
data_kv: key_val;
try_cache?: boolean;
log_lvl?: number;
api_cfg: any;
event_id: string;
event_badge_template_id: string;
data_kv: key_val;
try_cache?: boolean;
log_lvl?: number;
}) {
const result = await api.update_nested_obj({
api_cfg,
for_obj_type: 'event',
for_obj_id: event_id,
obj_type: 'event_badge_template',
obj_id: event_badge_template_id,
fields: data_kv,
log_lvl
});
const result = await api.update_nested_obj({
api_cfg,
for_obj_type: 'event',
for_obj_id: event_id,
obj_type: 'event_badge_template',
obj_id: event_badge_template_id,
fields: data_kv,
log_lvl
});
if (result && try_cache) {
const processed_obj_li = await process_ae_badge_template_props({
obj_li: [result],
log_lvl
});
await db_save_ae_obj_li__ae_obj({
db_instance: db_events,
table_name: 'badge_template',
obj_li: processed_obj_li,
properties_to_save,
log_lvl
});
}
return result;
if (result && try_cache) {
const processed_obj_li = await process_ae_badge_template_props({
obj_li: [result],
log_lvl
});
await db_save_ae_obj_li__ae_obj({
db_instance: db_events,
table_name: 'badge_template',
obj_li: processed_obj_li,
properties_to_save,
log_lvl
});
}
return result;
}
// Updated 2026-01-20 to V3
@@ -388,11 +402,15 @@ export async function search__event_badge_template({
and: [{ field: 'event_id', op: 'eq', value: event_id }]
};
if (enabled === 'enabled') search_query.and.push({ field: 'enable', op: 'eq', value: true });
else if (enabled === 'not_enabled') search_query.and.push({ field: 'enable', op: 'eq', value: false });
if (enabled === 'enabled')
search_query.and.push({ field: 'enable', op: 'eq', value: true });
else if (enabled === 'not_enabled')
search_query.and.push({ field: 'enable', op: 'eq', value: false });
if (hidden === 'hidden') search_query.and.push({ field: 'hide', op: 'eq', value: true });
else if (hidden === 'not_hidden') search_query.and.push({ field: 'hide', op: 'eq', value: false });
if (hidden === 'hidden')
search_query.and.push({ field: 'hide', op: 'eq', value: true });
else if (hidden === 'not_hidden')
search_query.and.push({ field: 'hide', op: 'eq', value: false });
const result_li = await api.search_ae_obj({
api_cfg,
@@ -418,4 +436,4 @@ export async function search__event_badge_template({
});
}
return result_li || [];
}
}

View File

@@ -28,7 +28,9 @@ export async function load_ae_obj_id__event_device({
log_lvl?: number;
}): Promise<ae_EventDevice | null> {
if (log_lvl) {
console.log(`*** load_ae_obj_id__event_device() *** [V3] id=${event_device_id}`);
console.log(
`*** load_ae_obj_id__event_device() *** [V3] id=${event_device_id}`
);
}
try {
@@ -57,26 +59,30 @@ export async function load_ae_obj_id__event_device({
});
}
} else if (try_cache) {
ae_promises.load__event_device_obj = await db_events.device.get(event_device_id);
ae_promises.load__event_device_obj =
await db_events.device.get(event_device_id);
}
} catch (error: any) {
console.log('V3 Request failed.', error);
if (try_cache) {
ae_promises.load__event_device_obj = await db_events.device.get(event_device_id);
ae_promises.load__event_device_obj =
await db_events.device.get(event_device_id);
}
}
if (!ae_promises.load__event_device_obj) return null;
if (inc_location_id) {
const current_location_id = ae_promises.load__event_device_obj.event_location_id;
const current_location_id =
ae_promises.load__event_device_obj.event_location_id;
if (current_location_id) {
ae_promises.load__event_device_obj.event_location_obj = await load_ae_obj_id__event_location({
api_cfg,
event_location_id: current_location_id,
try_cache,
log_lvl
});
ae_promises.load__event_device_obj.event_location_obj =
await load_ae_obj_id__event_location({
api_cfg,
event_location_id: current_location_id,
try_cache,
log_lvl
});
}
}
@@ -115,7 +121,9 @@ export async function load_ae_obj_li__event_device({
log_lvl?: number;
}): Promise<ae_EventDevice[]> {
if (log_lvl) {
console.log(`*** load_ae_obj_li__event_device() *** [V3] for=${for_obj_type}:${for_obj_id}`);
console.log(
`*** load_ae_obj_li__event_device() *** [V3] for=${for_obj_type}:${for_obj_id}`
);
}
try {
@@ -150,14 +158,16 @@ export async function load_ae_obj_li__event_device({
}
} else if (try_cache) {
ae_promises.load__event_device_obj_li = await db_events.device
.where('event_id').equals(for_obj_id)
.where('event_id')
.equals(for_obj_id)
.toArray();
}
} catch (error: any) {
console.log('V3 List Request failed.', error);
if (try_cache) {
ae_promises.load__event_device_obj_li = await db_events.device
.where('event_id').equals(for_obj_id)
.where('event_id')
.equals(for_obj_id)
.toArray();
}
}
@@ -165,12 +175,13 @@ export async function load_ae_obj_li__event_device({
if (inc_location_id && ae_promises.load__event_device_obj_li) {
for (const device of ae_promises.load__event_device_obj_li) {
if (device.event_location_id) {
device.event_location_obj = await load_ae_obj_id__event_location({
api_cfg,
event_location_id: device.event_location_id,
try_cache,
log_lvl
});
device.event_location_obj =
await load_ae_obj_id__event_location({
api_cfg,
event_location_id: device.event_location_id,
try_cache,
log_lvl
});
}
}
}
@@ -180,155 +191,161 @@ export async function load_ae_obj_li__event_device({
// Updated 2026-01-20 to V3
export async function create_ae_obj__event_device({
api_cfg,
event_id,
data_kv,
try_cache = true,
log_lvl = 0
api_cfg,
event_id,
data_kv,
try_cache = true,
log_lvl = 0
}: {
api_cfg: any;
event_id?: string;
data_kv: key_val;
try_cache?: boolean;
log_lvl?: number;
api_cfg: any;
event_id?: string;
data_kv: key_val;
try_cache?: boolean;
log_lvl?: number;
}): Promise<ae_EventDevice | null> {
if (!event_id) event_id = get(slct).event_id;
if (!event_id) {
console.error('create_ae_obj__event_device: event_id is required');
return null;
}
if (log_lvl) {
console.log(`*** create_ae_obj__event_device() *** [V3] event_id=${event_id}`);
}
if (log_lvl) {
console.log(
`*** create_ae_obj__event_device() *** [V3] event_id=${event_id}`
);
}
const result = await api.create_nested_obj({
api_cfg,
for_obj_type: 'event',
for_obj_id: event_id,
obj_type: 'event_device',
fields: { ...data_kv },
log_lvl
});
const result = await api.create_nested_obj({
api_cfg,
for_obj_type: 'event',
for_obj_id: event_id,
obj_type: 'event_device',
fields: { ...data_kv },
log_lvl
});
if (result) {
const processed = await process_ae_obj__event_device_props({
obj_li: [result],
log_lvl
});
const processed_obj = processed[0];
if (result) {
const processed = await process_ae_obj__event_device_props({
obj_li: [result],
log_lvl
});
const processed_obj = processed[0];
if (try_cache) {
await db_save_ae_obj_li__ae_obj({
db_instance: db_events,
table_name: 'device',
obj_li: [processed_obj],
properties_to_save,
log_lvl
});
}
return processed_obj;
}
if (try_cache) {
await db_save_ae_obj_li__ae_obj({
db_instance: db_events,
table_name: 'device',
obj_li: [processed_obj],
properties_to_save,
log_lvl
});
}
return processed_obj;
}
return null;
return null;
}
// Updated 2026-01-20 to V3
export async function delete_ae_obj_id__event_device({
api_cfg,
event_id,
event_device_id,
method = 'delete',
try_cache = true,
log_lvl = 0
api_cfg,
event_id,
event_device_id,
method = 'delete',
try_cache = true,
log_lvl = 0
}: {
api_cfg: any;
event_id?: string;
event_device_id: string;
method?: 'delete' | 'soft_delete' | 'disable' | 'hide';
try_cache?: boolean;
log_lvl?: number;
api_cfg: any;
event_id?: string;
event_device_id: string;
method?: 'delete' | 'soft_delete' | 'disable' | 'hide';
try_cache?: boolean;
log_lvl?: number;
}) {
if (!event_id) event_id = get(slct).event_id;
if (!event_id) {
console.error('delete_ae_obj_id__event_device: event_id is required');
return null;
}
if (log_lvl) {
console.log(`*** delete_ae_obj_id__event_device() *** [V3] id=${event_device_id}`);
}
if (log_lvl) {
console.log(
`*** delete_ae_obj_id__event_device() *** [V3] id=${event_device_id}`
);
}
const result = await api.delete_nested_ae_obj({
api_cfg,
for_obj_type: 'event',
for_obj_id: event_id,
obj_type: 'event_device',
obj_id: event_device_id,
method,
log_lvl
});
const result = await api.delete_nested_ae_obj({
api_cfg,
for_obj_type: 'event',
for_obj_id: event_id,
obj_type: 'event_device',
obj_id: event_device_id,
method,
log_lvl
});
if (try_cache) {
await db_events.device.delete(event_device_id);
}
if (try_cache) {
await db_events.device.delete(event_device_id);
}
return result;
return result;
}
// Updated 2026-01-20 to V3
export async function update_ae_obj__event_device({
api_cfg,
event_id,
event_device_id,
data_kv,
try_cache = true,
log_lvl = 0
api_cfg,
event_id,
event_device_id,
data_kv,
try_cache = true,
log_lvl = 0
}: {
api_cfg: any;
event_id?: string;
event_device_id: string;
data_kv: key_val;
try_cache?: boolean;
log_lvl?: number;
api_cfg: any;
event_id?: string;
event_device_id: string;
data_kv: key_val;
try_cache?: boolean;
log_lvl?: number;
}): Promise<ae_EventDevice | null> {
if (!event_id) event_id = get(slct).event_id;
if (!event_id) {
console.error('update_ae_obj__event_device: event_id is required');
return null;
}
if (log_lvl) {
console.log(`*** update_ae_obj__event_device() *** [V3] id=${event_device_id}`);
}
if (log_lvl) {
console.log(
`*** update_ae_obj__event_device() *** [V3] id=${event_device_id}`
);
}
const result = await api.update_nested_obj({
api_cfg,
for_obj_type: 'event',
for_obj_id: event_id,
obj_type: 'event_device',
obj_id: event_device_id,
fields: data_kv,
log_lvl
});
const result = await api.update_nested_obj({
api_cfg,
for_obj_type: 'event',
for_obj_id: event_id,
obj_type: 'event_device',
obj_id: event_device_id,
fields: data_kv,
log_lvl
});
if (result) {
const processed = await process_ae_obj__event_device_props({
obj_li: [result],
log_lvl
});
const processed_obj = processed[0];
if (result) {
const processed = await process_ae_obj__event_device_props({
obj_li: [result],
log_lvl
});
const processed_obj = processed[0];
if (try_cache) {
await db_save_ae_obj_li__ae_obj({
db_instance: db_events,
table_name: 'device',
obj_li: [processed_obj],
properties_to_save,
log_lvl
});
}
return processed_obj;
}
if (try_cache) {
await db_save_ae_obj_li__ae_obj({
db_instance: db_events,
table_name: 'device',
obj_li: [processed_obj],
properties_to_save,
log_lvl
});
}
return processed_obj;
}
return null;
return null;
}
// Updated 2026-01-20 to V3
@@ -361,7 +378,9 @@ export async function search__event_device({
log_lvl?: number;
}): Promise<ae_EventDevice[]> {
if (log_lvl) {
console.log(`*** search__event_device() *** [V3] event_id=${event_id} qry=${qry_str}`);
console.log(
`*** search__event_device() *** [V3] event_id=${event_id} qry=${qry_str}`
);
}
const search_query: any = {
@@ -370,11 +389,15 @@ export async function search__event_device({
};
// Logical filters
if (enabled === 'enabled') search_query.and.push({ field: 'enable', op: 'eq', value: true });
else if (enabled === 'not_enabled') search_query.and.push({ field: 'enable', op: 'eq', value: false });
if (enabled === 'enabled')
search_query.and.push({ field: 'enable', op: 'eq', value: true });
else if (enabled === 'not_enabled')
search_query.and.push({ field: 'enable', op: 'eq', value: false });
if (hidden === 'hidden') search_query.and.push({ field: 'hide', op: 'eq', value: true });
else if (hidden === 'not_hidden') search_query.and.push({ field: 'hide', op: 'eq', value: false });
if (hidden === 'hidden')
search_query.and.push({ field: 'hide', op: 'eq', value: true });
else if (hidden === 'not_hidden')
search_query.and.push({ field: 'hide', op: 'eq', value: false });
const result_li = await api.search_ae_obj({
api_cfg,
@@ -499,11 +522,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);

View File

@@ -191,7 +191,9 @@ async function _refresh_file_li_background({
if (typeof navigator !== 'undefined' && !navigator.onLine) return [];
try {
if (log_lvl) {
console.log(`📡 [DEBUG] _refresh_file_li_background: Fetching files for ${for_obj_type}:${for_obj_id}`);
console.log(
`📡 [DEBUG] _refresh_file_li_background: Fetching files for ${for_obj_type}:${for_obj_id}`
);
}
const result_li = await api.get_ae_obj_li({
@@ -211,7 +213,10 @@ async function _refresh_file_li_background({
if (result_li) {
if (log_lvl) {
console.log(`📦 [DEBUG] Raw API results count:`, result_li.length);
console.log(
`📦 [DEBUG] Raw API results count:`,
result_li.length
);
if (result_li.length > 0) {
console.log(`🔍 [DEBUG] Sample Raw IDs:`, {
id: result_li[0].id,
@@ -253,7 +258,10 @@ async function _refresh_file_li_background({
}
if (try_cache) {
if (log_lvl) console.log(`💾 [DEBUG] Saving ${processed.length} records to ae_events_db.file`);
if (log_lvl)
console.log(
`💾 [DEBUG] Saving ${processed.length} records to ae_events_db.file`
);
await db_save_ae_obj_li__ae_obj({
db_instance: db_events,
table_name: 'file',
@@ -265,10 +273,15 @@ async function _refresh_file_li_background({
return processed;
}
} catch (e) {
if (log_lvl) console.error(`❌ [DEBUG] Error in _refresh_file_li_background:`, e);
if (log_lvl)
console.error(
`❌ [DEBUG] Error in _refresh_file_li_background:`,
e
);
}
return [];
}export async function create_event_file_obj_from_hosted_file_async({
}
export async function create_event_file_obj_from_hosted_file_async({
api_cfg,
hosted_file_id,
params = {},
@@ -459,7 +472,7 @@ export async function search__event_file({
});
}
return processed; // Return processed data with mapped fields
return processed; // Return processed data with mapped fields
}
return [];
@@ -545,7 +558,11 @@ async function _process_generic_props<T extends Record<string, any>>({
if (key.endsWith('_random')) {
const newKey = key.slice(0, -7);
// ONLY overwrite if the random variant has a valid value
if (processed_obj[key] !== null && processed_obj[key] !== undefined && processed_obj[key] !== '') {
if (
processed_obj[key] !== null &&
processed_obj[key] !== undefined &&
processed_obj[key] !== ''
) {
(processed_obj as any)[newKey] = processed_obj[key];
}
}

View File

@@ -37,7 +37,9 @@ export async function load_ae_obj_id__event_location({
log_lvl?: number;
}): Promise<ae_EventLocation | null> {
if (log_lvl) {
console.log(`*** load_ae_obj_id__event_location() *** [V3] id=${event_location_id} (SWR)`);
console.log(
`*** load_ae_obj_id__event_location() *** [V3] id=${event_location_id} (SWR)`
);
}
// 1. FAST PATH: Cache hit
@@ -46,12 +48,26 @@ export async function load_ae_obj_id__event_location({
const cached = await db_events.location.get(event_location_id);
if (cached) {
_refresh_location_id_background({
api_cfg, event_location_id, view, try_cache,
inc_file_li, inc_session_li, inc_presentation_li, inc_presenter_li, inc_device_li, inc_all_file_li,
api_cfg,
event_location_id,
view,
try_cache,
inc_file_li,
inc_session_li,
inc_presentation_li,
inc_presenter_li,
inc_device_li,
inc_all_file_li,
log_lvl: 0
});
return await _handle_nested_loads(cached, {
api_cfg, inc_file_li, inc_session_li, inc_presentation_li, inc_presenter_li, inc_device_li, inc_all_file_li,
api_cfg,
inc_file_li,
inc_session_li,
inc_presentation_li,
inc_presenter_li,
inc_device_li,
inc_all_file_li,
log_lvl
});
}
@@ -60,24 +76,65 @@ export async function load_ae_obj_id__event_location({
// 2. SLOW PATH: Wait for API
return await _refresh_location_id_background({
api_cfg, event_location_id, view, try_cache,
inc_file_li, inc_session_li, inc_presentation_li, inc_presenter_li, inc_device_li, inc_all_file_li,
api_cfg,
event_location_id,
view,
try_cache,
inc_file_li,
inc_session_li,
inc_presentation_li,
inc_presenter_li,
inc_device_li,
inc_all_file_li,
log_lvl
});
}
async function _refresh_location_id_background({ api_cfg, event_location_id, view, try_cache, inc_file_li, inc_session_li, inc_presentation_li, inc_presenter_li, inc_device_li, inc_all_file_li, log_lvl }: any) {
async function _refresh_location_id_background({
api_cfg,
event_location_id,
view,
try_cache,
inc_file_li,
inc_session_li,
inc_presentation_li,
inc_presenter_li,
inc_device_li,
inc_all_file_li,
log_lvl
}: any) {
if (typeof navigator !== 'undefined' && !navigator.onLine) return null;
try {
const result = await api.get_ae_obj({ api_cfg, obj_type: 'event_location', obj_id: event_location_id, view, log_lvl });
const result = await api.get_ae_obj({
api_cfg,
obj_type: 'event_location',
obj_id: event_location_id,
view,
log_lvl
});
if (result) {
const processed = await process_ae_obj__event_location_props({ obj_li: [result], log_lvl });
const processed = await process_ae_obj__event_location_props({
obj_li: [result],
log_lvl
});
const processed_obj = processed[0];
if (try_cache) {
await db_save_ae_obj_li__ae_obj({ db_instance: db_events, table_name: 'location', obj_li: [processed_obj], properties_to_save, log_lvl });
await db_save_ae_obj_li__ae_obj({
db_instance: db_events,
table_name: 'location',
obj_li: [processed_obj],
properties_to_save,
log_lvl
});
}
return await _handle_nested_loads(processed_obj, {
api_cfg, inc_file_li, inc_session_li, inc_presentation_li, inc_presenter_li, inc_device_li, inc_all_file_li,
api_cfg,
inc_file_li,
inc_session_li,
inc_presentation_li,
inc_presenter_li,
inc_device_li,
inc_all_file_li,
log_lvl
});
}
@@ -129,23 +186,47 @@ export async function load_ae_obj_li__event_location({
log_lvl?: number;
}): Promise<ae_EventLocation[]> {
if (log_lvl) {
console.log(`*** load_ae_obj_li__event_location() *** [V3] for=${for_obj_type}:${for_obj_id} (SWR)`);
console.log(
`*** load_ae_obj_li__event_location() *** [V3] for=${for_obj_type}:${for_obj_id} (SWR)`
);
}
// 1. FAST PATH: Check cache
if (try_cache) {
try {
const cached_li = await db_events.location.where('event_id').equals(for_obj_id).toArray();
const cached_li = await db_events.location
.where('event_id')
.equals(for_obj_id)
.toArray();
if (cached_li && cached_li.length > 0) {
_refresh_location_li_background({
api_cfg, for_obj_type, for_obj_id,
inc_file_li, inc_session_li, inc_presentation_li, inc_presenter_li, inc_device_li, inc_all_file_li,
enabled, hidden, view, limit, offset, order_by_li, try_cache,
api_cfg,
for_obj_type,
for_obj_id,
inc_file_li,
inc_session_li,
inc_presentation_li,
inc_presenter_li,
inc_device_li,
inc_all_file_li,
enabled,
hidden,
view,
limit,
offset,
order_by_li,
try_cache,
log_lvl: 0
});
for (const loc of cached_li) {
_handle_nested_loads(loc, {
api_cfg, inc_file_li, inc_session_li, inc_presentation_li, inc_presenter_li, inc_device_li, inc_all_file_li,
api_cfg,
inc_file_li,
inc_session_li,
inc_presentation_li,
inc_presenter_li,
inc_device_li,
inc_all_file_li,
log_lvl: 0
});
}
@@ -156,31 +237,89 @@ export async function load_ae_obj_li__event_location({
// 2. SLOW PATH: API
return await _refresh_location_li_background({
api_cfg, for_obj_type, for_obj_id,
inc_file_li, inc_session_li, inc_presentation_li, inc_presenter_li, inc_device_li, inc_all_file_li,
enabled, hidden, view, limit, offset, order_by_li, try_cache,
api_cfg,
for_obj_type,
for_obj_id,
inc_file_li,
inc_session_li,
inc_presentation_li,
inc_presenter_li,
inc_device_li,
inc_all_file_li,
enabled,
hidden,
view,
limit,
offset,
order_by_li,
try_cache,
log_lvl
});
}
async function _refresh_location_li_background({ api_cfg, for_obj_type, for_obj_id, inc_file_li, inc_session_li, inc_presentation_li, inc_presenter_li, inc_device_li, inc_all_file_li, enabled, hidden, view, limit, offset, order_by_li, try_cache, log_lvl }: any) {
async function _refresh_location_li_background({
api_cfg,
for_obj_type,
for_obj_id,
inc_file_li,
inc_session_li,
inc_presentation_li,
inc_presenter_li,
inc_device_li,
inc_all_file_li,
enabled,
hidden,
view,
limit,
offset,
order_by_li,
try_cache,
log_lvl
}: any) {
if (typeof navigator !== 'undefined' && !navigator.onLine) return [];
try {
const result_li = await api.get_ae_obj_li({ api_cfg, obj_type: 'event_location', for_obj_type, for_obj_id, enabled, hidden, view, limit, offset, order_by_li, log_lvl });
const result_li = await api.get_ae_obj_li({
api_cfg,
obj_type: 'event_location',
for_obj_type,
for_obj_id,
enabled,
hidden,
view,
limit,
offset,
order_by_li,
log_lvl
});
if (result_li) {
const processed = await process_ae_obj__event_location_props({ obj_li: result_li, log_lvl });
const processed = await process_ae_obj__event_location_props({
obj_li: result_li,
log_lvl
});
// String-Only ID Vision: Ensure linking ID is set for indexing
if (for_obj_type === 'event') {
processed.forEach(loc => loc.event_id = for_obj_id);
processed.forEach((loc) => (loc.event_id = for_obj_id));
}
if (try_cache) {
await db_save_ae_obj_li__ae_obj({ db_instance: db_events, table_name: 'location', obj_li: processed, properties_to_save, log_lvl });
await db_save_ae_obj_li__ae_obj({
db_instance: db_events,
table_name: 'location',
obj_li: processed,
properties_to_save,
log_lvl
});
}
for (const loc of processed) {
_handle_nested_loads(loc, {
api_cfg, inc_file_li, inc_session_li, inc_presentation_li, inc_presenter_li, inc_device_li, inc_all_file_li,
api_cfg,
inc_file_li,
inc_session_li,
inc_presentation_li,
inc_presenter_li,
inc_device_li,
inc_all_file_li,
log_lvl: 0
});
}
@@ -193,34 +332,66 @@ async function _refresh_location_li_background({ api_cfg, for_obj_type, for_obj_
/**
* Handle nested data loads for a single location object.
*/
async function _handle_nested_loads(location_obj: any, { api_cfg, inc_file_li, inc_session_li, inc_presentation_li, inc_presenter_li, inc_device_li, inc_all_file_li, log_lvl }: any) {
const current_location_id = location_obj.id || location_obj.event_location_id;
async function _handle_nested_loads(
location_obj: any,
{
api_cfg,
inc_file_li,
inc_session_li,
inc_presentation_li,
inc_presenter_li,
inc_device_li,
inc_all_file_li,
log_lvl
}: any
) {
const current_location_id =
location_obj.id || location_obj.event_location_id;
if (!current_location_id) return location_obj;
const tasks = [];
if (inc_file_li) {
tasks.push(load_ae_obj_li__event_file({
api_cfg, for_obj_type: 'event_location', for_obj_id: current_location_id,
enabled: 'all', limit: 25, log_lvl
}).then(res => location_obj.event_file_li = res));
tasks.push(
load_ae_obj_li__event_file({
api_cfg,
for_obj_type: 'event_location',
for_obj_id: current_location_id,
enabled: 'all',
limit: 25,
log_lvl
}).then((res) => (location_obj.event_file_li = res))
);
}
if (inc_session_li) {
tasks.push(load_ae_obj_li__event_session({
api_cfg, for_obj_type: 'event_location', for_obj_id: current_location_id,
inc_file_li: inc_all_file_li,
inc_all_file_li: inc_all_file_li,
inc_presentation_li: inc_presentation_li,
inc_presenter_li: inc_presenter_li,
enabled: 'enabled', hidden: 'not_hidden', limit: 150, log_lvl
}).then(res => location_obj.event_session_obj_li = res));
tasks.push(
load_ae_obj_li__event_session({
api_cfg,
for_obj_type: 'event_location',
for_obj_id: current_location_id,
inc_file_li: inc_all_file_li,
inc_all_file_li: inc_all_file_li,
inc_presentation_li: inc_presentation_li,
inc_presenter_li: inc_presenter_li,
enabled: 'enabled',
hidden: 'not_hidden',
limit: 150,
log_lvl
}).then((res) => (location_obj.event_session_obj_li = res))
);
}
if (inc_device_li) {
tasks.push(load_ae_obj_li__event_device({
api_cfg, for_obj_type: 'event_location', for_obj_id: current_location_id,
enabled: 'all', limit: 50, log_lvl
}).then(res => location_obj.event_device_li = res));
tasks.push(
load_ae_obj_li__event_device({
api_cfg,
for_obj_type: 'event_location',
for_obj_id: current_location_id,
enabled: 'all',
limit: 50,
log_lvl
}).then((res) => (location_obj.event_device_li = res))
);
}
if (tasks.length > 0) await Promise.all(tasks);
@@ -229,132 +400,183 @@ async function _handle_nested_loads(location_obj: any, { api_cfg, inc_file_li, i
// Updated 2026-01-20 to V3
export async function create_ae_obj__event_location({
api_cfg,
event_id,
data_kv,
try_cache = true,
log_lvl = 0
api_cfg,
event_id,
data_kv,
try_cache = true,
log_lvl = 0
}: {
api_cfg: any;
event_id: string;
data_kv: key_val;
try_cache?: boolean;
log_lvl?: number;
api_cfg: any;
event_id: string;
data_kv: key_val;
try_cache?: boolean;
log_lvl?: number;
}): Promise<ae_EventLocation | null> {
const result = await api.create_nested_obj({
api_cfg,
for_obj_type: 'event',
for_obj_id: event_id,
obj_type: 'event_location',
fields: { ...data_kv },
log_lvl
});
const result = await api.create_nested_obj({
api_cfg,
for_obj_type: 'event',
for_obj_id: event_id,
obj_type: 'event_location',
fields: { ...data_kv },
log_lvl
});
if (result) {
const processed = await process_ae_obj__event_location_props({ obj_li: [result], log_lvl });
const processed_obj = processed[0];
if (try_cache) {
await db_save_ae_obj_li__ae_obj({
db_instance: db_events,
table_name: 'location',
obj_li: [processed_obj],
properties_to_save,
log_lvl
});
}
return processed_obj;
}
return null;
if (result) {
const processed = await process_ae_obj__event_location_props({
obj_li: [result],
log_lvl
});
const processed_obj = processed[0];
if (try_cache) {
await db_save_ae_obj_li__ae_obj({
db_instance: db_events,
table_name: 'location',
obj_li: [processed_obj],
properties_to_save,
log_lvl
});
}
return processed_obj;
}
return null;
}
// Updated 2026-01-20 to V3
export async function delete_ae_obj_id__event_location({
api_cfg,
event_id,
event_location_id,
method = 'delete',
try_cache = true,
log_lvl = 0
api_cfg,
event_id,
event_location_id,
method = 'delete',
try_cache = true,
log_lvl = 0
}: {
api_cfg: any;
event_id: string;
event_location_id: string;
method?: 'delete' | 'soft_delete' | 'disable' | 'hide';
try_cache?: boolean;
log_lvl?: number;
api_cfg: any;
event_id: string;
event_location_id: string;
method?: 'delete' | 'soft_delete' | 'disable' | 'hide';
try_cache?: boolean;
log_lvl?: number;
}) {
const result = await api.delete_nested_ae_obj({
api_cfg,
for_obj_type: 'event',
for_obj_id: event_id,
obj_type: 'event_location',
obj_id: event_location_id,
method,
log_lvl
});
if (try_cache) await db_events.location.delete(event_location_id);
return result;
const result = await api.delete_nested_ae_obj({
api_cfg,
for_obj_type: 'event',
for_obj_id: event_id,
obj_type: 'event_location',
obj_id: event_location_id,
method,
log_lvl
});
if (try_cache) await db_events.location.delete(event_location_id);
return result;
}
// Updated 2026-01-20 to V3
export async function update_ae_obj__event_location({
api_cfg,
event_id,
event_location_id,
data_kv,
try_cache = true,
log_lvl = 0
api_cfg,
event_id,
event_location_id,
data_kv,
try_cache = true,
log_lvl = 0
}: {
api_cfg: any;
event_id: string;
event_location_id: string;
data_kv: key_val;
try_cache?: boolean;
log_lvl?: number;
api_cfg: any;
event_id: string;
event_location_id: string;
data_kv: key_val;
try_cache?: boolean;
log_lvl?: number;
}): Promise<ae_EventLocation | null> {
const result = await api.update_nested_obj({
api_cfg,
for_obj_type: 'event',
for_obj_id: event_id,
obj_type: 'event_location',
obj_id: event_location_id,
fields: data_kv,
log_lvl
});
if (result) {
const processed = await process_ae_obj__event_location_props({ obj_li: [result], log_lvl });
const processed_obj = processed[0];
if (try_cache) {
await db_save_ae_obj_li__ae_obj({
db_instance: db_events,
table_name: 'location',
obj_li: [processed_obj],
properties_to_save,
log_lvl
});
}
return processed_obj;
}
return null;
const result = await api.update_nested_obj({
api_cfg,
for_obj_type: 'event',
for_obj_id: event_id,
obj_type: 'event_location',
obj_id: event_location_id,
fields: data_kv,
log_lvl
});
if (result) {
const processed = await process_ae_obj__event_location_props({
obj_li: [result],
log_lvl
});
const processed_obj = processed[0];
if (try_cache) {
await db_save_ae_obj_li__ae_obj({
db_instance: db_events,
table_name: 'location',
obj_li: [processed_obj],
properties_to_save,
log_lvl
});
}
return processed_obj;
}
return null;
}
// Updated 2026-01-20 to V3
export async function search__event_location({
api_cfg, event_id, qry_str = '', enabled = 'enabled', hidden = 'not_hidden', view = 'default', limit = 25, offset = 0, order_by_li = [{ sort: 'ASC' }, { name: 'ASC' }], try_cache = true, log_lvl = 0
api_cfg,
event_id,
qry_str = '',
enabled = 'enabled',
hidden = 'not_hidden',
view = 'default',
limit = 25,
offset = 0,
order_by_li = [{ sort: 'ASC' }, { name: 'ASC' }],
try_cache = true,
log_lvl = 0
}: {
api_cfg: any; event_id: string; qry_str?: string; enabled?: 'enabled' | 'all' | 'not_enabled'; hidden?: 'hidden' | 'all' | 'not_hidden'; view?: string; limit?: number; offset?: number; order_by_li?: any; try_cache?: boolean; log_lvl?: number;
api_cfg: any;
event_id: string;
qry_str?: string;
enabled?: 'enabled' | 'all' | 'not_enabled';
hidden?: 'hidden' | 'all' | 'not_hidden';
view?: string;
limit?: number;
offset?: number;
order_by_li?: any;
try_cache?: boolean;
log_lvl?: number;
}): Promise<ae_EventLocation[]> {
const search_query: any = { q: qry_str, and: [{ field: 'event_id', op: 'eq', value: event_id }] };
if (enabled === 'enabled') search_query.and.push({ field: 'enable', op: 'eq', value: true });
else if (enabled === 'not_enabled') search_query.and.push({ field: 'enable', op: 'eq', value: false });
if (hidden === 'hidden') search_query.and.push({ field: 'hide', op: 'eq', value: true });
else if (hidden === 'not_hidden') search_query.and.push({ field: 'hide', op: 'eq', value: false });
const search_query: any = {
q: qry_str,
and: [{ field: 'event_id', op: 'eq', value: event_id }]
};
if (enabled === 'enabled')
search_query.and.push({ field: 'enable', op: 'eq', value: true });
else if (enabled === 'not_enabled')
search_query.and.push({ field: 'enable', op: 'eq', value: false });
if (hidden === 'hidden')
search_query.and.push({ field: 'hide', op: 'eq', value: true });
else if (hidden === 'not_hidden')
search_query.and.push({ field: 'hide', op: 'eq', value: false });
const result_li = await api.search_ae_obj({ api_cfg, obj_type: 'event_location', search_query, order_by_li, view, limit, offset, log_lvl });
const result_li = await api.search_ae_obj({
api_cfg,
obj_type: 'event_location',
search_query,
order_by_li,
view,
limit,
offset,
log_lvl
});
if (result_li) {
const processed = await process_ae_obj__event_location_props({ obj_li: result_li, log_lvl });
const processed = await process_ae_obj__event_location_props({
obj_li: result_li,
log_lvl
});
if (try_cache) {
await db_save_ae_obj_li__ae_obj({ db_instance: db_events, table_name: 'location', obj_li: processed, properties_to_save, log_lvl });
await db_save_ae_obj_li__ae_obj({
db_instance: db_events,
table_name: 'location',
obj_li: processed,
properties_to_save,
log_lvl
});
}
return processed;
}
@@ -362,10 +584,40 @@ export async function search__event_location({
}
export const properties_to_save = [
'id', 'event_location_id', 'event_location_id_random', 'event_id', 'event_id_random', 'external_id', 'code', 'name', 'description', 'passcode', 'enable', 'hide', 'priority', 'sort', 'group', 'notes', 'created_on', 'updated_on', 'tmp_sort_1', 'tmp_sort_2', 'event_name'
'id',
'event_location_id',
'event_location_id_random',
'event_id',
'event_id_random',
'external_id',
'code',
'name',
'description',
'passcode',
'enable',
'hide',
'priority',
'sort',
'group',
'notes',
'created_on',
'updated_on',
'tmp_sort_1',
'tmp_sort_2',
'event_name'
];
async function _process_generic_props<T extends Record<string, any>>({ obj_li, obj_type, log_lvl = 0, specific_processor }: { obj_li: T[]; obj_type: string; log_lvl?: number; specific_processor?: (obj: T) => Promise<T> | T; }): Promise<T[]> {
async function _process_generic_props<T extends Record<string, any>>({
obj_li,
obj_type,
log_lvl = 0,
specific_processor
}: {
obj_li: T[];
obj_type: string;
log_lvl?: number;
specific_processor?: (obj: T) => Promise<T> | T;
}): Promise<T[]> {
if (!obj_li || obj_li.length === 0) return [];
const processed_obj_li: T[] = [];
for (const original_obj of obj_li) {
@@ -378,24 +630,42 @@ async function _process_generic_props<T extends Record<string, any>>({ obj_li, o
}
const randomIdKey = `${obj_type}_id_random`;
const baseIdKey = `${obj_type}_id`;
if (processed_obj[randomIdKey]) (processed_obj as any).id = processed_obj[randomIdKey];
else if (processed_obj[baseIdKey]) (processed_obj as any).id = processed_obj[baseIdKey];
if (processed_obj[randomIdKey])
(processed_obj as any).id = processed_obj[randomIdKey];
else if (processed_obj[baseIdKey])
(processed_obj as any).id = processed_obj[baseIdKey];
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;
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}`;
if (specific_processor) processed_obj = await Promise.resolve(specific_processor(processed_obj));
(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_li.push(processed_obj as T);
}
return processed_obj_li;
}
export async function process_ae_obj__event_location_props({ obj_li, log_lvl = 0 }: { obj_li: any[]; log_lvl?: number; }) {
return _process_generic_props({ obj_li, obj_type: 'event_location', log_lvl, specific_processor: (obj) => {
if (obj.event_id_random) obj.event_id = obj.event_id_random;
return obj;
}});
}
export async function process_ae_obj__event_location_props({
obj_li,
log_lvl = 0
}: {
obj_li: any[];
log_lvl?: number;
}) {
return _process_generic_props({
obj_li,
obj_type: 'event_location',
log_lvl,
specific_processor: (obj) => {
if (obj.event_id_random) obj.event_id = obj.event_id_random;
return obj;
}
});
}

View File

@@ -37,41 +37,116 @@ export async function load_ae_obj_id__event_presentation({
log_lvl?: number;
}): Promise<ae_EventPresentation | null> {
if (log_lvl) {
console.log(`*** load_ae_obj_id__event_presentation() *** id=${event_presentation_id} (SWR)`);
console.log(
`*** load_ae_obj_id__event_presentation() *** id=${event_presentation_id} (SWR)`
);
}
// 1. FAST PATH: Cache hit
if (try_cache) {
try {
const cached = await db_events.presentation.get(event_presentation_id);
const cached = await db_events.presentation.get(
event_presentation_id
);
if (cached) {
// Background refresh (non-blocking)
_refresh_presentation_id_background({ api_cfg, event_presentation_id, view, try_cache, inc_file_li, inc_presenter_li, enabled, hidden, limit, offset, log_lvl: 0 });
_refresh_presentation_id_background({
api_cfg,
event_presentation_id,
view,
try_cache,
inc_file_li,
inc_presenter_li,
enabled,
hidden,
limit,
offset,
log_lvl: 0
});
// Await nested loads from cache to return a complete object
return await _handle_nested_loads(cached, { api_cfg, inc_file_li, inc_presenter_li, enabled, hidden, limit, offset, try_cache, log_lvl: 0 });
return await _handle_nested_loads(cached, {
api_cfg,
inc_file_li,
inc_presenter_li,
enabled,
hidden,
limit,
offset,
try_cache,
log_lvl: 0
});
}
} catch (e) {}
}
// 2. SLOW PATH: Wait for API
return await _refresh_presentation_id_background({ api_cfg, event_presentation_id, view, try_cache, inc_file_li, inc_presenter_li, enabled, hidden, limit, offset, log_lvl });
return await _refresh_presentation_id_background({
api_cfg,
event_presentation_id,
view,
try_cache,
inc_file_li,
inc_presenter_li,
enabled,
hidden,
limit,
offset,
log_lvl
});
}
/**
* Internal background refresh for a single presentation
*/
async function _refresh_presentation_id_background({ api_cfg, event_presentation_id, view, try_cache, inc_file_li, inc_presenter_li, enabled, hidden, limit, offset, log_lvl }: any) {
async function _refresh_presentation_id_background({
api_cfg,
event_presentation_id,
view,
try_cache,
inc_file_li,
inc_presenter_li,
enabled,
hidden,
limit,
offset,
log_lvl
}: any) {
if (typeof navigator !== 'undefined' && !navigator.onLine) return null;
try {
const result = await api.get_ae_obj({ api_cfg, obj_type: 'event_presentation', obj_id: event_presentation_id, view, log_lvl });
const result = await api.get_ae_obj({
api_cfg,
obj_type: 'event_presentation',
obj_id: event_presentation_id,
view,
log_lvl
});
if (result) {
const processed = await process_ae_obj__event_presentation_props({ obj_li: [result], log_lvl });
const processed = await process_ae_obj__event_presentation_props({
obj_li: [result],
log_lvl
});
const processed_obj = processed[0];
if (try_cache) {
await db_save_ae_obj_li__ae_obj({ db_instance: db_events, table_name: 'presentation', obj_li: [processed_obj], properties_to_save, log_lvl });
await db_save_ae_obj_li__ae_obj({
db_instance: db_events,
table_name: 'presentation',
obj_li: [processed_obj],
properties_to_save,
log_lvl
});
}
// During refresh, we disable child SWR to prevent a request storm
return await _handle_nested_loads(processed_obj, { api_cfg, inc_file_li, inc_presenter_li, enabled, hidden, limit, offset, try_cache: false, log_lvl });
return await _handle_nested_loads(processed_obj, {
api_cfg,
inc_file_li,
inc_presenter_li,
enabled,
hidden,
limit,
offset,
try_cache: false,
log_lvl
});
}
} catch (e) {}
return null;
@@ -80,23 +155,54 @@ async function _refresh_presentation_id_background({ api_cfg, event_presentation
/**
* Helper to handle nested collection loads for a presentation
*/
async function _handle_nested_loads(presentation_obj: any, { api_cfg, inc_file_li, inc_presenter_li, enabled, hidden, limit, offset, try_cache, log_lvl }: any) {
const current_presentation_id = presentation_obj.id || presentation_obj.event_presentation_id;
async function _handle_nested_loads(
presentation_obj: any,
{
api_cfg,
inc_file_li,
inc_presenter_li,
enabled,
hidden,
limit,
offset,
try_cache,
log_lvl
}: any
) {
const current_presentation_id =
presentation_obj.id || presentation_obj.event_presentation_id;
if (!current_presentation_id) return presentation_obj;
const tasks = [];
if (inc_file_li) {
tasks.push(load_ae_obj_li__event_file({
api_cfg, for_obj_type: 'event_presentation', for_obj_id: current_presentation_id,
enabled, limit: 25, try_cache, log_lvl
}).then(res => presentation_obj.event_file_li = res));
tasks.push(
load_ae_obj_li__event_file({
api_cfg,
for_obj_type: 'event_presentation',
for_obj_id: current_presentation_id,
enabled,
limit: 25,
try_cache,
log_lvl
}).then((res) => (presentation_obj.event_file_li = res))
);
}
if (inc_presenter_li) {
tasks.push(load_ae_obj_li__event_presenter({
api_cfg, for_obj_type: 'event_presentation', for_obj_id: current_presentation_id,
inc_file_li, enabled, hidden, limit, offset, try_cache, log_lvl
}).then(res => presentation_obj.event_presenter_li = res));
tasks.push(
load_ae_obj_li__event_presenter({
api_cfg,
for_obj_type: 'event_presentation',
for_obj_id: current_presentation_id,
inc_file_li,
enabled,
hidden,
limit,
offset,
try_cache,
log_lvl
}).then((res) => (presentation_obj.event_presenter_li = res))
);
}
if (tasks.length > 0) await Promise.all(tasks);
@@ -140,17 +246,36 @@ export async function load_ae_obj_li__event_presentation({
log_lvl?: number;
}): Promise<ae_EventPresentation[]> {
if (log_lvl) {
console.log(`*** load_ae_obj_li__event_presentation() *** for=${for_obj_type}:${for_obj_id} (SWR)`);
console.log(
`*** load_ae_obj_li__event_presentation() *** for=${for_obj_type}:${for_obj_id} (SWR)`
);
}
// 1. FAST PATH: Cache hit
if (try_cache) {
try {
// String-Only ID Vision: query event_session_id using the string ID
const cached_li = await db_events.presentation.where('event_session_id').equals(for_obj_id).toArray();
const cached_li = await db_events.presentation
.where('event_session_id')
.equals(for_obj_id)
.toArray();
if (cached_li && cached_li.length > 0) {
// Background refresh (non-blocking)
_refresh_presentation_li_background({ api_cfg, for_obj_type, for_obj_id, inc_file_li, inc_presenter_li, enabled, hidden, view, limit, offset, order_by_li, try_cache, log_lvl: 0 });
_refresh_presentation_li_background({
api_cfg,
for_obj_type,
for_obj_id,
inc_file_li,
inc_presenter_li,
enabled,
hidden,
view,
limit,
offset,
order_by_li,
try_cache,
log_lvl: 0
});
// Warm cache for nested loads in the background (FIRE AND FORGET)
// DEPRECATED Optimization: Don't fire child loads for every item in a list here.
@@ -167,23 +292,72 @@ export async function load_ae_obj_li__event_presentation({
}
// 2. SLOW PATH: Wait for API
return await _refresh_presentation_li_background({ api_cfg, for_obj_type, for_obj_id, inc_file_li, inc_presenter_li, enabled, hidden, view, limit, offset, order_by_li, try_cache, log_lvl });
return await _refresh_presentation_li_background({
api_cfg,
for_obj_type,
for_obj_id,
inc_file_li,
inc_presenter_li,
enabled,
hidden,
view,
limit,
offset,
order_by_li,
try_cache,
log_lvl
});
}
async function _refresh_presentation_li_background({ api_cfg, for_obj_type, for_obj_id, inc_file_li, inc_presenter_li, enabled, hidden, view, limit, offset, order_by_li, try_cache, log_lvl }: any) {
async function _refresh_presentation_li_background({
api_cfg,
for_obj_type,
for_obj_id,
inc_file_li,
inc_presenter_li,
enabled,
hidden,
view,
limit,
offset,
order_by_li,
try_cache,
log_lvl
}: any) {
if (typeof navigator !== 'undefined' && !navigator.onLine) return [];
try {
const result_li = await api.get_ae_obj_li({ api_cfg, obj_type: 'event_presentation', for_obj_type, for_obj_id, enabled, hidden, view, limit, offset, order_by_li, log_lvl });
const result_li = await api.get_ae_obj_li({
api_cfg,
obj_type: 'event_presentation',
for_obj_type,
for_obj_id,
enabled,
hidden,
view,
limit,
offset,
order_by_li,
log_lvl
});
if (result_li) {
const processed = await process_ae_obj__event_presentation_props({ obj_li: result_li, log_lvl });
const processed = await process_ae_obj__event_presentation_props({
obj_li: result_li,
log_lvl
});
// Ensure the linking ID is set correctly for indexing
if (for_obj_type === 'event_session') {
processed.forEach(p => p.event_session_id = for_obj_id);
processed.forEach((p) => (p.event_session_id = for_obj_id));
}
if (try_cache) {
await db_save_ae_obj_li__ae_obj({ db_instance: db_events, table_name: 'presentation', obj_li: processed, properties_to_save, log_lvl });
await db_save_ae_obj_li__ae_obj({
db_instance: db_events,
table_name: 'presentation',
obj_li: processed,
properties_to_save,
log_lvl
});
// CRITICAL FIX (2026-02-26): Yield to microtask queue so Dexie liveQuery observers
// fire before we return. Without this, component-mounted liveQueries may subscribe
// to IDB *before* the write completes, causing empty results on cold-start.
@@ -194,9 +368,21 @@ async function _refresh_presentation_li_background({ api_cfg, for_obj_type, for_
// before presenter data was loaded, causing "refresh twice" bug on cold-start.
// Now we await all nested loads AND preserve try_cache so presenters are written to IDB.
if (inc_file_li || inc_presenter_li) {
await Promise.all(processed.map(p =>
_handle_nested_loads(p, { api_cfg, inc_file_li, inc_presenter_li, enabled, hidden, limit, offset, try_cache, log_lvl: 0 })
));
await Promise.all(
processed.map((p) =>
_handle_nested_loads(p, {
api_cfg,
inc_file_li,
inc_presenter_li,
enabled,
hidden,
limit,
offset,
try_cache,
log_lvl: 0
})
)
);
}
return processed;
}
@@ -206,42 +392,45 @@ async function _refresh_presentation_li_background({ api_cfg, for_obj_type, for_
// Updated 2026-01-20 to V3
export async function create_ae_obj__event_presentation({
api_cfg,
event_session_id,
data_kv,
try_cache = true,
log_lvl = 0
api_cfg,
event_session_id,
data_kv,
try_cache = true,
log_lvl = 0
}: {
api_cfg: any;
event_session_id: string;
data_kv: key_val;
try_cache?: boolean;
log_lvl?: number;
api_cfg: any;
event_session_id: string;
data_kv: key_val;
try_cache?: boolean;
log_lvl?: number;
}): Promise<ae_EventPresentation | null> {
const result = await api.create_nested_obj({
api_cfg,
for_obj_type: 'event_session',
for_obj_id: event_session_id,
obj_type: 'event_presentation',
fields: { ...data_kv },
log_lvl
});
const result = await api.create_nested_obj({
api_cfg,
for_obj_type: 'event_session',
for_obj_id: event_session_id,
obj_type: 'event_presentation',
fields: { ...data_kv },
log_lvl
});
if (result) {
const processed = await process_ae_obj__event_presentation_props({ obj_li: [result], log_lvl });
const processed_obj = processed[0];
if (try_cache) {
await db_save_ae_obj_li__ae_obj({
db_instance: db_events,
table_name: 'presentation',
obj_li: [processed_obj],
properties_to_save,
log_lvl
});
}
return processed_obj;
}
return null;
if (result) {
const processed = await process_ae_obj__event_presentation_props({
obj_li: [result],
log_lvl
});
const processed_obj = processed[0];
if (try_cache) {
await db_save_ae_obj_li__ae_obj({
db_instance: db_events,
table_name: 'presentation',
obj_li: [processed_obj],
properties_to_save,
log_lvl
});
}
return processed_obj;
}
return null;
}
// Updated 2026-01-20 to V3
@@ -259,7 +448,11 @@ export async function delete_ae_obj_id__event_presentation({
log_lvl?: number;
}) {
const result = await api.delete_ae_obj({
api_cfg, obj_type: 'event_presentation', obj_id: event_presentation_id, method, log_lvl
api_cfg,
obj_type: 'event_presentation',
obj_id: event_presentation_id,
method,
log_lvl
});
if (try_cache) await db_events.presentation.delete(event_presentation_id);
return result;
@@ -280,13 +473,26 @@ export async function update_ae_obj__event_presentation({
log_lvl?: number;
}): Promise<ae_EventPresentation | null> {
const result = await api.update_ae_obj({
api_cfg, obj_type: 'event_presentation', obj_id: event_presentation_id, fields: data_kv, log_lvl
api_cfg,
obj_type: 'event_presentation',
obj_id: event_presentation_id,
fields: data_kv,
log_lvl
});
if (result) {
const processed = await process_ae_obj__event_presentation_props({ obj_li: [result], log_lvl });
const processed = await process_ae_obj__event_presentation_props({
obj_li: [result],
log_lvl
});
const processed_obj = processed[0];
if (try_cache) {
await db_save_ae_obj_li__ae_obj({ db_instance: db_events, table_name: 'presentation', obj_li: [processed_obj], properties_to_save, log_lvl });
await db_save_ae_obj_li__ae_obj({
db_instance: db_events,
table_name: 'presentation',
obj_li: [processed_obj],
properties_to_save,
log_lvl
});
}
return processed_obj;
}
@@ -295,24 +501,74 @@ export async function update_ae_obj__event_presentation({
// Updated 2026-01-21 to Restore Full Aether Search Logic
export async function search__event_presentation({
api_cfg, event_id, fulltext_search_qry_str = '', like_search_qry_str = '', enabled = 'enabled', hidden = 'not_hidden', view = 'default', limit = 50, offset = 0, order_by_li = [{ sort: 'ASC' }, { start_datetime: 'ASC' }, { name: 'ASC' }], try_cache = true, log_lvl = 0
api_cfg,
event_id,
fulltext_search_qry_str = '',
like_search_qry_str = '',
enabled = 'enabled',
hidden = 'not_hidden',
view = 'default',
limit = 50,
offset = 0,
order_by_li = [{ sort: 'ASC' }, { start_datetime: 'ASC' }, { name: 'ASC' }],
try_cache = true,
log_lvl = 0
}: {
api_cfg: any; event_id: string; fulltext_search_qry_str?: string; like_search_qry_str?: string; enabled?: 'enabled' | 'all' | 'not_enabled'; hidden?: 'hidden' | 'all' | 'not_hidden'; view?: string; limit?: number; offset?: number; order_by_li?: any; try_cache?: boolean; log_lvl?: number;
api_cfg: any;
event_id: string;
fulltext_search_qry_str?: string;
like_search_qry_str?: string;
enabled?: 'enabled' | 'all' | 'not_enabled';
hidden?: 'hidden' | 'all' | 'not_hidden';
view?: string;
limit?: number;
offset?: number;
order_by_li?: any;
try_cache?: boolean;
log_lvl?: number;
}): Promise<ae_EventPresentation[]> {
const search_query: any = { q: '', and: [{ field: 'event_id', op: 'eq', value: event_id }] };
const search_query: any = {
q: '',
and: [{ field: 'event_id', op: 'eq', value: event_id }]
};
const params: key_val = {};
if (fulltext_search_qry_str && fulltext_search_qry_str.length > 2) params['ft_qry'] = { 'default_qry_str': fulltext_search_qry_str };
if (like_search_qry_str) params['lk_qry'] = { 'default_qry_str': like_search_qry_str };
if (enabled === 'enabled') search_query.and.push({ field: 'enable', op: 'eq', value: 1 });
else if (enabled === 'not_enabled') search_query.and.push({ field: 'enable', op: 'eq', value: 0 });
if (hidden === 'hidden') search_query.and.push({ field: 'hide', op: 'eq', value: 1 });
else if (hidden === 'not_hidden') search_query.and.push({ field: 'hide', op: 'eq', value: 0 });
if (fulltext_search_qry_str && fulltext_search_qry_str.length > 2)
params['ft_qry'] = { default_qry_str: fulltext_search_qry_str };
if (like_search_qry_str)
params['lk_qry'] = { default_qry_str: like_search_qry_str };
if (enabled === 'enabled')
search_query.and.push({ field: 'enable', op: 'eq', value: 1 });
else if (enabled === 'not_enabled')
search_query.and.push({ field: 'enable', op: 'eq', value: 0 });
if (hidden === 'hidden')
search_query.and.push({ field: 'hide', op: 'eq', value: 1 });
else if (hidden === 'not_hidden')
search_query.and.push({ field: 'hide', op: 'eq', value: 0 });
const result_li = await api.search_ae_obj({ api_cfg, obj_type: 'event_presentation', search_query, order_by_li, params, view, limit, offset, log_lvl });
const result_li = await api.search_ae_obj({
api_cfg,
obj_type: 'event_presentation',
search_query,
order_by_li,
params,
view,
limit,
offset,
log_lvl
});
if (result_li) {
const processed = await process_ae_obj__event_presentation_props({ obj_li: result_li, log_lvl });
const processed = await process_ae_obj__event_presentation_props({
obj_li: result_li,
log_lvl
});
if (try_cache) {
await db_save_ae_obj_li__ae_obj({ db_instance: db_events, table_name: 'presentation', obj_li: processed, properties_to_save, log_lvl });
await db_save_ae_obj_li__ae_obj({
db_instance: db_events,
table_name: 'presentation',
obj_li: processed,
properties_to_save,
log_lvl
});
}
return processed;
}
@@ -322,10 +578,53 @@ export async function search__event_presentation({
export const qry__event_presentation = search__event_presentation;
export const properties_to_save = [
'id', 'event_presentation_id', 'event_presentation_id_random', 'external_id', 'code', 'for_type', 'for_id', 'for_id_random', 'type_code', 'event_id', 'event_session_id', 'event_abstract_id', 'event_id_random', 'event_session_id_random', 'event_abstract_id_random', 'abstract_code', 'name', 'description', 'start_datetime', 'end_datetime', 'passcode', 'hide_event_launcher', 'enable', 'hide', 'priority', 'sort', 'group', 'notes', 'created_on', 'updated_on', 'tmp_sort_1', 'tmp_sort_2', 'event_session_code', 'event_session_name'
'id',
'event_presentation_id',
'event_presentation_id_random',
'external_id',
'code',
'for_type',
'for_id',
'for_id_random',
'type_code',
'event_id',
'event_session_id',
'event_abstract_id',
'event_id_random',
'event_session_id_random',
'event_abstract_id_random',
'abstract_code',
'name',
'description',
'start_datetime',
'end_datetime',
'passcode',
'hide_event_launcher',
'enable',
'hide',
'priority',
'sort',
'group',
'notes',
'created_on',
'updated_on',
'tmp_sort_1',
'tmp_sort_2',
'event_session_code',
'event_session_name'
];
async function _process_generic_props<T extends Record<string, any>>({ obj_li, obj_type, log_lvl = 0, specific_processor }: { obj_li: T[]; obj_type: string; log_lvl?: number; specific_processor?: (obj: T) => Promise<T> | T; }): Promise<T[]> {
async function _process_generic_props<T extends Record<string, any>>({
obj_li,
obj_type,
log_lvl = 0,
specific_processor
}: {
obj_li: T[];
obj_type: string;
log_lvl?: number;
specific_processor?: (obj: T) => Promise<T> | T;
}): Promise<T[]> {
if (!obj_li || obj_li.length === 0) return [];
const processed_obj_li: T[] = [];
for (const original_obj of obj_li) {
@@ -338,26 +637,48 @@ async function _process_generic_props<T extends Record<string, any>>({ obj_li, o
}
const randomIdKey = `${obj_type}_id_random`;
const baseIdKey = `${obj_type}_id`;
if (processed_obj[randomIdKey]) (processed_obj as any).id = processed_obj[randomIdKey];
else if (processed_obj[baseIdKey]) (processed_obj as any).id = processed_obj[baseIdKey];
if (processed_obj[randomIdKey])
(processed_obj as any).id = processed_obj[randomIdKey];
else if (processed_obj[baseIdKey])
(processed_obj as any).id = processed_obj[baseIdKey];
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}`;
if (specific_processor) processed_obj = await Promise.resolve(specific_processor(processed_obj));
(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_li.push(processed_obj as T);
}
return processed_obj_li;
}
export async function process_ae_obj__event_presentation_props({ obj_li, log_lvl = 0 }: { obj_li: any[]; log_lvl?: number; }) {
return _process_generic_props({ obj_li, obj_type: 'event_presentation', log_lvl, specific_processor: (obj) => {
// Ensure linking IDs are the string versions for indexing
if (obj.event_session_id_random) obj.event_session_id = obj.event_session_id_random;
if (obj.event_id_random) obj.event_id = obj.event_id_random;
return obj;
}});
}
export async function process_ae_obj__event_presentation_props({
obj_li,
log_lvl = 0
}: {
obj_li: any[];
log_lvl?: number;
}) {
return _process_generic_props({
obj_li,
obj_type: 'event_presentation',
log_lvl,
specific_processor: (obj) => {
// Ensure linking IDs are the string versions for indexing
if (obj.event_session_id_random)
obj.event_session_id = obj.event_session_id_random;
if (obj.event_id_random) obj.event_id = obj.event_id_random;
return obj;
}
});
}

View File

@@ -29,7 +29,9 @@ export async function load_ae_obj_id__event_presenter({
log_lvl?: number;
}): Promise<ae_EventPresenter | null> {
if (log_lvl) {
console.log(`*** load_ae_obj_id__event_presenter() *** [V3] id=${event_presenter_id} (SWR)`);
console.log(
`*** load_ae_obj_id__event_presenter() *** [V3] id=${event_presenter_id} (SWR)`
);
}
// 1. FAST PATH: Cache hit
@@ -38,30 +40,71 @@ export async function load_ae_obj_id__event_presenter({
const cached = await db_events.presenter.get(event_presenter_id);
if (cached) {
// Background refresh (non-blocking)
_refresh_presenter_id_background({ api_cfg, event_presenter_id, view, try_cache, inc_file_li, log_lvl: 0 });
_refresh_presenter_id_background({
api_cfg,
event_presenter_id,
view,
try_cache,
inc_file_li,
log_lvl: 0
});
return cached;
}
} catch (e) {}
}
// 2. SLOW PATH: Wait for API
return await _refresh_presenter_id_background({ api_cfg, event_presenter_id, view, try_cache, inc_file_li, log_lvl });
return await _refresh_presenter_id_background({
api_cfg,
event_presenter_id,
view,
try_cache,
inc_file_li,
log_lvl
});
}
async function _refresh_presenter_id_background({ api_cfg, event_presenter_id, view, try_cache, inc_file_li, log_lvl }: any) {
async function _refresh_presenter_id_background({
api_cfg,
event_presenter_id,
view,
try_cache,
inc_file_li,
log_lvl
}: any) {
if (typeof navigator !== 'undefined' && !navigator.onLine) return null;
try {
const result = await api.get_ae_obj({ api_cfg, obj_type: 'event_presenter', obj_id: event_presenter_id, view, log_lvl });
const result = await api.get_ae_obj({
api_cfg,
obj_type: 'event_presenter',
obj_id: event_presenter_id,
view,
log_lvl
});
if (result) {
const processed = await process_ae_obj__event_presenter_props({ obj_li: [result], log_lvl });
const processed = await process_ae_obj__event_presenter_props({
obj_li: [result],
log_lvl
});
const processed_obj = processed[0];
if (try_cache) {
await db_save_ae_obj_li__ae_obj({ db_instance: db_events, table_name: 'presenter', obj_li: [processed_obj], properties_to_save, log_lvl });
await db_save_ae_obj_li__ae_obj({
db_instance: db_events,
table_name: 'presenter',
obj_li: [processed_obj],
properties_to_save,
log_lvl
});
}
if (inc_file_li) {
processed_obj.event_file_li = await load_ae_obj_li__event_file({
api_cfg, for_obj_type: 'event_presenter', for_obj_id: event_presenter_id,
enabled: 'all', limit: 25, try_cache: false, log_lvl
api_cfg,
for_obj_type: 'event_presenter',
for_obj_id: event_presenter_id,
enabled: 'all',
limit: 25,
try_cache: false,
log_lvl
});
}
return processed_obj;
@@ -104,7 +147,9 @@ export async function load_ae_obj_li__event_presenter({
log_lvl?: number;
}): Promise<ae_EventPresenter[]> {
if (log_lvl) {
console.log(`*** load_ae_obj_li__event_presenter() *** [V3] for=${for_obj_type}:${for_obj_id} (SWR)`);
console.log(
`*** load_ae_obj_li__event_presenter() *** [V3] for=${for_obj_type}:${for_obj_id} (SWR)`
);
}
// 1. FAST PATH: Check cache using specific indices
@@ -112,31 +157,94 @@ export async function load_ae_obj_li__event_presenter({
try {
let cached_li: any[] = [];
if (for_obj_type === 'event_presentation') {
cached_li = await db_events.presenter.where('event_presentation_id').equals(for_obj_id).toArray();
cached_li = await db_events.presenter
.where('event_presentation_id')
.equals(for_obj_id)
.toArray();
} else if (for_obj_type === 'event_session') {
cached_li = await db_events.presenter.where('event_session_id').equals(for_obj_id).toArray();
cached_li = await db_events.presenter
.where('event_session_id')
.equals(for_obj_id)
.toArray();
} else if (for_obj_type === 'event') {
cached_li = await db_events.presenter.where('event_id').equals(for_obj_id).toArray();
cached_li = await db_events.presenter
.where('event_id')
.equals(for_obj_id)
.toArray();
}
if (cached_li && cached_li.length > 0) {
// Background refresh (non-blocking)
_refresh_presenter_li_background({ api_cfg, for_obj_type, for_obj_id, inc_file_li, enabled, hidden, view, limit, offset, order_by_li, try_cache, log_lvl: 0 });
_refresh_presenter_li_background({
api_cfg,
for_obj_type,
for_obj_id,
inc_file_li,
enabled,
hidden,
view,
limit,
offset,
order_by_li,
try_cache,
log_lvl: 0
});
return cached_li;
}
} catch (e) {}
}
// 2. SLOW PATH: Wait for API
return await _refresh_presenter_li_background({ api_cfg, for_obj_type, for_obj_id, inc_file_li, enabled, hidden, view, limit, offset, order_by_li, try_cache, log_lvl });
return await _refresh_presenter_li_background({
api_cfg,
for_obj_type,
for_obj_id,
inc_file_li,
enabled,
hidden,
view,
limit,
offset,
order_by_li,
try_cache,
log_lvl
});
}
async function _refresh_presenter_li_background({ api_cfg, for_obj_type, for_obj_id, inc_file_li, enabled, hidden, view, limit, offset, order_by_li, try_cache, log_lvl }: any) {
async function _refresh_presenter_li_background({
api_cfg,
for_obj_type,
for_obj_id,
inc_file_li,
enabled,
hidden,
view,
limit,
offset,
order_by_li,
try_cache,
log_lvl
}: any) {
if (typeof navigator !== 'undefined' && !navigator.onLine) return [];
try {
const result_li = await api.get_ae_obj_li({ api_cfg, obj_type: 'event_presenter', for_obj_type, for_obj_id, enabled, hidden, view, limit, offset, order_by_li, log_lvl });
const result_li = await api.get_ae_obj_li({
api_cfg,
obj_type: 'event_presenter',
for_obj_type,
for_obj_id,
enabled,
hidden,
view,
limit,
offset,
order_by_li,
log_lvl
});
if (result_li) {
const processed = await process_ae_obj__event_presenter_props({ obj_li: result_li, log_lvl });
const processed = await process_ae_obj__event_presenter_props({
obj_li: result_li,
log_lvl
});
// String-Only ID Vision: Ensure linking ID is set for indexing
processed.forEach((p) => {
@@ -155,7 +263,13 @@ async function _refresh_presenter_li_background({ api_cfg, for_obj_type, for_obj
});
if (try_cache) {
await db_save_ae_obj_li__ae_obj({ db_instance: db_events, table_name: 'presenter', obj_li: processed, properties_to_save, log_lvl });
await db_save_ae_obj_li__ae_obj({
db_instance: db_events,
table_name: 'presenter',
obj_li: processed,
properties_to_save,
log_lvl
});
// CRITICAL FIX (2026-02-26): Yield to microtask queue so Dexie liveQuery observers
// fire before we return. Without this, component-mounted liveQueries may subscribe
// to IDB *before* the write completes, causing empty results on cold-start.
@@ -164,10 +278,15 @@ async function _refresh_presenter_li_background({ api_cfg, for_obj_type, for_obj
// Background nested loads for refreshed items (FIRE AND FORGET)
if (inc_file_li) {
processed.forEach(p => {
processed.forEach((p) => {
load_ae_obj_li__event_file({
api_cfg, for_obj_type: 'event_presenter', for_obj_id: p.id,
enabled: 'all', limit: 25, try_cache: false, log_lvl: 0
api_cfg,
for_obj_type: 'event_presenter',
for_obj_id: p.id,
enabled: 'all',
limit: 25,
try_cache: false,
log_lvl: 0
});
});
}
@@ -180,128 +299,143 @@ async function _refresh_presenter_li_background({ api_cfg, for_obj_type, for_obj
// Updated 2026-01-20 to V3
export async function create_ae_obj__event_presenter({
api_cfg,
event_presentation_id,
data_kv,
try_cache = true,
log_lvl = 0
api_cfg,
event_presentation_id,
data_kv,
try_cache = true,
log_lvl = 0
}: {
api_cfg: any;
event_presentation_id?: string;
data_kv: key_val;
try_cache?: boolean;
log_lvl?: number;
api_cfg: any;
event_presentation_id?: string;
data_kv: key_val;
try_cache?: boolean;
log_lvl?: number;
}): Promise<ae_EventPresenter | null> {
if (!event_presentation_id) event_presentation_id = get(events_slct).event_presentation_id;
if (!event_presentation_id)
event_presentation_id = get(events_slct).event_presentation_id;
if (!event_presentation_id) {
console.error('create_ae_obj__event_presenter: event_presentation_id is required');
console.error(
'create_ae_obj__event_presenter: event_presentation_id is required'
);
return null;
}
const result = await api.create_nested_obj({
api_cfg,
for_obj_type: 'event_presentation',
for_obj_id: event_presentation_id,
obj_type: 'event_presenter',
fields: { ...data_kv },
log_lvl
});
const result = await api.create_nested_obj({
api_cfg,
for_obj_type: 'event_presentation',
for_obj_id: event_presentation_id,
obj_type: 'event_presenter',
fields: { ...data_kv },
log_lvl
});
if (result) {
const processed = await process_ae_obj__event_presenter_props({ obj_li: [result], log_lvl });
const processed_obj = processed[0];
if (try_cache) {
await db_save_ae_obj_li__ae_obj({
db_instance: db_events,
table_name: 'presenter',
obj_li: [processed_obj],
properties_to_save,
log_lvl
});
}
return processed_obj;
}
return null;
if (result) {
const processed = await process_ae_obj__event_presenter_props({
obj_li: [result],
log_lvl
});
const processed_obj = processed[0];
if (try_cache) {
await db_save_ae_obj_li__ae_obj({
db_instance: db_events,
table_name: 'presenter',
obj_li: [processed_obj],
properties_to_save,
log_lvl
});
}
return processed_obj;
}
return null;
}
// Updated 2026-01-20 to V3
export async function delete_ae_obj_id__event_presenter({
api_cfg,
event_presentation_id,
event_presenter_id,
method = 'delete',
try_cache = true,
log_lvl = 0
api_cfg,
event_presentation_id,
event_presenter_id,
method = 'delete',
try_cache = true,
log_lvl = 0
}: {
api_cfg: any;
event_presentation_id?: string;
event_presenter_id: string;
method?: 'delete' | 'soft_delete' | 'disable' | 'hide';
try_cache?: boolean;
log_lvl?: number;
api_cfg: any;
event_presentation_id?: string;
event_presenter_id: string;
method?: 'delete' | 'soft_delete' | 'disable' | 'hide';
try_cache?: boolean;
log_lvl?: number;
}) {
if (!event_presentation_id) event_presentation_id = get(events_slct).event_presentation_id;
if (!event_presentation_id)
event_presentation_id = get(events_slct).event_presentation_id;
if (!event_presentation_id) {
console.error('delete_ae_obj_id__event_presenter: event_presentation_id is required');
console.error(
'delete_ae_obj_id__event_presenter: event_presentation_id is required'
);
return null;
}
const result = await api.delete_nested_ae_obj({
api_cfg,
for_obj_type: 'event_presentation',
for_obj_id: event_presentation_id,
obj_type: 'event_presenter',
obj_id: event_presenter_id,
method,
log_lvl
});
if (try_cache) await db_events.presenter.delete(event_presenter_id);
return result;
const result = await api.delete_nested_ae_obj({
api_cfg,
for_obj_type: 'event_presentation',
for_obj_id: event_presentation_id,
obj_type: 'event_presenter',
obj_id: event_presenter_id,
method,
log_lvl
});
if (try_cache) await db_events.presenter.delete(event_presenter_id);
return result;
}
// Updated 2026-01-20 to V3
export async function update_ae_obj__event_presenter({
api_cfg,
event_presentation_id,
event_presenter_id,
data_kv,
try_cache = true,
log_lvl = 0
api_cfg,
event_presentation_id,
event_presenter_id,
data_kv,
try_cache = true,
log_lvl = 0
}: {
api_cfg: any;
event_presentation_id?: string;
event_presenter_id: string;
data_kv: key_val;
try_cache?: boolean;
log_lvl?: number;
api_cfg: any;
event_presentation_id?: string;
event_presenter_id: string;
data_kv: key_val;
try_cache?: boolean;
log_lvl?: number;
}): Promise<ae_EventPresenter | null> {
if (!event_presentation_id) event_presentation_id = get(events_slct).event_presentation_id;
if (!event_presentation_id)
event_presentation_id = get(events_slct).event_presentation_id;
if (!event_presentation_id) {
console.error('update_ae_obj__event_presenter: event_presentation_id is required');
console.error(
'update_ae_obj__event_presenter: event_presentation_id is required'
);
return null;
}
const result = await api.update_nested_obj({
api_cfg,
for_obj_type: 'event_presentation',
for_obj_id: event_presentation_id,
obj_type: 'event_presenter',
obj_id: event_presenter_id,
fields: data_kv,
log_lvl
});
if (result) {
const processed = await process_ae_obj__event_presenter_props({ obj_li: [result], log_lvl });
const processed_obj = processed[0];
if (try_cache) {
await db_save_ae_obj_li__ae_obj({
db_instance: db_events,
table_name: 'presenter',
obj_li: [processed_obj],
properties_to_save,
log_lvl
});
}
return processed_obj;
}
return null;
const result = await api.update_nested_obj({
api_cfg,
for_obj_type: 'event_presentation',
for_obj_id: event_presentation_id,
obj_type: 'event_presenter',
obj_id: event_presenter_id,
fields: data_kv,
log_lvl
});
if (result) {
const processed = await process_ae_obj__event_presenter_props({
obj_li: [result],
log_lvl
});
const processed_obj = processed[0];
if (try_cache) {
await db_save_ae_obj_li__ae_obj({
db_instance: db_events,
table_name: 'presenter',
obj_li: [processed_obj],
properties_to_save,
log_lvl
});
}
return processed_obj;
}
return null;
}
// Updated 2026-01-21 to Restore Full Aether Search Logic
@@ -320,7 +454,11 @@ export async function search__event_presenter({
view = 'default',
limit = 25,
offset = 0,
order_by_li = [{ sort: 'ASC' }, { given_name: 'ASC' }, { family_name: 'ASC' }],
order_by_li = [
{ sort: 'ASC' },
{ given_name: 'ASC' },
{ family_name: 'ASC' }
],
try_cache = true,
log_lvl = 0
}: {
@@ -342,29 +480,71 @@ export async function search__event_presenter({
try_cache?: boolean;
log_lvl?: number;
}): Promise<ae_EventPresenter[]> {
const search_query: any = { q: '', and: [{ field: 'event_id', op: 'eq', value: event_id }] };
const search_query: any = {
q: '',
and: [{ field: 'event_id', op: 'eq', value: event_id }]
};
const params: key_val = {};
if (fulltext_search_qry_str && fulltext_search_qry_str.length > 2)
params['ft_qry'] = { default_qry_str: fulltext_search_qry_str };
if (ft_presenter_search_qry_str && ft_presenter_search_qry_str.length > 2)
params['ft_qry'] = { ...params['ft_qry'], event_presenter_li_qry_str: ft_presenter_search_qry_str };
if (like_search_qry_str) params['lk_qry'] = { default_qry_str: like_search_qry_str };
params['ft_qry'] = {
...params['ft_qry'],
event_presenter_li_qry_str: ft_presenter_search_qry_str
};
if (like_search_qry_str)
params['lk_qry'] = { default_qry_str: like_search_qry_str };
if (like_presentation_search_qry_str)
params['lk_qry'] = { ...params['lk_qry'], event_presentation_li_qry_str: like_presentation_search_qry_str };
params['lk_qry'] = {
...params['lk_qry'],
event_presentation_li_qry_str: like_presentation_search_qry_str
};
if (like_presenter_search_qry_str)
params['lk_qry'] = { ...params['lk_qry'], event_presenter_li_qry_str: like_presenter_search_qry_str };
if (agree !== null) search_query.and.push({ field: 'agree', op: 'eq', value: agree ? 1 : 0 });
if (biography === true) search_query.and.push({ field: 'biography', op: 'ne', value: '' });
if (enabled === 'enabled') search_query.and.push({ field: 'enable', op: 'eq', value: 1 });
else if (enabled === 'not_enabled') search_query.and.push({ field: 'enable', op: 'eq', value: 0 });
if (hidden === 'hidden') search_query.and.push({ field: 'hide', op: 'eq', value: 1 });
else if (hidden === 'not_hidden') search_query.and.push({ field: 'hide', op: 'eq', value: 0 });
params['lk_qry'] = {
...params['lk_qry'],
event_presenter_li_qry_str: like_presenter_search_qry_str
};
if (agree !== null)
search_query.and.push({
field: 'agree',
op: 'eq',
value: agree ? 1 : 0
});
if (biography === true)
search_query.and.push({ field: 'biography', op: 'ne', value: '' });
if (enabled === 'enabled')
search_query.and.push({ field: 'enable', op: 'eq', value: 1 });
else if (enabled === 'not_enabled')
search_query.and.push({ field: 'enable', op: 'eq', value: 0 });
if (hidden === 'hidden')
search_query.and.push({ field: 'hide', op: 'eq', value: 1 });
else if (hidden === 'not_hidden')
search_query.and.push({ field: 'hide', op: 'eq', value: 0 });
const result_li = await api.search_ae_obj({ api_cfg, obj_type: 'event_presenter', search_query, order_by_li, params, view, limit, offset, log_lvl });
const result_li = await api.search_ae_obj({
api_cfg,
obj_type: 'event_presenter',
search_query,
order_by_li,
params,
view,
limit,
offset,
log_lvl
});
if (result_li) {
const processed = await process_ae_obj__event_presenter_props({ obj_li: result_li, log_lvl });
const processed = await process_ae_obj__event_presenter_props({
obj_li: result_li,
log_lvl
});
if (try_cache) {
await db_save_ae_obj_li__ae_obj({ db_instance: db_events, table_name: 'presenter', obj_li: processed, properties_to_save, log_lvl });
await db_save_ae_obj_li__ae_obj({
db_instance: db_events,
table_name: 'presenter',
obj_li: processed,
properties_to_save,
log_lvl
});
}
return processed;
}
@@ -400,8 +580,16 @@ export async function email_sign_in__event_presenter({
session_name?: string | null;
presentation_name?: string | null;
}) {
if (!to_email || !person_id || !person_passcode || !event_id || !event_presenter_id) {
console.error('Missing required parameters for email_sign_in__event_presenter');
if (
!to_email ||
!person_id ||
!person_passcode ||
!event_id ||
!event_presenter_id
) {
console.error(
'Missing required parameters for email_sign_in__event_presenter'
);
return null;
}
const subject = `Pres Mgmt Hub Sign In Link for Presenter: ${to_name ?? 'Presenter'}`;
@@ -420,10 +608,75 @@ export async function email_sign_in__event_presenter({
}
export const properties_to_save = [
'id', 'event_presenter_id', 'event_presenter_id_random', 'external_id', 'code', 'event_id', 'event_session_id', 'event_presentation_id', 'event_person_id', 'person_id', 'person_profile_id', 'person_id_random', 'person_profile_id_random', 'pronouns', 'informal_name', 'title_names', 'given_name', 'middle_name', 'family_name', 'designations', 'professional_title', 'full_name', 'affiliations', 'email', 'biography', 'agree', 'comments', 'passcode', 'hide_event_launcher', 'data_json', 'enable', 'hide', 'priority', 'sort', 'group', 'notes', 'created_on', 'updated_on', 'tmp_sort_1', 'tmp_sort_2', 'file_count', 'event_session_code', 'event_session_name', 'event_session_start_datetime', 'event_presentation_code', 'event_presentation_name', 'event_presentation_start_datetime', 'person_external_id', 'person_external_sys_id', 'person_given_name', 'person_family_name', 'person_full_name', 'person_professional_title', 'person_affiliations', 'person_primary_email', 'person_passcode'
'id',
'event_presenter_id',
'event_presenter_id_random',
'external_id',
'code',
'event_id',
'event_session_id',
'event_presentation_id',
'event_person_id',
'person_id',
'person_profile_id',
'person_id_random',
'person_profile_id_random',
'pronouns',
'informal_name',
'title_names',
'given_name',
'middle_name',
'family_name',
'designations',
'professional_title',
'full_name',
'affiliations',
'email',
'biography',
'agree',
'comments',
'passcode',
'hide_event_launcher',
'data_json',
'enable',
'hide',
'priority',
'sort',
'group',
'notes',
'created_on',
'updated_on',
'tmp_sort_1',
'tmp_sort_2',
'file_count',
'event_session_code',
'event_session_name',
'event_session_start_datetime',
'event_presentation_code',
'event_presentation_name',
'event_presentation_start_datetime',
'person_external_id',
'person_external_sys_id',
'person_given_name',
'person_family_name',
'person_full_name',
'person_professional_title',
'person_affiliations',
'person_primary_email',
'person_passcode'
];
async function _process_generic_props<T extends Record<string, any>>({ obj_li, obj_type, log_lvl = 0, specific_processor }: { obj_li: T[]; obj_type: string; log_lvl?: number; specific_processor?: (obj: T) => Promise<T> | T; }): Promise<T[]> {
async function _process_generic_props<T extends Record<string, any>>({
obj_li,
obj_type,
log_lvl = 0,
specific_processor
}: {
obj_li: T[];
obj_type: string;
log_lvl?: number;
specific_processor?: (obj: T) => Promise<T> | T;
}): Promise<T[]> {
if (!obj_li || obj_li.length === 0) return [];
const processed_obj_li: T[] = [];
for (const original_obj of obj_li) {
@@ -439,28 +692,50 @@ async function _process_generic_props<T extends Record<string, any>>({ obj_li, o
if (processed_obj[randomIdKey]) {
(processed_obj as any).id = processed_obj[randomIdKey];
(processed_obj as any)[baseIdKey] = processed_obj[randomIdKey];
}
else if (processed_obj[baseIdKey]) (processed_obj as any).id = processed_obj[baseIdKey];
} else if (processed_obj[baseIdKey])
(processed_obj as any).id = processed_obj[baseIdKey];
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}`;
if (specific_processor) processed_obj = await Promise.resolve(specific_processor(processed_obj));
(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_li.push(processed_obj as T);
}
return processed_obj_li;
}
export async function process_ae_obj__event_presenter_props({ obj_li, log_lvl = 0 }: { obj_li: any[]; log_lvl?: number; }) {
return _process_generic_props({ obj_li, obj_type: 'event_presenter', log_lvl, specific_processor: (obj) => {
// String-Only ID Vision: Ensure linking IDs are the string versions for indexing
if (obj.event_presenter_id_random) obj.event_presenter_id = obj.event_presenter_id_random;
if (obj.event_presentation_id_random) obj.event_presentation_id = obj.event_presentation_id_random;
if (obj.event_session_id_random) obj.event_session_id = obj.event_session_id_random;
if (obj.event_id_random) obj.event_id = obj.event_id_random;
return obj;
}});
export async function process_ae_obj__event_presenter_props({
obj_li,
log_lvl = 0
}: {
obj_li: any[];
log_lvl?: number;
}) {
return _process_generic_props({
obj_li,
obj_type: 'event_presenter',
log_lvl,
specific_processor: (obj) => {
// String-Only ID Vision: Ensure linking IDs are the string versions for indexing
if (obj.event_presenter_id_random)
obj.event_presenter_id = obj.event_presenter_id_random;
if (obj.event_presentation_id_random)
obj.event_presentation_id = obj.event_presentation_id_random;
if (obj.event_session_id_random)
obj.event_session_id = obj.event_session_id_random;
if (obj.event_id_random) obj.event_id = obj.event_id_random;
return obj;
}
});
}

View File

@@ -44,7 +44,9 @@ export async function load_ae_obj_id__event_session({
}): Promise<ae_EventSession | null> {
const start_time = performance.now();
if (log_lvl) {
console.log(`🔎 [Trace] load_ae_obj_id__event_session: START (id=${event_session_id}, try_cache=${try_cache})`);
console.log(
`🔎 [Trace] load_ae_obj_id__event_session: START (id=${event_session_id}, try_cache=${try_cache})`
);
}
// Hierarchy Enforcement: Pulling presenters requires pulling presentations first
@@ -56,65 +58,167 @@ export async function load_ae_obj_id__event_session({
const cached = await db_events.session.get(event_session_id);
if (cached) {
const elapsed = (performance.now() - start_time).toFixed(2);
if (log_lvl) console.log(`✅ [Trace] load_ae_obj_id: CACHE HIT at ${elapsed}ms. Returning stale shell for id=${event_session_id}`);
if (log_lvl)
console.log(
`✅ [Trace] load_ae_obj_id: CACHE HIT at ${elapsed}ms. Returning stale shell for id=${event_session_id}`
);
// Background tasks: refresh parent and warm child caches (non-blocking)
_refresh_session_id_background({
api_cfg, event_session_id, view, try_cache,
inc_file_li, inc_all_file_li, inc_presentation_li, inc_presenter_li,
enabled, hidden, limit, offset, log_lvl: log_lvl > 1 ? log_lvl : 0
api_cfg,
event_session_id,
view,
try_cache,
inc_file_li,
inc_all_file_li,
inc_presentation_li,
inc_presenter_li,
enabled,
hidden,
limit,
offset,
log_lvl: log_lvl > 1 ? log_lvl : 0
});
// In SWR mode, we fire child loads in background to warm IDB for the view's LiveQueries
_handle_nested_loads(cached, { api_cfg, inc_file_li, inc_all_file_li, inc_presentation_li, inc_presenter_li, enabled, hidden, limit, offset, try_cache, log_lvl: 0 });
_handle_nested_loads(cached, {
api_cfg,
inc_file_li,
inc_all_file_li,
inc_presentation_li,
inc_presenter_li,
enabled,
hidden,
limit,
offset,
try_cache,
log_lvl: 0
});
return cached; // Return immediately without awaiting nested loads
} else if (log_lvl) {
console.log(`⏳ [Trace] load_ae_obj_id: CACHE MISS at ${(performance.now() - start_time).toFixed(2)}ms for id=${event_session_id}`);
console.log(
`⏳ [Trace] load_ae_obj_id: CACHE MISS at ${(performance.now() - start_time).toFixed(2)}ms for id=${event_session_id}`
);
}
} catch (e) {
if (log_lvl) console.error(`❌ [Trace] load_ae_obj_id: Cache access error:`, e);
if (log_lvl)
console.error(
`❌ [Trace] load_ae_obj_id: Cache access error:`,
e
);
}
}
// 2. SLOW PATH: Wait for API
if (log_lvl) console.log(`🚀 [Trace] load_ae_obj_id: Proceeding to API path for id=${event_session_id}`);
return await _refresh_session_id_background({ api_cfg, event_session_id, view, try_cache, inc_file_li, inc_all_file_li, inc_presentation_li, inc_presenter_li, enabled, hidden, limit, offset, log_lvl });
if (log_lvl)
console.log(
`🚀 [Trace] load_ae_obj_id: Proceeding to API path for id=${event_session_id}`
);
return await _refresh_session_id_background({
api_cfg,
event_session_id,
view,
try_cache,
inc_file_li,
inc_all_file_li,
inc_presentation_li,
inc_presenter_li,
enabled,
hidden,
limit,
offset,
log_lvl
});
}
/**
* Internal background refresh for a single session
*/
async function _refresh_session_id_background({ api_cfg, event_session_id, view, try_cache, inc_file_li, inc_all_file_li, inc_presentation_li, inc_presenter_li, enabled, hidden, limit, offset, log_lvl }: any) {
async function _refresh_session_id_background({
api_cfg,
event_session_id,
view,
try_cache,
inc_file_li,
inc_all_file_li,
inc_presentation_li,
inc_presenter_li,
enabled,
hidden,
limit,
offset,
log_lvl
}: any) {
const start_time = performance.now();
if (typeof navigator !== 'undefined' && !navigator.onLine) return null;
try {
if (log_lvl) console.log(`📡 [Trace] _refresh_session_id: API Fetching id=${event_session_id}`);
const result = await api.get_ae_obj({ api_cfg, obj_type: 'event_session', obj_id: event_session_id, view, log_lvl });
if (log_lvl)
console.log(
`📡 [Trace] _refresh_session_id: API Fetching id=${event_session_id}`
);
const result = await api.get_ae_obj({
api_cfg,
obj_type: 'event_session',
obj_id: event_session_id,
view,
log_lvl
});
if (result) {
const processed = await process_ae_obj__event_session_props({ obj_li: [result], log_lvl });
const processed = await process_ae_obj__event_session_props({
obj_li: [result],
log_lvl
});
const processed_obj = processed[0];
const elapsed = (performance.now() - start_time).toFixed(2);
if (log_lvl) console.log(`📦 [Trace] _refresh_session_id: Received from API at ${elapsed}ms (id=${processed_obj.id})`);
if (log_lvl)
console.log(
`📦 [Trace] _refresh_session_id: Received from API at ${elapsed}ms (id=${processed_obj.id})`
);
if (try_cache) {
await db_save_ae_obj_li__ae_obj({ db_instance: db_events, table_name: 'session', obj_li: [processed_obj], properties_to_save, log_lvl });
await db_save_ae_obj_li__ae_obj({
db_instance: db_events,
table_name: 'session',
obj_li: [processed_obj],
properties_to_save,
log_lvl
});
// CRITICAL FIX (2026-02-26): Yield to microtask queue so Dexie liveQuery observers
// fire before we return. Without this, component-mounted liveQueries may subscribe
// to IDB *before* the write completes, causing empty results on cold-start.
await Promise.resolve();
if (log_lvl) console.log(`💾 [Trace] _refresh_session_id: Saved to IDB cache.`);
if (log_lvl)
console.log(
`💾 [Trace] _refresh_session_id: Saved to IDB cache.`
);
}
// CRITICAL FIX (2026-02-26): Preserve parent's try_cache value when loading nested data.
// Previously set to `false`, which meant presentations/presenters were fetched from API
// but NEVER written to IndexedDB, causing "refresh twice" bug on cold-start.
// Now nested loads inherit parent's caching behavior for deterministic first-render.
return await _handle_nested_loads(processed_obj, { api_cfg, inc_file_li, inc_all_file_li, inc_presentation_li, inc_presenter_li, enabled, hidden, limit, offset, try_cache, log_lvl });
return await _handle_nested_loads(processed_obj, {
api_cfg,
inc_file_li,
inc_all_file_li,
inc_presentation_li,
inc_presenter_li,
enabled,
hidden,
limit,
offset,
try_cache,
log_lvl
});
}
} catch (e) {
if (log_lvl) console.error(`❌ [Trace] _refresh_session_id: API error for id=${event_session_id}:`, e);
if (log_lvl)
console.error(
`❌ [Trace] _refresh_session_id: API error for id=${event_session_id}:`,
e
);
}
return null;
}
@@ -122,29 +226,65 @@ async function _refresh_session_id_background({ api_cfg, event_session_id, view,
/**
* Helper to handle nested collection loads for a session
*/
async function _handle_nested_loads(session_obj: any, { api_cfg, inc_file_li, inc_all_file_li, inc_presentation_li, inc_presenter_li, enabled, hidden, limit, offset, try_cache, log_lvl }: any) {
async function _handle_nested_loads(
session_obj: any,
{
api_cfg,
inc_file_li,
inc_all_file_li,
inc_presentation_li,
inc_presenter_li,
enabled,
hidden,
limit,
offset,
try_cache,
log_lvl
}: any
) {
const start_time = performance.now();
const current_session_id = session_obj.id || session_obj.event_session_id;
if (!current_session_id) return session_obj;
const tasks = [];
if (inc_file_li) {
tasks.push(load_ae_obj_li__event_file({
api_cfg, for_obj_type: 'event_session', for_obj_id: current_session_id,
enabled, limit: 15, try_cache, log_lvl
}).then(res => session_obj.event_file_li = res));
tasks.push(
load_ae_obj_li__event_file({
api_cfg,
for_obj_type: 'event_session',
for_obj_id: current_session_id,
enabled,
limit: 15,
try_cache,
log_lvl
}).then((res) => (session_obj.event_file_li = res))
);
}
if (inc_presentation_li) {
tasks.push(load_ae_obj_li__event_presentation({
api_cfg, for_obj_type: 'event_session', for_obj_id: current_session_id,
inc_file_li: inc_all_file_li, inc_presenter_li, enabled, hidden, limit, offset, try_cache, log_lvl
}).then(res => session_obj.event_presentation_li = res));
tasks.push(
load_ae_obj_li__event_presentation({
api_cfg,
for_obj_type: 'event_session',
for_obj_id: current_session_id,
inc_file_li: inc_all_file_li,
inc_presenter_li,
enabled,
hidden,
limit,
offset,
try_cache,
log_lvl
}).then((res) => (session_obj.event_presentation_li = res))
);
}
if (tasks.length > 0) {
await Promise.all(tasks);
if (log_lvl) console.log(`🔗 [Trace] _handle_nested_loads: Finished child collections in ${(performance.now() - start_time).toFixed(2)}ms`);
if (log_lvl)
console.log(
`🔗 [Trace] _handle_nested_loads: Finished child collections in ${(performance.now() - start_time).toFixed(2)}ms`
);
}
return session_obj;
}
@@ -191,7 +331,9 @@ export async function load_ae_obj_li__event_session({
}): Promise<ae_EventSession[]> {
const start_time = performance.now();
if (log_lvl) {
console.log(`🔎 [Trace] load_ae_obj_li__event_session: START (for=${for_obj_type}:${for_obj_id}, try_cache=${try_cache})`);
console.log(
`🔎 [Trace] load_ae_obj_li__event_session: START (for=${for_obj_type}:${for_obj_id}, try_cache=${try_cache})`
);
}
// Hierarchy Enforcement: Pulling presenters requires pulling presentations first
@@ -201,226 +343,409 @@ export async function load_ae_obj_li__event_session({
try {
// Robust lookup logic
let query;
if (for_obj_type === 'event_location') query = db_events.session.where('event_location_id').equals(for_obj_id);
else if (for_obj_type === 'event') query = db_events.session.where('event_id').equals(for_obj_id);
if (for_obj_type === 'event_location')
query = db_events.session
.where('event_location_id')
.equals(for_obj_id);
else if (for_obj_type === 'event')
query = db_events.session.where('event_id').equals(for_obj_id);
else query = db_events.session.where('for_id').equals(for_obj_id);
const cached_li = await query.toArray();
if (cached_li && cached_li.length > 0) {
const elapsed = (performance.now() - start_time).toFixed(2);
if (log_lvl) console.log(`✅ [Trace] load_ae_obj_li: CACHE HIT at ${elapsed}ms (${cached_li.length} items).`);
if (log_lvl)
console.log(
`✅ [Trace] load_ae_obj_li: CACHE HIT at ${elapsed}ms (${cached_li.length} items).`
);
// Background refresh (non-blocking)
_refresh_session_li_background({
api_cfg, for_obj_type, for_obj_id, view,
api_cfg,
for_obj_type,
for_obj_id,
view,
// Optimization: Disable nested loads for list members to prevent request storms
inc_file_li: false, inc_all_file_li: false, inc_presentation_li: false, inc_presenter_li: false,
enabled, hidden, limit, offset, order_by_li, try_cache,
inc_file_li: false,
inc_all_file_li: false,
inc_presentation_li: false,
inc_presenter_li: false,
enabled,
hidden,
limit,
offset,
order_by_li,
try_cache,
log_lvl: log_lvl > 1 ? log_lvl : 0
});
return cached_li;
} else if (log_lvl) {
console.log(`⏳ [Trace] load_ae_obj_li: CACHE MISS at ${(performance.now() - start_time).toFixed(2)}ms for type=${for_obj_type} id=${for_obj_id}`);
console.log(
`⏳ [Trace] load_ae_obj_li: CACHE MISS at ${(performance.now() - start_time).toFixed(2)}ms for type=${for_obj_type} id=${for_obj_id}`
);
}
} catch (e) {
if (log_lvl) console.error(`❌ [Trace] load_ae_obj_li: Cache access error:`, e);
if (log_lvl)
console.error(
`❌ [Trace] load_ae_obj_li: Cache access error:`,
e
);
}
}
return await _refresh_session_li_background({ api_cfg, for_obj_type, for_obj_id, view, inc_file_li, inc_all_file_li, inc_presentation_li, inc_presenter_li, enabled, hidden, limit, offset, order_by_li, try_cache, log_lvl });
return await _refresh_session_li_background({
api_cfg,
for_obj_type,
for_obj_id,
view,
inc_file_li,
inc_all_file_li,
inc_presentation_li,
inc_presenter_li,
enabled,
hidden,
limit,
offset,
order_by_li,
try_cache,
log_lvl
});
}
async function _refresh_session_li_background({ api_cfg, for_obj_type, for_obj_id, view, inc_file_li, inc_all_file_li, inc_presentation_li, inc_presenter_li, enabled, hidden, limit, offset, order_by_li, try_cache, log_lvl }: any) {
async function _refresh_session_li_background({
api_cfg,
for_obj_type,
for_obj_id,
view,
inc_file_li,
inc_all_file_li,
inc_presentation_li,
inc_presenter_li,
enabled,
hidden,
limit,
offset,
order_by_li,
try_cache,
log_lvl
}: any) {
const start_time = performance.now();
if (typeof navigator !== 'undefined' && !navigator.onLine) return [];
try {
if (log_lvl) console.log(`📡 [Trace] _refresh_session_li: API Fetching for=${for_obj_type}:${for_obj_id} (view=${view})`);
const result_li = await api.get_ae_obj_li({ api_cfg, obj_type: 'event_session', for_obj_type, for_obj_id, view, enabled, hidden, limit, offset, order_by_li, log_lvl });
if (log_lvl)
console.log(
`📡 [Trace] _refresh_session_li: API Fetching for=${for_obj_type}:${for_obj_id} (view=${view})`
);
const result_li = await api.get_ae_obj_li({
api_cfg,
obj_type: 'event_session',
for_obj_type,
for_obj_id,
view,
enabled,
hidden,
limit,
offset,
order_by_li,
log_lvl
});
if (result_li) {
const processed = await process_ae_obj__event_session_props({ obj_li: result_li, log_lvl });
const processed = await process_ae_obj__event_session_props({
obj_li: result_li,
log_lvl
});
const elapsed = (performance.now() - start_time).toFixed(2);
if (log_lvl) console.log(`📦 [Trace] _refresh_session_li: Received ${processed.length} items from API at ${elapsed}ms.`);
if (log_lvl)
console.log(
`📦 [Trace] _refresh_session_li: Received ${processed.length} items from API at ${elapsed}ms.`
);
if (try_cache) {
await db_save_ae_obj_li__ae_obj({ db_instance: db_events, table_name: 'session', obj_li: processed, properties_to_save, log_lvl });
if (log_lvl) console.log(`💾 [Trace] _refresh_session_li: Saved to IDB cache.`);
await db_save_ae_obj_li__ae_obj({
db_instance: db_events,
table_name: 'session',
obj_li: processed,
properties_to_save,
log_lvl
});
if (log_lvl)
console.log(
`💾 [Trace] _refresh_session_li: Saved to IDB cache.`
);
}
// Fire nested loads for each session only if explicitly requested (usually only for single objects)
if (inc_file_li || inc_presentation_li) {
processed.forEach(s => {
_handle_nested_loads(s, { api_cfg, inc_file_li, inc_all_file_li, inc_presentation_li, inc_presenter_li, enabled, hidden, limit, offset, try_cache, log_lvl: 0 });
processed.forEach((s) => {
_handle_nested_loads(s, {
api_cfg,
inc_file_li,
inc_all_file_li,
inc_presentation_li,
inc_presenter_li,
enabled,
hidden,
limit,
offset,
try_cache,
log_lvl: 0
});
});
}
return processed;
}
} catch (e) {
if (log_lvl) console.error(`❌ [Trace] _refresh_session_li: API error:`, e);
if (log_lvl)
console.error(`❌ [Trace] _refresh_session_li: API error:`, e);
}
return [];
}
export async function create_ae_obj__event_session({
api_cfg,
event_id,
data_kv,
try_cache = true,
log_lvl = 0
api_cfg,
event_id,
data_kv,
try_cache = true,
log_lvl = 0
}: {
api_cfg: any;
event_id?: string;
data_kv: key_val;
try_cache?: boolean;
log_lvl?: number;
api_cfg: any;
event_id?: string;
data_kv: key_val;
try_cache?: boolean;
log_lvl?: number;
}): Promise<ae_EventSession | null> {
if (!event_id) event_id = get(slct).event_id;
if (!event_id) {
console.error('create_ae_obj__event_session: event_id is required');
return null;
}
const result = await api.create_nested_obj({
api_cfg,
for_obj_type: 'event',
for_obj_id: event_id,
obj_type: 'event_session',
fields: { ...data_kv },
log_lvl
});
if (result) {
const processed = await process_ae_obj__event_session_props({ obj_li: [result], log_lvl });
const processed_obj = processed[0];
if (try_cache) {
await db_save_ae_obj_li__ae_obj({
db_instance: db_events,
table_name: 'session',
obj_li: [processed_obj],
properties_to_save,
log_lvl
});
}
return processed_obj;
}
return null;
const result = await api.create_nested_obj({
api_cfg,
for_obj_type: 'event',
for_obj_id: event_id,
obj_type: 'event_session',
fields: { ...data_kv },
log_lvl
});
if (result) {
const processed = await process_ae_obj__event_session_props({
obj_li: [result],
log_lvl
});
const processed_obj = processed[0];
if (try_cache) {
await db_save_ae_obj_li__ae_obj({
db_instance: db_events,
table_name: 'session',
obj_li: [processed_obj],
properties_to_save,
log_lvl
});
}
return processed_obj;
}
return null;
}
export async function delete_ae_obj_id__event_session({
api_cfg,
event_id,
event_session_id,
method = 'delete',
try_cache = true,
log_lvl = 0
api_cfg,
event_id,
event_session_id,
method = 'delete',
try_cache = true,
log_lvl = 0
}: {
api_cfg: any;
event_id?: string;
event_session_id: string;
method?: 'delete' | 'soft_delete' | 'disable' | 'hide';
try_cache?: boolean;
log_lvl?: number;
api_cfg: any;
event_id?: string;
event_session_id: string;
method?: 'delete' | 'soft_delete' | 'disable' | 'hide';
try_cache?: boolean;
log_lvl?: number;
}) {
if (!event_id) event_id = get(slct).event_id;
if (!event_id) {
console.error('delete_ae_obj_id__event_session: event_id is required');
return null;
}
const result = await api.delete_nested_ae_obj({
api_cfg,
for_obj_type: 'event',
for_obj_id: event_id,
obj_type: 'event_session',
obj_id: event_session_id,
method,
log_lvl
});
if (try_cache) await db_events.session.delete(event_session_id);
return result;
const result = await api.delete_nested_ae_obj({
api_cfg,
for_obj_type: 'event',
for_obj_id: event_id,
obj_type: 'event_session',
obj_id: event_session_id,
method,
log_lvl
});
if (try_cache) await db_events.session.delete(event_session_id);
return result;
}
export async function update_ae_obj__event_session({
api_cfg,
event_id,
event_session_id,
data_kv,
try_cache = true,
log_lvl = 0
api_cfg,
event_id,
event_session_id,
data_kv,
try_cache = true,
log_lvl = 0
}: {
api_cfg: any;
event_id?: string;
event_session_id: string;
data_kv: key_val;
try_cache?: boolean;
log_lvl?: number;
api_cfg: any;
event_id?: string;
event_session_id: string;
data_kv: key_val;
try_cache?: boolean;
log_lvl?: number;
}): Promise<ae_EventSession | null> {
if (!event_id) event_id = get(slct).event_id;
if (!event_id) {
console.error('update_ae_obj__event_session: event_id is required');
return null;
}
const result = await api.update_nested_obj({
api_cfg,
for_obj_type: 'event',
for_obj_id: event_id,
obj_type: 'event_session',
obj_id: event_session_id,
fields: data_kv,
log_lvl
});
if (result) {
const processed = await process_ae_obj__event_session_props({ obj_li: [result], log_lvl });
const processed_obj = processed[0];
if (try_cache) {
await db_save_ae_obj_li__ae_obj({
db_instance: db_events,
table_name: 'session',
obj_li: [processed_obj],
properties_to_save,
log_lvl
});
}
return processed_obj;
}
return null;
const result = await api.update_nested_obj({
api_cfg,
for_obj_type: 'event',
for_obj_id: event_id,
obj_type: 'event_session',
obj_id: event_session_id,
fields: data_kv,
log_lvl
});
if (result) {
const processed = await process_ae_obj__event_session_props({
obj_li: [result],
log_lvl
});
const processed_obj = processed[0];
if (try_cache) {
await db_save_ae_obj_li__ae_obj({
db_instance: db_events,
table_name: 'session',
obj_li: [processed_obj],
properties_to_save,
log_lvl
});
}
return processed_obj;
}
return null;
}
export async function search__event_session({
api_cfg, event_id, fulltext_search_qry_str = '', ft_presenter_search_qry_str = '', like_search_qry_str = '', like_presentation_search_qry_str = '', like_presenter_search_qry_str = '', like_poc_name_qry_str = '', location_name = null, qry_files = null, qry_poc_agree = null, qry_poc_kv_json = null, qry_start_datetime = null, enabled = 'enabled', hidden = 'not_hidden', view = 'default', limit = 50, offset = 0, order_by_li = [{ sort: 'ASC' }, { start_datetime: 'ASC' }, { name: 'ASC' }], try_cache = true, log_lvl = 0
api_cfg,
event_id,
fulltext_search_qry_str = '',
ft_presenter_search_qry_str = '',
like_search_qry_str = '',
like_presentation_search_qry_str = '',
like_presenter_search_qry_str = '',
like_poc_name_qry_str = '',
location_name = null,
qry_files = null,
qry_poc_agree = null,
qry_poc_kv_json = null,
qry_start_datetime = null,
enabled = 'enabled',
hidden = 'not_hidden',
view = 'default',
limit = 50,
offset = 0,
order_by_li = [{ sort: 'ASC' }, { start_datetime: 'ASC' }, { name: 'ASC' }],
try_cache = true,
log_lvl = 0
}: {
api_cfg: any; event_id: string; fulltext_search_qry_str?: string; ft_presenter_search_qry_str?: string | null; like_search_qry_str?: string; like_presentation_search_qry_str?: string; like_presenter_search_qry_str?: string; like_poc_name_qry_str?: string; location_name?: null | string; qry_files?: null | boolean; qry_poc_agree?: null | boolean; qry_poc_kv_json?: null | boolean; qry_start_datetime?: string | null; enabled?: 'enabled' | 'all' | 'not_enabled'; hidden?: 'hidden' | 'all' | 'not_hidden'; view?: string; limit?: number; offset?: number; order_by_li?: any; try_cache?: boolean; log_lvl?: number;
api_cfg: any;
event_id: string;
fulltext_search_qry_str?: string;
ft_presenter_search_qry_str?: string | null;
like_search_qry_str?: string;
like_presentation_search_qry_str?: string;
like_presenter_search_qry_str?: string;
like_poc_name_qry_str?: string;
location_name?: null | string;
qry_files?: null | boolean;
qry_poc_agree?: null | boolean;
qry_poc_kv_json?: null | boolean;
qry_start_datetime?: string | null;
enabled?: 'enabled' | 'all' | 'not_enabled';
hidden?: 'hidden' | 'all' | 'not_hidden';
view?: string;
limit?: number;
offset?: number;
order_by_li?: any;
try_cache?: boolean;
log_lvl?: number;
}): Promise<ae_EventSession[]> {
const search_query: any = { q: '', and: [{ field: 'event_id', op: 'eq', value: event_id }] };
const search_query: any = {
q: '',
and: [{ field: 'event_id', op: 'eq', value: event_id }]
};
if (fulltext_search_qry_str || ft_presenter_search_qry_str) {
const ft: any = {};
if (fulltext_search_qry_str && fulltext_search_qry_str.length > 2) ft['default_qry_str'] = fulltext_search_qry_str;
if (ft_presenter_search_qry_str && ft_presenter_search_qry_str.length > 2) ft['event_presenter_li_qry_str'] = ft_presenter_search_qry_str;
if (fulltext_search_qry_str && fulltext_search_qry_str.length > 2)
ft['default_qry_str'] = fulltext_search_qry_str;
if (
ft_presenter_search_qry_str &&
ft_presenter_search_qry_str.length > 2
)
ft['event_presenter_li_qry_str'] = ft_presenter_search_qry_str;
if (Object.keys(ft).length) search_query.params = { ft_qry: ft };
}
if (enabled === 'enabled') search_query.and.push({ field: 'enable', op: 'eq', value: 1 });
else if (enabled === 'not_enabled') search_query.and.push({ field: 'enable', op: 'eq', value: 0 });
if (hidden === 'hidden') search_query.and.push({ field: 'hide', op: 'eq', value: 1 });
else if (hidden === 'not_hidden') search_query.and.push({ field: 'hide', op: 'eq', value: 0 });
if (enabled === 'enabled')
search_query.and.push({ field: 'enable', op: 'eq', value: 1 });
else if (enabled === 'not_enabled')
search_query.and.push({ field: 'enable', op: 'eq', value: 0 });
if (hidden === 'hidden')
search_query.and.push({ field: 'hide', op: 'eq', value: 1 });
else if (hidden === 'not_hidden')
search_query.and.push({ field: 'hide', op: 'eq', value: 0 });
if (location_name) {
search_query.and.push({ field: 'event_location_name', op: 'eq', value: location_name });
search_query.and.push({
field: 'event_location_name',
op: 'eq',
value: location_name
});
}
const result_li = await api.search_ae_obj({ api_cfg, obj_type: 'event_session', search_query, order_by_li, view, limit, offset, log_lvl });
const result_li = await api.search_ae_obj({
api_cfg,
obj_type: 'event_session',
search_query,
order_by_li,
view,
limit,
offset,
log_lvl
});
// Handle V3 API envelope
let valid_result_li: ae_EventSession[] = [];
if (Array.isArray(result_li)) {
valid_result_li = result_li;
} else if (result_li && typeof result_li === 'object' && Array.isArray((result_li as any).data)) {
} else if (
result_li &&
typeof result_li === 'object' &&
Array.isArray((result_li as any).data)
) {
valid_result_li = (result_li as any).data;
}
if (valid_result_li && valid_result_li.length > 0) {
const processed = await process_ae_obj__event_session_props({ obj_li: valid_result_li, log_lvl });
const processed = await process_ae_obj__event_session_props({
obj_li: valid_result_li,
log_lvl
});
if (try_cache) {
await db_save_ae_obj_li__ae_obj({ db_instance: db_events, table_name: 'session', obj_li: processed, properties_to_save, log_lvl });
await db_save_ae_obj_li__ae_obj({
db_instance: db_events,
table_name: 'session',
obj_li: processed,
properties_to_save,
log_lvl
});
}
return processed;
}
@@ -429,18 +754,103 @@ export async function search__event_session({
export const qry__event_session = search__event_session;
export async function email_sign_in__event_session({ api_cfg, to_email, to_name, base_url, person_id, person_passcode, event_id, event_session_id, session_name }: { api_cfg: any; to_email: string; to_name: string; base_url: string; person_id: string; person_passcode: string; event_id: string; event_session_id: string; session_name: string; }) {
export async function email_sign_in__event_session({
api_cfg,
to_email,
to_name,
base_url,
person_id,
person_passcode,
event_id,
event_session_id,
session_name
}: {
api_cfg: any;
to_email: string;
to_name: string;
base_url: string;
person_id: string;
person_passcode: string;
event_id: string;
event_session_id: string;
session_name: string;
}) {
const subject = `Pres Mgmt Hub Sign In Link for ${session_name}`;
const sign_in_url = encodeURI(`${base_url}/events/${event_id}/session/${event_session_id}?person_id=${person_id}&person_pass=${person_passcode}`);
const sign_in_url = encodeURI(
`${base_url}/events/${event_id}/session/${event_session_id}?person_id=${person_id}&person_pass=${person_passcode}`
);
const body_html = `<div>${to_name},<p>Your sign-in link for ${session_name}: <a href="${sign_in_url}">${sign_in_url}</a></p></div>`;
return await api.send_email({ api_cfg, from_email: 'noreply+presmgmt@oneskyit.com', from_name: 'Aether Pres Mgmt', to_email, subject, body_html });
return await api.send_email({
api_cfg,
from_email: 'noreply+presmgmt@oneskyit.com',
from_name: 'Aether Pres Mgmt',
to_email,
subject,
body_html
});
}
export const properties_to_save = [
'id', 'event_session_id', 'event_session_id_random', 'external_id', 'code', 'for_type', 'for_id', 'for_id_random', 'type_code', 'event_id', 'event_location_id', 'poc_person_id', 'poc_agree', 'poc_kv_json', 'name', 'description', 'start_datetime', 'end_datetime', 'passcode', 'hide_event_launcher', 'alert', 'alert_msg', 'data_json', 'ux_mode', 'enable', 'hide', 'priority', 'sort', 'group', 'notes', 'created_on', 'updated_on', 'tmp_sort_1', 'tmp_sort_2', 'file_count', 'file_count_all', 'internal_use_count', 'event_file_id_li_json', 'poc_person_given_name', 'poc_person_family_name', 'poc_person_full_name', 'poc_person_primary_email', 'poc_person_passcode', 'event_name', 'event_location_code', 'event_location_name', 'event_presentation_li'
'id',
'event_session_id',
'event_session_id_random',
'external_id',
'code',
'for_type',
'for_id',
'for_id_random',
'type_code',
'event_id',
'event_location_id',
'poc_person_id',
'poc_agree',
'poc_kv_json',
'name',
'description',
'start_datetime',
'end_datetime',
'passcode',
'hide_event_launcher',
'alert',
'alert_msg',
'data_json',
'ux_mode',
'enable',
'hide',
'priority',
'sort',
'group',
'notes',
'created_on',
'updated_on',
'tmp_sort_1',
'tmp_sort_2',
'file_count',
'file_count_all',
'internal_use_count',
'event_file_id_li_json',
'poc_person_given_name',
'poc_person_family_name',
'poc_person_full_name',
'poc_person_primary_email',
'poc_person_passcode',
'event_name',
'event_location_code',
'event_location_name',
'event_presentation_li'
];
async function _process_generic_props<T extends Record<string, any>>({ obj_li, obj_type, log_lvl = 0, specific_processor }: { obj_li: T[]; obj_type: string; log_lvl?: number; specific_processor?: (obj: T) => Promise<T> | T; }): Promise<T[]> {
async function _process_generic_props<T extends Record<string, any>>({
obj_li,
obj_type,
log_lvl = 0,
specific_processor
}: {
obj_li: T[];
obj_type: string;
log_lvl?: number;
specific_processor?: (obj: T) => Promise<T> | T;
}): Promise<T[]> {
if (!obj_li || obj_li.length === 0) return [];
const processed_obj_li: T[] = [];
for (const original_obj of obj_li) {
@@ -456,22 +866,40 @@ async function _process_generic_props<T extends Record<string, any>>({ obj_li, o
if (processed_obj[randomIdKey]) {
(processed_obj as any).id = processed_obj[randomIdKey];
(processed_obj as any)[baseIdKey] = processed_obj[randomIdKey];
}
else if (processed_obj[baseIdKey]) (processed_obj as any).id = processed_obj[baseIdKey];
} else if (processed_obj[baseIdKey])
(processed_obj as any).id = processed_obj[baseIdKey];
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}`;
if (specific_processor) processed_obj = await Promise.resolve(specific_processor(processed_obj));
(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_li.push(processed_obj as T);
}
return processed_obj_li;
}
export async function process_ae_obj__event_session_props({ obj_li, log_lvl = 0 }: { obj_li: any[]; log_lvl?: number; }) {
return _process_generic_props({ obj_li, obj_type: 'event_session', log_lvl });
}
export async function process_ae_obj__event_session_props({
obj_li,
log_lvl = 0
}: {
obj_li: any[];
log_lvl?: number;
}) {
return _process_generic_props({
obj_li,
obj_type: 'event_session',
log_lvl
});
}

View File

@@ -24,4 +24,4 @@ export async function load_ae_obj_li__event_track({
view,
log_lvl
});
}
}

View File

@@ -17,4 +17,4 @@ export const editable_fields__event_exhibit = [
'sort',
'group',
'notes'
];
];

View File

@@ -92,11 +92,15 @@ async function _process_generic_props<T extends Record<string, any>>({
(processed_obj as any).description = '';
}
(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);
@@ -137,7 +141,9 @@ export async function load_ae_obj_id__event_exhibit({
}): Promise<ae_EventExhibit | null> {
const start_time = performance.now();
if (log_lvl) {
console.log(`🔎 [Trace] load_ae_obj_id__event_exhibit: START (id=${exhibit_id}, try_cache=${try_cache})`);
console.log(
`🔎 [Trace] load_ae_obj_id__event_exhibit: START (id=${exhibit_id}, try_cache=${try_cache})`
);
}
// 1. FAST PATH: Return cached data immediately
@@ -146,34 +152,74 @@ export async function load_ae_obj_id__event_exhibit({
const cached = await db_events.exhibit.get(exhibit_id);
if (cached) {
const elapsed = (performance.now() - start_time).toFixed(2);
if (log_lvl) console.log(`✅ [Trace] load_ae_obj_id: CACHE HIT at ${elapsed}ms. Returning stale exhibit shell.`);
if (log_lvl)
console.log(
`✅ [Trace] load_ae_obj_id: CACHE HIT at ${elapsed}ms. Returning stale exhibit shell.`
);
// Background refresh (non-blocking)
_refresh_exhibit_id_background({ api_cfg, exhibit_id, view, try_cache, log_lvl: log_lvl > 1 ? log_lvl : 0 });
_refresh_exhibit_id_background({
api_cfg,
exhibit_id,
view,
try_cache,
log_lvl: log_lvl > 1 ? log_lvl : 0
});
return cached;
}
} catch (e) {
if (log_lvl) console.error(`❌ [Trace] load_ae_obj_id: Cache access error:`, e);
if (log_lvl)
console.error(
`❌ [Trace] load_ae_obj_id: Cache access error:`,
e
);
}
}
// 2. SLOW PATH: Wait for API
return await _refresh_exhibit_id_background({ api_cfg, exhibit_id, view, try_cache, log_lvl });
return await _refresh_exhibit_id_background({
api_cfg,
exhibit_id,
view,
try_cache,
log_lvl
});
}
async function _refresh_exhibit_id_background({ api_cfg, exhibit_id, view, try_cache, log_lvl }: any) {
async function _refresh_exhibit_id_background({
api_cfg,
exhibit_id,
view,
try_cache,
log_lvl
}: any) {
const start_time = performance.now();
if (typeof navigator !== 'undefined' && !navigator.onLine) return null;
try {
if (log_lvl) console.log(`📡 [Trace] _refresh_exhibit_id: API Fetching id=${exhibit_id}`);
const result = await api.get_ae_obj({ api_cfg, obj_type: 'event_exhibit', obj_id: exhibit_id, view, log_lvl });
if (log_lvl)
console.log(
`📡 [Trace] _refresh_exhibit_id: API Fetching id=${exhibit_id}`
);
const result = await api.get_ae_obj({
api_cfg,
obj_type: 'event_exhibit',
obj_id: exhibit_id,
view,
log_lvl
});
if (result) {
const processed = await process_ae_obj__exhibit_props({ obj_li: [result], log_lvl });
const processed = await process_ae_obj__exhibit_props({
obj_li: [result],
log_lvl
});
const processed_obj = processed[0];
const elapsed = (performance.now() - start_time).toFixed(2);
if (log_lvl) console.log(`📦 [Trace] _refresh_exhibit_id: Received from API at ${elapsed}ms`);
if (log_lvl)
console.log(
`📦 [Trace] _refresh_exhibit_id: Received from API at ${elapsed}ms`
);
if (try_cache) {
await db_save_ae_obj_li__ae_obj({
@@ -187,7 +233,11 @@ async function _refresh_exhibit_id_background({ api_cfg, exhibit_id, view, try_c
return processed_obj;
}
} catch (e) {
if (log_lvl) console.error(`❌ [Trace] _refresh_exhibit_id: API error for id=${exhibit_id}:`, e);
if (log_lvl)
console.error(
`❌ [Trace] _refresh_exhibit_id: API error for id=${exhibit_id}:`,
e
);
}
return null;
}
@@ -225,36 +275,82 @@ export async function load_ae_obj_li__event_exhibit({
}): Promise<ae_EventExhibit[]> {
const start_time = performance.now();
if (log_lvl) {
console.log(`🔎 [Trace] load_ae_obj_li__event_exhibit: START (event=${event_id}, try_cache=${try_cache})`);
console.log(
`🔎 [Trace] load_ae_obj_li__event_exhibit: START (event=${event_id}, try_cache=${try_cache})`
);
}
if (try_cache) {
try {
const cached_li = await db_events.exhibit
.where('event_id').equals(event_id)
.where('event_id')
.equals(event_id)
.toArray();
if (cached_li && cached_li.length > 0) {
const elapsed = (performance.now() - start_time).toFixed(2);
if (log_lvl) console.log(`✅ [Trace] load_ae_obj_li: CACHE HIT at ${elapsed}ms (${cached_li.length} items).`);
if (log_lvl)
console.log(
`✅ [Trace] load_ae_obj_li: CACHE HIT at ${elapsed}ms (${cached_li.length} items).`
);
// Background refresh (non-blocking)
_refresh_exhibit_li_background({ api_cfg, event_id, enabled, hidden, view, limit, offset, order_by_li, try_cache, log_lvl: log_lvl > 1 ? log_lvl : 0 });
_refresh_exhibit_li_background({
api_cfg,
event_id,
enabled,
hidden,
view,
limit,
offset,
order_by_li,
try_cache,
log_lvl: log_lvl > 1 ? log_lvl : 0
});
return cached_li;
}
} catch (e) {
if (log_lvl) console.error(`❌ [Trace] load_ae_obj_li: Cache access error:`, e);
if (log_lvl)
console.error(
`❌ [Trace] load_ae_obj_li: Cache access error:`,
e
);
}
}
return await _refresh_exhibit_li_background({ api_cfg, event_id, enabled, hidden, view, limit, offset, order_by_li, try_cache, log_lvl });
return await _refresh_exhibit_li_background({
api_cfg,
event_id,
enabled,
hidden,
view,
limit,
offset,
order_by_li,
try_cache,
log_lvl
});
}
async function _refresh_exhibit_li_background({ api_cfg, event_id, enabled, hidden, view, limit, offset, order_by_li, try_cache, log_lvl }: any) {
async function _refresh_exhibit_li_background({
api_cfg,
event_id,
enabled,
hidden,
view,
limit,
offset,
order_by_li,
try_cache,
log_lvl
}: any) {
const start_time = performance.now();
if (typeof navigator !== 'undefined' && !navigator.onLine) return [];
try {
if (log_lvl) console.log(`📡 [Trace] _refresh_exhibit_li: API Fetching exhibits for event=${event_id}`);
if (log_lvl)
console.log(
`📡 [Trace] _refresh_exhibit_li: API Fetching exhibits for event=${event_id}`
);
const result_li = await api.get_ae_obj_li({
api_cfg,
obj_type: 'event_exhibit',
@@ -270,9 +366,15 @@ async function _refresh_exhibit_li_background({ api_cfg, event_id, enabled, hidd
});
if (result_li) {
const processed = await process_ae_obj__exhibit_props({ obj_li: result_li, log_lvl });
const processed = await process_ae_obj__exhibit_props({
obj_li: result_li,
log_lvl
});
const elapsed = (performance.now() - start_time).toFixed(2);
if (log_lvl) console.log(`📦 [Trace] _refresh_exhibit_li: Received ${processed.length} items from API at ${elapsed}ms.`);
if (log_lvl)
console.log(
`📦 [Trace] _refresh_exhibit_li: Received ${processed.length} items from API at ${elapsed}ms.`
);
if (try_cache) {
await db_save_ae_obj_li__ae_obj({
@@ -286,7 +388,8 @@ async function _refresh_exhibit_li_background({ api_cfg, event_id, enabled, hidd
return processed;
}
} catch (e) {
if (log_lvl) console.error(`❌ [Trace] _refresh_exhibit_li: API error:`, e);
if (log_lvl)
console.error(`❌ [Trace] _refresh_exhibit_li: API error:`, e);
}
return [];
}
@@ -295,87 +398,93 @@ async function _refresh_exhibit_li_background({ api_cfg, event_id, enabled, hidd
* Exhibit Create (V3)
*/
export async function create_ae_obj__exhibit({
api_cfg,
event_id,
data_kv,
try_cache = true,
log_lvl = 0
api_cfg,
event_id,
data_kv,
try_cache = true,
log_lvl = 0
}: {
api_cfg: any;
event_id: string;
data_kv: key_val;
try_cache?: boolean;
log_lvl?: number;
api_cfg: any;
event_id: string;
data_kv: key_val;
try_cache?: boolean;
log_lvl?: number;
}): Promise<ae_EventExhibit | null> {
const result = await api.create_nested_obj({
api_cfg,
parent_type: 'event',
parent_id: event_id,
child_type: 'event_exhibit',
fields: { ...data_kv },
log_lvl
});
const result = await api.create_nested_obj({
api_cfg,
parent_type: 'event',
parent_id: event_id,
child_type: 'event_exhibit',
fields: { ...data_kv },
log_lvl
});
if (result) {
const processed = await process_ae_obj__exhibit_props({ obj_li: [result], log_lvl });
const processed_obj = processed[0];
if (try_cache) {
await db_save_ae_obj_li__ae_obj({
db_instance: db_events,
table_name: 'exhibit',
obj_li: [processed_obj],
properties_to_save: properties_to_save_exhibit,
log_lvl
});
}
return processed_obj;
}
return null;
if (result) {
const processed = await process_ae_obj__exhibit_props({
obj_li: [result],
log_lvl
});
const processed_obj = processed[0];
if (try_cache) {
await db_save_ae_obj_li__ae_obj({
db_instance: db_events,
table_name: 'exhibit',
obj_li: [processed_obj],
properties_to_save: properties_to_save_exhibit,
log_lvl
});
}
return processed_obj;
}
return null;
}
/**
* Exhibit Update (V3)
*/
export async function update_ae_obj__exhibit({
api_cfg,
event_id,
exhibit_id,
data_kv,
try_cache = true,
log_lvl = 0
api_cfg,
event_id,
exhibit_id,
data_kv,
try_cache = true,
log_lvl = 0
}: {
api_cfg: any;
event_id: string;
exhibit_id: string;
data_kv: key_val;
try_cache?: boolean;
log_lvl?: number;
api_cfg: any;
event_id: string;
exhibit_id: string;
data_kv: key_val;
try_cache?: boolean;
log_lvl?: number;
}): Promise<ae_EventExhibit | null> {
const result = await api.update_nested_obj({
api_cfg,
parent_type: 'event',
parent_id: event_id,
child_type: 'event_exhibit',
child_id: exhibit_id,
fields: data_kv,
log_lvl
});
const result = await api.update_nested_obj({
api_cfg,
parent_type: 'event',
parent_id: event_id,
child_type: 'event_exhibit',
child_id: exhibit_id,
fields: data_kv,
log_lvl
});
if (result) {
const processed = await process_ae_obj__exhibit_props({ obj_li: [result], log_lvl });
const processed_obj = processed[0];
if (try_cache) {
await db_save_ae_obj_li__ae_obj({
db_instance: db_events,
table_name: 'exhibit',
obj_li: [processed_obj],
properties_to_save: properties_to_save_exhibit,
log_lvl
});
}
return processed_obj;
}
return null;
if (result) {
const processed = await process_ae_obj__exhibit_props({
obj_li: [result],
log_lvl
});
const processed_obj = processed[0];
if (try_cache) {
await db_save_ae_obj_li__ae_obj({
db_instance: db_events,
table_name: 'exhibit',
obj_li: [processed_obj],
properties_to_save: properties_to_save_exhibit,
log_lvl
});
}
return processed_obj;
}
return null;
}
/**
@@ -409,24 +518,30 @@ export async function search__exhibit({
log_lvl?: number;
}): Promise<ae_EventExhibit[]> {
if (log_lvl) {
console.log(`*** search__exhibit() *** event_id=${event_id} ft=${fulltext_search_qry_str}`);
console.log(
`*** search__exhibit() *** event_id=${event_id} ft=${fulltext_search_qry_str}`
);
}
const search_query: any = {
q: fulltext_search_qry_str || '',
and: [
{ field: 'event_id', op: 'eq', value: event_id }
]
and: [{ field: 'event_id', op: 'eq', value: event_id }]
};
if (enabled === 'enabled') search_query.and.push({ field: 'enable', op: 'eq', value: 1 });
else if (enabled === 'not_enabled') search_query.and.push({ field: 'enable', op: 'eq', value: 0 });
if (enabled === 'enabled')
search_query.and.push({ field: 'enable', op: 'eq', value: 1 });
else if (enabled === 'not_enabled')
search_query.and.push({ field: 'enable', op: 'eq', value: 0 });
if (hidden === 'hidden') search_query.and.push({ field: 'hide', op: 'eq', value: 1 });
else if (hidden === 'not_hidden') search_query.and.push({ field: 'hide', op: 'eq', value: 0 });
if (hidden === 'hidden')
search_query.and.push({ field: 'hide', op: 'eq', value: 1 });
else if (hidden === 'not_hidden')
search_query.and.push({ field: 'hide', op: 'eq', value: 0 });
if (priority === 'priority') search_query.and.push({ field: 'priority', op: 'eq', value: 1 });
else if (priority === 'not_priority') search_query.and.push({ field: 'priority', op: 'eq', value: 0 });
if (priority === 'priority')
search_query.and.push({ field: 'priority', op: 'eq', value: 1 });
else if (priority === 'not_priority')
search_query.and.push({ field: 'priority', op: 'eq', value: 0 });
try {
const result_li = await api.search_ae_obj({
@@ -443,7 +558,10 @@ export async function search__exhibit({
});
if (result_li && Array.isArray(result_li)) {
const processed = await process_ae_obj__exhibit_props({ obj_li: result_li, log_lvl });
const processed = await process_ae_obj__exhibit_props({
obj_li: result_li,
log_lvl
});
if (try_cache) {
await db_save_ae_obj_li__ae_obj({
db_instance: db_events,
@@ -460,4 +578,4 @@ export async function search__exhibit({
}
return [];
}
}

View File

@@ -106,11 +106,15 @@ async function _process_generic_props<T extends Record<string, any>>({
(processed_obj as any).exhibitor_notes = '';
}
(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);
@@ -151,43 +155,86 @@ export async function load_ae_obj_id__event_exhibit_tracking({
}): Promise<ae_EventExhibitTracking | null> {
const start_time = performance.now();
if (log_lvl) {
console.log(`🔎 [Trace] load_ae_obj_id__event_exhibit_tracking: START (id=${exhibit_tracking_id}, try_cache=${try_cache})`);
console.log(
`🔎 [Trace] load_ae_obj_id__event_exhibit_tracking: START (id=${exhibit_tracking_id}, try_cache=${try_cache})`
);
}
// 1. FAST PATH: Return cached data immediately
if (try_cache) {
try {
const cached = await db_events.exhibit_tracking.get(exhibit_tracking_id);
const cached =
await db_events.exhibit_tracking.get(exhibit_tracking_id);
if (cached) {
const elapsed = (performance.now() - start_time).toFixed(2);
if (log_lvl) console.log(`✅ [Trace] load_ae_obj_id: CACHE HIT at ${elapsed}ms. Returning stale lead shell.`);
if (log_lvl)
console.log(
`✅ [Trace] load_ae_obj_id: CACHE HIT at ${elapsed}ms. Returning stale lead shell.`
);
// Background refresh (non-blocking)
_refresh_tracking_id_background({ api_cfg, exhibit_tracking_id, view, try_cache, log_lvl: log_lvl > 1 ? log_lvl : 0 });
_refresh_tracking_id_background({
api_cfg,
exhibit_tracking_id,
view,
try_cache,
log_lvl: log_lvl > 1 ? log_lvl : 0
});
return cached;
}
} catch (e) {
if (log_lvl) console.error(`❌ [Trace] load_ae_obj_id: Cache access error:`, e);
if (log_lvl)
console.error(
`❌ [Trace] load_ae_obj_id: Cache access error:`,
e
);
}
}
// 2. SLOW PATH: Wait for API
return await _refresh_tracking_id_background({ api_cfg, exhibit_tracking_id, view, try_cache, log_lvl });
return await _refresh_tracking_id_background({
api_cfg,
exhibit_tracking_id,
view,
try_cache,
log_lvl
});
}
async function _refresh_tracking_id_background({ api_cfg, exhibit_tracking_id, view, try_cache, log_lvl }: any) {
async function _refresh_tracking_id_background({
api_cfg,
exhibit_tracking_id,
view,
try_cache,
log_lvl
}: any) {
const start_time = performance.now();
if (typeof navigator !== 'undefined' && !navigator.onLine) return null;
try {
if (log_lvl) console.log(`📡 [Trace] _refresh_tracking_id: API Fetching id=${exhibit_tracking_id}`);
const result = await api.get_ae_obj({ api_cfg, obj_type: 'event_exhibit_tracking', obj_id: exhibit_tracking_id, view, log_lvl });
if (log_lvl)
console.log(
`📡 [Trace] _refresh_tracking_id: API Fetching id=${exhibit_tracking_id}`
);
const result = await api.get_ae_obj({
api_cfg,
obj_type: 'event_exhibit_tracking',
obj_id: exhibit_tracking_id,
view,
log_lvl
});
if (result) {
const processed = await process_ae_obj__exhibit_tracking_props({ obj_li: [result], log_lvl });
const processed = await process_ae_obj__exhibit_tracking_props({
obj_li: [result],
log_lvl
});
const processed_obj = processed[0];
const elapsed = (performance.now() - start_time).toFixed(2);
if (log_lvl) console.log(`📦 [Trace] _refresh_tracking_id: Received from API at ${elapsed}ms`);
if (log_lvl)
console.log(
`📦 [Trace] _refresh_tracking_id: Received from API at ${elapsed}ms`
);
if (try_cache) {
await db_save_ae_obj_li__ae_obj({
@@ -201,7 +248,11 @@ async function _refresh_tracking_id_background({ api_cfg, exhibit_tracking_id, v
return processed_obj;
}
} catch (e) {
if (log_lvl) console.error(`❌ [Trace] _refresh_tracking_id: API error for id=${exhibit_tracking_id}:`, e);
if (log_lvl)
console.error(
`❌ [Trace] _refresh_tracking_id: API error for id=${exhibit_tracking_id}:`,
e
);
}
return null;
}
@@ -238,36 +289,82 @@ export async function load_ae_obj_li__event_exhibit_tracking({
}): Promise<ae_EventExhibitTracking[]> {
const start_time = performance.now();
if (log_lvl) {
console.log(`🔎 [Trace] load_ae_obj_li__event_exhibit_tracking: START (exhibit=${exhibit_id}, try_cache=${try_cache})`);
console.log(
`🔎 [Trace] load_ae_obj_li__event_exhibit_tracking: START (exhibit=${exhibit_id}, try_cache=${try_cache})`
);
}
if (try_cache) {
try {
const cached_li = await db_events.exhibit_tracking
.where('event_exhibit_id').equals(exhibit_id)
.where('event_exhibit_id')
.equals(exhibit_id)
.toArray();
if (cached_li && cached_li.length > 0) {
const elapsed = (performance.now() - start_time).toFixed(2);
if (log_lvl) console.log(`✅ [Trace] load_ae_obj_li: CACHE HIT at ${elapsed}ms (${cached_li.length} items).`);
if (log_lvl)
console.log(
`✅ [Trace] load_ae_obj_li: CACHE HIT at ${elapsed}ms (${cached_li.length} items).`
);
// Background refresh (non-blocking)
_refresh_tracking_li_background({ api_cfg, exhibit_id, enabled, hidden, view, limit, offset, order_by_li, try_cache, log_lvl: log_lvl > 1 ? log_lvl : 0 });
_refresh_tracking_li_background({
api_cfg,
exhibit_id,
enabled,
hidden,
view,
limit,
offset,
order_by_li,
try_cache,
log_lvl: log_lvl > 1 ? log_lvl : 0
});
return cached_li;
}
} catch (e) {
if (log_lvl) console.error(`❌ [Trace] load_ae_obj_li: Cache access error:`, e);
if (log_lvl)
console.error(
`❌ [Trace] load_ae_obj_li: Cache access error:`,
e
);
}
}
return await _refresh_tracking_li_background({ api_cfg, exhibit_id, enabled, hidden, view, limit, offset, order_by_li, try_cache, log_lvl });
return await _refresh_tracking_li_background({
api_cfg,
exhibit_id,
enabled,
hidden,
view,
limit,
offset,
order_by_li,
try_cache,
log_lvl
});
}
async function _refresh_tracking_li_background({ api_cfg, exhibit_id, enabled, hidden, view, limit, offset, order_by_li, try_cache, log_lvl }: any) {
async function _refresh_tracking_li_background({
api_cfg,
exhibit_id,
enabled,
hidden,
view,
limit,
offset,
order_by_li,
try_cache,
log_lvl
}: any) {
const start_time = performance.now();
if (typeof navigator !== 'undefined' && !navigator.onLine) return [];
try {
if (log_lvl) console.log(`📡 [Trace] _refresh_tracking_li: API Fetching leads for exhibit=${exhibit_id}`);
if (log_lvl)
console.log(
`📡 [Trace] _refresh_tracking_li: API Fetching leads for exhibit=${exhibit_id}`
);
const result_li = await api.get_ae_obj_li({
api_cfg,
obj_type: 'event_exhibit_tracking',
@@ -283,9 +380,15 @@ async function _refresh_tracking_li_background({ api_cfg, exhibit_id, enabled, h
});
if (result_li) {
const processed = await process_ae_obj__exhibit_tracking_props({ obj_li: result_li, log_lvl });
const processed = await process_ae_obj__exhibit_tracking_props({
obj_li: result_li,
log_lvl
});
const elapsed = (performance.now() - start_time).toFixed(2);
if (log_lvl) console.log(`📦 [Trace] _refresh_tracking_li: Received ${processed.length} items from API at ${elapsed}ms.`);
if (log_lvl)
console.log(
`📦 [Trace] _refresh_tracking_li: Received ${processed.length} items from API at ${elapsed}ms.`
);
if (try_cache) {
await db_save_ae_obj_li__ae_obj({
@@ -299,7 +402,8 @@ async function _refresh_tracking_li_background({ api_cfg, exhibit_id, enabled, h
return processed;
}
} catch (e) {
if (log_lvl) console.error(`❌ [Trace] _refresh_tracking_li: API error:`, e);
if (log_lvl)
console.error(`❌ [Trace] _refresh_tracking_li: API error:`, e);
}
return [];
}
@@ -308,95 +412,101 @@ async function _refresh_tracking_li_background({ api_cfg, exhibit_id, enabled, h
* Lead Capture (V3)
*/
export async function create_ae_obj__exhibit_tracking({
api_cfg,
exhibit_id,
event_badge_id,
external_person_id,
group,
try_cache = true,
log_lvl = 0
api_cfg,
exhibit_id,
event_badge_id,
external_person_id,
group,
try_cache = true,
log_lvl = 0
}: {
api_cfg: any;
exhibit_id: string;
event_badge_id: string;
external_person_id: string;
group?: string;
try_cache?: boolean;
log_lvl?: number;
api_cfg: any;
exhibit_id: string;
event_badge_id: string;
external_person_id: string;
group?: string;
try_cache?: boolean;
log_lvl?: number;
}): Promise<ae_EventExhibitTracking | null> {
const result = await api.create_nested_obj({
api_cfg,
parent_type: 'event_exhibit',
parent_id: exhibit_id,
child_type: 'event_exhibit_tracking',
fields: {
event_badge_id: event_badge_id,
external_person_id,
group
},
log_lvl
});
const result = await api.create_nested_obj({
api_cfg,
parent_type: 'event_exhibit',
parent_id: exhibit_id,
child_type: 'event_exhibit_tracking',
fields: {
event_badge_id: event_badge_id,
external_person_id,
group
},
log_lvl
});
if (result) {
const processed = await process_ae_obj__exhibit_tracking_props({ obj_li: [result], log_lvl });
const processed_obj = processed[0];
if (try_cache) {
await db_save_ae_obj_li__ae_obj({
db_instance: db_events,
table_name: 'exhibit_tracking',
obj_li: [processed_obj],
properties_to_save: properties_to_save_exhibit_tracking,
log_lvl
});
}
return processed_obj;
}
return null;
if (result) {
const processed = await process_ae_obj__exhibit_tracking_props({
obj_li: [result],
log_lvl
});
const processed_obj = processed[0];
if (try_cache) {
await db_save_ae_obj_li__ae_obj({
db_instance: db_events,
table_name: 'exhibit_tracking',
obj_li: [processed_obj],
properties_to_save: properties_to_save_exhibit_tracking,
log_lvl
});
}
return processed_obj;
}
return null;
}
/**
* Lead Update (V3)
*/
export async function update_ae_obj__exhibit_tracking({
api_cfg,
exhibit_id,
exhibit_tracking_id,
data,
try_cache = true,
log_lvl = 0
api_cfg,
exhibit_id,
exhibit_tracking_id,
data,
try_cache = true,
log_lvl = 0
}: {
api_cfg: any;
exhibit_id: string;
exhibit_tracking_id: string;
data: any;
try_cache?: boolean;
log_lvl?: number;
api_cfg: any;
exhibit_id: string;
exhibit_tracking_id: string;
data: any;
try_cache?: boolean;
log_lvl?: number;
}): Promise<ae_EventExhibitTracking | null> {
const result = await api.update_nested_obj({
api_cfg,
parent_type: 'event_exhibit',
parent_id: exhibit_id,
child_type: 'event_exhibit_tracking',
child_id: exhibit_tracking_id,
fields: data,
log_lvl
});
const result = await api.update_nested_obj({
api_cfg,
parent_type: 'event_exhibit',
parent_id: exhibit_id,
child_type: 'event_exhibit_tracking',
child_id: exhibit_tracking_id,
fields: data,
log_lvl
});
if (result) {
const processed = await process_ae_obj__exhibit_tracking_props({ obj_li: [result], log_lvl });
const processed_obj = processed[0];
if (try_cache) {
await db_save_ae_obj_li__ae_obj({
db_instance: db_events,
table_name: 'exhibit_tracking',
obj_li: [processed_obj],
properties_to_save: properties_to_save_exhibit_tracking,
log_lvl
});
}
return processed_obj;
}
return null;
if (result) {
const processed = await process_ae_obj__exhibit_tracking_props({
obj_li: [result],
log_lvl
});
const processed_obj = processed[0];
if (try_cache) {
await db_save_ae_obj_li__ae_obj({
db_instance: db_events,
table_name: 'exhibit_tracking',
obj_li: [processed_obj],
properties_to_save: properties_to_save_exhibit_tracking,
log_lvl
});
}
return processed_obj;
}
return null;
}
/**
@@ -428,13 +538,15 @@ export async function download_export__event_exhibit_tracking({
log_lvl?: number;
}) {
if (log_lvl) {
console.log(`*** download_export__event_exhibit_tracking() *** exhibit_id=${exhibit_id}`);
console.log(
`*** download_export__event_exhibit_tracking() *** exhibit_id=${exhibit_id}`
);
}
const endpoint = `/v3/action/event_exhibit/${exhibit_id}/tracking_export`;
const params: key_val = {
file_type,
return_file: 'true' // V3 convention: string 'true' for bool query flags
return_file: 'true' // V3 convention: string 'true' for bool query flags
};
return await api.get_object({
@@ -485,7 +597,9 @@ export async function search__exhibit_tracking({
log_lvl?: number;
}): Promise<ae_EventExhibitTracking[]> {
if (log_lvl) {
console.log(`*** search__exhibit_tracking() *** exhibit_id=${event_exhibit_id} ft=${fulltext_search_qry_str}`);
console.log(
`*** search__exhibit_tracking() *** exhibit_id=${event_exhibit_id} ft=${fulltext_search_qry_str}`
);
}
if (!event_id || !event_exhibit_id) return [];
@@ -498,15 +612,30 @@ export async function search__exhibit_tracking({
]
};
if (qry_group) search_query.and.push({ field: 'group', op: 'eq', value: qry_group });
if (qry_external_person_id) search_query.and.push({ field: 'external_person_id', op: 'eq', value: qry_external_person_id });
if (qry_badge_id) search_query.and.push({ field: 'event_badge_id', op: 'eq', value: qry_badge_id });
if (qry_group)
search_query.and.push({ field: 'group', op: 'eq', value: qry_group });
if (qry_external_person_id)
search_query.and.push({
field: 'external_person_id',
op: 'eq',
value: qry_external_person_id
});
if (qry_badge_id)
search_query.and.push({
field: 'event_badge_id',
op: 'eq',
value: qry_badge_id
});
if (enabled === 'enabled') search_query.and.push({ field: 'enable', op: 'eq', value: 1 });
else if (enabled === 'not_enabled') search_query.and.push({ field: 'enable', op: 'eq', value: 0 });
if (enabled === 'enabled')
search_query.and.push({ field: 'enable', op: 'eq', value: 1 });
else if (enabled === 'not_enabled')
search_query.and.push({ field: 'enable', op: 'eq', value: 0 });
if (hidden === 'hidden') search_query.and.push({ field: 'hide', op: 'eq', value: 1 });
else if (hidden === 'not_hidden') search_query.and.push({ field: 'hide', op: 'eq', value: 0 });
if (hidden === 'hidden')
search_query.and.push({ field: 'hide', op: 'eq', value: 1 });
else if (hidden === 'not_hidden')
search_query.and.push({ field: 'hide', op: 'eq', value: 0 });
try {
const result_li = await api.search_ae_obj({
@@ -525,7 +654,10 @@ export async function search__exhibit_tracking({
});
if (result_li && Array.isArray(result_li)) {
const processed = await process_ae_obj__exhibit_tracking_props({ obj_li: result_li, log_lvl });
const processed = await process_ae_obj__exhibit_tracking_props({
obj_li: result_li,
log_lvl
});
if (try_cache) {
await db_save_ae_obj_li__ae_obj({
db_instance: db_events,
@@ -542,4 +674,4 @@ export async function search__exhibit_tracking({
}
return [];
}
}

View File

@@ -56,13 +56,18 @@ const export_obj = {
search__event_badge: event_badge.search__event_badge,
// Event Badge Templates
load_ae_obj_id__event_badge_template: event_badge_template.load_ae_obj_id__event_badge_template,
load_ae_obj_li__event_badge_template: event_badge_template.load_ae_obj_li__event_badge_template,
create_ae_obj__event_badge_template: event_badge_template.create_ae_obj__event_badge_template,
load_ae_obj_id__event_badge_template:
event_badge_template.load_ae_obj_id__event_badge_template,
load_ae_obj_li__event_badge_template:
event_badge_template.load_ae_obj_li__event_badge_template,
create_ae_obj__event_badge_template:
event_badge_template.create_ae_obj__event_badge_template,
delete_ae_obj_id__event_badge_template:
event_badge_template.delete_ae_obj_id__event_badge_template,
update_ae_obj__event_badge_template: event_badge_template.update_ae_obj__event_badge_template,
search__event_badge_template: event_badge_template.search__event_badge_template,
update_ae_obj__event_badge_template:
event_badge_template.update_ae_obj__event_badge_template,
search__event_badge_template:
event_badge_template.search__event_badge_template,
// Event Devices
load_ae_obj_id__event_device: event_device.load_ae_obj_id__event_device,
@@ -77,53 +82,72 @@ const export_obj = {
search__exhibit: search__exhibit,
create_ae_obj__exhibit: create_ae_obj__exhibit,
update_ae_obj__exhibit: update_ae_obj__exhibit,
load_ae_obj_id__event_exhibit_tracking: load_ae_obj_id__event_exhibit_tracking,
load_ae_obj_li__event_exhibit_tracking: load_ae_obj_li__event_exhibit_tracking,
load_ae_obj_id__event_exhibit_tracking:
load_ae_obj_id__event_exhibit_tracking,
load_ae_obj_li__event_exhibit_tracking:
load_ae_obj_li__event_exhibit_tracking,
search__exhibit_tracking: search__exhibit_tracking,
create_ae_obj__exhibit_tracking: create_ae_obj__exhibit_tracking,
update_ae_obj__exhibit_tracking: update_ae_obj__exhibit_tracking,
download_export__event_exhibit_tracking: download_export__event_exhibit_tracking,
download_export__event_exhibit_tracking:
download_export__event_exhibit_tracking,
// Event Files
load_ae_obj_id__event_file: event_file.load_ae_obj_id__event_file,
load_ae_obj_li__event_file: event_file.load_ae_obj_li__event_file,
create_event_file_obj_from_hosted_file_async: event_file.create_event_file_obj_from_hosted_file_async,
create_event_file_obj_from_hosted_file_async:
event_file.create_event_file_obj_from_hosted_file_async,
delete_ae_obj_id__event_file: event_file.delete_ae_obj_id__event_file,
update_ae_obj__event_file: event_file.update_ae_obj__event_file,
qry__event_file: event_file.qry__event_file,
search__event_file: event_file.search__event_file,
// Event Locations
load_ae_obj_id__event_location: event_location.load_ae_obj_id__event_location,
load_ae_obj_li__event_location: event_location.load_ae_obj_li__event_location,
load_ae_obj_id__event_location:
event_location.load_ae_obj_id__event_location,
load_ae_obj_li__event_location:
event_location.load_ae_obj_li__event_location,
create_ae_obj__event_location: event_location.create_ae_obj__event_location,
delete_ae_obj_id__event_location: event_location.delete_ae_obj_id__event_location,
delete_ae_obj_id__event_location:
event_location.delete_ae_obj_id__event_location,
update_ae_obj__event_location: event_location.update_ae_obj__event_location,
// Event Sessions
load_ae_obj_id__event_session: event_session.load_ae_obj_id__event_session,
load_ae_obj_li__event_session: event_session.load_ae_obj_li__event_session,
create_ae_obj__event_session: event_session.create_ae_obj__event_session,
delete_ae_obj_id__event_session: event_session.delete_ae_obj_id__event_session,
delete_ae_obj_id__event_session:
event_session.delete_ae_obj_id__event_session,
update_ae_obj__event_session: event_session.update_ae_obj__event_session,
qry__event_session: event_session.qry__event_session,
search__event_session: event_session.search__event_session,
email_sign_in__event_session: event_session.email_sign_in__event_session,
// Event Presentations
load_ae_obj_id__event_presentation: event_presentation.load_ae_obj_id__event_presentation,
load_ae_obj_li__event_presentation: event_presentation.load_ae_obj_li__event_presentation,
create_ae_obj__event_presentation: event_presentation.create_ae_obj__event_presentation,
delete_ae_obj_id__event_presentation: event_presentation.delete_ae_obj_id__event_presentation,
update_ae_obj__event_presentation: event_presentation.update_ae_obj__event_presentation,
load_ae_obj_id__event_presentation:
event_presentation.load_ae_obj_id__event_presentation,
load_ae_obj_li__event_presentation:
event_presentation.load_ae_obj_li__event_presentation,
create_ae_obj__event_presentation:
event_presentation.create_ae_obj__event_presentation,
delete_ae_obj_id__event_presentation:
event_presentation.delete_ae_obj_id__event_presentation,
update_ae_obj__event_presentation:
event_presentation.update_ae_obj__event_presentation,
// Event Presenters
load_ae_obj_id__event_presenter: event_presenter.load_ae_obj_id__event_presenter,
load_ae_obj_li__event_presenter: event_presenter.load_ae_obj_li__event_presenter,
create_ae_obj__event_presenter: event_presenter.create_ae_obj__event_presenter,
delete_ae_obj_id__event_presenter: event_presenter.delete_ae_obj_id__event_presenter,
update_ae_obj__event_presenter: event_presenter.update_ae_obj__event_presenter,
load_ae_obj_id__event_presenter:
event_presenter.load_ae_obj_id__event_presenter,
load_ae_obj_li__event_presenter:
event_presenter.load_ae_obj_li__event_presenter,
create_ae_obj__event_presenter:
event_presenter.create_ae_obj__event_presenter,
delete_ae_obj_id__event_presenter:
event_presenter.delete_ae_obj_id__event_presenter,
update_ae_obj__event_presenter:
event_presenter.update_ae_obj__event_presenter,
search__event_presenter: event_presenter.search__event_presenter,
email_sign_in__event_presenter: event_presenter.email_sign_in__event_presenter
email_sign_in__event_presenter:
event_presenter.email_sign_in__event_presenter
};
export const events_func = export_obj;
export const events_func = export_obj;

View File

@@ -18,7 +18,7 @@
/* --- Badge front --- */
[data-layout="badge_4x5_fanfold"] .badge_front {
[data-layout='badge_4x5_fanfold'] .badge_front {
width: 4in;
min-height: 5in;
max-height: 5in;
@@ -28,18 +28,18 @@
}
/* Body area: 5in total ~1in header ~0.5in footer = ~3.5in for content */
[data-layout="badge_4x5_fanfold"] .badge_body {
[data-layout='badge_4x5_fanfold'] .badge_body {
max-height: 3.5in;
}
/* --- Badge back --- */
[data-layout="badge_4x5_fanfold"] .badge_back {
[data-layout='badge_4x5_fanfold'] .badge_back {
width: 4in;
min-height: 5in;
max-height: 5in;
}
[data-layout="badge_4x5_fanfold"] .badge_back_content {
[data-layout='badge_4x5_fanfold'] .badge_back_content {
max-height: 4.5in;
}

View File

@@ -16,19 +16,19 @@
/* --- Badge front --- */
[data-layout="badge_3.5x5.5_pvc"] .badge_front {
[data-layout='badge_3.5x5.5_pvc'] .badge_front {
width: 3.5in;
min-height: 5.5in;
max-height: 5.5in;
/* debug */
/* outline: thick solid orange; */
/* debug */
/* outline: thick solid orange; */
}
/* Outer wrapper: remove the generic 4×6 fanfold defaults so the blue dashed
outline hugs the card tightly. The badge_front CSS above supplies the exact
3.5×5.5in size; wrapper just needs to fit it with no extra space. */
[data-layout="badge_3.5x5.5_pvc"].event_badge_wrapper {
[data-layout='badge_3.5x5.5_pvc'].event_badge_wrapper {
padding: 0;
gap: 0;
min-height: 0;
@@ -37,7 +37,7 @@
}
@media print {
[data-layout="badge_3.5x5.5_pvc"].event_badge_wrapper {
[data-layout='badge_3.5x5.5_pvc'].event_badge_wrapper {
width: 3.5in !important;
height: 5.5in !important;
max-width: 3.5in !important;

View File

@@ -829,7 +829,7 @@ export class MySubClassedDexie extends Dexie {
constructor() {
super('ae_events_db');
this.version(6).stores({
// NO LONGER USE "_random"
// NO LONGER USE "_random"
event: `
id, event_id, event_id_random,
code,

View File

@@ -817,7 +817,10 @@ 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 =
@@ -862,7 +865,8 @@ export async function process_ae_obj__journal_props({
obj.cfg_json = obj.cfg_json ?? {};
obj.data_json = obj.data_json ?? {};
const updated = obj.updated_on ?? obj.created_on ?? new Date(0).toISOString();
const updated =
obj.updated_on ?? obj.created_on ?? new Date(0).toISOString();
obj.tmp_sort_3 = `${obj.group ?? '0'}_${obj.priority ? 1 : 0}_${obj.sort ?? '0'}_${
obj.name
}_${updated}`;

View File

@@ -942,7 +942,10 @@ 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 =
@@ -1026,7 +1029,8 @@ export async function process_ae_obj__journal_entry_props({
// Journal entry-specific computed sort fields, overriding generic ones if needed
const sort_val = (obj.sort ?? 0).toString().padStart(3, '0');
const updated = obj.updated_on ?? obj.created_on ?? new Date(0).toISOString();
const updated =
obj.updated_on ?? obj.created_on ?? new Date(0).toISOString();
obj.tmp_sort_1 = `${obj.group ?? ''}_${obj.priority ? '1' : '0'}_${
sort_val
}_${updated}`;

View File

@@ -74,18 +74,19 @@ export async function load_ae_obj_id__post({
});
if (inc_comment_li && ae_promises.load__post_obj) {
ae_promises.load__post_obj.post_comment_li = await load_ae_obj_li__post_comment({
api_cfg: api_cfg,
for_obj_type: 'post',
for_obj_id: post_id,
enabled,
hidden,
limit,
offset,
params,
try_cache,
log_lvl
});
ae_promises.load__post_obj.post_comment_li =
await load_ae_obj_li__post_comment({
api_cfg: api_cfg,
for_obj_type: 'post',
for_obj_id: post_id,
enabled,
hidden,
limit,
offset,
params,
try_cache,
log_lvl
});
}
return ae_promises.load__post_obj;
@@ -151,7 +152,8 @@ export async function load_ae_obj_li__post({
if (try_cache) {
const processed_obj_li = await process_ae_obj__post_props({
obj_li: post_obj_li_get_result,
account_id: for_obj_type === 'account' ? for_obj_id : undefined,
account_id:
for_obj_type === 'account' ? for_obj_id : undefined,
log_lvl: log_lvl
});
await db_save_ae_obj_li__ae_obj({
@@ -171,18 +173,19 @@ export async function load_ae_obj_li__post({
if (inc_comment_li && ae_promises.load__post_obj_li) {
for (let i = 0; i < ae_promises.load__post_obj_li.length; i++) {
const post_obj = ae_promises.load__post_obj_li[i];
ae_promises.load__post_obj_li[i].post_comment_li = await load_ae_obj_li__post_comment({
api_cfg: api_cfg,
for_obj_type: 'post',
for_obj_id: post_obj.post_id,
enabled,
hidden,
limit,
offset,
params,
try_cache,
log_lvl
});
ae_promises.load__post_obj_li[i].post_comment_li =
await load_ae_obj_li__post_comment({
api_cfg: api_cfg,
for_obj_type: 'post',
for_obj_id: post_obj.post_id,
enabled,
hidden,
limit,
offset,
params,
try_cache,
log_lvl
});
}
}
@@ -291,7 +294,10 @@ export async function update_ae_obj__post({
log_lvl?: number;
}): Promise<ae_Post | null> {
if (log_lvl) {
console.log(`*** update_ae_obj__post() *** post_id=${post_id}`, data_kv);
console.log(
`*** update_ae_obj__post() *** post_id=${post_id}`,
data_kv
);
}
const result = await api.update_ae_obj({
@@ -358,7 +364,11 @@ export async function qry__post({
const search_query: any = { and: [] };
if (account_id) {
search_query.and.push({ field: 'account_id', op: 'eq', value: account_id });
search_query.and.push({
field: 'account_id',
op: 'eq',
value: account_id
});
}
if (qry_str) {
@@ -366,7 +376,11 @@ export async function qry__post({
}
if (qry_person_id) {
search_query.and.push({ field: 'external_person_id', op: 'eq', value: qry_person_id });
search_query.and.push({
field: 'external_person_id',
op: 'eq',
value: qry_person_id
});
}
// if (qry_archive_on) {
@@ -428,18 +442,19 @@ export async function qry__post({
if (inc_comment_li && ae_promises.load__post_obj_li) {
for (let i = 0; i < ae_promises.load__post_obj_li.length; i++) {
const post_obj = ae_promises.load__post_obj_li[i];
ae_promises.load__post_obj_li[i].post_comment_li = await load_ae_obj_li__post_comment({
api_cfg: api_cfg,
for_obj_type: 'post',
for_obj_id: post_obj.post_id,
enabled,
hidden,
limit,
offset,
// params,
try_cache: true, // Always cache comments if we are caching posts
log_lvl
});
ae_promises.load__post_obj_li[i].post_comment_li =
await load_ae_obj_li__post_comment({
api_cfg: api_cfg,
for_obj_type: 'post',
for_obj_id: post_obj.post_id,
enabled,
hidden,
limit,
offset,
// params,
try_cache: true, // Always cache comments if we are caching posts
log_lvl
});
}
}
@@ -512,14 +527,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.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);
@@ -549,7 +571,8 @@ export async function process_ae_obj__post_props({
}
obj.name = obj.title;
const sort_val = (obj.sort ?? 0).toString().padStart(3, '0');
const updated = obj.updated_on ?? obj.created_on ?? new Date(0).toISOString();
const updated =
obj.updated_on ?? obj.created_on ?? new Date(0).toISOString();
obj.tmp_sort_1 = `${obj.group ?? ''}_${obj.priority ? '1' : '0'}_${
sort_val
}_${updated}`;
@@ -560,4 +583,4 @@ export async function process_ae_obj__post_props({
return obj;
}
});
}
}

View File

@@ -24,7 +24,9 @@ export async function load_ae_obj_id__post_comment({
log_lvl?: number;
}): Promise<ae_PostComment | null> {
if (log_lvl) {
console.log(`*** load_ae_obj_id__post_comment() *** post_comment_id=${post_comment_id}`);
console.log(
`*** load_ae_obj_id__post_comment() *** post_comment_id=${post_comment_id}`
);
}
ae_promises.load__post_comment_obj = await api
@@ -39,10 +41,11 @@ export async function load_ae_obj_id__post_comment({
.then(async function (post_comment_obj_get_result) {
if (post_comment_obj_get_result) {
if (try_cache) {
const processed_obj_li = await process_ae_obj__post_comment_props({
obj_li: [post_comment_obj_get_result],
log_lvl: log_lvl
});
const processed_obj_li =
await process_ae_obj__post_comment_props({
obj_li: [post_comment_obj_get_result],
log_lvl: log_lvl
});
await db_save_ae_obj_li__ae_obj({
db_instance: db_posts,
table_name: 'comment',
@@ -121,10 +124,11 @@ export async function load_ae_obj_li__post_comment({
.then(async function (post_comment_obj_li_get_result) {
if (post_comment_obj_li_get_result) {
if (try_cache) {
const processed_obj_li = await process_ae_obj__post_comment_props({
obj_li: post_comment_obj_li_get_result,
log_lvl: log_lvl
});
const processed_obj_li =
await process_ae_obj__post_comment_props({
obj_li: post_comment_obj_li_get_result,
log_lvl: log_lvl
});
await db_save_ae_obj_li__ae_obj({
db_instance: db_posts,
table_name: 'comment',
@@ -161,7 +165,9 @@ export async function create_ae_obj__post_comment({
log_lvl?: number;
}): Promise<ae_PostComment | null> {
if (log_lvl) {
console.log(`*** create_ae_obj__post_comment() *** account_id=${account_id} post_id=${post_id}`);
console.log(
`*** create_ae_obj__post_comment() *** account_id=${account_id} post_id=${post_id}`
);
}
const result = await api.create_nested_obj({
@@ -212,7 +218,9 @@ export async function delete_ae_obj_id__post_comment({
log_lvl?: number;
}) {
if (log_lvl) {
console.log(`*** delete_ae_obj_id__post_comment() *** post_comment_id=${post_comment_id}`);
console.log(
`*** delete_ae_obj_id__post_comment() *** post_comment_id=${post_comment_id}`
);
}
const result = await api.delete_ae_obj({
@@ -248,7 +256,10 @@ export async function update_ae_obj__post_comment({
log_lvl?: number;
}): Promise<ae_PostComment | null> {
if (log_lvl) {
console.log(`*** update_ae_obj__post_comment() *** post_comment_id=${post_comment_id}`, data_kv);
console.log(
`*** update_ae_obj__post_comment() *** post_comment_id=${post_comment_id}`,
data_kv
);
}
const result = await api.update_ae_obj({
@@ -336,14 +347,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);
@@ -366,7 +384,8 @@ export async function process_ae_obj__post_comment_props({
log_lvl,
specific_processor: (obj) => {
const sort_val = (obj.sort ?? 0).toString().padStart(3, '0');
const updated = obj.updated_on ?? obj.created_on ?? new Date(0).toISOString();
const updated =
obj.updated_on ?? obj.created_on ?? new Date(0).toISOString();
obj.tmp_sort_1 = `${obj.group ?? ''}_${obj.priority ? '1' : '0'}_${
sort_val
}_${updated}`;
@@ -377,4 +396,4 @@ export async function process_ae_obj__post_comment_props({
return obj;
}
});
}
}

View File

@@ -1,6 +1,9 @@
import type { key_val } from '$lib/stores/ae_stores';
import { api } from '$lib/api/api';
import { process_ae_obj__activity_log_props, properties_to_save } from '$lib/ae_core/ae_core__activity_log';
import {
process_ae_obj__activity_log_props,
properties_to_save
} from '$lib/ae_core/ae_core__activity_log';
import { db_save_ae_obj_li__ae_obj } from '$lib/ae_core/core__idb_dexie';
import { db_core } from '$lib/ae_core/db_core';
@@ -17,129 +20,144 @@ import { db_core } from '$lib/ae_core/db_core';
* @returns A structured array of meeting report objects.
*/
export async function qry__jitsi_report({
api_cfg,
account_id,
enabled = 'all',
hidden = 'all',
limit = 500,
try_cache = true,
log_lvl = 0
api_cfg,
account_id,
enabled = 'all',
hidden = 'all',
limit = 500,
try_cache = true,
log_lvl = 0
}: {
api_cfg: any;
account_id: string;
enabled?: 'enabled' | 'all' | 'not_enabled' | undefined;
hidden?: 'hidden' | 'all' | 'not_hidden' | undefined;
limit?: number;
try_cache?: boolean;
log_lvl?: number;
api_cfg: any;
account_id: string;
enabled?: 'enabled' | 'all' | 'not_enabled' | undefined;
hidden?: 'hidden' | 'all' | 'not_hidden' | undefined;
limit?: number;
try_cache?: boolean;
log_lvl?: number;
}) {
if (log_lvl) console.log('*** qry__jitsi_report() ***');
if (log_lvl) console.log('*** qry__jitsi_report() ***');
// Step 1: Query all relevant activity logs from the API.
const search_query = {
or: [
{ field: 'name', op: 'eq', value: 'jitsi_meeting_event' },
{ field: 'name', op: 'eq', value: 'jitsi_meeting_stats' }
],
and: [
{ field: 'account_id_random', op: 'eq', value: account_id }
]
};
// Step 1: Query all relevant activity logs from the API.
const search_query = {
or: [
{ field: 'name', op: 'eq', value: 'jitsi_meeting_event' },
{ field: 'name', op: 'eq', value: 'jitsi_meeting_stats' }
],
and: [{ field: 'account_id_random', op: 'eq', value: account_id }]
};
const result = await api.search_ae_obj({
api_cfg: api_cfg,
obj_type: 'activity_log',
search_query,
headers: { 'x-account-id': account_id },
enabled,
hidden,
limit,
log_lvl: log_lvl
});
const result = await api.search_ae_obj({
api_cfg: api_cfg,
obj_type: 'activity_log',
search_query,
headers: { 'x-account-id': account_id },
enabled,
hidden,
limit,
log_lvl: log_lvl
});
// Handle potential V3 API envelope
let flat_log_list: any[] = [];
if (Array.isArray(result)) {
flat_log_list = result;
} else if (result && typeof result === 'object' && Array.isArray((result as any).data)) {
flat_log_list = (result as any).data;
}
// Handle potential V3 API envelope
let flat_log_list: any[] = [];
if (Array.isArray(result)) {
flat_log_list = result;
} else if (
result &&
typeof result === 'object' &&
Array.isArray((result as any).data)
) {
flat_log_list = (result as any).data;
}
if (!flat_log_list || !Array.isArray(flat_log_list) || flat_log_list.length === 0) {
if (log_lvl) console.log('No Jitsi activity logs found or error occurred.');
return [];
}
if (
!flat_log_list ||
!Array.isArray(flat_log_list) ||
flat_log_list.length === 0
) {
if (log_lvl)
console.log('No Jitsi activity logs found or error occurred.');
return [];
}
// Frontier Standard: Process and Save to local cache
if (try_cache) {
const processed_obj_li = await process_ae_obj__activity_log_props({
obj_li: flat_log_list,
log_lvl: log_lvl
});
await db_save_ae_obj_li__ae_obj({
db_instance: db_core,
table_name: 'activity_log',
obj_li: processed_obj_li,
properties_to_save: properties_to_save,
log_lvl: log_lvl
});
}
// Frontier Standard: Process and Save to local cache
if (try_cache) {
const processed_obj_li = await process_ae_obj__activity_log_props({
obj_li: flat_log_list,
log_lvl: log_lvl
});
await db_save_ae_obj_li__ae_obj({
db_instance: db_core,
table_name: 'activity_log',
obj_li: processed_obj_li,
properties_to_save: properties_to_save,
log_lvl: log_lvl
});
}
// Step 2: Process the flat list into a structured report.
const meetings = new Map<string, any>();
// Step 2: Process the flat list into a structured report.
const meetings = new Map<string, any>();
for (const log of flat_log_list) {
const meeting_id = log.external_client_id;
if (!meeting_id) continue;
for (const log of flat_log_list) {
const meeting_id = log.external_client_id;
if (!meeting_id) continue;
// Make sure the name field is prefixed with "jitsi_"
if (!log.name.startsWith('jitsi_')) continue;
// Make sure the name field is prefixed with "jitsi_"
if (!log.name.startsWith('jitsi_')) continue;
// Ensure a base entry for the meeting exists
if (!meetings.has(meeting_id)) {
meetings.set(meeting_id, {
meeting_id: meeting_id,
room_name: 'Unknown',
start_time: log.created_on, // Fallback start time
final_duration: '00:00:00',
final_participants: [],
final_participant_count: 0,
events: []
});
}
// Ensure a base entry for the meeting exists
if (!meetings.has(meeting_id)) {
meetings.set(meeting_id, {
meeting_id: meeting_id,
room_name: 'Unknown',
start_time: log.created_on, // Fallback start time
final_duration: '00:00:00',
final_participants: [],
final_participant_count: 0,
events: []
});
}
const meeting_report = meetings.get(meeting_id);
const meeting_report = meetings.get(meeting_id);
if (log.action === 'jitsi_meeting_init') {
// This is the main log entry, containing the final state.
meeting_report.room_name = log.description;
meeting_report.start_time = log.created_on; // The init log has the true start time
if (log.meta_json) {
meeting_report.final_duration = log.meta_json.duration;
meeting_report.final_participants = log.meta_json.participants;
meeting_report.final_participant_count = log.meta_json.participant_count;
}
} else {
// This is a discrete event log.
meeting_report.events.push({
timestamp: log.created_on,
action: log.action,
details: log.meta_json
});
}
}
if (log.action === 'jitsi_meeting_init') {
// This is the main log entry, containing the final state.
meeting_report.room_name = log.description;
meeting_report.start_time = log.created_on; // The init log has the true start time
if (log.meta_json) {
meeting_report.final_duration = log.meta_json.duration;
meeting_report.final_participants = log.meta_json.participants;
meeting_report.final_participant_count =
log.meta_json.participant_count;
}
} else {
// This is a discrete event log.
meeting_report.events.push({
timestamp: log.created_on,
action: log.action,
details: log.meta_json
});
}
}
// Sort events within each meeting chronologically
for (const report of meetings.values()) {
report.events.sort((a: any, b: any) => new Date(a.timestamp).getTime() - new Date(b.timestamp).getTime());
}
// Sort events within each meeting chronologically
for (const report of meetings.values()) {
report.events.sort(
(a: any, b: any) =>
new Date(a.timestamp).getTime() -
new Date(b.timestamp).getTime()
);
}
const final_report = Array.from(meetings.values());
final_report.sort((a, b) => new Date(b.start_time).getTime() - new Date(a.start_time).getTime());
const final_report = Array.from(meetings.values());
final_report.sort(
(a, b) =>
new Date(b.start_time).getTime() - new Date(a.start_time).getTime()
);
if (log_lvl) console.log('Final Jitsi report:', final_report);
if (log_lvl) console.log('Final Jitsi report:', final_report);
return final_report;
return final_report;
}
export const load_jitsi_report = qry__jitsi_report;

View File

@@ -175,11 +175,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);
@@ -201,7 +205,9 @@ export async function load_ae_obj_id__sponsorship_cfg({
log_lvl?: number;
}): Promise<ae_SponsorshipCfg | null> {
if (log_lvl) {
console.log(`*** load_ae_obj_id__sponsorship_cfg() *** [V3] id=${sponsorship_cfg_id} (SWR)`);
console.log(
`*** load_ae_obj_id__sponsorship_cfg() *** [V3] id=${sponsorship_cfg_id} (SWR)`
);
}
// 1. FAST PATH: Cache hit
@@ -209,17 +215,32 @@ export async function load_ae_obj_id__sponsorship_cfg({
try {
const cached = await db_sponsorships.cfg.get(sponsorship_cfg_id);
if (cached) {
_refresh_sponsorship_cfg_id_background({ api_cfg, sponsorship_cfg_id, try_cache, log_lvl: 0 });
_refresh_sponsorship_cfg_id_background({
api_cfg,
sponsorship_cfg_id,
try_cache,
log_lvl: 0
});
return cached as unknown as ae_SponsorshipCfg;
}
} catch (e) {}
}
// 2. SLOW PATH: Wait for API
return await _refresh_sponsorship_cfg_id_background({ api_cfg, sponsorship_cfg_id, try_cache, log_lvl });
return await _refresh_sponsorship_cfg_id_background({
api_cfg,
sponsorship_cfg_id,
try_cache,
log_lvl
});
}
async function _refresh_sponsorship_cfg_id_background({ api_cfg, sponsorship_cfg_id, try_cache, log_lvl }: any) {
async function _refresh_sponsorship_cfg_id_background({
api_cfg,
sponsorship_cfg_id,
try_cache,
log_lvl
}: any) {
if (typeof navigator !== 'undefined' && !navigator.onLine) return null;
try {
const result = await api.get_ae_obj({
@@ -230,7 +251,11 @@ async function _refresh_sponsorship_cfg_id_background({ api_cfg, sponsorship_cfg
});
if (result) {
if (try_cache) {
const processed_obj_li = await process_ae_obj__sponsorship_cfg_props({ obj_li: [result], log_lvl });
const processed_obj_li =
await process_ae_obj__sponsorship_cfg_props({
obj_li: [result],
log_lvl
});
await db_save_ae_obj_li__ae_obj({
db_instance: db_sponsorships,
table_name: 'cfg',
@@ -258,25 +283,43 @@ export async function load_ae_obj_id__sponsorship({
log_lvl?: number;
}): Promise<ae_Sponsorship | null> {
if (log_lvl) {
console.log(`*** load_ae_obj_id__sponsorship() *** [V3] id=${sponsorship_id} (SWR)`);
console.log(
`*** load_ae_obj_id__sponsorship() *** [V3] id=${sponsorship_id} (SWR)`
);
}
// 1. FAST PATH: Cache hit
if (try_cache) {
try {
const cached = await db_sponsorships.sponsorship.get(sponsorship_id);
const cached =
await db_sponsorships.sponsorship.get(sponsorship_id);
if (cached) {
_refresh_sponsorship_id_background({ api_cfg, sponsorship_id, try_cache, log_lvl: 0 });
_refresh_sponsorship_id_background({
api_cfg,
sponsorship_id,
try_cache,
log_lvl: 0
});
return cached as unknown as ae_Sponsorship;
}
} catch (e) {}
}
// 2. SLOW PATH: Wait for API
return await _refresh_sponsorship_id_background({ api_cfg, sponsorship_id, try_cache, log_lvl });
return await _refresh_sponsorship_id_background({
api_cfg,
sponsorship_id,
try_cache,
log_lvl
});
}
async function _refresh_sponsorship_id_background({ api_cfg, sponsorship_id, try_cache, log_lvl }: any) {
async function _refresh_sponsorship_id_background({
api_cfg,
sponsorship_id,
try_cache,
log_lvl
}: any) {
if (typeof navigator !== 'undefined' && !navigator.onLine) return null;
try {
const result = await api.get_ae_obj({
@@ -287,7 +330,11 @@ async function _refresh_sponsorship_id_background({ api_cfg, sponsorship_id, try
});
if (result) {
if (try_cache) {
const processed_obj_li = await process_ae_obj__sponsorship_props({ obj_li: [result], log_lvl });
const processed_obj_li =
await process_ae_obj__sponsorship_props({
obj_li: [result],
log_lvl
});
await db_save_ae_obj_li__ae_obj({
db_instance: db_sponsorships,
table_name: 'sponsorship',
@@ -332,7 +379,9 @@ export async function load_ae_obj_li__sponsorship({
log_lvl?: number;
}): Promise<ae_Sponsorship[]> {
if (log_lvl) {
console.log(`*** load_ae_obj_li__sponsorship() *** [V3] for=${for_obj_type}:${for_obj_id}`);
console.log(
`*** load_ae_obj_li__sponsorship() *** [V3] for=${for_obj_type}:${for_obj_id}`
);
}
const result_li = await api.get_ae_obj_li({
@@ -472,4 +521,4 @@ export const spons_func = {
update_ae_obj__sponsorship,
delete_ae_obj__sponsorship,
download_export__sponsorship
};
};

View File

@@ -9,7 +9,10 @@ import {
import { get_obj_li_w_match_prop } from './ae_utils__get_obj_li_w_match_prop';
import { file_extension_icon } from './ae_utils__file_extension_icon';
import { file_extension_icon_lucide } from './ae_utils__file_extension_icon_lucide';
import { process_permission_checks, compare_access_levels } from './ae_utils__perm_checks';
import {
process_permission_checks,
compare_access_levels
} from './ae_utils__perm_checks';
import { iso_datetime_formatter } from './ae_utils__datetime_format';
import { is_datetime_recent } from './ae_utils__is_datetime_recent';
import { extract_prefixed_form_data } from './ae_utils__extract_prefixed_form_data';
@@ -81,7 +84,6 @@ function handle_url_and_message(name: string, value: null | string) {
// console.log('Message sent to parent (iframe):', message);
}
// ALERT: Not referenced anywhere -2026-02-03
function create_a_element({
account_id,

View File

@@ -8,12 +8,22 @@ async function generate_iv() {
}
// Updated 2025-05-08
export const encrypt_content = async function encrypt_content(content: string, keyData: string) {
export const encrypt_content = async function encrypt_content(
content: string,
keyData: string
) {
const iv = await generate_iv();
const keyBytes = await crypto.subtle.digest('SHA-256', new TextEncoder().encode(keyData));
const key = await crypto.subtle.importKey('raw', keyBytes, { name: 'AES-CBC' }, false, [
'encrypt'
]);
const keyBytes = await crypto.subtle.digest(
'SHA-256',
new TextEncoder().encode(keyData)
);
const key = await crypto.subtle.importKey(
'raw',
keyBytes,
{ name: 'AES-CBC' },
false,
['encrypt']
);
const encodedContent = await crypto.subtle.encrypt(
{ name: 'AES-CBC', iv: iv.buffer as ArrayBuffer },
key,
@@ -52,7 +62,10 @@ export const combine_iv_and_base64 = function combine_iv_and_base64(
};
// Updated 2025-05-08
export const encrypt_wrapper = async function encrypt_wrapper(content: string, keyData: string) {
export const encrypt_wrapper = async function encrypt_wrapper(
content: string,
keyData: string
) {
if (!content) {
console.error('No content provided. Returning empty string.');
return '';
@@ -73,11 +86,20 @@ export const decrypt_content = async function decrypt_content(
iv: Uint8Array,
keyData: string
) {
const keyBytes = await crypto.subtle.digest('SHA-256', new TextEncoder().encode(keyData));
const key = await crypto.subtle.importKey('raw', keyBytes, { name: 'AES-CBC' }, false, [
'decrypt'
]);
const encryptedContent = Uint8Array.from(atob(base64Content), (c) => c.charCodeAt(0));
const keyBytes = await crypto.subtle.digest(
'SHA-256',
new TextEncoder().encode(keyData)
);
const key = await crypto.subtle.importKey(
'raw',
keyBytes,
{ name: 'AES-CBC' },
false,
['decrypt']
);
const encryptedContent = Uint8Array.from(atob(base64Content), (c) =>
c.charCodeAt(0)
);
const decryptedContent = await crypto.subtle.decrypt(
{ name: 'AES-CBC', iv: iv.buffer as ArrayBuffer },
key,
@@ -89,7 +111,9 @@ export const decrypt_content = async function decrypt_content(
};
// Updated 2025-05-08
export const split_iv_and_base64 = function split_iv_and_base64(combined: string) {
export const split_iv_and_base64 = function split_iv_and_base64(
combined: string
) {
if (!combined) {
console.error('No combined string provided. Returning empty object.');
return { iv: new Uint8Array(), base64: '' };
@@ -97,7 +121,9 @@ export const split_iv_and_base64 = function split_iv_and_base64(combined: string
const [iv_hex, encrypted_base64_string] = combined.split(':');
const base64 = encrypted_base64_string;
const match_result = iv_hex.match(/.{1,2}/g);
const iv = new Uint8Array((match_result || []).map((byte) => parseInt(byte, 16)));
const iv = new Uint8Array(
(match_result || []).map((byte) => parseInt(byte, 16))
);
if (log_lvl) {
console.log(`IV: ${iv}; Encrypted:`, base64);
}
@@ -105,7 +131,10 @@ export const split_iv_and_base64 = function split_iv_and_base64(combined: string
};
// Updated 2025-05-15
export const decrypt_wrapper = async function decrypt_wrapper(combined: string, keyData: string) {
export const decrypt_wrapper = async function decrypt_wrapper(
combined: string,
keyData: string
) {
if (!combined) {
console.error('No combined string provided. Returning empty string.');
return false;

View File

@@ -66,7 +66,9 @@ export const iso_datetime_formatter = function iso_datetime_formatter(
datetime_string = dayjs(raw_datetime).format('MM-DD hh:mm A');
break;
case 'datetime_iso_tz':
datetime_string = dayjs(raw_datetime).format('YYYY-MM-DD HH:mm:ss Z');
datetime_string = dayjs(raw_datetime).format(
'YYYY-MM-DD HH:mm:ss Z'
);
break;
case 'datetime_iso_12_no_seconds':
datetime_string = dayjs(raw_datetime).format('YYYY-MM-DD hh:mm A');
@@ -75,7 +77,9 @@ export const iso_datetime_formatter = function iso_datetime_formatter(
// datetime_string = dayjs(raw_datetime).format('YYYY-MM-DD hh:mm A');
// break;
case 'datetime_us':
datetime_string = dayjs(raw_datetime).format('MM/DD/YYYY hh:mm:ss A');
datetime_string = dayjs(raw_datetime).format(
'MM/DD/YYYY hh:mm:ss A'
);
break;
case 'datetime_short':
datetime_string = dayjs(raw_datetime).format('MMM D, YY HH:mm');
@@ -93,13 +97,17 @@ export const iso_datetime_formatter = function iso_datetime_formatter(
datetime_string = dayjs(raw_datetime).format('MMMM D, YYYY HH:mm');
break;
case 'datetime_12_long':
datetime_string = dayjs(raw_datetime).format('MMMM D, YYYY hh:mm A');
datetime_string = dayjs(raw_datetime).format(
'MMMM D, YYYY hh:mm A'
);
break;
case 'datetime_medium_sec':
datetime_string = dayjs(raw_datetime).format('MMM D, YYYY H:mm:ss');
break;
case 'datetime_12_medium_sec':
datetime_string = dayjs(raw_datetime).format('MMM D, YYYY h:mm:ss A');
datetime_string = dayjs(raw_datetime).format(
'MMM D, YYYY h:mm:ss A'
);
break;
case 'datetime_short_month':
datetime_string = dayjs(raw_datetime).format('MMM D hh:mm A');

View File

@@ -50,7 +50,9 @@ export const extract_prefixed_form_data = function extract_prefixed_form_data({
for (const field of form_data) {
let [obj_prop_name, obj_prop_value] = field;
if (log_lvl > 1) {
console.log(`${obj_prop_name}: ${obj_prop_value} type=${typeof obj_prop_value}`);
console.log(
`${obj_prop_name}: ${obj_prop_value} type=${typeof obj_prop_value}`
);
}
// Trim string values if needed
@@ -83,19 +85,31 @@ export const extract_prefixed_form_data = function extract_prefixed_form_data({
// if (obj_prop_name.startsWith(prefix)) {
obj_prop_name = obj_prop_name.replace(prefix, '');
if (log_lvl) {
console.log(`Checking: (${prefix})${obj_prop_name} value=${obj_prop_value}`);
console.log(
`Checking: (${prefix})${obj_prop_name} value=${obj_prop_value}`
);
}
if (rm_empty_id && obj_prop_name.endsWith('id_random') && !obj_prop_value) {
if (
rm_empty_id &&
obj_prop_name.endsWith('id_random') &&
!obj_prop_value
) {
if (log_lvl) {
console.log(`Match but empty *_id_random. Ignoring/removing: ${obj_prop_name}`);
console.log(
`Match but empty *_id_random. Ignoring/removing: ${obj_prop_name}`
);
}
} else if (rm_empty && !obj_prop_value) {
if (log_lvl) {
console.log(`Match but empty. Ignoring/removing: ${obj_prop_name}`);
console.log(
`Match but empty. Ignoring/removing: ${obj_prop_name}`
);
}
} else {
if (log_lvl) {
console.log(`Match: ${prefix})${obj_prop_name} value=${obj_prop_value}`);
console.log(
`Match: ${prefix})${obj_prop_name} value=${obj_prop_value}`
);
}
data_obj[obj_prop_name] = obj_prop_value;
}
@@ -107,19 +121,31 @@ export const extract_prefixed_form_data = function extract_prefixed_form_data({
} else {
// No prefix set
if (log_lvl) {
console.log(`Checking: ${obj_prop_name} value=${obj_prop_value}`);
console.log(
`Checking: ${obj_prop_name} value=${obj_prop_value}`
);
}
if (rm_empty_id && obj_prop_name.endsWith('id_random') && !obj_prop_value) {
if (
rm_empty_id &&
obj_prop_name.endsWith('id_random') &&
!obj_prop_value
) {
if (log_lvl > 1) {
console.log(`Match but empty *_id_random. Ignoring/removing: ${obj_prop_name}`);
console.log(
`Match but empty *_id_random. Ignoring/removing: ${obj_prop_name}`
);
}
} else if (rm_empty && !obj_prop_value) {
if (log_lvl > 1) {
console.log(`Match but empty. Ignoring/removing: ${obj_prop_name}`);
console.log(
`Match but empty. Ignoring/removing: ${obj_prop_name}`
);
}
} else {
if (log_lvl > 1) {
console.log(`Match: ${obj_prop_name} value=${obj_prop_value}`);
console.log(
`Match: ${obj_prop_name} value=${obj_prop_value}`
);
}
data_obj[obj_prop_name] = obj_prop_value;
}

View File

@@ -5,61 +5,63 @@ import * as Lucide from 'lucide-svelte';
* @param extension The file extension (e.g., 'pdf', 'jpg').
* @returns The Lucide icon component.
*/
export function file_extension_icon_lucide(extension: string | undefined | null): any {
export function file_extension_icon_lucide(
extension: string | undefined | null
): any {
const ext = extension?.toLowerCase() || '';
const icon_map: Record<string, any> = {
'pdf': Lucide.FileText,
'doc': Lucide.FileText,
'docx': Lucide.FileText,
'txt': Lucide.FileText,
'rtf': Lucide.FileText,
'xls': Lucide.FileSpreadsheet,
'xlsx': Lucide.FileSpreadsheet,
'csv': Lucide.FileSpreadsheet,
'png': Lucide.FileImage,
'jpg': Lucide.FileImage,
'jpeg': Lucide.FileImage,
'gif': Lucide.FileImage,
'webp': Lucide.FileImage,
'bmp': Lucide.FileImage,
'svg': Lucide.FileImage,
'mp3': Lucide.FileAudio,
'wav': Lucide.FileAudio,
'm4a': Lucide.FileAudio,
'flac': Lucide.FileAudio,
'aac': Lucide.FileAudio,
'aif': Lucide.FileAudio,
'aiff': Lucide.FileAudio,
'mp4': Lucide.FileVideo,
'mkv': Lucide.FileVideo,
'mov': Lucide.FileVideo,
'avi': Lucide.FileVideo,
pdf: Lucide.FileText,
doc: Lucide.FileText,
docx: Lucide.FileText,
txt: Lucide.FileText,
rtf: Lucide.FileText,
xls: Lucide.FileSpreadsheet,
xlsx: Lucide.FileSpreadsheet,
csv: Lucide.FileSpreadsheet,
png: Lucide.FileImage,
jpg: Lucide.FileImage,
jpeg: Lucide.FileImage,
gif: Lucide.FileImage,
webp: Lucide.FileImage,
bmp: Lucide.FileImage,
svg: Lucide.FileImage,
mp3: Lucide.FileAudio,
wav: Lucide.FileAudio,
m4a: Lucide.FileAudio,
flac: Lucide.FileAudio,
aac: Lucide.FileAudio,
aif: Lucide.FileAudio,
aiff: Lucide.FileAudio,
mp4: Lucide.FileVideo,
mkv: Lucide.FileVideo,
mov: Lucide.FileVideo,
avi: Lucide.FileVideo,
'3gp': Lucide.FileVideo,
'ppt': Lucide.Presentation,
'pptx': Lucide.Presentation,
'key': Lucide.Presentation,
'odp': Lucide.Presentation,
'zip': Lucide.FileArchive,
ppt: Lucide.Presentation,
pptx: Lucide.Presentation,
key: Lucide.Presentation,
odp: Lucide.Presentation,
zip: Lucide.FileArchive,
'7z': Lucide.FileArchive,
'rar': Lucide.FileArchive,
'tar': Lucide.FileArchive,
'gz': Lucide.FileArchive,
'json': Lucide.FileJson,
'html': Lucide.FileCode,
'htm': Lucide.FileCode,
'js': Lucide.FileCode,
'ts': Lucide.FileCode,
'css': Lucide.FileCode,
'php': Lucide.FileCode
rar: Lucide.FileArchive,
tar: Lucide.FileArchive,
gz: Lucide.FileArchive,
json: Lucide.FileJson,
html: Lucide.FileCode,
htm: Lucide.FileCode,
js: Lucide.FileCode,
ts: Lucide.FileCode,
css: Lucide.FileCode,
php: Lucide.FileCode
};
return icon_map[ext] || Lucide.File;

View File

@@ -12,12 +12,18 @@ export const clean_filename = function clean_filename(
return '';
}
const cleaned_filename = filename.replace(unacceptable_chars, replacement_char);
const cleaned_filename = filename.replace(
unacceptable_chars,
replacement_char
);
// console.log(cleaned_filename);
return cleaned_filename;
};
export const format_bytes = function format_bytes(bytes: number, decimals: number = 2) {
export const format_bytes = function format_bytes(
bytes: number,
decimals: number = 2
) {
if (bytes === 0) return '0 Bytes';
const k = 1024;
@@ -30,14 +36,19 @@ export const format_bytes = function format_bytes(bytes: number, decimals: numbe
};
// Updated 2024-08-12
export const guess_file_name = function guess_file_name(filename_string: string) {
export const guess_file_name = function guess_file_name(
filename_string: string
) {
// console.log('*** guess_file_name() ***');
if (!filename_string) {
return '';
}
if (filename_string.includes('.')) {
const file_name = filename_string.substring(0, filename_string.lastIndexOf('.'));
const file_name = filename_string.substring(
0,
filename_string.lastIndexOf('.')
);
// console.log(file_name);
return file_name;
} else {
@@ -46,7 +57,9 @@ export const guess_file_name = function guess_file_name(filename_string: string)
};
// Updated 2024-08-12
export const guess_file_extension = function guess_file_extension(filename_string: string) {
export const guess_file_extension = function guess_file_extension(
filename_string: string
) {
// console.log('*** guess_file_extension() ***');
if (!filename_string) {
return '';
@@ -57,21 +70,27 @@ export const guess_file_extension = function guess_file_extension(filename_strin
}
const file_extension =
filename_string.substring(filename_string.lastIndexOf('.') + 1, filename_string.length) ||
filename_string;
filename_string.substring(
filename_string.lastIndexOf('.') + 1,
filename_string.length
) || filename_string;
// console.log(file_extension);
return file_extension;
};
// Updated 2024-08-12
export const get_file_hash = async function get_file_hash(file: File): Promise<string> {
export const get_file_hash = async function get_file_hash(
file: File
): Promise<string> {
return new Promise((resolve, reject) => {
const file_reader = new FileReader();
file_reader.onload = async function () {
const result = file_reader.result;
if (!result || typeof result === 'string') {
console.log('File was not read completely or is in wrong format');
console.log(
'File was not read completely or is in wrong format'
);
reject('Error reading the file');
return;
}
@@ -84,7 +103,9 @@ export const get_file_hash = async function get_file_hash(file: File): Promise<s
const hash_buffer = await crypto.subtle.digest('SHA-256', result);
const hash_array = Array.from(new Uint8Array(hash_buffer));
const hash_hex = hash_array.map((b) => b.toString(16).padStart(2, '0')).join('');
const hash_hex = hash_array
.map((b) => b.toString(16).padStart(2, '0'))
.join('');
resolve(hash_hex);
};

View File

@@ -4,17 +4,22 @@
*/
export function format_html(html: string): string {
if (!html) return '';
let indent = 0;
const tab = ' ';
return html
.replace(/>\s*</g, '>\n<') // Add newlines between tags
.split('\n')
.map(line => {
.map((line) => {
line = line.trim();
if (line.match(/<\//)) indent--; // Decrease indent for closing tags
const out = tab.repeat(Math.max(0, indent)) + line;
if (line.match(/<[^\/!]/) && !line.match(/\/>/) && !line.match(/<\//)) indent++; // Increase indent for opening tags
if (
line.match(/<[^\/!]/) &&
!line.match(/\/>/) &&
!line.match(/<\//)
)
indent++; // Increase indent for opening tags
return out;
})
.join('\n');

View File

@@ -9,7 +9,9 @@ export const is_datetime_recent = function is_datetime_recent({
log_lvl?: number;
}) {
if (log_lvl) {
console.log(`*** is_datetime_recent() *** datetime=${datetime} minutes=${minutes}`);
console.log(
`*** is_datetime_recent() *** datetime=${datetime} minutes=${minutes}`
);
}
const now: any = new Date();

View File

@@ -17,7 +17,10 @@ export const access_level_order = [
* Compares two access levels based on the hierarchy.
* @returns 1 if level_a is higher, -1 if level_b is higher, 0 if equal.
*/
export const compare_access_levels = function (level_a: string, level_b: string): number {
export const compare_access_levels = function (
level_a: string,
level_b: string
): number {
const index_a = access_level_order.indexOf(level_a || 'anonymous');
const index_b = access_level_order.indexOf(level_b || 'anonymous');
@@ -30,7 +33,9 @@ export const compare_access_levels = function (level_a: string, level_b: string)
// NOTE: I know there is a better more efficient way to do this, but I don't have time for that right now.
// Reminder: super > manager > administrator > trusted > public > authenticated > anonymous
// Super is the highest level. Anonymous is the lowest level.
export const process_permission_checks = function process_permission_checks(access_type: string) {
export const process_permission_checks = function process_permission_checks(
access_type: string
) {
// let access_checks = { 'access_type': null, 'super_check': null };
const access_checks: key_val = {};

View File

@@ -17,7 +17,9 @@ import type { key_val } from './ae_utils';
*/
// Updated 2022-02-11
export const process_data_string = function process_data_string(data_string: string) {
export const process_data_string = function process_data_string(
data_string: string
) {
console.log('*** process_data_string() ***');
// console.log(data_string);
if (!data_string || data_string.length < 1) {
@@ -70,14 +72,18 @@ export const process_data_string = function process_data_string(data_string: str
obj['type'] = 'url';
obj['url'] = data_string;
} else {
console.log('The unknown data string type was found. Returning the string part.');
console.log(
'The unknown data string type was found. Returning the string part.'
);
const unknown_str = data_string.slice(colon_index + 1);
console.log(unknown_str);
obj['str'] = unknown_str;
}
} else {
console.log('The data string type was not found. Returning the entire string.');
console.log(
'The data string type was not found. Returning the entire string.'
);
console.log(data_string);
obj['qr_type'] = 'UNKNOWN';

View File

@@ -1,9 +1,9 @@
export function return_obj_type_path({
obj_type = null,
obj_type_prop_name = null
}: {
obj_type?: string | null,
obj_type_prop_name?: string | null
export function return_obj_type_path({
obj_type = null,
obj_type_prop_name = null
}: {
obj_type?: string | null;
obj_type_prop_name?: string | null;
}) {
console.log('*** return_obj_type_path() ***');
@@ -14,29 +14,69 @@ export function return_obj_type_path({
{ name: 'archive', display: 'Archive', path: 'archive' },
{ name: 'address', display: 'Address', path: 'address' },
{ name: 'archive', display: 'Archive', path: 'archive' },
{ name: 'archive_content', display: 'Archive Content', path: 'archive/content' },
{
name: 'archive_content',
display: 'Archive Content',
path: 'archive/content'
},
{ name: 'contact', display: 'Contact', path: 'contact' },
{ name: 'data_store', display: 'Data Store', path: 'data_store' },
{ name: 'event_abstract', display: 'Event Abstract', path: 'event/abstract' },
{
name: 'event_abstract',
display: 'Event Abstract',
path: 'event/abstract'
},
{ name: 'event_badge', display: 'Event Badge', path: 'event/badge' },
{ name: 'event_device', display: 'Event Device', path: 'event/device' },
{ name: 'event_exhibit', display: 'Event Exhibit', path: 'event/exhibit' },
{
name: 'event_exhibit',
display: 'Event Exhibit',
path: 'event/exhibit'
},
{ name: 'event_file', display: 'Event File', path: 'event/file' },
{ name: 'event_location', display: 'Event Location', path: 'event/location' },
{
name: 'event_location',
display: 'Event Location',
path: 'event/location'
},
{ name: 'event_person', display: 'Event Person', path: 'event/person' },
{ name: 'event_presentation', display: 'Event Presentation', path: 'event/' },
{ name: 'event_presenter', display: 'Event Presenter', path: 'event/presenter' },
{ name: 'event_registration', display: 'Event Registration', path: 'event/registration' },
{ name: 'event_session', display: 'Event Session', path: 'event/session' },
{
name: 'event_presentation',
display: 'Event Presentation',
path: 'event/'
},
{
name: 'event_presenter',
display: 'Event Presenter',
path: 'event/presenter'
},
{
name: 'event_registration',
display: 'Event Registration',
path: 'event/registration'
},
{
name: 'event_session',
display: 'Event Session',
path: 'event/session'
},
{ name: 'event', display: 'Event', path: 'event' },
{ name: 'hosted_file', display: 'Hosted File', path: 'hosted_file' },
{ name: 'journal', display: 'Journal', path: 'journal' },
{ name: 'journal_entry', display: 'Journal Entry', path: 'journal/entry' },
{
name: 'journal_entry',
display: 'Journal Entry',
path: 'journal/entry'
},
{ name: 'order_line', display: 'Order Line', path: 'order/line' },
{ name: 'order', display: 'Order', path: 'order' },
{ name: 'person', display: 'Person', path: 'person' },
{ name: 'post', display: 'Archive', path: 'post' },
{ name: 'post_comment', display: 'Archive Content', path: 'post/comment' },
{
name: 'post_comment',
display: 'Archive Content',
path: 'post/comment'
},
{ name: 'user', display: 'User', path: 'user' }
];

View File

@@ -76,7 +76,10 @@ export function set_obj_prop_display_name({
// console.log(known_obj_type_li_dict[i]);
if (prop_name.startsWith(known_obj_type_li_dict[i].name)) {
// console.log(`Found ${known_obj_type_li_dict[i].name}`);
prop_display_name = prop_name.replace(known_obj_type_li_dict[i].name, '');
prop_display_name = prop_name.replace(
known_obj_type_li_dict[i].name,
''
);
break;
}
}
@@ -101,7 +104,10 @@ export function set_obj_prop_display_name({
// console.log(`Found ${known_obj_type_li_dict[i].name}`);
found_obj_type = known_obj_type_li_dict[i].name;
if (found_obj_type == obj_type) {
prop_display_name = prop_name.replace(known_obj_type_li_dict[i].name, '');
prop_display_name = prop_name.replace(
known_obj_type_li_dict[i].name,
''
);
}
break;

View File

@@ -20,7 +20,8 @@ export function to_title_case(text_string: string) {
array[index - 3] !== ':' &&
array[index + 1] !== ':' &&
/* Ignore small words that start a hyphenated phrase */
(array[index + 1] !== '-' || (array[index - 1] === '-' && array[index + 1] === '-'))
(array[index + 1] !== '-' ||
(array[index - 1] === '-' && array[index + 1] === '-'))
) {
return current.toLowerCase();
}
@@ -36,9 +37,12 @@ export function to_title_case(text_string: string) {
}
/* Capitalize the first letter */
return current.replace(alphanumericPattern, function (match: string) {
return match.toUpperCase();
});
return current.replace(
alphanumericPattern,
function (match: string) {
return match.toUpperCase();
}
);
})
.join('');
}

View File

@@ -1,5 +1,9 @@
import { describe, it, expect } from 'vitest';
import { compare_access_levels, process_permission_checks, access_level_order } from './ae_utils__perm_checks';
import {
compare_access_levels,
process_permission_checks,
access_level_order
} from './ae_utils__perm_checks';
describe('Permission Hierarchy Tests', () => {
describe('compare_access_levels', () => {
@@ -15,7 +19,9 @@ describe('Permission Hierarchy Tests', () => {
it('should correctly identify downgrades', () => {
// Low to High should return -1
expect(compare_access_levels('manager', 'super')).toBe(-1);
expect(compare_access_levels('anonymous', 'authenticated')).toBe(-1);
expect(compare_access_levels('anonymous', 'authenticated')).toBe(
-1
);
});
it('should return 0 for equal levels', () => {
@@ -25,7 +31,9 @@ describe('Permission Hierarchy Tests', () => {
it('should handle null/empty as anonymous', () => {
expect(compare_access_levels('trusted', '')).toBe(1);
expect(compare_access_levels(null as any, 'authenticated')).toBe(-1);
expect(compare_access_levels(null as any, 'authenticated')).toBe(
-1
);
});
});

View File

@@ -62,7 +62,9 @@ export const get_ae_obj_li_for_lu = async function get_ae_obj_li_for_lu({
log_lvl?: number;
}) {
if (log_lvl) {
console.log(`*** get_ae_obj_li_for_lu() *** for_lu_type=${for_lu_type}`);
console.log(
`*** get_ae_obj_li_for_lu() *** for_lu_type=${for_lu_type}`
);
}
// Pass headers as-is — get_object will auto-promote the real account_id from api_cfg.
@@ -331,7 +333,9 @@ export const delete_ae_obj_id_crud = async function delete_ae_obj_id_crud({
log_lvl?: number;
}) {
if (log_lvl) {
console.log(`*** delete_ae_obj_id_crud() *** obj_type: ${obj_type} obj_id: ${obj_id}`);
console.log(
`*** delete_ae_obj_id_crud() *** obj_type: ${obj_type} obj_id: ${obj_id}`
);
}
data['super_key'] = key;
@@ -369,7 +373,6 @@ export const delete_ae_obj_id_crud = async function delete_ae_obj_id_crud({
return object_obj_delete_promise;
};
/* BEGIN: Hosted File Related */
// Updated 2026-01-07
@@ -473,74 +476,75 @@ export const delete_hosted_file = async function delete_hosted_file({
/* BEGIN: Data Store Related */
// Updated 2023-06-29
export const get_data_store_obj_w_code = async function get_data_store_obj_w_code({
api_cfg,
data_store_code,
data_type = 'text',
headers = {},
params = {},
timeout = 25000,
log_lvl = 0
}: {
api_cfg: any;
data_store_code: string;
data_type?: string;
headers?: key_val;
params?: key_val;
timeout?: number;
log_lvl?: number;
}) {
if (log_lvl) {
console.log('*** get_data_store_obj_w_code() ***');
}
export const get_data_store_obj_w_code =
async function get_data_store_obj_w_code({
api_cfg,
data_store_code,
data_type = 'text',
headers = {},
params = {},
timeout = 25000,
log_lvl = 0
}: {
api_cfg: any;
data_store_code: string;
data_type?: string;
headers?: key_val;
params?: key_val;
timeout?: number;
log_lvl?: number;
}) {
if (log_lvl) {
console.log('*** get_data_store_obj_w_code() ***');
}
// let get_item_result = window.localStorage.getItem(code);
// let get_item_result = window.localStorage.getItem(code);
const endpoint = `/data_store/code/${data_store_code}`;
let data_store_obj_get_promise = await api.get_object({
api_cfg: api_cfg,
endpoint: endpoint,
headers: headers,
params: params,
timeout: timeout,
log_lvl: log_lvl
});
if (data_store_obj_get_promise === false) {
console.log('Data Store - RUN AGAIN WITH BACKUP');
const original_api_base_url = api_cfg['base_url'];
const temp_api = api_cfg;
temp_api['base_url'] = temp_api['base_url_bak'];
data_store_obj_get_promise = await api.get_object({
api_cfg: temp_api,
const endpoint = `/data_store/code/${data_store_code}`;
let data_store_obj_get_promise = await api.get_object({
api_cfg: api_cfg,
endpoint: endpoint,
headers: headers,
params: params,
timeout: timeout,
log_lvl: log_lvl
});
temp_api['base_url'] = original_api_base_url;
}
const data_store_obj = data_store_obj_get_promise;
if (data_store_obj_get_promise === false) {
console.log('Data Store - RUN AGAIN WITH BACKUP');
const original_api_base_url = api_cfg['base_url'];
if (data_type == 'text') {
// console.log(data_store_obj.text);
// window.localStorage.setItem(data_store_code, data_store_obj.text);
// localStorage.setItem(data_store_code, data_store_obj.text);
} else if (data_type == 'json') {
// console.log(data_store_obj.json);
// window.localStorage.setItem(data_store_code, JSON.stringify(data_store_obj.json));
// localStorage.setItem(data_store_code, JSON.stringify(data_store_obj.json));
}
const temp_api = api_cfg;
temp_api['base_url'] = temp_api['base_url_bak'];
if (log_lvl > 1) {
console.log('Response Data:', data_store_obj);
}
return data_store_obj;
};
data_store_obj_get_promise = await api.get_object({
api_cfg: temp_api,
endpoint: endpoint,
headers: headers,
params: params,
timeout: timeout,
log_lvl: log_lvl
});
temp_api['base_url'] = original_api_base_url;
}
const data_store_obj = data_store_obj_get_promise;
if (data_type == 'text') {
// console.log(data_store_obj.text);
// window.localStorage.setItem(data_store_code, data_store_obj.text);
// localStorage.setItem(data_store_code, data_store_obj.text);
} else if (data_type == 'json') {
// console.log(data_store_obj.json);
// window.localStorage.setItem(data_store_code, JSON.stringify(data_store_obj.json));
// localStorage.setItem(data_store_code, JSON.stringify(data_store_obj.json));
}
if (log_lvl > 1) {
console.log('Response Data:', data_store_obj);
}
return data_store_obj;
};
/* END: Data Store Related */
/* BEGIN: Utility: Email Related */
@@ -591,8 +595,13 @@ export const send_email = async function send_email({
// Skip email sending entirely when running in a test/Playwright environment.
// Set window.__ae_test_mode = true via addInitScript to activate this guard.
if (typeof globalThis !== 'undefined' && (globalThis as any).__ae_test_mode) {
console.log(`[TEST MODE] send_email() suppressed — would have sent to: ${to_email}, subject: ${subject}`);
if (
typeof globalThis !== 'undefined' &&
(globalThis as any).__ae_test_mode
) {
console.log(
`[TEST MODE] send_email() suppressed — would have sent to: ${to_email}, subject: ${subject}`
);
return null;
}
@@ -663,7 +672,7 @@ const obj = {
create_nested_obj: create_nested_obj,
update_ae_obj: update_ae_obj,
update_nested_obj: update_nested_obj,
delete_ae_obj:delete_ae_obj,
delete_ae_obj: delete_ae_obj,
delete_nested_ae_obj: delete_nested_ae_obj,
create_ae_obj_crud: create_ae_obj_crud,
update_ae_obj_id_crud: update_ae_obj_id_crud,
@@ -676,4 +685,4 @@ const obj = {
send_email: send_email
};
export const api = obj;
// module.exports = api;
// module.exports = api;

View File

@@ -1,295 +1,320 @@
<script lang="ts">
// *** Import Svelte specific
import { onDestroy, onMount, tick } from 'svelte';
import { afterNavigate } from '$app/navigation';
// *** Import Svelte specific
import { onDestroy, onMount, tick } from 'svelte';
import { afterNavigate } from '$app/navigation';
// *** Import other supporting libraries
// import { liveQuery } from "dexie";
import { Lock, LockOpen, RefreshCw, ShieldEllipsis, ShieldMinus, ShieldPlus, ShieldUser, Unlink, User, UserCheck, UserRound, Wand2 } from '@lucide/svelte';
// *** Import Aether specific variables and functions
import { ae_util } from '$lib/ae_utils/ae_utils';
import { ae_loc, ae_sess, ae_api, slct, slct_trigger } from '$lib/stores/ae_stores';
// import { core_func } from '$lib/ae_core/ae_core_functions';
// Ideally the Event related stores should not be imported here?
import { events_loc } from '$lib/stores/ae_events_stores';
// import { db_events } from "$lib/db_events";
// *** Import other supporting libraries
// import { liveQuery } from "dexie";
import {
Lock,
LockOpen,
RefreshCw,
ShieldEllipsis,
ShieldMinus,
ShieldPlus,
ShieldUser,
Unlink,
User,
UserCheck,
UserRound,
Wand2
} from '@lucide/svelte';
// *** Import Aether specific variables and functions
import { ae_util } from '$lib/ae_utils/ae_utils';
import {
ae_loc,
ae_sess,
ae_api,
slct,
slct_trigger
} from '$lib/stores/ae_stores';
// import { core_func } from '$lib/ae_core/ae_core_functions';
// Ideally the Event related stores should not be imported here?
import { events_loc } from '$lib/stores/ae_events_stores';
// import { db_events } from "$lib/db_events";
// export let hidden: boolean = false;
// export let hidden: boolean = false;
// *** Setup Svelte properties
interface Props {
log_lvl?: number;
hide?: null | boolean;
focus_input: boolean;
expand?: boolean;
show_passcode_input: boolean;
trigger_clear_access: null | boolean;
// *** Setup Svelte properties
interface Props {
log_lvl?: number;
hide?: null | boolean;
focus_input: boolean;
expand?: boolean;
show_passcode_input: boolean;
trigger_clear_access: null | boolean;
}
let {
log_lvl = $bindable(0),
hide = $bindable(false),
focus_input = $bindable(false),
expand = $bindable(false),
show_passcode_input = $bindable(false),
trigger_clear_access = $bindable(null)
}: Props = $props();
let entered_passcode: null | string = $state(null);
let checked_passcode: null | string = $state(null);
// let password_checked: boolean = $state(false);
// let entered_passcode: null|string = '';
// let show_passcode_input: boolean = $state(false);
// let show_passcode_input: boolean = false;
// let trigger: null|string|boolean = null;
let trigger: null | string | boolean = $state(null);
// const dispatch = createEventDispatcher();
// WARNING: There is a bug (I think) around here related to the entered_passcode not being cleared. There seems to be something different about how Svelte handles state in this component compared to the others. This might be related to the `$effect` or `$derived` usage. Maybe there are conflicting things trying to update the $ae_loc store at the same time.
onMount(() => {
// log_lvl = 2;
if (log_lvl > 1) {
console.log('** Element Mounted: ** Element Access Type');
}
entered_passcode = '';
trigger = null;
// /** @type {HTMLElement | null} */
// const to_focus = document.getElementById('access_passcode_input');
// to_focus?.focus();
});
onDestroy(() => {
if (log_lvl > 1) {
console.log('** Element Destroyed: ** Element Access Type');
}
let {
log_lvl = $bindable(0),
hide = $bindable(false),
focus_input = $bindable(false),
expand = $bindable(false),
show_passcode_input = $bindable(false),
trigger_clear_access = $bindable(null)
}: Props = $props();
// Clean up any references or listeners if needed
entered_passcode = null; // Clear the entered passcode
show_passcode_input = false;
let entered_passcode: null | string = $state(null);
let checked_passcode: null | string = $state(null);
// let password_checked: boolean = $state(false);
// let entered_passcode: null|string = '';
// let show_passcode_input: boolean = $state(false);
// let show_passcode_input: boolean = false;
// Reset trigger
trigger = null;
});
// let trigger: null|string|boolean = null;
let trigger: null | string | boolean = $state(null);
// afterNavigate(() => {
// /** @type {HTMLElement | null} */
// const to_focus = document.getElementById('access_passcode_input');
// to_focus?.focus();
// });
// const dispatch = createEventDispatcher();
$effect(() => {
if (
entered_passcode &&
entered_passcode.length >= 5 &&
entered_passcode != checked_passcode
) {
checked_passcode = entered_passcode;
if (log_lvl) {
console.log(`entered_passcode=${entered_passcode}`);
}
handle_check_access_type_passcode();
}
});
// WARNING: There is a bug (I think) around here related to the entered_passcode not being cleared. There seems to be something different about how Svelte handles state in this component compared to the others. This might be related to the `$effect` or `$derived` usage. Maybe there are conflicting things trying to update the $ae_loc store at the same time.
onMount(() => {
$effect(() => {
if (trigger && $ae_loc.access_type) {
trigger = false;
if (log_lvl) {
console.log(`$ae_loc.access_type=${$ae_loc.access_type}`);
}
let access_checks_results = ae_util.process_permission_checks(
$ae_loc.access_type
);
$ae_loc = { ...$ae_loc, ...access_checks_results };
$ae_loc = $ae_loc;
$ae_loc.sys_menu.expand = false;
} else if (trigger) {
trigger = false;
if (log_lvl) {
console.log(`$ae_loc.access_type=not set`);
}
// Send an empty string to reset the permissions. This is the same as sending 'anonymous'.
let access_checks_results = ae_util.process_permission_checks('');
$ae_loc = { ...$ae_loc, ...access_checks_results };
$ae_loc = $ae_loc;
}
});
// This does not seem to work. I feel like it should though...?
$effect(() => {
if (!hide && focus_input) {
focus_input = false;
// log_lvl = 2;
// await tick();
// document.getElementById('access_passcode_input')?.focus();
if (log_lvl > 1) {
console.log('** Element Mounted: ** Element Access Type');
console.log('Effect: Setting focus on the passcode input field');
}
entered_passcode = '';
trigger = null;
/** @type {HTMLElement | null} */
const to_focus = document.getElementById('access_passcode_input');
to_focus?.focus();
}
});
// /** @type {HTMLElement | null} */
// const to_focus = document.getElementById('access_passcode_input');
// to_focus?.focus();
});
onDestroy(() => {
if (log_lvl > 1) {
console.log('** Element Destroyed: ** Element Access Type');
$effect(() => {
if (trigger_clear_access) {
trigger_clear_access = false;
if (log_lvl) {
console.log(`trigger_clear_access=${trigger_clear_access}`);
}
handle_clear_access();
}
});
// Clean up any references or listeners if needed
entered_passcode = null; // Clear the entered passcode
show_passcode_input = false;
function handle_check_access_type_passcode() {
if (log_lvl > 1) {
console.log(
`*** handle_check_access_type_passcode() *** passcode list:`,
$ae_loc.site_access_code_kv
);
}
// Reset trigger
trigger = null;
});
// Reminder: super > manager > administrator > trusted > public > authenticated > anonymous
// afterNavigate(() => {
// /** @type {HTMLElement | null} */
// const to_focus = document.getElementById('access_passcode_input');
// to_focus?.focus();
// });
$effect(() => {
if (entered_passcode && entered_passcode.length >= 5) {
if (
entered_passcode &&
entered_passcode.length >= 5 &&
entered_passcode != checked_passcode
$ae_loc.site_access_code_kv.super.length >= 8 &&
$ae_loc.site_access_code_kv.super == entered_passcode
) {
checked_passcode = entered_passcode;
if (log_lvl) {
console.log(`entered_passcode=${entered_passcode}`);
}
handle_check_access_type_passcode();
}
});
console.log('Super passcode matched');
$effect(() => {
if (trigger && $ae_loc.access_type) {
trigger = false;
if (log_lvl) {
console.log(`$ae_loc.access_type=${$ae_loc.access_type}`);
}
window.localStorage.setItem('access_type', 'super');
let access_checks_results = ae_util.process_permission_checks($ae_loc.access_type);
$ae_loc.access_type = 'super';
} else if (
$ae_loc.site_access_code_kv.manager.length >= 5 &&
$ae_loc.site_access_code_kv.manager == entered_passcode
) {
console.log('Manager passcode matched');
$ae_loc = { ...$ae_loc, ...access_checks_results };
$ae_loc = $ae_loc;
$ae_loc.sys_menu.expand = false;
} else if (trigger) {
trigger = false;
if (log_lvl) {
console.log(`$ae_loc.access_type=not set`);
}
window.localStorage.setItem('access_type', 'manager');
// Send an empty string to reset the permissions. This is the same as sending 'anonymous'.
let access_checks_results = ae_util.process_permission_checks('');
$ae_loc.access_type = 'manager';
} else if (
$ae_loc.site_access_code_kv.administrator.length >= 5 &&
$ae_loc.site_access_code_kv.administrator == entered_passcode
) {
console.log('Administrator passcode matched');
$ae_loc = { ...$ae_loc, ...access_checks_results };
$ae_loc = $ae_loc;
}
});
window.localStorage.setItem('access_type', 'administrator');
// This does not seem to work. I feel like it should though...?
$effect(() => {
if (!hide && focus_input) {
focus_input = false;
// log_lvl = 2;
// await tick();
// document.getElementById('access_passcode_input')?.focus();
$ae_loc.access_type = 'administrator';
} else if (
$ae_loc.site_access_code_kv.trusted.length >= 5 &&
$ae_loc.site_access_code_kv.trusted == entered_passcode
) {
console.log('Trusted passcode matched');
window.localStorage.setItem('access_type', 'trusted');
$ae_loc.access_type = 'trusted';
} else if (
$ae_loc.site_access_code_kv.public.length >= 5 &&
$ae_loc.site_access_code_kv.public == entered_passcode
) {
console.log('Public passcode matched');
window.localStorage.setItem('access_type', 'public');
$ae_loc.access_type = 'public';
} else if (
$ae_loc.site_access_code_kv.authenticated == entered_passcode
) {
console.log('Authenticated passcode matched');
window.localStorage.setItem('access_type', 'authenticated');
$ae_loc.access_type = 'authenticated';
} else {
if (log_lvl > 1) {
console.log('Effect: Setting focus on the passcode input field');
}
/** @type {HTMLElement | null} */
const to_focus = document.getElementById('access_passcode_input');
to_focus?.focus();
}
});
$effect(() => {
if (trigger_clear_access) {
trigger_clear_access = false;
if (log_lvl) {
console.log(`trigger_clear_access=${trigger_clear_access}`);
}
handle_clear_access();
}
});
function handle_check_access_type_passcode() {
if (log_lvl > 1) {
console.log(
`*** handle_check_access_type_passcode() *** passcode list:`,
$ae_loc.site_access_code_kv
);
}
// Reminder: super > manager > administrator > trusted > public > authenticated > anonymous
if (entered_passcode && entered_passcode.length >= 5) {
if (
$ae_loc.site_access_code_kv.super.length >= 8 &&
$ae_loc.site_access_code_kv.super == entered_passcode
) {
console.log('Super passcode matched');
window.localStorage.setItem('access_type', 'super');
$ae_loc.access_type = 'super';
} else if (
$ae_loc.site_access_code_kv.manager.length >= 5 &&
$ae_loc.site_access_code_kv.manager == entered_passcode
) {
console.log('Manager passcode matched');
window.localStorage.setItem('access_type', 'manager');
$ae_loc.access_type = 'manager';
} else if (
$ae_loc.site_access_code_kv.administrator.length >= 5 &&
$ae_loc.site_access_code_kv.administrator == entered_passcode
) {
console.log('Administrator passcode matched');
window.localStorage.setItem('access_type', 'administrator');
$ae_loc.access_type = 'administrator';
} else if (
$ae_loc.site_access_code_kv.trusted.length >= 5 &&
$ae_loc.site_access_code_kv.trusted == entered_passcode
) {
console.log('Trusted passcode matched');
window.localStorage.setItem('access_type', 'trusted');
$ae_loc.access_type = 'trusted';
} else if (
$ae_loc.site_access_code_kv.public.length >= 5 &&
$ae_loc.site_access_code_kv.public == entered_passcode
) {
console.log('Public passcode matched');
window.localStorage.setItem('access_type', 'public');
$ae_loc.access_type = 'public';
} else if ($ae_loc.site_access_code_kv.authenticated == entered_passcode) {
console.log('Authenticated passcode matched');
window.localStorage.setItem('access_type', 'authenticated');
$ae_loc.access_type = 'authenticated';
} else {
if (log_lvl > 1) {
console.log('Entered passcode does not match any of the site access codes.');
}
if ($ae_loc.access_type != 'anonymous') {
console.log('Access type is not anonymous');
}
// window.localStorage.setItem('access_type', 'anonymous');
// $ae_loc.access_type = 'anonymous';
// trigger = 'process_permission_check';
// $ae_loc = $ae_loc; // Trigger Svelte just in case
// ae_loc.set($ae_loc);
// console.log($ae_loc);
// dispatch_access_type_changed();
return false;
console.log(
'Entered passcode does not match any of the site access codes.'
);
}
entered_passcode = '';
show_passcode_input = false;
trigger = 'process_permission_check';
$ae_loc.app_cfg.show_element__menu = false;
$ae_loc.app_cfg.show_element__menu_btn = true;
// WARNING 2024-08-21: For some reason the config element does not auto show or hide when the access type changes.
if (!$ae_loc.iframe && $ae_loc.authenticated_access) {
$ae_loc.app_cfg.show_element__access_type = true;
$ae_loc.app_cfg.show_element__cfg = true;
} else if ($ae_loc.iframe && $ae_loc.trusted_access) {
$ae_loc.app_cfg.show_element__access_type = true;
$ae_loc.app_cfg.show_element__cfg = true;
} else {
$ae_loc.app_cfg.show_element__access_type = true;
$ae_loc.app_cfg.show_element__cfg = false;
if ($ae_loc.access_type != 'anonymous') {
console.log('Access type is not anonymous');
}
// window.localStorage.setItem('access_type', 'anonymous');
// $ae_loc.access_type = 'anonymous';
// trigger = 'process_permission_check';
// $ae_loc = $ae_loc; // Trigger Svelte just in case
// ae_loc.set($ae_loc);
// console.log($ae_loc);
// dispatch_access_type_changed();
return true;
} else {
if (log_lvl > 1) {
console.log('Entered passcode too short.');
}
// $ae_loc.access_type = null; // 'anonymous';
// dispatch_access_type_changed()
return null;
return false;
}
}
function handle_clear_access() {
// console.log('handle_clear_access()');
// NOTE: I think it makes since to reset this to anonymous even if logged in as an admin or similar.
window.localStorage.setItem('access_type', 'anonymous');
// $ae_loc.access_type = null; // 'anonymous';
// Revert back to the user's access type after quick access (temporarily escalate permissions) is turned off.
$ae_loc.access_type = $ae_loc.user_access_type ?? 'anonymous';
entered_passcode = '';
show_passcode_input = false;
trigger = 'process_permission_check';
entered_passcode = ''; // Clear the entered passcode
// Reset so the same passcode can be re-entered after clearing.
// Without this, the $effect guard (entered_passcode != checked_passcode) silently
// blocks re-entry of the same passcode until the component remounts (page refresh).
checked_passcode = null;
show_passcode_input = true;
$ae_loc.app_cfg.show_element__menu = false;
$ae_loc.app_cfg.show_element__menu_btn = true;
$ae_loc.edit_mode = false;
// WARNING 2024-08-21: For some reason the config element does not auto show or hide when the access type changes.
if (!$ae_loc.iframe && $ae_loc.authenticated_access) {
$ae_loc.app_cfg.show_element__access_type = true;
$ae_loc.app_cfg.show_element__cfg = true;
} else if ($ae_loc.iframe && $ae_loc.trusted_access) {
$ae_loc.app_cfg.show_element__access_type = true;
$ae_loc.app_cfg.show_element__cfg = true;
} else {
$ae_loc.app_cfg.show_element__access_type = true;
$ae_loc.app_cfg.show_element__cfg = false;
}
// dispatch_access_type_changed();
return true;
} else {
if (log_lvl > 1) {
console.log('Entered passcode too short.');
}
// $ae_loc.access_type = null; // 'anonymous';
// dispatch_access_type_changed()
return null;
}
}
function handle_clear_access() {
// console.log('handle_clear_access()');
// NOTE: I think it makes since to reset this to anonymous even if logged in as an admin or similar.
window.localStorage.setItem('access_type', 'anonymous');
// $ae_loc.access_type = null; // 'anonymous';
// Revert back to the user's access type after quick access (temporarily escalate permissions) is turned off.
$ae_loc.access_type = $ae_loc.user_access_type ?? 'anonymous';
trigger = 'process_permission_check';
entered_passcode = ''; // Clear the entered passcode
// Reset so the same passcode can be re-entered after clearing.
// Without this, the $effect guard (entered_passcode != checked_passcode) silently
// blocks re-entry of the same passcode until the component remounts (page refresh).
checked_passcode = null;
show_passcode_input = true;
$ae_loc.app_cfg.show_element__menu = false;
$ae_loc.app_cfg.show_element__menu_btn = true;
$ae_loc.edit_mode = false;
return true;
}
</script>
<section
@@ -298,24 +323,23 @@
ae_access_type
hidden-print
bg-blue-100 text-gray-900
dark:bg-blue-800 dark:text-gray-200
flex w-full
flex-col flex-wrap
flex flex-col flex-wrap gap-1
items-end justify-center
items-end justify-center gap-1 border-2
border-gray-200 bg-blue-100
w-full
p-1
border-2 border-gray-200
text-gray-900
transition-all delay-150
duration-300 delay-150 hover:delay-1000 hover:ease-out
transition-all
duration-300 hover:delay-1000 hover:ease-out dark:bg-blue-800
dark:text-gray-200
"
class:hidden={hide}
>
class:hidden={hide}>
<!-- class:hidden={!$ae_sess.show__sign_in_out__fields} -->
<header class="ae_header hidden">
<h2 class="text-sm text-center font-semibold">Passcode Sign In</h2>
<h2 class="text-center text-sm font-semibold">Passcode Sign In</h2>
</header>
<!-- Show list of authorized sessions and presentations for a user -->
@@ -349,9 +373,8 @@
// tick();
return false;
}}
class="btn btn-sm preset-tonal-success border border-success-500 hover:preset-filled-success-500 transition-all hover:transition-all *:hover:inline"
title="Syncing the local configuration with the remote configuration."
>
class="btn btn-sm preset-tonal-success border-success-500 hover:preset-filled-success-500 border transition-all hover:transition-all *:hover:inline"
title="Syncing the local configuration with the remote configuration.">
<RefreshCw size="1em" class="m-1" />
<span class="hidden"> Sync </span>
</button>
@@ -369,9 +392,8 @@
// tick();
return true;
}}
class="btn btn-sm preset-tonal-warning border border-warning-500 hover:preset-filled-warning-500 transition-all hover:transition-all *:hover:inline"
title="Currently not syncing with the remote server. Re-sync the local configuration with the remote configuration?"
>
class="btn btn-sm preset-tonal-warning border-warning-500 hover:preset-filled-warning-500 border transition-all hover:transition-all *:hover:inline"
title="Currently not syncing with the remote server. Re-sync the local configuration with the remote configuration?">
<Unlink size="1em" class="m-1" />
<span class="hidden"> Re-sync? </span>
</button>
@@ -411,7 +433,8 @@
{/if}
</div>
<div class="flex flex-row flex-wrap gap-1 items-end justify-end w-full transition-all">
<div
class="flex w-full flex-row flex-wrap items-end justify-end gap-1 transition-all">
{#if $ae_loc?.access_type && $ae_loc?.access_type == 'anonymous'}
<span>
<button
@@ -421,15 +444,16 @@
show_passcode_input = !show_passcode_input;
}}
class="btn btn-sm preset-tonal-success hover:preset-filled-warning-500 access_type_unlock_btn transition-all"
title={show_passcode_input ? 'Cancel passcode entry' : 'Enter a passcode to unlock access'}
>
title={show_passcode_input
? 'Cancel passcode entry'
: 'Enter a passcode to unlock access'}>
{#if show_passcode_input}
<LockOpen size="1em" class="mx-1" />
<span>Cancel</span>
{:else}
<Lock size="1em" class="mx-1" />
<span class="lock_icon">Locked</span>
<LockOpen size="1em" class="mx-1 unlock_icon hidden" />
<LockOpen size="1em" class="unlock_icon mx-1 hidden" />
<span class="unlock_text">Access?</span>
{/if}
</button>
@@ -437,14 +461,13 @@
{/if}
{#if $ae_loc?.access_type && $ae_loc?.access_type != 'anonymous'}
<span class="flex flex-row gap-1 items-center justify-center">
<span class="flex flex-row items-center justify-center gap-1">
<!-- <span class="fas fa-unlock mx-1"></span> -->
<ShieldPlus class="inline-block" />
<span
class="*:hover:inline"
title={`Current access type/level: ${$ae_loc.access_type}`}
>
title={`Current access type/level: ${$ae_loc.access_type}`}>
{#if $ae_loc.access_type == 'super'}
<Wand2 size="1em" class="m-1" />
<span class="hidden">Super</span>
@@ -478,9 +501,8 @@
// trigger_clear_access = true;
show_passcode_input = true;
}}
class="btn btn-sm variant-outline-surface hover:preset-tonal-warning border border-warning-500 transition-all"
title={`Current user access level: "${$ae_loc.user_access_type}". Click use passcode for a different access level.`}
>
class="btn btn-sm variant-outline-surface hover:preset-tonal-warning border-warning-500 border transition-all"
title={`Current user access level: "${$ae_loc.user_access_type}". Click use passcode for a different access level.`}>
<!-- <span class="fas fa-lock mx-1"></span> -->
<!-- <ShieldMinus /> -->
<ShieldEllipsis class="inline-block" />
@@ -495,9 +517,8 @@
show_passcode_input = true;
// show_passcode_input = true;
}}
class="btn btn-sm variant-outline-warning hover:preset-tonal-warning border border-warning-500 transition-all"
title={`Current access level: "${$ae_loc.access_type}". Click to clear the temporary access level.`}
>
class="btn btn-sm variant-outline-warning hover:preset-tonal-warning border-warning-500 border transition-all"
title={`Current access level: "${$ae_loc.access_type}". Click to clear the temporary access level.`}>
<!-- <span class="fas fa-lock mx-1"></span> -->
<ShieldMinus class="inline-block" />
Clear?
@@ -507,7 +528,8 @@
{/if}
{#if show_passcode_input}
<span class="flex flex-row gap-1 items-center justify-between w-full">
<span
class="flex w-full flex-row items-center justify-between gap-1">
<span>
<ShieldEllipsis class="inline-block text-gray-500" />
<span class="unlock_text text-sm">Passcode:</span>
@@ -521,8 +543,7 @@
class:hidden={!show_passcode_input}
type="text"
placeholder="Passcode"
autofocus={show_passcode_input}
/>
autofocus={show_passcode_input} />
<!-- <div class="current_text transition-all">{$ae_loc.access_type}</div> -->
</span>
{/if}
@@ -530,8 +551,8 @@
</section>
<style lang="scss">
/* BEGIN: AE's Svelte Quick Access Type component */
/*
/* BEGIN: AE's Svelte Quick Access Type component */
/*
#xxx-AE-Quick-Access-Type {
// position: absolute;
position: fixed;
@@ -569,7 +590,7 @@
}
*/
/*
/*
#xxx-AE-Quick-Access-Type:hover {
border-top: solid thin hsla(0,0%,0%,.95);
@@ -586,21 +607,21 @@
}
*/
/* #Access-Type .unlock_text {
/* #Access-Type .unlock_text {
transition: width 2s, height 2s, background-color 2s, transform 2s;
} */
/* END: Svelte Access Type component */
/* END: Svelte Access Type component */
.access_type_unlock_btn .unlock_text {
display: none;
}
.access_type_unlock_btn .unlock_text {
display: none;
}
.access_type_unlock_btn:hover .unlock_text {
display: initial;
/* outline: solid thin red; */
}
.access_type_unlock_btn:hover .unlock_text {
display: initial;
/* outline: solid thin red; */
}
/*
/*
.ae_access_type .current_text {
display: none;
}

View File

@@ -1,44 +1,54 @@
<script lang="ts" module>
declare global {
interface Window {
dataLayer: any[];
gtag: (...args: any[]) => void;
}
declare global {
interface Window {
dataLayer: any[];
gtag: (...args: any[]) => void;
}
}
</script>
<script lang="ts">
import { browser } from '$app/environment';
import { browser } from '$app/environment';
interface Props {
log_lvl?: number;
site_google_tracking_id?: string;
}
interface Props {
log_lvl?: number;
site_google_tracking_id?: string;
}
let { log_lvl = 0, site_google_tracking_id = $bindable('') }: Props = $props();
let { log_lvl = 0, site_google_tracking_id = $bindable('') }: Props = $props();
$effect(() => {
if (browser && site_google_tracking_id) {
if (log_lvl) {
console.log(`AE Analytics: site_google_tracking_id = `, site_google_tracking_id);
}
$effect(() => {
if (browser && site_google_tracking_id) {
if (log_lvl) {
console.log(
`AE Analytics: site_google_tracking_id = `,
site_google_tracking_id
);
}
window.dataLayer = window.dataLayer || [];
window.gtag = window.gtag || function gtag() {
window.dataLayer = window.dataLayer || [];
window.gtag =
window.gtag ||
function gtag() {
window.dataLayer.push(arguments);
};
window.gtag('js', new Date());
window.gtag('config', site_google_tracking_id);
if (log_lvl) {
console.log(`AE Analytics: Google Analytics Tracking ID = `, site_google_tracking_id);
}
window.gtag('js', new Date());
window.gtag('config', site_google_tracking_id);
if (log_lvl) {
console.log(
`AE Analytics: Google Analytics Tracking ID = `,
site_google_tracking_id
);
}
});
}
});
</script>
<svelte:head>
{#if site_google_tracking_id}
<script async src="https://www.googletagmanager.com/gtag/js?id={site_google_tracking_id}"></script>
<script
async
src="https://www.googletagmanager.com/gtag/js?id={site_google_tracking_id}"></script>
{/if}
</svelte:head>

View File

@@ -1,103 +1,120 @@
<script lang="ts">
import { Code, Eraser, LockOpen, RefreshCw, Settings, ShieldCheck, ShieldUser, Trash2, UserRound, Users } from '@lucide/svelte';
import { ae_util } from '$lib/ae_utils/ae_utils';
import { ae_loc, ae_sess, ae_api, slct, slct_trigger } from '$lib/stores/ae_stores';
import E_app_url_builder from '$lib/app_components/e_app_url_builder.svelte';
import {
Code,
Eraser,
LockOpen,
RefreshCw,
Settings,
ShieldCheck,
ShieldUser,
Trash2,
UserRound,
Users
} from '@lucide/svelte';
import { ae_util } from '$lib/ae_utils/ae_utils';
import {
ae_loc,
ae_sess,
ae_api,
slct,
slct_trigger
} from '$lib/stores/ae_stores';
import E_app_url_builder from '$lib/app_components/e_app_url_builder.svelte';
// import Element_theme from '$lib/e_app_theme.svelte';
// import Element_theme from '$lib/e_app_theme.svelte';
let notes: null | string = null;
let all: boolean = false;
let notes: null | string = null;
let all: boolean = false;
interface Props {
log_lvl?: number;
hide?: null | boolean;
expand?: boolean;
// export let theme_mode: null|string = null;
// set_theme_mode?: null|boolean;
// export let theme_name: null|string = null;
// set_theme_name?: null|boolean;
}
interface Props {
log_lvl?: number;
hide?: null | boolean;
expand?: boolean;
// export let theme_mode: null|string = null;
// set_theme_mode?: null|boolean;
// export let theme_name: null|string = null;
// set_theme_name?: null|boolean;
}
let {
log_lvl = $bindable(0),
hide = $bindable(false),
expand = $bindable(false)
// set_theme_mode = null,
// set_theme_name = null
}: Props = $props();
let {
log_lvl = $bindable(0),
hide = $bindable(false),
expand = $bindable(false)
// set_theme_mode = null,
// set_theme_name = null
}: Props = $props();
// const dispatch = createEventDispatcher();
// const dispatch = createEventDispatcher();
// onMount(() => {
// // console.log('** Element Mounted: ** Element App Config');
// if (set_theme_mode) {
// $slct_trigger = 'set_theme_mode';
// }
// if (set_theme_name) {
// $slct_trigger = 'set_theme_name';
// }
// });
// onMount(() => {
// // console.log('** Element Mounted: ** Element App Config');
// if (set_theme_mode) {
// $slct_trigger = 'set_theme_mode';
// }
// if (set_theme_name) {
// $slct_trigger = 'set_theme_name';
// }
// });
// $: if ($slct_trigger == 'set_theme_mode' && $ae_loc?.app_cfg?.theme_mode) {
// console.log(`$ae_loc.app_cfg.theme_mode=${$ae_loc?.app_cfg?.theme_mode}`);
// $slct_trigger = null;
// if ($ae_loc.app_cfg.theme_mode == 'light') {
// document.documentElement.classList.remove('dark');
// document.documentElement.classList.add('light');
// } else if ($ae_loc.app_cfg.theme_mode == 'dark') {
// document.documentElement.classList.remove('light');
// document.documentElement.classList.add('dark');
// }
// }
// $: if ($slct_trigger == 'set_theme_mode' && $ae_loc?.app_cfg?.theme_mode) {
// console.log(`$ae_loc.app_cfg.theme_mode=${$ae_loc?.app_cfg?.theme_mode}`);
// $slct_trigger = null;
// if ($ae_loc.app_cfg.theme_mode == 'light') {
// document.documentElement.classList.remove('dark');
// document.documentElement.classList.add('light');
// } else if ($ae_loc.app_cfg.theme_mode == 'dark') {
// document.documentElement.classList.remove('light');
// document.documentElement.classList.add('dark');
// }
// }
// $: if ($slct_trigger == 'set_theme_name' && $ae_loc?.app_cfg?.theme_name) {
// console.log(`$ae_loc?.app_cfg?.theme_name=${$ae_loc?.app_cfg?.theme_name}`);
// $slct_trigger = null;
// // Update the body attribute named "data-theme" to the current theme name.
// document.body.setAttribute('data-theme', $ae_loc?.app_cfg?.theme_name);
// }
// $: if ($slct_trigger == 'set_theme_name' && $ae_loc?.app_cfg?.theme_name) {
// console.log(`$ae_loc?.app_cfg?.theme_name=${$ae_loc?.app_cfg?.theme_name}`);
// $slct_trigger = null;
// // Update the body attribute named "data-theme" to the current theme name.
// document.body.setAttribute('data-theme', $ae_loc?.app_cfg?.theme_name);
// }
// $: if (entered_passcode && entered_passcode.length >= 5) {
// console.log(`entered_passcode=${entered_passcode}`);
// handle_check_access_type_passcode();
// }
// $: if (entered_passcode && entered_passcode.length >= 5) {
// console.log(`entered_passcode=${entered_passcode}`);
// handle_check_access_type_passcode();
// }
// $: if (trigger && $ae_loc.access_type) {
// console.log(`$ae_loc.access_type=${$ae_loc.access_type}`);
// $: if (trigger && $ae_loc.access_type) {
// console.log(`$ae_loc.access_type=${$ae_loc.access_type}`);
// let access_checks_results = ae_util.process_permission_checks($ae_loc.access_type);
// let access_checks_results = ae_util.process_permission_checks($ae_loc.access_type);
// $ae_loc = {...$ae_loc, ...access_checks_results};
// } else if (trigger) {
// console.log(`$ae_loc.access_type=not set`);
// $ae_loc = {...$ae_loc, ...access_checks_results};
// } else if (trigger) {
// console.log(`$ae_loc.access_type=not set`);
// // Send an empty string to reset the permissions. This is the same as sending 'anonymous'.
// let access_checks_results = ae_util.process_permission_checks('');
// // Send an empty string to reset the permissions. This is the same as sending 'anonymous'.
// let access_checks_results = ae_util.process_permission_checks('');
// $ae_loc = {...$ae_loc, ...access_checks_results};
// }
// $ae_loc = {...$ae_loc, ...access_checks_results};
// }
function handle_something() {
// console.log('*** handle_something() ***');
}
function handle_something() {
// console.log('*** handle_something() ***');
}
function handle_clear_storage(item: null | string) {
// console.log('*** handle_clear_storage() ***');
// window.localStorage.setItem('access_type', 'anonymous');
// return true;
}
function handle_clear_storage(item: null | string) {
// console.log('*** handle_clear_storage() ***');
// window.localStorage.setItem('access_type', 'anonymous');
// return true;
}
// function dispatch_something_changed() {
// console.log('*** dispatch_something_changed() ***');
// function dispatch_something_changed() {
// console.log('*** dispatch_something_changed() ***');
// console.log(ae_util);
// console.log($ae_loc);
// console.log(ae_util);
// console.log($ae_loc);
// dispatch('access_type_changed', {
// access_type: $ae_loc.access_type
// });
// }
// dispatch('access_type_changed', {
// access_type: $ae_loc.access_type
// });
// }
</script>
<!-- transition duration-500 delay-1000 hover:duration-500 hover:delay-1000 hover:transition-all -->
@@ -107,30 +124,28 @@
ae_app_cfg
hidden-print
bg-blue-100 text-gray-900
dark:bg-blue-800 dark:text-gray-200
flex w-72
max-w-72 flex-col
flex flex-col flex-wrap gap-1
items-end justify-center
w-72 max-w-72
p-1
flex-wrap items-end justify-center gap-1
border-2 border-gray-200
duration-300 delay-150 hover:delay-1000 hover:ease-out
transition-all
bg-blue-100 p-1
text-gray-900
transition-all delay-150
duration-300 hover:delay-1000 hover:ease-out dark:bg-blue-800
dark:text-gray-200
"
class:hidden={hide}
>
class:hidden={hide}>
<header class:hidden={!expand} class="ae_header w-full">
<h2 class="text-sm text-center font-semibold">Config</h2>
<h2 class="text-center text-sm font-semibold">Config</h2>
</header>
<div
class="ae_cfg_content text-xs space-y-4 my-4"
class="ae_cfg_content my-4 space-y-4 text-xs"
class:hidden={!expand}
data-sveltekit-preload-data="false"
>
data-sveltekit-preload-data="false">
<section class="space-y-2">
<div>
<h2 class="strong">Access Type:</h2>
@@ -172,12 +187,16 @@
</a>
{#if $ae_loc.iframe}
<a class="btn btn-sm preset-tonal-secondary" href="/?iframe=false">
<a
class="btn btn-sm preset-tonal-secondary"
href="/?iframe=false">
<Code size="1em" class="mx-1" />
Exit iframe Mode
</a>
{:else}
<a class="btn btn-sm preset-tonal-secondary" href="/?iframe=true">
<a
class="btn btn-sm preset-tonal-secondary"
href="/?iframe=true">
<Code size="1em" class="mx-1" />
Use iframe Mode
</a>
@@ -190,8 +209,7 @@
onclick={() => {
// $ae_loc.
window.location.reload();
}}
>
}}>
<RefreshCw size="1em" class="mx-1" />
Reload &
<Trash2 size="1em" class="mx-1" />
@@ -219,8 +237,7 @@
window.location.reload();
// alert('Local and Session Storage cleared and Indexed DBs deleted. You will probably want to refresh the page.');
}}
>
}}>
<Eraser size="1em" class="mx-1" />
Clear Storage & DB
</button>
@@ -235,7 +252,7 @@
<!-- class:justify-between={expand}
class:justify-end={!expand} -->
<div class="flex flex-row gap-2 items-center justify-between w-full">
<div class="flex w-full flex-row items-center justify-between gap-2">
<!-- {#if !expand} -->
<span>
{#if $ae_loc.access_type && $ae_loc.access_type != 'anonymous'}
@@ -270,14 +287,13 @@ class:justify-end={!expand} -->
<button
class="
ae_cfg_btn
btn btn-sm text-sm
preset-tonal-warning
group transition-all
btn btn-sm preset-tonal-warning
group
text-sm transition-all
"
onclick={() => {
expand = !expand;
}}
>
}}>
<!-- <span class="fas fa-cog m-1"></span> -->
<span class="inline-block" title="Settings">
<Settings class="m-1" />
@@ -287,8 +303,7 @@ class:justify-end={!expand} -->
cfg_text
hidden
group-hover:inline
"
>
">
Settings
</span>
</button>
@@ -296,21 +311,21 @@ class:justify-end={!expand} -->
</section>
<style lang="postcss">
.ae_cfg_btn .cfg_text {
/* display: none; */
}
.ae_cfg_btn .cfg_text {
/* display: none; */
}
.ae_cfg_btn:hover .cfg_text {
/* display: initial; */
/* outline: solid thin red; */
}
.ae_cfg_btn:hover .cfg_text {
/* display: initial; */
/* outline: solid thin red; */
}
/* .access_type .current_text {
/* .access_type .current_text {
display: none;
} */
/* .access_type:hover .current_text {
/* .access_type:hover .current_text {
display: initial;
} */
/* END: AE's Svelte App Config component */
/* END: AE's Svelte App Config component */
</style>

View File

@@ -1,73 +1,73 @@
<script lang="ts">
interface Props {
children?: import('svelte').Snippet;
log_lvl?: number;
value: any;
success?: boolean;
btn_text?: string;
btn_title?: string;
btn_class?: string;
hide_icon?: boolean;
hide_text?: boolean;
icon_name?: string;
interface Props {
children?: import('svelte').Snippet;
log_lvl?: number;
value: any;
success?: boolean;
btn_text?: string;
btn_title?: string;
btn_class?: string;
hide_icon?: boolean;
hide_text?: boolean;
icon_name?: string;
}
let {
children,
log_lvl = 0,
value = $bindable(''),
success = $bindable(false),
btn_text = 'Copy to Clipboard',
btn_title = 'Copy to Clipboard',
btn_class = 'btn btn-sm preset-tonal-warning text-warning-500 m-1',
hide_icon = false,
hide_text = false,
icon_name = 'copy' // copy, check, link
}: Props = $props();
// *** Import Svelte specific
// import { browser } from '$app/environment';
// *** Import other supporting libraries
import {
// ArrowBigRight,
// CircleX,
CircleCheck,
Copy,
// Eye, EyeOff,
// Key,
Link
// LogIn, LogOut, LockKeyhole,
// Mail, MailCheck,
// Menu,
// RefreshCw, RefreshCcw, RefreshCcwDot,
// ShieldEllipsis, ShieldMinus, ShieldPlus, ShieldUser,
// User, UserCheck
} from '@lucide/svelte';
$effect(() => {
if (log_lvl) {
console.log(`Clipboard component initialized with value:`, value);
}
});
let {
children,
log_lvl = 0,
value = $bindable(''),
success = $bindable(false),
btn_text = 'Copy to Clipboard',
btn_title = 'Copy to Clipboard',
btn_class = 'btn btn-sm preset-tonal-warning text-warning-500 m-1',
hide_icon = false,
hide_text = false,
icon_name = 'copy' // copy, check, link
}: Props = $props();
// Select your trigger element
// const elemButton: HTMLButtonElement | null = document.querySelector('[data-button]');
// *** Import Svelte specific
// import { browser } from '$app/environment';
// *** Import other supporting libraries
import {
// ArrowBigRight,
// CircleX,
CircleCheck,
Copy,
// Eye, EyeOff,
// Key,
Link
// LogIn, LogOut, LockKeyhole,
// Mail, MailCheck,
// Menu,
// RefreshCw, RefreshCcw, RefreshCcwDot,
// ShieldEllipsis, ShieldMinus, ShieldPlus, ShieldUser,
// User, UserCheck
} from '@lucide/svelte';
$effect(() => {
if (log_lvl) {
console.log(`Clipboard component initialized with value:`, value);
}
});
// Select your trigger element
// const elemButton: HTMLButtonElement | null = document.querySelector('[data-button]');
// Add a click event handler to the trigger
// elemButton?.addEventListener('click', () => {
// // Call the Clipboard API
// navigator.clipboard
// // Use the `writeText` method write content to the clipboard
// .writeText(value)
// // Handle confirmation
// .then(() => {
// if (log_lvl) {
// console.log(`Clipboard write successful: ${value}`);
// }
// success = true;
// });
// });
// Add a click event handler to the trigger
// elemButton?.addEventListener('click', () => {
// // Call the Clipboard API
// navigator.clipboard
// // Use the `writeText` method write content to the clipboard
// .writeText(value)
// // Handle confirmation
// .then(() => {
// if (log_lvl) {
// console.log(`Clipboard write successful: ${value}`);
// }
// success = true;
// });
// });
</script>
<button
@@ -93,15 +93,20 @@
// }
}}
class={btn_class}
title={btn_title}
>
title={btn_title}>
<!-- {@render btn_text} -->
{#if icon_name === 'link'}
<Link class="mx-1 {hide_icon ? 'hidden' : 'inline-block'}" size="1.2em" />
<Link
class="mx-1 {hide_icon ? 'hidden' : 'inline-block'}"
size="1.2em" />
{:else if icon_name === 'check'}
<CircleCheck class="mx-1 {hide_icon ? 'hidden' : 'inline-block'}" size="1.2em" />
<CircleCheck
class="mx-1 {hide_icon ? 'hidden' : 'inline-block'}"
size="1.2em" />
{:else}
<Copy class="mx-1 {hide_icon ? 'hidden' : 'inline-block'}" size="1.2em" />
<Copy
class="mx-1 {hide_icon ? 'hidden' : 'inline-block'}"
size="1.2em" />
{/if}
<span class={hide_text ? 'hidden' : 'inline-block'}>
{btn_text}

View File

@@ -1,25 +1,31 @@
<script lang="ts">
// *** Import Svelte specific
// *** Import Svelte specific
// *** Import other supporting libraries
import { Bug, CircleX, Info, ToggleLeft, ToggleRight, X } from '@lucide/svelte';
// *** Import other supporting libraries
import { Bug, CircleX, Info, ToggleLeft, ToggleRight, X } from '@lucide/svelte';
// *** Import Aether specific variables and functions
// import { ae_util } from '$lib/ae_utils/ae_utils';
import { ae_loc, ae_sess, ae_api, slct, slct_trigger } from '$lib/stores/ae_stores';
// *** Import Aether specific variables and functions
// import { ae_util } from '$lib/ae_utils/ae_utils';
import {
ae_loc,
ae_sess,
ae_api,
slct,
slct_trigger
} from '$lib/stores/ae_stores';
// *** Setup Svelte properties
interface Props {
log_lvl?: number;
hide?: null | boolean;
expand?: boolean;
}
// *** Setup Svelte properties
interface Props {
log_lvl?: number;
hide?: null | boolean;
expand?: boolean;
}
let {
log_lvl = $bindable(0),
hide = $bindable(false),
expand = $bindable(false)
}: Props = $props();
let {
log_lvl = $bindable(0),
hide = $bindable(false),
expand = $bindable(false)
}: Props = $props();
</script>
<!-- App Debug Menu -->
@@ -31,28 +37,28 @@ hover:opacity-100 -->
ae_app__debug_menu
hidden-print
z-50
absolute bottom-0 left-0
md:text-md
lg:text-md xl:text-md transition-delay-1000
flex
text-sm sm:text-sm md:text-md lg:text-md xl:text-md 2xl:text-lg
dark:text-slate-400 dark:hover:text-slate-200
bg-red-100/60 dark:bg-red-800/50
hover:bg-red-200 hover:dark:bg-red-900
mx-1 my-2
max-h-min
max-w-lg
transition-all
transition-delay-1000
transition-duration-1000
absolute bottom-0 left-0 z-50 mx-1 my-2
flex max-h-min
max-w-lg border-2
border-red-300 bg-red-100/60
text-sm transition-all
ease-in
hover:border-red-500
hover:bg-red-200
sm:text-sm
2xl:text-lg
dark:border-red-700
border-2
border-red-300 dark:border-red-700
hover:border-red-500 hover:dark:border-red-500
dark:bg-red-800/50
dark:text-slate-400 hover:dark:border-red-500
hover:dark:bg-red-900 dark:hover:text-slate-200
"
class:top-0={expand}
class:w-full={expand}
@@ -60,29 +66,27 @@ hover:opacity-100 -->
class:border-transparent={!expand}
class:dark:border-transparent={!expand}
class:hover:border-transparent={!expand}
class:hover:bg-transparent={!expand}
>
class:hover:bg-transparent={!expand}>
<div
class:hidden={!expand}
class:border-red-200={expand}
class:dark:border-red-800={expand}
class="
transition-all
transition-delay-1000
transition-duration-1000
ease-in
overflow-y-auto
px-1 py-4
opacity-50
hover:opacity-100
relative
"
>
overflow-y-auto
px-1
py-4 opacity-50
transition-all
ease-in
hover:opacity-100
">
<!-- flex flex-col items-center justify-center max-h-full outline -->
<div>
<!-- <span class="fas fa-bug mx-1"></span> -->
<Bug class="inline-block mx-1" />
<Bug class="mx-1 inline-block" />
<span>Debug</span>
</div>
@@ -91,7 +95,8 @@ hover:opacity-100 -->
</pre>
</div>
<span class="absolute top-0 right-0 flex flex-row gap-1 items-center justify-center">
<span
class="absolute top-0 right-0 flex flex-row items-center justify-center gap-1">
<button
type="button"
onclick={() => {
@@ -104,16 +109,21 @@ hover:opacity-100 -->
hover:preset-tonal-success
transition-all
"
title="Turn debug content and styles off and on"
>
title="Turn debug content and styles off and on">
{#if $ae_loc?.debug_mode}
<!-- <span class="fas fa-toggle-on mx-1"></span> -->
<ToggleRight strokeWidth="2.5" color="green" class="inline-block mx-1" />
<ToggleRight
strokeWidth="2.5"
color="green"
class="mx-1 inline-block" />
<span>Debug</span>
<span class="hidden"> Mode On </span>
{:else}
<!-- <span class="fas fa-toggle-off mx-1"></span> -->
<ToggleLeft strokeWidth="1" color="gray" class="inline-block mx-1" />
<ToggleLeft
strokeWidth="1"
color="gray"
class="mx-1 inline-block" />
<span>Debug?</span>
<span class="hidden"> Mode Off </span>
{/if}
@@ -137,10 +147,9 @@ hover:opacity-100 -->
preset-outlined-surface-400-600 preset-filled-suface-200-800
hover:preset-tonal-success
transition-all
"
>
">
<!-- <span class="fas fa-info-circle mx-1"></span> -->
<Info class="inline-block mx-1" />
<Info class="mx-1 inline-block" />
Quick Info
</button>
@@ -157,11 +166,10 @@ hover:opacity-100 -->
hover:preset-tonal-warning
transition-all
"
title="Turn debug content and styles off and on"
>
title="Turn debug content and styles off and on">
<!-- <span class="fas fa-toggle-on mx-1"></span> -->
<!-- <ToggleRight class="inline-block mx-1" /> -->
<CircleX class="inline-block mx-1" />
<CircleX class="mx-1 inline-block" />
<span class="hidden"> Close </span>
<span>Debug</span>
</button>
@@ -177,18 +185,17 @@ hover:opacity-100 -->
id="AE-Quick-Debug"
class="
btn btn-icon
text-xs
p-1
preset-outlined-surface-100-900
hover:preset-filled-warning-100-900
fixed
bottom-8
transition-all
fixed bottom-8 left-2
text-neutral-300 hover:text-neutral-800
left-2
p-1 text-xs text-neutral-300
transition-all hover:text-neutral-800
dark:text-neutral-700 dark:hover:text-neutral-200
"
title="Turn debug content and styles off and on"
>
title="Turn debug content and styles off and on">
<!-- absolute bottom-2 left-2 -->
<!-- fixed bottom-0 left-0 -->
&pi;

View File

@@ -1,80 +1,87 @@
<script lang="ts">
interface Props {
log_lvl?: number;
additional_kv?: key_val;
e_success?: boolean;
e_class?: string;
e_title?: string;
e_text?: string;
e_class_h1?: string;
e_class_h2?: string;
e_class_form_hidden?: string;
e_class_form_showing?: string;
btn_text?: string;
btn_title?: string;
btn_class?: string;
show_btn_class?: string;
hide_icon?: boolean;
}
interface Props {
log_lvl?: number;
additional_kv?: key_val;
e_success?: boolean;
e_class?: string;
e_title?: string;
e_text?: string;
e_class_h1?: string;
e_class_h2?: string;
e_class_form_hidden?: string;
e_class_form_showing?: string;
btn_text?: string;
btn_title?: string;
btn_class?: string;
show_btn_class?: string;
hide_icon?: boolean;
}
let {
log_lvl = $bindable(0),
additional_kv = $bindable({}),
e_success = $bindable(false),
e_class = '',
// e_title = 'Technical Help',
// e_text = 'Request technical help for this application.',
e_class_h1 = $bindable(''),
e_class_h2 = $bindable(''),
e_class_form_hidden = $bindable(''),
e_class_form_showing = $bindable(''),
btn_text = 'Technical Help',
btn_title = 'Technical support help',
btn_class = '',
show_btn_class = '',
hide_icon = false
}: Props = $props();
let {
log_lvl = $bindable(0),
additional_kv = $bindable({}),
e_success = $bindable(false),
e_class = '',
// e_title = 'Technical Help',
// e_text = 'Request technical help for this application.',
e_class_h1 = $bindable(''),
e_class_h2 = $bindable(''),
e_class_form_hidden = $bindable(''),
e_class_form_showing = $bindable(''),
btn_text = 'Technical Help',
btn_title = 'Technical support help',
btn_class = '',
show_btn_class = '',
hide_icon = false
}: Props = $props();
// *** Import Svelte specific
import { goto } from '$app/navigation';
// *** Import Svelte specific
import { goto } from '$app/navigation';
// *** Import other supporting libraries
import { BadgeQuestionMark, ChevronDown, ChevronRight, LifeBuoy, RefreshCw, SquareX } from '@lucide/svelte';
// *** Import Aether specific variables and functions
import {
ae_snip,
ae_loc,
ae_sess,
ae_api,
ae_trig,
slct,
slct_trigger,
type key_val
} from '$lib/stores/ae_stores';
import { api } from '$lib/api/api';
// *** Import other supporting libraries
import {
BadgeQuestionMark,
ChevronDown,
ChevronRight,
LifeBuoy,
RefreshCw,
SquareX
} from '@lucide/svelte';
// *** Import Aether specific variables and functions
import {
ae_snip,
ae_loc,
ae_sess,
ae_api,
ae_trig,
slct,
slct_trigger,
type key_val
} from '$lib/stores/ae_stores';
import { api } from '$lib/api/api';
if (log_lvl) {
console.log(`Help - technical support component loaded`);
}
let help_tech_text: string = $state('');
let hide_additional_info: boolean = $state(true);
function prevent_default<T extends Event>(fn: (event: T) => void) {
return function (event: T) {
event.preventDefault();
fn(event);
};
}
function send_help_tech_email() {
if (log_lvl) {
console.log(`Help - technical support component loaded`);
console.log(`*** send_help_tech_email() ***`);
}
let help_tech_text: string = $state('');
let hide_additional_info: boolean = $state(true);
let subject = `Technical Notification - ${$ae_loc.name}`;
function prevent_default<T extends Event>(fn: (event: T) => void) {
return function (event: T) {
event.preventDefault();
fn(event);
};
}
function send_help_tech_email() {
if (log_lvl) {
console.log(`*** send_help_tech_email() ***`);
}
let subject = `Technical Notification - ${$ae_loc.name}`;
let body_html = `
let body_html = `
<div>
Technical Notification,\n\n
<ul>
@@ -102,25 +109,24 @@
</div>
`;
api.send_email({
api_cfg: $ae_api,
from_email:
$ae_loc.site_cfg_json?.noreply_email ??
'noreply+tech@oneskyit.com',
from_name: $ae_loc.site_cfg_json?.noreply_name ?? 'IT NoReply',
// to_email: $ae_loc.site_cfg_json?.admin_email ?? 'admin+tech@oneskyit.com', // 'scott+idaabb@oneskyit.com',
to_email: 'it+tech@oneskyit.com',
// to_email: $idaa_slct.post_obj.email,
// to_email: 'scott+idaabb@oneskyit.com',
// to_name: $ae_loc.site_cfg_json?.admin_name ?? 'IT Tech',
to_name: 'IT Tech',
// to_name: $idaa_slct.post_obj.full_name ?? 'IDAA BB Poster',
subject: subject,
body_html: body_html
});
api.send_email({
api_cfg: $ae_api,
from_email:
$ae_loc.site_cfg_json?.noreply_email ?? 'noreply+tech@oneskyit.com',
from_name: $ae_loc.site_cfg_json?.noreply_name ?? 'IT NoReply',
// to_email: $ae_loc.site_cfg_json?.admin_email ?? 'admin+tech@oneskyit.com', // 'scott+idaabb@oneskyit.com',
to_email: 'it+tech@oneskyit.com',
// to_email: $idaa_slct.post_obj.email,
// to_email: 'scott+idaabb@oneskyit.com',
// to_name: $ae_loc.site_cfg_json?.admin_name ?? 'IT Tech',
to_name: 'IT Tech',
// to_name: $idaa_slct.post_obj.full_name ?? 'IDAA BB Poster',
subject: subject,
body_html: body_html
});
help_tech_text = ''; // Clear the text area after sending
}
help_tech_text = ''; // Clear the text area after sending
}
</script>
<!-- class:bg-radial-[at_55%_50%]={$ae_sess.show_help_tech}
@@ -131,8 +137,8 @@ class:to-90%={$ae_sess.show_help_tech} -->
class="
flex flex-row
items-center justify-center
rounded-lg shadow-2xl
border border-transparent
rounded-lg border
border-transparent shadow-2xl
transition-all
{e_class}
{!$ae_sess.show_help_tech ? e_class_form_hidden : e_class_form_showing}
@@ -148,48 +154,42 @@ class:to-90%={$ae_sess.show_help_tech} -->
class:hover:shadow-blue-200={$ae_sess.show_help_tech}
class:hover:dark:shadow-blue-800={$ae_sess.show_help_tech}
class:bg-blue-100={$ae_sess.show_help_tech}
class:dark:bg-blue-900={$ae_sess.show_help_tech}
>
class:dark:bg-blue-900={$ae_sess.show_help_tech}>
{#if $ae_sess.show_help_tech}
<div
class="
w-xl
flex flex-col gap-1
items-center justify-center
flex
w-xl flex-col items-center
justify-center gap-1
rounded-xl
border border-gray-500/20 bg-blue-200
p-2
border rounded-xl border-gray-500/20
bg-blue-200
dark:bg-blue-800
transition-all
"
>
dark:bg-blue-800
">
<div
class="
d-flex align-items-center justify-content-between
flex flex-row gap-1 items-center justify-between
w-full
"
>
flex w-full flex-row items-center justify-between
gap-1
">
<h1
class="
h1
text-base font-semibold text-gray-800 dark:text-gray-200
w-fit
w-fit text-base font-semibold text-gray-800
dark:text-gray-200
{e_class_h1}
"
>
">
{#if e_success}
<span
class="text-lg text-green-800 dark:text-green-200 font-semibold"
>
<BadgeQuestionMark class="inline-block mr-2" />
class="text-lg font-semibold text-green-800 dark:text-green-200">
<BadgeQuestionMark class="mr-2 inline-block" />
Help Requested
</span>
{:else}
<span
class="text-lg text-gray-800 dark:text-gray-200 font-semibold"
>
<BadgeQuestionMark class="inline-block mr-2" />
class="text-lg font-semibold text-gray-800 dark:text-gray-200">
<BadgeQuestionMark class="mr-2 inline-block" />
<!-- Request Technical Help -->
Notify Technical Support
</span>
@@ -211,8 +211,7 @@ class:to-90%={$ae_sess.show_help_tech} -->
transition-all
{btn_class}
"
title="Close Help Request Form"
>
title="Close Help Request Form">
<!-- <span class="fas fa-times"></span> -->
<SquareX size="1em" />
<span class="sr-only">Close</span>
@@ -222,9 +221,9 @@ class:to-90%={$ae_sess.show_help_tech} -->
<form
class="
m-1
flex flex-col gap-1
items-center justify-center
w-md max-w-lg
flex w-md max-w-lg
flex-col items-center
justify-center gap-1
"
onsubmit={prevent_default(() => {
// Do stuff...
@@ -234,40 +233,37 @@ class:to-90%={$ae_sess.show_help_tech} -->
$ae_sess.show_help_tech = false;
alert('Notification sent to the IT team.');
})}
>
})}>
<textarea
class="
form-control
w-full max-w-lg h-24 p-2
border border-gray-300 rounded
text-gray-950 dark:text-gray-50
bg-white dark:bg-gray-500
h-24 w-full max-w-lg rounded
border border-gray-300 bg-white
p-2 text-gray-950
dark:bg-gray-500 dark:text-gray-50
hover:dark:bg-gray-50
hover:dark:text-gray-950
"
placeholder="Send with or without a description...."
bind:value={help_tech_text}
></textarea>
bind:value={help_tech_text}></textarea>
<button
type="submit"
class="
btn btn-lg
m-1
preset-tonal-warning
preset-outlined-warning-100-900
hover:preset-filled-warning-200-800
m-1
transition-all
{btn_class}
"
title={btn_title}
>
title={btn_title}>
{#if !hide_icon}
<!-- <BadgeQuestionMark class="inline-block mr-2" /> -->
<LifeBuoy class="inline-block m-1" />
<LifeBuoy class="m-1 inline-block" />
{/if}
{#if !help_tech_text}
@@ -280,9 +276,8 @@ class:to-90%={$ae_sess.show_help_tech} -->
<div
class="
text-sm text-gray-700 dark:text-gray-300 text-center italic
"
>
text-center text-sm text-gray-700 italic dark:text-gray-300
">
This is intended for technical issues only. Please contact your
organization's staff if you have a question about your
membership, recorded content, meetings, or posts.
@@ -290,29 +285,25 @@ class:to-90%={$ae_sess.show_help_tech} -->
<div
class="
border border-gray-300 rounded p-2
w-full
max-w-2xl
overflow-scroll
"
>
w-full max-w-2xl overflow-scroll rounded
border
border-gray-300
p-2
">
<div
class="
d-flex align-items-center justify-content-between
flex flex-row gap-1 items-center justify-between
w-full
"
>
flex w-full flex-row items-center justify-between
gap-1
">
<h2
class="
h2
text-base font-semibold text-gray-800 dark:text-gray-200
{e_class_h2}
"
>
">
<span
class="text-base font-semibold text-gray-800 dark:text-gray-200"
>
class="text-base font-semibold text-gray-800 dark:text-gray-200">
Additional Information Included
</span>
</h2>
@@ -325,8 +316,7 @@ class:to-90%={$ae_sess.show_help_tech} -->
"
onclick={() =>
(hide_additional_info = !hide_additional_info)}
title="Toggle additional information"
>
title="Toggle additional information">
<span>
{#if hide_additional_info}
<!-- <span class="fas fa-caret-right"></span> -->
@@ -341,89 +331,75 @@ class:to-90%={$ae_sess.show_help_tech} -->
</button>
</div>
<ul
class="list-disc list-inside text-sm text-gray-800 dark:text-gray-200"
class:hidden={hide_additional_info}
>
class="list-inside list-disc text-sm text-gray-800 dark:text-gray-200"
class:hidden={hide_additional_info}>
<li>
<span class="text-sm text-gray-500 dark:text-gray-400"
>Datetime =</span
>
>Datetime =</span>
{new Date().toISOString()}
</li>
<li>
<span class="text-sm text-gray-500 dark:text-gray-400"
>URL =</span
>
>URL =</span>
{window.location.href}
</li>
<li>
<span class="text-sm text-gray-500 dark:text-gray-400"
>Browser =</span
>
>Browser =</span>
{navigator.userAgent}
</li>
<li>
<span class="text-sm text-gray-500 dark:text-gray-400"
>Viewport Size =</span
>
>Viewport Size =</span>
{window.innerWidth} x {window.innerHeight}
</li>
<li>
<span class="text-sm text-gray-500 dark:text-gray-400"
>Screen Resolution =</span
>
>Screen Resolution =</span>
{window.screen.width} x {window.screen.height}
</li>
<li>
<span class="text-sm text-gray-500 dark:text-gray-400"
>Dark mode =</span
>
>Dark mode =</span>
{window?.matchMedia?.('(prefers-color-scheme:dark)')
?.matches ?? false}
</li>
<li>
<span class="text-sm text-gray-500 dark:text-gray-400"
>In iframe =</span
>
>In iframe =</span>
{$ae_loc?.iframe}
</li>
<li>
<span class="text-sm text-gray-500 dark:text-gray-400"
>Theme Mode =</span
>
>Theme Mode =</span>
{$ae_loc?.theme_mode}
</li>
<li>
<span class="text-sm text-gray-500 dark:text-gray-400"
>Theme Name =</span
>
>Theme Name =</span>
{$ae_loc?.theme_name}
</li>
<li>
<span class="text-sm text-gray-500 dark:text-gray-400"
>Account ID =</span
>
>Account ID =</span>
{$slct.account_id}
</li>
<li>
<span class="text-sm text-gray-500 dark:text-gray-400"
>Access Type =</span
>
>Access Type =</span>
{$ae_loc?.access_type}
</li>
{#if $ae_loc?.person_id}
<li>
<span
class="text-sm text-gray-500 dark:text-gray-400"
>person_id =</span
>
>person_id =</span>
{$ae_loc?.person_id}
</li>
<li>
<span
class="text-sm text-gray-500 dark:text-gray-400"
>full_name =</span
>
>full_name =</span>
{$ae_loc?.full_name}
</li>
{/if}
@@ -431,29 +407,25 @@ class:to-90%={$ae_sess.show_help_tech} -->
<li>
<span
class="text-sm text-gray-500 dark:text-gray-400"
>user_id =</span
>
>user_id =</span>
{$ae_loc?.user_id}
</li>
<li>
<span
class="text-sm text-gray-500 dark:text-gray-400"
>username =</span
>
>username =</span>
{$ae_loc?.username}
</li>
<li>
<span
class="text-sm text-gray-500 dark:text-gray-400"
>email =</span
>
>email =</span>
{$ae_loc?.email}
</li>
{/if}
<li>
<span class="text-sm text-gray-500 dark:text-gray-400"
>API Base URL =</span
>
>API Base URL =</span>
{$ae_api.base_url}
</li>
@@ -461,13 +433,12 @@ class:to-90%={$ae_sess.show_help_tech} -->
<h2 class="text-base font-semibold text-gray-800">
Component Info:
</h2>
<ul class="list-disc list-inside text-gray-800 text-sm">
<ul class="list-inside list-disc text-sm text-gray-800">
{#each Object.entries(additional_kv) as [key, value] (key)}
<li>
<span
class="text-sm text-gray-500 dark:text-gray-400"
>{key} =</span
>
>{key} =</span>
{value ?? '-- not set --'}
</li>
{/each}
@@ -475,8 +446,7 @@ class:to-90%={$ae_sess.show_help_tech} -->
{/if}
</ul>
<div
class="text-sm text-gray-700 dark:text-gray-300 text-center italic"
>
class="text-center text-sm text-gray-700 italic dark:text-gray-300">
This information will be included in the help request to
assist technical support in diagnosing the issue.
</div>
@@ -484,11 +454,10 @@ class:to-90%={$ae_sess.show_help_tech} -->
<div
class="
flex flex-row gap-2 items-center justify-around
w-full
mt-2
"
>
mt-2 flex w-full flex-row items-center
justify-around
gap-2
">
<button
type="button"
onclick={() => {
@@ -562,8 +531,7 @@ class:to-90%={$ae_sess.show_help_tech} -->
transition-all
{btn_class}
"
title="Clear App Data & Settings: Clear IndexedDB and reload. If in edit mode localStorage and sessionStorage will also be cleared."
>
title="Clear App Data & Settings: Clear IndexedDB and reload. If in edit mode localStorage and sessionStorage will also be cleared.">
<!-- <span class="fas fa-eraser mx-1"></span> -->
<!-- <span class="fas fa-sync mx-1"></span> -->
<RefreshCw size="1em" class="inline-block" />
@@ -582,8 +550,7 @@ class:to-90%={$ae_sess.show_help_tech} -->
transition-all
{btn_class}
"
title="Close Help Request Form"
>
title="Close Help Request Form">
<!-- <span class="fas fa-times"></span> -->
<SquareX size="1em" class="inline-block" />
<span class="">Cancel</span>
@@ -603,10 +570,9 @@ class:to-90%={$ae_sess.show_help_tech} -->
{btn_class}
{show_btn_class}
"
title={btn_title}
>
title={btn_title}>
{#if !hide_icon}
<BadgeQuestionMark class="inline-block m-0.5" />
<BadgeQuestionMark class="m-0.5 inline-block" />
{/if}
<span class="hidden">
{btn_text}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -1,56 +1,63 @@
<script lang="ts">
// *** Import Svelte specific
// import { tick } from 'svelte';
// import { goto, invalidateAll } from '$app/navigation';
// *** Import Svelte specific
// import { tick } from 'svelte';
// import { goto, invalidateAll } from '$app/navigation';
// *** Import other supporting libraries
import {
// ArrowBigRight,
// Bug,
CircleX,
// Eye, EyeOff,
// Key,
// LogIn, LogOut, LockKeyhole,
// Mail, MailCheck,
Menu,
// RefreshCw, RefreshCcwDot,
ShieldEllipsis,
ShieldMinus,
ShieldPlus,
ShieldUser,
ToggleLeft, ToggleRight,
User,
UserCheck,
UserCog,
Wand2,
X
} from '@lucide/svelte';
// *** Import other supporting libraries
import {
// ArrowBigRight,
// Bug,
CircleX,
// Eye, EyeOff,
// Key,
// LogIn, LogOut, LockKeyhole,
// Mail, MailCheck,
Menu,
// RefreshCw, RefreshCcwDot,
ShieldEllipsis,
ShieldMinus,
ShieldPlus,
ShieldUser,
ToggleLeft,
ToggleRight,
User,
UserCheck,
UserCog,
Wand2,
X
} from '@lucide/svelte';
// *** Import Aether specific variables and functions
// import { ae_util } from '$lib/ae_utils/ae_utils';
import { ae_loc, ae_sess, ae_api, slct, slct_trigger } from '$lib/stores/ae_stores';
// *** Import Aether specific variables and functions
// import { ae_util } from '$lib/ae_utils/ae_utils';
import {
ae_loc,
ae_sess,
ae_api,
slct,
slct_trigger
} from '$lib/stores/ae_stores';
import Element_access_type from '$lib/app_components/e_app_access_type.svelte';
import Element_app_cfg from '$lib/app_components/e_app_cfg.svelte';
import Element_sign_in_out from '$lib/app_components/e_app_sign_in_out.svelte';
import Element_theme from '$lib/app_components/e_app_theme.svelte';
import Element_access_type from '$lib/app_components/e_app_access_type.svelte';
import Element_app_cfg from '$lib/app_components/e_app_cfg.svelte';
import Element_sign_in_out from '$lib/app_components/e_app_sign_in_out.svelte';
import Element_theme from '$lib/app_components/e_app_theme.svelte';
// *** Setup Svelte properties
interface Props {
log_lvl?: number;
data: any;
hide?: null | boolean;
expand?: boolean;
}
// *** Setup Svelte properties
interface Props {
log_lvl?: number;
data: any;
hide?: null | boolean;
expand?: boolean;
}
let {
log_lvl = $bindable(0),
data = null,
hide = $bindable(false),
expand = $bindable(false)
}: Props = $props();
let {
log_lvl = $bindable(0),
data = null,
hide = $bindable(false),
expand = $bindable(false)
}: Props = $props();
let trigger_clear_access: null | boolean = $state(null);
let trigger_clear_access: null | boolean = $state(null);
</script>
<!-- App System Menu -->
@@ -93,38 +100,37 @@ max-w-max -->
ae_app__sys_menu
hidden-print
md:text-md
lg:text-md xl:text-md absolute
right-0
bottom-6
z-50
absolute bottom-6 right-0
flex
opacity-90
hover:opacity-100
w-min
max-w-md
flex flex-col-reverse gap-1
w-min max-w-md flex-col-reverse
items-end justify-center
text-sm sm:text-sm md:text-md lg:text-md xl:text-md 2xl:text-lg
gap-1 rounded-lg border-2 border-blue-300/20 bg-blue-200/90 text-sm
bg-blue-200/90
opacity-90
border-2 rounded-lg
border-blue-300/20 dark:border-blue-700/20
hover:border-blue-500/20 hover:dark:border-blue-500/20
transition-all delay-500
duration-500 ease-in-out
hover:border-blue-500/20 hover:opacity-100
transition-all
delay-500 hover:delay-200
duration-500 hover:duration-200
ease-in-out
hover:delay-200
hover:duration-200 sm:text-sm
2xl:text-lg dark:border-blue-700/20
hover:dark:border-blue-500/20
"
class:top-0={expand && (1 as any) == 3}
class:opacity-100={expand}
class:w-full={expand}
class:hidden={hide}
class:border-transparent={!expand}
class:bg-transparent={!expand}
>
class:bg-transparent={!expand}>
<!-- class:hidden={!expand} -->
<!-- class:preset-filled-warning-100-900={expand} -->
<div
@@ -134,38 +140,40 @@ max-w-max -->
class:dark:bg-blue-500={expand}
class="
hidden-print
flex flex-col items-end justify-end
light:bg-blue-200/10 relative flex w-full
flex-col
items-end
justify-end
gap-1
overflow-y-auto
p-1
w-full
light:bg-blue-200/10
dark:bg-blue-800/10
hover:opacity-100
relative
transition-all
delay-1000 hover:delay-500
duration-200 hover:duration-200
ease-in-out
delay-1000
duration-200
ease-in-out hover:opacity-100
hover:delay-500 hover:duration-200
dark:bg-blue-800/10
"
title="
ID: {$ae_loc?.person_id ?? '-- not set --'} / {$ae_loc?.user_id ?? '-- not set --'}
ID: {$ae_loc?.person_id ?? '-- not set --'} / {$ae_loc?.user_id ??
'-- not set --'}
Name: {$ae_loc?.person?.full_name ?? '-- not set --'}
Username: {$ae_loc?.user?.username ?? '-- not set --'}
Email: {$ae_loc?.user?.email ?? '-- not set --'}
Access Type: {$ae_loc?.access_type ?? '-- not set --'}
"
>
">
{#if $ae_loc?.person_id}
<div class="flex flex-row gap-1 items-center justify-end transition-all w-full group">
<div
class="group flex w-full flex-row items-center justify-end gap-1 transition-all">
<User size="1em" class="mx-1 inline-block text-gray-500" />
<span class:hidden={!expand} class="group-hover:inline-block">
{$ae_loc?.person?.informal_name ?? $ae_loc?.person?.given_name}
{$ae_loc?.person?.informal_name ??
$ae_loc?.person?.given_name}
</span>
</div>
{/if}
@@ -174,54 +182,60 @@ max-w-max -->
<!-- This is currently not set to show if not expanded. Saving space. -->
<div
class:hidden={!expand}
class="flex flex-row gap-1 items-center justify-end transition-all w-full group"
>
<ShieldUser size="1em" class="mx-1 inline-block text-gray-500" />
class="group flex w-full flex-row items-center justify-end gap-1 transition-all">
<ShieldUser
size="1em"
class="mx-1 inline-block text-gray-500" />
<span class:hidden={!expand} class="group-hover:inline-block">
{$ae_loc?.user?.username ?? '-- not set --'}
</span>
</div>
{/if}
<div class="flex flex-row gap-1 items-center justify-end transition-all w-full group">
<div
class="group flex w-full flex-row items-center justify-end gap-1 transition-all">
{#if $ae_loc.access_type && $ae_loc.access_type != 'anonymous'}
<span
class="flex flex-row-reverse gap-1 group text-base"
title={`Current access type/level: ${$ae_loc.access_type}`}
>
class="group flex flex-row-reverse gap-1 text-base"
title={`Current access type/level: ${$ae_loc.access_type}`}>
<!-- <span class="fas fa-unlock mx-1"></span> -->
<!-- <ShieldPlus class="inline-block" /> -->
{#if $ae_loc.access_type == 'super'}
<Wand2 size="1em" class="m-1 inline-block" />
<span class:hidden={!expand} class="hidden group-hover:inline-block"
>Super</span
>
<span
class:hidden={!expand}
class="hidden group-hover:inline-block">Super</span>
{:else if $ae_loc.access_type == 'manager'}
<ShieldUser size="1em" class="m-1 inline-block" />
<span class:hidden={!expand} class="hidden group-hover:inline-block"
>Manager</span
>
<span
class:hidden={!expand}
class="hidden group-hover:inline-block"
>Manager</span>
{:else if $ae_loc.access_type == 'administrator'}
<UserCog size="1em" class="m-1 inline-block" />
<span class:hidden={!expand} class="hidden group-hover:inline-block"
>Administrator</span
>
<span
class:hidden={!expand}
class="hidden group-hover:inline-block"
>Administrator</span>
{:else if $ae_loc.access_type == 'trusted'}
<UserCheck size="1em" class="m-1 inline-block" />
<span class:hidden={!expand} class="hidden group-hover:inline-block"
>Trusted Access</span
>
<span
class:hidden={!expand}
class="hidden group-hover:inline-block"
>Trusted Access</span>
{:else if $ae_loc.access_type == 'public'}
Public
<span class:hidden={!expand} class="hidden group-hover:inline-block"
>Access</span
>
<span
class:hidden={!expand}
class="hidden group-hover:inline-block"
>Access</span>
{:else if $ae_loc.access_type == 'authenticated'}
Authenticated
<span class:hidden={!expand} class="hidden group-hover:inline-block"
>Access</span
>
<span
class:hidden={!expand}
class="hidden group-hover:inline-block"
>Access</span>
{:else if $ae_loc.access_type == 'anonymous'}
Anonymous Access
{:else}
@@ -254,13 +268,13 @@ max-w-max -->
// $ae_loc.sys_menu.expand_btn = true;
}
}}
class="btn btn-sm text-xs variant-outline-surface hover:variant-ghost-warning transition-all group"
title={`Current user access level: "${$ae_loc.user_access_type}". Click use passcode for a different access level.`}
>
class="btn btn-sm variant-outline-surface hover:variant-ghost-warning group text-xs transition-all"
title={`Current user access level: "${$ae_loc.user_access_type}". Click use passcode for a different access level.`}>
<!-- <span class="fas fa-lock mx-1"></span> -->
<!-- <ShieldMinus /> -->
<ShieldEllipsis size="2em" class="inline-block" />
<span class="hidden group-hover:inline-block">Passcode?</span>
<span class="hidden group-hover:inline-block"
>Passcode?</span>
</button>
{:else}
<button
@@ -291,16 +305,17 @@ max-w-max -->
}
}}
class="
btn btn-sm text-xs
flex-row-reverse
variant-outline-surface hover:variant-ghost-warning
transition-all group
btn btn-sm variant-outline-surface
hover:variant-ghost-warning
group flex-row-reverse
text-xs transition-all
"
title={`Current access level: "${$ae_loc.access_type}". Click to clear the temporary access level.`}
>
title={`Current access level: "${$ae_loc.access_type}". Click to clear the temporary access level.`}>
<!-- <span class="fas fa-lock mx-1"></span> Lock? -->
<ShieldMinus class="inline-block" />
<span class="hidden group-hover:inline-block"> Clear? </span>
<span class="hidden group-hover:inline-block">
Clear?
</span>
</button>
{/if}
{:else}
@@ -320,19 +335,22 @@ max-w-max -->
// $ae_loc.app_cfg.show_element__access_type = true;
// $ae_loc.app_cfg.show_element__passcode_input = true;
// await tick();
console.log('Layout button click: Focus on passcode input!');
console.log(
'Layout button click: Focus on passcode input!'
);
/** @type {HTMLElement | null} */
const to_focus = document.getElementById('access_passcode_input');
const to_focus = document.getElementById(
'access_passcode_input'
);
to_focus?.focus();
}}
class="
btn btn-sm text-xs
flex-row-reverse
variant-outline-surface hover:variant-ghost-success
transition-all group
btn btn-sm variant-outline-surface
hover:variant-ghost-success
group flex-row-reverse
text-xs transition-all
"
title="Anonymous public access is currently set. You must Sign In or use a passcode to change your access level."
>
title="Anonymous public access is currently set. You must Sign In or use a passcode to change your access level.">
<!-- <span class="fas fa-lock mx-1 lock_icon"></span> -->
<!-- <span class="">Unlock?</span> -->
<ShieldUser class="inline-block" />
@@ -349,19 +367,21 @@ max-w-max -->
// dispatch_edit_mode_changed();
}}
class="
btn btn-base text-sm
preset-tonal-warning
btn btn-base preset-tonal-warning
preset-outlined-warning-800-200
hover:preset-tonal-success
transition-all group
min-w-22 md:min-w-30 w-full max-w-fit
gap-1
group
w-full max-w-fit
min-w-22 gap-1 text-sm transition-all
md:min-w-30
"
title="Click to turn off edit mode. Edit mode is currently on."
>
title="Click to turn off edit mode. Edit mode is currently on.">
<ToggleRight size="1em" class="m-1 inline-block" />
<span class="text-xs">Edit</span>
<span class="hidden group-hover:inline-block group-hover:text-xs"> Off </span>
<span
class="hidden group-hover:inline-block group-hover:text-xs">
Off
</span>
</button>
{:else if $ae_loc.authenticated_access}
<button
@@ -371,19 +391,21 @@ max-w-max -->
// dispatch_edit_mode_changed();
}}
class="
btn btn-base text-sm
preset-tonal-surface
btn btn-base preset-tonal-surface
preset-outlined-warning-400-600
hover:preset-tonal-warning
transition-all group
min-w-22 md:min-w-30 w-full max-w-fit
gap-1
group
w-full max-w-fit
min-w-22 gap-1 text-sm transition-all
md:min-w-30
"
title="Click to torn on/enable edit mode. Edit mode is currently off/disabled."
>
title="Click to torn on/enable edit mode. Edit mode is currently off/disabled.">
<ToggleLeft size="1em" class="m-1 inline-block" />
<span class="text-xs">Edit</span>
<span class="hidden group-hover:inline-block group-hover:text-xs"> On? </span>
<span
class="hidden group-hover:inline-block group-hover:text-xs">
On?
</span>
</button>
{/if}
@@ -392,12 +414,12 @@ max-w-max -->
type="button"
class:w-full={expand}
class="
btn btn-base text-sm
preset-tonal-surface
btn btn-base preset-tonal-surface
hover:preset-tonal-primary
px-1 py-1
min-w-22 md:min-w-30 w-full max-w-fit
transition-all group
group
w-full max-w-fit
min-w-22 px-1 py-1 text-sm
transition-all md:min-w-30
"
title="Cycle font size: default → larger → smaller"
onclick={() => {
@@ -409,17 +431,19 @@ max-w-max -->
} else {
$ae_loc.font_size_mode = 'default';
}
}}
>
}}>
{#if !$ae_loc.font_size_mode || $ae_loc.font_size_mode === 'default'}
<span class="font-bold text-sm leading-none">A</span>
<span class="hidden group-hover:inline-block text-xs">Font: Normal</span>
<span class="text-sm leading-none font-bold">A</span>
<span class="hidden text-xs group-hover:inline-block"
>Font: Normal</span>
{:else if $ae_loc.font_size_mode === 'larger'}
<span class="font-bold text-base leading-none">A+</span>
<span class="hidden group-hover:inline-block text-xs">Font: Larger</span>
<span class="text-base leading-none font-bold">A+</span>
<span class="hidden text-xs group-hover:inline-block"
>Font: Larger</span>
{:else}
<span class="font-bold text-xs leading-none">A</span>
<span class="hidden group-hover:inline-block text-xs">Font: Smaller</span>
<span class="text-xs leading-none font-bold">A</span>
<span class="hidden text-xs group-hover:inline-block"
>Font: Smaller</span>
{/if}
</button>
@@ -432,13 +456,13 @@ max-w-max -->
type="button"
class:w-full={expand}
class="
btn btn-base text-sm
preset-filled-tertiary-400-600
btn btn-base preset-filled-tertiary-400-600
preset-outlined-tertiary-400-600
hover:preset-filled-success active:preset-filled-success
px-1 py-1
min-w-22 md:min-w-30 w-full max-w-fit
transition-all group
hover:preset-filled-success
active:preset-filled-success group
w-full max-w-fit
min-w-22 px-1 py-1 text-sm
transition-all md:min-w-30
"
title="Show/Hide the system menu"
onclick={async () => {
@@ -472,15 +496,14 @@ max-w-max -->
// $ae_loc.app_cfg.show_element__passcode_input = false;
}
// $ae_loc.sys_menu.expand_btn = !expand_btn;
}}
>
}}>
<!-- <span class=""> -->
{#if expand}
<CircleX class="m-1 inline-block" />
{:else}
<Menu class="m-1 inline-block" />
{/if}
<span class="text-xs hidden group-hover:inline-block"> Menu </span>
<span class="hidden text-xs group-hover:inline-block"> Menu </span>
<!-- </span> -->
</button>
<!-- </div> -->
@@ -499,27 +522,26 @@ max-w-max -->
ae_app__sys_menu
hidden-print
flex flex-col
items-end
justify-end
gap-2
z-20 flex
min-w-48
flex-col
items-end
bg-white dark:bg-gray-800
justify-end
border border-gray-200 dark:border-gray-700
rounded-lg
px-1 py-2
gap-2 rounded-lg
transition-all
delay-1000 hover:delay-100
duration-100 hover:duration-200
ease-in-out
border border-gray-200 bg-white
px-1
py-2 transition-all
z-20 hover:z-30
"
>
delay-1000
duration-100 ease-in-out
hover:z-30 hover:delay-100
hover:duration-200
dark:border-gray-700 dark:bg-gray-800
">
<button
type="button"
class:w-full={expand}
@@ -528,8 +550,8 @@ max-w-max -->
preset-filled-tertiary-400-600
preset-outlined-tertiary-400-600
hover:preset-filled-success active:preset-filled-success
px-6 py-1
transition-all group
group px-6
py-1 transition-all
"
title="Show/Hide the system menu"
onclick={() => {
@@ -549,8 +571,7 @@ max-w-max -->
}
// $ae_loc.sys_menu.expand_btn = !expand_btn;
// $ae_loc.sys_menu.expand = !expand;
}}
>
}}>
{#if expand}
<CircleX class="m-1 inline-block" />
<span class="hidden group-hover:inline-block"> Hide Menu </span>
@@ -564,16 +585,14 @@ max-w-max -->
<span class:hidden={!$ae_loc.edit_mode}>
<Element_app_cfg
hide={$ae_loc.sys_menu.hide_app_cfg}
expand={$ae_loc.sys_menu.expand_app_cfg}
/>
expand={$ae_loc.sys_menu.expand_app_cfg} />
</span>
<span class:hidden={!$ae_loc.edit_mode}>
<Element_theme
hide={$ae_loc.sys_menu.hide_app_cfg}
expand={$ae_loc.sys_menu.expand_app_cfg}
set_theme_mode={true}
set_theme_name={true}
/>
set_theme_name={true} />
</span>
<!-- {/if} -->
@@ -582,8 +601,8 @@ max-w-max -->
<!-- expand={$ae_loc.sys_menu.expand_user} -->
<Element_sign_in_out
{data}
hidden={$ae_loc.iframe || !$ae_loc.app_cfg?.show_element__sign_in_out}
/>
hidden={$ae_loc.iframe ||
!$ae_loc.app_cfg?.show_element__sign_in_out} />
{/if}
{#if !$ae_loc?.sys_menu?.hide_access_type && !$ae_loc?.iframe}
@@ -592,9 +611,10 @@ max-w-max -->
bind:hide={$ae_loc.sys_menu.hide_access_type}
bind:focus_input={$ae_sess.sys_menu.focus_passcode_input}
bind:expand={$ae_loc.sys_menu.expand_access_type}
bind:show_passcode_input={$ae_sess.app_cfg.show_element__passcode_input}
bind:trigger_clear_access
/>
bind:show_passcode_input={
$ae_sess.app_cfg.show_element__passcode_input
}
bind:trigger_clear_access />
{/if}
</div>
</section>

View File

@@ -1,22 +1,28 @@
<script lang="ts">
import { Minimize2, Moon, Sun } from '@lucide/svelte';
import { ae_loc, ae_sess, ae_api, slct, slct_trigger } from '$lib/stores/ae_stores';
import { Minimize2, Moon, Sun } from '@lucide/svelte';
import {
ae_loc,
ae_sess,
ae_api,
slct,
slct_trigger
} from '$lib/stores/ae_stores';
interface Props {
log_lvl?: number;
hide?: null | boolean;
expand?: boolean;
set_theme_mode: any;
set_theme_name: any;
}
interface Props {
log_lvl?: number;
hide?: null | boolean;
expand?: boolean;
set_theme_mode: any;
set_theme_name: any;
}
let {
log_lvl = $bindable(0),
hide = $bindable(false),
expand = $bindable(false),
set_theme_mode,
set_theme_name
}: Props = $props();
let {
log_lvl = $bindable(0),
hide = $bindable(false),
expand = $bindable(false),
set_theme_mode,
set_theme_name
}: Props = $props();
</script>
<!-- Change light and dark mode -->
@@ -37,25 +43,23 @@ if ($ae_loc.app_cfg.theme_mode == 'light') {
bg-surface-100 dark:bg-surface-800
text-surface-900 dark:text-surface-100
border border-surface-200 dark:border-surface-700
border-surface-200 dark:border-surface-700 flex
flex flex-col flex-wrap gap-1
w-72 max-w-72 flex-col flex-wrap
items-end justify-center
w-72 max-w-72
gap-1 rounded-lg
border
p-1
rounded-lg
shadow-md
duration-300 delay-150 hover:delay-1000 hover:ease-out
transition-all
transition-all delay-150 duration-300 hover:delay-1000
hover:ease-out
"
class:hidden={hide}
>
class:hidden={hide}>
<div
class:hidden={!expand}
class="flex flex-row flex-wrap gap-2 items-center justify-between w-full"
>
class="flex w-full flex-row flex-wrap items-center justify-between gap-2">
<!-- Theme Name: -->
<span class="text-sm font-semibold">
{$ae_loc.theme_name}
@@ -74,7 +78,10 @@ if ($ae_loc.app_cfg.theme_mode == 'light') {
// document.body.setAttribute('data-theme', $ae_loc?.theme_name);
// NEW for Tailwind v4: Update the html attribute named "data-theme" to the current theme name.
document.documentElement.setAttribute('data-theme', new_theme_name);
document.documentElement.setAttribute(
'data-theme',
new_theme_name
);
// if ($ae_loc.theme_mode == 'light') {
// document.documentElement.classList.remove('dark');
@@ -86,8 +93,7 @@ if ($ae_loc.app_cfg.theme_mode == 'light') {
}}
bind:value={$ae_loc.theme_name}
class="select w-32"
title="Theme name"
>
title="Theme name">
<option value="">-- None --</option>
<option value="cerberus">Cerberus</option>
<option value="concord">Concord</option>
@@ -112,41 +118,38 @@ if ($ae_loc.app_cfg.theme_mode == 'light') {
</div>
<div
class="flex flex-row flex-wrap gap-2 items-center w-full"
class="flex w-full flex-row flex-wrap items-center gap-2"
class:justify-between={expand}
class:justify-end={!expand}
>
class:justify-end={!expand}>
{#if expand}
<!-- Hide theme options -->
<button
class="
btn btn-sm text-xs
preset-tonal-secondary hover:preset-filled-secondary-500
transition-all group
btn btn-sm preset-tonal-secondary
hover:preset-filled-secondary-500 group
text-xs transition-all
"
onclick={() => {
expand = !expand;
}}
title="Hide Theme Options"
>
title="Hide Theme Options">
<!-- <span class="fas fa-compress-alt"></span> -->
<Minimize2 size="1em" class="m-1" />
<span
class="
hidden
group-hover:inline-block
text-xs
"
>
group-hover:inline-block
">
Hide Theme Options
</span>
</button>
<button
class="
btn btn-sm text-xs
preset-tonal-secondary hover:preset-filled-secondary-500
transition-all group
btn btn-sm preset-tonal-secondary
hover:preset-filled-secondary-500 group
text-xs transition-all
"
onclick={() => {
if ($ae_loc.theme_mode == 'light') {
@@ -157,23 +160,24 @@ if ($ae_loc.app_cfg.theme_mode == 'light') {
// DOM sync is handled reactively by the layout effect in +layout.svelte
}}
title="Change light and dark mode"
>
title="Change light and dark mode">
<!-- <span class="fas fa-adjust"></span> -->
{#if $ae_loc.theme_mode == 'light'}
<Sun class="m-1" />
<span class="hidden md:inline-block group-hover:inline-block">Light Mode</span>
<span
class="hidden group-hover:inline-block md:inline-block"
>Light Mode</span>
{:else if $ae_loc.theme_mode == 'dark'}
<Moon class="m-1" />
<span class="hidden group-hover:inline-block">Dark Mode</span>
<span class="hidden group-hover:inline-block"
>Dark Mode</span>
{/if}
<span
class="
hidden
group-hover:inline-block
text-xs
"
>
group-hover:inline-block
">
Change
</span>
</button>
@@ -197,18 +201,19 @@ if ($ae_loc.app_cfg.theme_mode == 'light') {
expand = !expand;
}}
title="Change light and dark mode"
>
title="Change light and dark mode">
{#if $ae_loc.theme_mode == 'light'}
<span class="inline-block" title="Light Mode">
<Sun />
</span>
<span class="hidden group-hover:inline-block">Light Mode</span>
<span class="hidden group-hover:inline-block"
>Light Mode</span>
{:else if $ae_loc.theme_mode == 'dark'}
<span class="inline-block" title="Dark Mode">
<Moon />
</span>
<span class="hidden group-hover:inline-block">Dark Mode</span>
<span class="hidden group-hover:inline-block"
>Dark Mode</span>
{/if}
</button>
{/if}

View File

@@ -1,120 +1,136 @@
<script lang="ts">
/**
* e_app_url_builder.svelte
* URL Param Builder — lets admins construct and copy shareable URLs
* with any combination of the supported URL params applied.
*
* Core params:
* ?iframe=true|false — persist kiosk mode (stays in URL)
* ?theme=<name> — set theme name on load (stripped after apply)
* ?theme_mode=light|dark — set light/dark mode on load (stripped after apply)
* ?key=<access_key> — site access key
*
* Launcher params (all stripped from URL after apply):
* ?launcher_menu=hide|show
* ?launcher_header=hide|show
* ?launcher_footer=hide|show
*/
import { page } from '$app/stores';
import { Copy, Check, Link } from '@lucide/svelte';
/**
* e_app_url_builder.svelte
* URL Param Builder — lets admins construct and copy shareable URLs
* with any combination of the supported URL params applied.
*
* Core params:
* ?iframe=true|false — persist kiosk mode (stays in URL)
* ?theme=<name> — set theme name on load (stripped after apply)
* ?theme_mode=light|dark — set light/dark mode on load (stripped after apply)
* ?key=<access_key> — site access key
*
* Launcher params (all stripped from URL after apply):
* ?launcher_menu=hide|show
* ?launcher_header=hide|show
* ?launcher_footer=hide|show
*/
import { page } from '$app/stores';
import { Copy, Check, Link } from '@lucide/svelte';
// --- Per-param: include this param in the output URL?
let use_iframe = $state(false);
let use_theme = $state(false);
let use_theme_mode = $state(false);
let use_key = $state(false);
let use_launcher_menu = $state(false);
let use_launcher_header = $state(false);
let use_launcher_footer = $state(false);
// --- Per-param: include this param in the output URL?
let use_iframe = $state(false);
let use_theme = $state(false);
let use_theme_mode = $state(false);
let use_key = $state(false);
let use_launcher_menu = $state(false);
let use_launcher_header = $state(false);
let use_launcher_footer = $state(false);
// --- Param values
let val_iframe = $state<'true' | 'false'>('true');
let val_theme = $state('nouveau');
let val_theme_mode = $state<'light' | 'dark'>('dark');
let val_key = $state('');
let val_launcher_menu = $state<'hide' | 'show'>('hide');
let val_launcher_header = $state<'hide' | 'show'>('hide');
let val_launcher_footer = $state<'hide' | 'show'>('hide');
// --- Param values
let val_iframe = $state<'true' | 'false'>('true');
let val_theme = $state('nouveau');
let val_theme_mode = $state<'light' | 'dark'>('dark');
let val_key = $state('');
let val_launcher_menu = $state<'hide' | 'show'>('hide');
let val_launcher_header = $state<'hide' | 'show'>('hide');
let val_launcher_footer = $state<'hide' | 'show'>('hide');
// Build the output URL reactively
let built_url = $derived.by(() => {
const base = $page.url;
const u = new URL(base.pathname + base.search + base.hash, base.origin);
// Build the output URL reactively
let built_url = $derived.by(() => {
const base = $page.url;
const u = new URL(base.pathname + base.search + base.hash, base.origin);
// Remove all known params first so we start clean each time
u.searchParams.delete('iframe');
u.searchParams.delete('theme');
u.searchParams.delete('theme_mode');
u.searchParams.delete('key');
u.searchParams.delete('launcher_menu');
u.searchParams.delete('launcher_header');
u.searchParams.delete('launcher_footer');
// Remove all known params first so we start clean each time
u.searchParams.delete('iframe');
u.searchParams.delete('theme');
u.searchParams.delete('theme_mode');
u.searchParams.delete('key');
u.searchParams.delete('launcher_menu');
u.searchParams.delete('launcher_header');
u.searchParams.delete('launcher_footer');
if (use_iframe) u.searchParams.set('iframe', val_iframe);
if (use_theme) u.searchParams.set('theme', val_theme);
if (use_theme_mode) u.searchParams.set('theme_mode', val_theme_mode);
if (use_key && val_key.trim()) u.searchParams.set('key', val_key.trim());
if (use_launcher_menu) u.searchParams.set('launcher_menu', val_launcher_menu);
if (use_launcher_header) u.searchParams.set('launcher_header', val_launcher_header);
if (use_launcher_footer) u.searchParams.set('launcher_footer', val_launcher_footer);
if (use_iframe) u.searchParams.set('iframe', val_iframe);
if (use_theme) u.searchParams.set('theme', val_theme);
if (use_theme_mode) u.searchParams.set('theme_mode', val_theme_mode);
if (use_key && val_key.trim()) u.searchParams.set('key', val_key.trim());
if (use_launcher_menu)
u.searchParams.set('launcher_menu', val_launcher_menu);
if (use_launcher_header)
u.searchParams.set('launcher_header', val_launcher_header);
if (use_launcher_footer)
u.searchParams.set('launcher_footer', val_launcher_footer);
return u.toString();
return u.toString();
});
// Output mode: full URL (default) or params-only string
let params_only = $state(false);
let output = $derived.by(() => {
if (!params_only) return built_url;
const u = new URL(built_url);
return u.search || '(no params set)';
});
let copied = $state(false);
function copy_url() {
navigator.clipboard.writeText(output).then(() => {
copied = true;
setTimeout(() => (copied = false), 2000);
});
}
// Output mode: full URL (default) or params-only string
let params_only = $state(false);
let output = $derived.by(() => {
if (!params_only) return built_url;
const u = new URL(built_url);
return u.search || '(no params set)';
});
let copied = $state(false);
function copy_url() {
navigator.clipboard.writeText(output).then(() => {
copied = true;
setTimeout(() => copied = false, 2000);
});
}
const theme_options = [
{ value: 'cerberus', label: 'Cerberus' },
{ value: 'concord', label: 'Concord' },
{ value: 'crimson', label: 'Crimson' },
{ value: 'hamlindigo', label: 'Hamlindigo' },
{ value: 'modern', label: 'Modern' },
{ value: 'nouveau', label: 'Nouveau' },
{ value: 'rocket', label: 'Rocket' },
{ value: 'terminus', label: 'Terminus' },
{ value: 'vintage', label: 'Vintage' },
{ value: 'wintry', label: 'Wintry' },
{ value: 'AE_OSIT_default', label: 'OSIT' },
{ value: 'AE_Firefly', label: 'Firefly ✦' },
{ value: 'AE_Firefly_SteelBlue', label: 'Firefly SteelBlue ✦' },
{ value: 'AE_Firefly_Indigo', label: 'Firefly Indigo ✦' },
{ value: 'AE_Firefly_Rainbow', label: 'Firefly Rainbow ✨' },
{ value: 'AE_c_IDAA_light', label: 'IDAA light' },
{ value: 'AE_c_LCI', label: 'LCI' },
];
const theme_options = [
{ value: 'cerberus', label: 'Cerberus' },
{ value: 'concord', label: 'Concord' },
{ value: 'crimson', label: 'Crimson' },
{ value: 'hamlindigo', label: 'Hamlindigo' },
{ value: 'modern', label: 'Modern' },
{ value: 'nouveau', label: 'Nouveau' },
{ value: 'rocket', label: 'Rocket' },
{ value: 'terminus', label: 'Terminus' },
{ value: 'vintage', label: 'Vintage' },
{ value: 'wintry', label: 'Wintry' },
{ value: 'AE_OSIT_default', label: 'OSIT' },
{ value: 'AE_Firefly', label: 'Firefly ✦' },
{ value: 'AE_Firefly_SteelBlue', label: 'Firefly SteelBlue ✦' },
{ value: 'AE_Firefly_Indigo', label: 'Firefly Indigo ✦' },
{ value: 'AE_Firefly_Rainbow', label: 'Firefly Rainbow ✨' },
{ value: 'AE_c_IDAA_light', label: 'IDAA light' },
{ value: 'AE_c_LCI', label: 'LCI' }
];
</script>
<section class="space-y-3 text-sm">
<h2 class="text-xs font-semibold uppercase tracking-widest text-surface-500 dark:text-surface-400 flex items-center gap-1">
<h2
class="text-surface-500 dark:text-surface-400 flex items-center gap-1 text-xs font-semibold tracking-widest uppercase">
<Link size="0.9em" /> URL Param Builder
</h2>
<!-- ── Core params ─────────────────────────────────────────────────── -->
<div class="space-y-1.5">
<p class="text-[9px] font-bold uppercase tracking-widest opacity-40 ml-0.5">Core</p>
<p
class="ml-0.5 text-[9px] font-bold tracking-widest uppercase opacity-40">
Core
</p>
<!-- iframe -->
<div class="flex items-center gap-2">
<input id="ubld_iframe" type="checkbox" class="checkbox checkbox-sm shrink-0" bind:checked={use_iframe} />
<label for="ubld_iframe" class="w-24 text-xs font-mono cursor-pointer select-none" class:opacity-35={!use_iframe}>iframe</label>
<select bind:value={val_iframe} disabled={!use_iframe} class="select select-sm text-xs flex-1 disabled:opacity-40">
<input
id="ubld_iframe"
type="checkbox"
class="checkbox checkbox-sm shrink-0"
bind:checked={use_iframe} />
<label
for="ubld_iframe"
class="w-24 cursor-pointer font-mono text-xs select-none"
class:opacity-35={!use_iframe}>iframe</label>
<select
bind:value={val_iframe}
disabled={!use_iframe}
class="select select-sm flex-1 text-xs disabled:opacity-40">
<option value="true">true</option>
<option value="false">false</option>
</select>
@@ -122,9 +138,19 @@
<!-- theme -->
<div class="flex items-center gap-2">
<input id="ubld_theme" type="checkbox" class="checkbox checkbox-sm shrink-0" bind:checked={use_theme} />
<label for="ubld_theme" class="w-24 text-xs font-mono cursor-pointer select-none" class:opacity-35={!use_theme}>theme</label>
<select bind:value={val_theme} disabled={!use_theme} class="select select-sm text-xs flex-1 disabled:opacity-40">
<input
id="ubld_theme"
type="checkbox"
class="checkbox checkbox-sm shrink-0"
bind:checked={use_theme} />
<label
for="ubld_theme"
class="w-24 cursor-pointer font-mono text-xs select-none"
class:opacity-35={!use_theme}>theme</label>
<select
bind:value={val_theme}
disabled={!use_theme}
class="select select-sm flex-1 text-xs disabled:opacity-40">
{#each theme_options as opt}
<option value={opt.value}>{opt.label}</option>
{/each}
@@ -133,9 +159,19 @@
<!-- theme_mode -->
<div class="flex items-center gap-2">
<input id="ubld_theme_mode" type="checkbox" class="checkbox checkbox-sm shrink-0" bind:checked={use_theme_mode} />
<label for="ubld_theme_mode" class="w-24 text-xs font-mono cursor-pointer select-none" class:opacity-35={!use_theme_mode}>theme_mode</label>
<select bind:value={val_theme_mode} disabled={!use_theme_mode} class="select select-sm text-xs flex-1 disabled:opacity-40">
<input
id="ubld_theme_mode"
type="checkbox"
class="checkbox checkbox-sm shrink-0"
bind:checked={use_theme_mode} />
<label
for="ubld_theme_mode"
class="w-24 cursor-pointer font-mono text-xs select-none"
class:opacity-35={!use_theme_mode}>theme_mode</label>
<select
bind:value={val_theme_mode}
disabled={!use_theme_mode}
class="select select-sm flex-1 text-xs disabled:opacity-40">
<option value="light">light</option>
<option value="dark">dark</option>
</select>
@@ -143,27 +179,46 @@
<!-- key -->
<div class="flex items-center gap-2">
<input id="ubld_key" type="checkbox" class="checkbox checkbox-sm shrink-0" bind:checked={use_key} />
<label for="ubld_key" class="w-24 text-xs font-mono cursor-pointer select-none" class:opacity-35={!use_key}>key</label>
<input
id="ubld_key"
type="checkbox"
class="checkbox checkbox-sm shrink-0"
bind:checked={use_key} />
<label
for="ubld_key"
class="w-24 cursor-pointer font-mono text-xs select-none"
class:opacity-35={!use_key}>key</label>
<input
type="text"
bind:value={val_key}
disabled={!use_key}
placeholder="access key"
class="input input-sm text-xs font-mono flex-1 disabled:opacity-40"
/>
class="input input-sm flex-1 font-mono text-xs disabled:opacity-40" />
</div>
</div>
<!-- ── Launcher params ─────────────────────────────────────────────── -->
<div class="space-y-1.5 border-t border-surface-500/20 pt-2.5">
<p class="text-[9px] font-bold uppercase tracking-widest opacity-40 ml-0.5">Launcher</p>
<div class="border-surface-500/20 space-y-1.5 border-t pt-2.5">
<p
class="ml-0.5 text-[9px] font-bold tracking-widest uppercase opacity-40">
Launcher
</p>
<!-- launcher_menu -->
<div class="flex items-center gap-2">
<input id="ubld_launcher_menu" type="checkbox" class="checkbox checkbox-sm shrink-0" bind:checked={use_launcher_menu} />
<label for="ubld_launcher_menu" class="w-24 text-xs font-mono cursor-pointer select-none" class:opacity-35={!use_launcher_menu}>menu</label>
<select bind:value={val_launcher_menu} disabled={!use_launcher_menu} class="select select-sm text-xs flex-1 disabled:opacity-40">
<input
id="ubld_launcher_menu"
type="checkbox"
class="checkbox checkbox-sm shrink-0"
bind:checked={use_launcher_menu} />
<label
for="ubld_launcher_menu"
class="w-24 cursor-pointer font-mono text-xs select-none"
class:opacity-35={!use_launcher_menu}>menu</label>
<select
bind:value={val_launcher_menu}
disabled={!use_launcher_menu}
class="select select-sm flex-1 text-xs disabled:opacity-40">
<option value="hide">hide</option>
<option value="show">show</option>
</select>
@@ -171,9 +226,19 @@
<!-- launcher_header -->
<div class="flex items-center gap-2">
<input id="ubld_launcher_header" type="checkbox" class="checkbox checkbox-sm shrink-0" bind:checked={use_launcher_header} />
<label for="ubld_launcher_header" class="w-24 text-xs font-mono cursor-pointer select-none" class:opacity-35={!use_launcher_header}>header</label>
<select bind:value={val_launcher_header} disabled={!use_launcher_header} class="select select-sm text-xs flex-1 disabled:opacity-40">
<input
id="ubld_launcher_header"
type="checkbox"
class="checkbox checkbox-sm shrink-0"
bind:checked={use_launcher_header} />
<label
for="ubld_launcher_header"
class="w-24 cursor-pointer font-mono text-xs select-none"
class:opacity-35={!use_launcher_header}>header</label>
<select
bind:value={val_launcher_header}
disabled={!use_launcher_header}
class="select select-sm flex-1 text-xs disabled:opacity-40">
<option value="hide">hide</option>
<option value="show">show</option>
</select>
@@ -181,9 +246,19 @@
<!-- launcher_footer -->
<div class="flex items-center gap-2">
<input id="ubld_launcher_footer" type="checkbox" class="checkbox checkbox-sm shrink-0" bind:checked={use_launcher_footer} />
<label for="ubld_launcher_footer" class="w-24 text-xs font-mono cursor-pointer select-none" class:opacity-35={!use_launcher_footer}>footer</label>
<select bind:value={val_launcher_footer} disabled={!use_launcher_footer} class="select select-sm text-xs flex-1 disabled:opacity-40">
<input
id="ubld_launcher_footer"
type="checkbox"
class="checkbox checkbox-sm shrink-0"
bind:checked={use_launcher_footer} />
<label
for="ubld_launcher_footer"
class="w-24 cursor-pointer font-mono text-xs select-none"
class:opacity-35={!use_launcher_footer}>footer</label>
<select
bind:value={val_launcher_footer}
disabled={!use_launcher_footer}
class="select select-sm flex-1 text-xs disabled:opacity-40">
<option value="hide">hide</option>
<option value="show">show</option>
</select>
@@ -191,26 +266,33 @@
</div>
<!-- ── Output ──────────────────────────────────────────────────────── -->
<div class="border-t border-surface-500/20 pt-2.5 space-y-1.5">
<div class="border-surface-500/20 space-y-1.5 border-t pt-2.5">
<div class="flex items-center gap-2">
<input id="ubld_params_only" type="checkbox" class="checkbox checkbox-sm shrink-0" bind:checked={params_only} />
<label for="ubld_params_only" class="text-xs cursor-pointer select-none" class:opacity-35={!params_only}>Params only</label>
<input
id="ubld_params_only"
type="checkbox"
class="checkbox checkbox-sm shrink-0"
bind:checked={params_only} />
<label
for="ubld_params_only"
class="cursor-pointer text-xs select-none"
class:opacity-35={!params_only}>Params only</label>
</div>
<div class="flex gap-1 items-stretch">
<div class="flex items-stretch gap-1">
<input
type="text"
readonly
value={output}
class="input input-sm text-xs font-mono flex-1 bg-surface-50/50 dark:bg-surface-700/50 cursor-text"
class="input input-sm bg-surface-50/50 dark:bg-surface-700/50 flex-1 cursor-text font-mono text-xs"
onclick={(e) => (e.target as HTMLInputElement).select()}
title="Click to select all"
/>
title="Click to select all" />
<button
class="btn btn-sm {copied ? 'preset-filled-success' : 'preset-tonal-primary'} shrink-0 transition-all"
class="btn btn-sm {copied
? 'preset-filled-success'
: 'preset-tonal-primary'} shrink-0 transition-all"
onclick={copy_url}
title="Copy URL to clipboard"
>
title="Copy URL to clipboard">
{#if copied}
<Check size="1em" />
{:else}
@@ -219,5 +301,4 @@
</button>
</div>
</div>
</section>

View File

@@ -1,57 +1,62 @@
<script lang="ts" module>
import type { WithElementRef } from 'bits-ui';
import type { HTMLAnchorAttributes, HTMLButtonAttributes } from 'svelte/elements';
import { type VariantProps, tv } from 'tailwind-variants';
import type { WithElementRef } from 'bits-ui';
import type {
HTMLAnchorAttributes,
HTMLButtonAttributes
} from 'svelte/elements';
import { type VariantProps, tv } from 'tailwind-variants';
export const buttonVariants = tv({
base: 'ring-offset-background focus-visible:ring-ring inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-colors focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0',
variants: {
variant: {
default: 'bg-primary text-primary-foreground hover:bg-primary/90',
destructive: 'bg-destructive text-destructive-foreground hover:bg-destructive/90',
outline:
'border-input bg-background hover:bg-accent hover:text-accent-foreground border',
secondary: 'bg-secondary text-secondary-foreground hover:bg-secondary/80',
ghost: 'hover:bg-accent hover:text-accent-foreground',
link: 'text-primary underline-offset-4 hover:underline'
},
size: {
default: 'h-10 px-4 py-2',
sm: 'h-9 rounded-md px-3',
lg: 'h-11 rounded-md px-8',
icon: 'h-10 w-10'
}
export const buttonVariants = tv({
base: 'ring-offset-background focus-visible:ring-ring inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-colors focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0',
variants: {
variant: {
default: 'bg-primary text-primary-foreground hover:bg-primary/90',
destructive:
'bg-destructive text-destructive-foreground hover:bg-destructive/90',
outline:
'border-input bg-background hover:bg-accent hover:text-accent-foreground border',
secondary:
'bg-secondary text-secondary-foreground hover:bg-secondary/80',
ghost: 'hover:bg-accent hover:text-accent-foreground',
link: 'text-primary underline-offset-4 hover:underline'
},
defaultVariants: {
variant: 'default',
size: 'default'
size: {
default: 'h-10 px-4 py-2',
sm: 'h-9 rounded-md px-3',
lg: 'h-11 rounded-md px-8',
icon: 'h-10 w-10'
}
});
},
defaultVariants: {
variant: 'default',
size: 'default'
}
});
export type ButtonVariant = VariantProps<typeof buttonVariants>['variant'];
export type ButtonSize = VariantProps<typeof buttonVariants>['size'];
export type ButtonVariant = VariantProps<typeof buttonVariants>['variant'];
export type ButtonSize = VariantProps<typeof buttonVariants>['size'];
export type ButtonProps = WithElementRef<HTMLButtonAttributes> &
WithElementRef<HTMLAnchorAttributes> & {
variant?: ButtonVariant;
size?: ButtonSize;
class?: any;
};
export type ButtonProps = WithElementRef<HTMLButtonAttributes> &
WithElementRef<HTMLAnchorAttributes> & {
variant?: ButtonVariant;
size?: ButtonSize;
class?: any;
};
</script>
<script lang="ts">
import { cn } from '$lib/utils/utils.js';
import { cn } from '$lib/utils/utils.js';
let {
class: className,
variant = 'default',
size = 'default',
ref = $bindable(null),
href = undefined,
type = 'button',
children,
...restProps
}: ButtonProps = $props();
let {
class: className,
variant = 'default',
size = 'default',
ref = $bindable(null),
href = undefined,
type = 'button',
children,
...restProps
}: ButtonProps = $props();
</script>
{#if href}
@@ -59,8 +64,7 @@
bind:this={ref}
class={cn(buttonVariants({ variant, size, className }))}
{href}
{...restProps}
>
{...restProps}>
{@render children?.()}
</a>
{:else}
@@ -68,8 +72,7 @@
bind:this={ref}
class={cn(buttonVariants({ variant, size, className }))}
{type}
{...restProps}
>
{...restProps}>
{@render children?.()}
</button>
{/if}

View File

@@ -1,20 +1,23 @@
<script lang="ts">
import { DropdownMenu as DropdownMenuPrimitive, type WithoutChildrenOrChild } from 'bits-ui';
import Check from 'lucide-svelte/icons/check';
import Minus from 'lucide-svelte/icons/minus';
import { cn } from '$lib/utils/utils.js';
import type { Snippet } from 'svelte';
import {
DropdownMenu as DropdownMenuPrimitive,
type WithoutChildrenOrChild
} from 'bits-ui';
import Check from 'lucide-svelte/icons/check';
import Minus from 'lucide-svelte/icons/minus';
import { cn } from '$lib/utils/utils.js';
import type { Snippet } from 'svelte';
let {
ref = $bindable(null),
checked = $bindable(false),
indeterminate = $bindable(false),
class: className,
children: childrenProp,
...restProps
}: WithoutChildrenOrChild<DropdownMenuPrimitive.CheckboxItemProps> & {
children?: Snippet;
} = $props();
let {
ref = $bindable(null),
checked = $bindable(false),
indeterminate = $bindable(false),
class: className,
children: childrenProp,
...restProps
}: WithoutChildrenOrChild<DropdownMenuPrimitive.CheckboxItemProps> & {
children?: Snippet;
} = $props();
</script>
<DropdownMenuPrimitive.CheckboxItem
@@ -22,11 +25,10 @@
bind:checked
bind:indeterminate
class={cn(
'data-[highlighted]:bg-accent data-[highlighted]:text-accent-foreground relative flex cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-hidden data-disabled:pointer-events-none data-disabled:opacity-50',
'data-[highlighted]:bg-accent data-[highlighted]:text-accent-foreground relative flex cursor-default items-center rounded-sm py-1.5 pr-2 pl-8 text-sm outline-hidden select-none data-disabled:pointer-events-none data-disabled:opacity-50',
className
)}
{...restProps}
>
{...restProps}>
{#snippet children({ checked, indeterminate })}
<span class="absolute left-2 flex size-3.5 items-center justify-center">
{#if indeterminate}

View File

@@ -1,16 +1,16 @@
<script lang="ts">
import { cn } from '$lib/utils/utils.js';
import { DropdownMenu as DropdownMenuPrimitive } from 'bits-ui';
import { cn } from '$lib/utils/utils.js';
import { DropdownMenu as DropdownMenuPrimitive } from 'bits-ui';
let {
ref = $bindable(null),
sideOffset = 4,
portalProps,
class: className,
...restProps
}: DropdownMenuPrimitive.ContentProps & {
portalProps?: DropdownMenuPrimitive.PortalProps;
} = $props();
let {
ref = $bindable(null),
sideOffset = 4,
portalProps,
class: className,
...restProps
}: DropdownMenuPrimitive.ContentProps & {
portalProps?: DropdownMenuPrimitive.PortalProps;
} = $props();
</script>
<DropdownMenuPrimitive.Portal {...portalProps}>
@@ -21,6 +21,5 @@
'bg-popover text-popover-foreground data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 z-50 min-w-32 overflow-hidden rounded-md border p-1 shadow-md outline-hidden',
className
)}
{...restProps}
/>
{...restProps} />
</DropdownMenuPrimitive.Portal>

View File

@@ -1,19 +1,18 @@
<script lang="ts">
import { DropdownMenu as DropdownMenuPrimitive } from 'bits-ui';
import { cn } from '$lib/utils/utils.js';
import { DropdownMenu as DropdownMenuPrimitive } from 'bits-ui';
import { cn } from '$lib/utils/utils.js';
let {
ref = $bindable(null),
class: className,
inset,
...restProps
}: DropdownMenuPrimitive.GroupHeadingProps & {
inset?: boolean;
} = $props();
let {
ref = $bindable(null),
class: className,
inset,
...restProps
}: DropdownMenuPrimitive.GroupHeadingProps & {
inset?: boolean;
} = $props();
</script>
<DropdownMenuPrimitive.GroupHeading
bind:ref
class={cn('px-2 py-1.5 text-sm font-semibold', inset && 'pl-8', className)}
{...restProps}
/>
{...restProps} />

View File

@@ -1,23 +1,22 @@
<script lang="ts">
import { cn } from '$lib/utils/utils.js';
import { DropdownMenu as DropdownMenuPrimitive } from 'bits-ui';
import { cn } from '$lib/utils/utils.js';
import { DropdownMenu as DropdownMenuPrimitive } from 'bits-ui';
let {
ref = $bindable(null),
class: className,
inset,
...restProps
}: DropdownMenuPrimitive.ItemProps & {
inset?: boolean;
} = $props();
let {
ref = $bindable(null),
class: className,
inset,
...restProps
}: DropdownMenuPrimitive.ItemProps & {
inset?: boolean;
} = $props();
</script>
<DropdownMenuPrimitive.Item
bind:ref
class={cn(
'data-[highlighted]:bg-accent data-[highlighted]:text-accent-foreground relative flex cursor-default select-none items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-hidden transition-colors data-disabled:pointer-events-none data-disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0',
'data-[highlighted]:bg-accent data-[highlighted]:text-accent-foreground relative flex cursor-default items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-hidden transition-colors select-none data-disabled:pointer-events-none data-disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0',
inset && 'pl-8',
className
)}
{...restProps}
/>
{...restProps} />

View File

@@ -1,23 +1,22 @@
<script lang="ts">
import { cn } from '$lib/utils/utils.js';
import { type WithElementRef } from 'bits-ui';
import type { HTMLAttributes } from 'svelte/elements';
import { cn } from '$lib/utils/utils.js';
import { type WithElementRef } from 'bits-ui';
import type { HTMLAttributes } from 'svelte/elements';
let {
ref = $bindable(null),
class: className,
inset,
children,
...restProps
}: WithElementRef<HTMLAttributes<HTMLDivElement>> & {
inset?: boolean;
} = $props();
let {
ref = $bindable(null),
class: className,
inset,
children,
...restProps
}: WithElementRef<HTMLAttributes<HTMLDivElement>> & {
inset?: boolean;
} = $props();
</script>
<div
bind:this={ref}
class={cn('px-2 py-1.5 text-sm font-semibold', inset && 'pl-8', className)}
{...restProps}
>
{...restProps}>
{@render children?.()}
</div>

View File

@@ -1,24 +1,26 @@
<script lang="ts">
import { DropdownMenu as DropdownMenuPrimitive, type WithoutChild } from 'bits-ui';
import Circle from 'lucide-svelte/icons/circle';
import { cn } from '$lib/utils/utils.js';
import {
DropdownMenu as DropdownMenuPrimitive,
type WithoutChild
} from 'bits-ui';
import Circle from 'lucide-svelte/icons/circle';
import { cn } from '$lib/utils/utils.js';
let {
ref = $bindable(null),
class: className,
children: childrenProp,
...restProps
}: WithoutChild<DropdownMenuPrimitive.RadioItemProps> = $props();
let {
ref = $bindable(null),
class: className,
children: childrenProp,
...restProps
}: WithoutChild<DropdownMenuPrimitive.RadioItemProps> = $props();
</script>
<DropdownMenuPrimitive.RadioItem
bind:ref
class={cn(
'data-[highlighted]:bg-accent data-[highlighted]:text-accent-foreground relative flex cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-hidden data-disabled:pointer-events-none data-disabled:opacity-50',
'data-[highlighted]:bg-accent data-[highlighted]:text-accent-foreground relative flex cursor-default items-center rounded-sm py-1.5 pr-2 pl-8 text-sm outline-hidden select-none data-disabled:pointer-events-none data-disabled:opacity-50',
className
)}
{...restProps}
>
{...restProps}>
{#snippet children({ checked })}
<span class="absolute left-2 flex size-3.5 items-center justify-center">
{#if checked}

View File

@@ -1,16 +1,15 @@
<script lang="ts">
import { DropdownMenu as DropdownMenuPrimitive } from 'bits-ui';
import { cn } from '$lib/utils/utils.js';
import { DropdownMenu as DropdownMenuPrimitive } from 'bits-ui';
import { cn } from '$lib/utils/utils.js';
let {
ref = $bindable(null),
class: className,
...restProps
}: DropdownMenuPrimitive.SeparatorProps = $props();
let {
ref = $bindable(null),
class: className,
...restProps
}: DropdownMenuPrimitive.SeparatorProps = $props();
</script>
<DropdownMenuPrimitive.Separator
bind:ref
class={cn('bg-muted -mx-1 my-1 h-px', className)}
{...restProps}
/>
{...restProps} />

View File

@@ -1,20 +1,19 @@
<script lang="ts">
import type { HTMLAttributes } from 'svelte/elements';
import { type WithElementRef } from 'bits-ui';
import { cn } from '$lib/utils/utils.js';
import type { HTMLAttributes } from 'svelte/elements';
import { type WithElementRef } from 'bits-ui';
import { cn } from '$lib/utils/utils.js';
let {
ref = $bindable(null),
class: className,
children,
...restProps
}: WithElementRef<HTMLAttributes<HTMLSpanElement>> = $props();
let {
ref = $bindable(null),
class: className,
children,
...restProps
}: WithElementRef<HTMLAttributes<HTMLSpanElement>> = $props();
</script>
<span
bind:this={ref}
class={cn('ml-auto text-xs tracking-widest opacity-60', className)}
{...restProps}
>
{...restProps}>
{@render children?.()}
</span>

View File

@@ -1,12 +1,12 @@
<script lang="ts">
import { DropdownMenu as DropdownMenuPrimitive } from 'bits-ui';
import { cn } from '$lib/utils/utils.js';
import { DropdownMenu as DropdownMenuPrimitive } from 'bits-ui';
import { cn } from '$lib/utils/utils.js';
let {
ref = $bindable(null),
class: className,
...restProps
}: DropdownMenuPrimitive.SubContentProps = $props();
let {
ref = $bindable(null),
class: className,
...restProps
}: DropdownMenuPrimitive.SubContentProps = $props();
</script>
<DropdownMenuPrimitive.SubContent
@@ -15,5 +15,4 @@
'bg-popover text-popover-foreground z-50 min-w-32 rounded-md border p-1 shadow-lg focus:outline-hidden',
className
)}
{...restProps}
/>
{...restProps} />

View File

@@ -1,28 +1,27 @@
<script lang="ts">
import { DropdownMenu as DropdownMenuPrimitive } from 'bits-ui';
import ChevronRight from 'lucide-svelte/icons/chevron-right';
import { cn } from '$lib/utils/utils.js';
import { DropdownMenu as DropdownMenuPrimitive } from 'bits-ui';
import ChevronRight from 'lucide-svelte/icons/chevron-right';
import { cn } from '$lib/utils/utils.js';
let {
ref = $bindable(null),
class: className,
inset,
children,
...restProps
}: DropdownMenuPrimitive.SubTriggerProps & {
inset?: boolean;
} = $props();
let {
ref = $bindable(null),
class: className,
inset,
children,
...restProps
}: DropdownMenuPrimitive.SubTriggerProps & {
inset?: boolean;
} = $props();
</script>
<DropdownMenuPrimitive.SubTrigger
bind:ref
class={cn(
'data-[highlighted]:bg-accent data-[state=open]:bg-accent flex cursor-default select-none items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-hidden [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0',
'data-[highlighted]:bg-accent data-[state=open]:bg-accent flex cursor-default items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-hidden select-none [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0',
inset && 'pl-8',
className
)}
{...restProps}
>
{...restProps}>
{@render children?.()}
<ChevronRight class="ml-auto" />
</DropdownMenuPrimitive.SubTrigger>

View File

@@ -1,22 +1,21 @@
<script lang="ts">
import type { HTMLInputAttributes } from 'svelte/elements';
import type { WithElementRef } from 'bits-ui';
import { cn } from '$lib/utils/utils.js';
import type { HTMLInputAttributes } from 'svelte/elements';
import type { WithElementRef } from 'bits-ui';
import { cn } from '$lib/utils/utils.js';
let {
ref = $bindable(null),
value = $bindable(),
class: className,
...restProps
}: WithElementRef<HTMLInputAttributes> = $props();
let {
ref = $bindable(null),
value = $bindable(),
class: className,
...restProps
}: WithElementRef<HTMLInputAttributes> = $props();
</script>
<input
bind:this={ref}
class={cn(
'border-input bg-background ring-offset-background placeholder:text-muted-foreground focus-visible:ring-ring flex h-10 w-full rounded-md border px-3 py-2 text-sm file:border-0 file:bg-transparent file:text-sm file:font-medium focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50',
'border-input bg-background ring-offset-background placeholder:text-muted-foreground focus-visible:ring-ring flex h-10 w-full rounded-md border px-3 py-2 text-sm file:border-0 file:bg-transparent file:text-sm file:font-medium focus-visible:ring-2 focus-visible:ring-offset-2 focus-visible:outline-hidden disabled:cursor-not-allowed disabled:opacity-50',
className
)}
bind:value
{...restProps}
/>
{...restProps} />

View File

@@ -1,17 +1,17 @@
<script lang="ts">
import { cn } from '$lib/utils/utils.js';
import { Popover as PopoverPrimitive } from 'bits-ui';
import { cn } from '$lib/utils/utils.js';
import { Popover as PopoverPrimitive } from 'bits-ui';
let {
ref = $bindable(null),
class: className,
sideOffset = 4,
align = 'center',
portalProps,
...restProps
}: PopoverPrimitive.ContentProps & {
portalProps?: PopoverPrimitive.PortalProps;
} = $props();
let {
ref = $bindable(null),
class: className,
sideOffset = 4,
align = 'center',
portalProps,
...restProps
}: PopoverPrimitive.ContentProps & {
portalProps?: PopoverPrimitive.PortalProps;
} = $props();
</script>
<PopoverPrimitive.Portal {...portalProps}>
@@ -23,6 +23,5 @@
'bg-popover text-popover-foreground data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 z-50 w-72 rounded-md border p-4 shadow-md outline-hidden',
className
)}
{...restProps}
/>
{...restProps} />
</PopoverPrimitive.Portal>

View File

@@ -1,13 +1,13 @@
<script lang="ts">
import { Separator as SeparatorPrimitive } from 'bits-ui';
import { cn } from '$lib/utils/utils.js';
import { Separator as SeparatorPrimitive } from 'bits-ui';
import { cn } from '$lib/utils/utils.js';
let {
ref = $bindable(null),
class: className,
orientation = 'horizontal',
...restProps
}: SeparatorPrimitive.RootProps = $props();
let {
ref = $bindable(null),
class: className,
orientation = 'horizontal',
...restProps
}: SeparatorPrimitive.RootProps = $props();
</script>
<SeparatorPrimitive.Root
@@ -18,5 +18,4 @@
className
)}
{orientation}
{...restProps}
/>
{...restProps} />

Some files were not shown because too many files have changed in this diff Show More