fix(idaa): fix country/subdivision/timezone dropdowns — switch to in-memory sort
- Country and state/province fields were showing as plain text inputs because liveQuery used orderBy() on non-indexed columns, causing silent Dexie errors that left the store as undefined indefinitely. - Fix: replaced orderBy() with toArray() + in-memory sort across all three lookup types (country, country_subdivision, time_zone). - Sort convention matches Aether backend: sort DESC (higher = first, NULL=0 last), then name ASC — puts priority entries at the top. - Added db_lookups.ts (IDB schema for lookup tables) and updated core__countries, core__country_subdivisions, core__time_zones to IDB-backed SWR pattern. - Affected: archive edit, archive content edit, recovery meeting edit. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -1,65 +1,64 @@
|
|||||||
import type { key_val } from '$lib/stores/ae_stores';
|
|
||||||
import { api } from '$lib/api/api';
|
import { api } from '$lib/api/api';
|
||||||
|
import { db_lookups, LOOKUP_TTL_MS } from '$lib/ae_core/db_lookups';
|
||||||
|
|
||||||
import { db_core } from '$lib/ae_core/db_core';
|
/**
|
||||||
|
* Country lookup — IDB-backed SWR helper.
|
||||||
|
*
|
||||||
|
* Calling this function triggers a background API refresh if IDB is empty or
|
||||||
|
* older than 24 hours. The function returns immediately without awaiting the
|
||||||
|
* refresh. Components subscribe to db_lookups.lu_country via liveQuery and
|
||||||
|
* receive automatic updates when the refresh completes.
|
||||||
|
*
|
||||||
|
* Updated 2026-03-23 — replaced localStorage pattern with IDB + 24h TTL
|
||||||
|
*/
|
||||||
|
|
||||||
const ae_promises: key_val = {};
|
async function _refresh_lu_country_background({
|
||||||
|
|
||||||
// Updated 2024-10-14
|
|
||||||
export async function load_ae_obj_li__country({
|
|
||||||
api_cfg,
|
api_cfg,
|
||||||
// account_id,
|
|
||||||
enabled = 'enabled',
|
|
||||||
hidden = 'not_hidden',
|
|
||||||
limit = 275, // There are roughly 249 as of 2026-02
|
|
||||||
offset = 0,
|
|
||||||
order_by_li = { sort: 'DESC', english_short_name: 'ASC', alpha_2_code: 'ASC' } as const,
|
|
||||||
params = {},
|
|
||||||
try_cache = true,
|
|
||||||
log_lvl = 0
|
log_lvl = 0
|
||||||
}: {
|
}: {
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
api_cfg: any;
|
api_cfg: any;
|
||||||
// account_id: string,
|
|
||||||
enabled?: 'enabled' | 'all' | 'not_enabled' | undefined;
|
|
||||||
hidden?: 'hidden' | 'all' | 'not_hidden' | undefined;
|
|
||||||
limit?: number;
|
|
||||||
offset?: number;
|
|
||||||
order_by_li?: key_val;
|
|
||||||
params?: key_val;
|
|
||||||
try_cache?: boolean;
|
|
||||||
log_lvl?: number;
|
log_lvl?: number;
|
||||||
}) {
|
}) {
|
||||||
if (log_lvl) {
|
if (log_lvl) console.log('*** _refresh_lu_country_background() ***');
|
||||||
console.log(`*** load_ae_obj_li__country() ***`);
|
try {
|
||||||
}
|
const result = await api.get_ae_obj_li_for_lu({
|
||||||
|
api_cfg,
|
||||||
const params_json: key_val = {};
|
|
||||||
|
|
||||||
// console.log('params_json:', params_json);
|
|
||||||
|
|
||||||
ae_promises.load__country_li = await api
|
|
||||||
.get_ae_obj_li_for_lu({
|
|
||||||
api_cfg: api_cfg,
|
|
||||||
for_lu_type: 'country',
|
for_lu_type: 'country',
|
||||||
enabled: enabled,
|
enabled: 'enabled',
|
||||||
hidden: hidden,
|
hidden: 'not_hidden',
|
||||||
limit: limit,
|
limit: 275,
|
||||||
offset: offset,
|
log_lvl
|
||||||
params: params,
|
|
||||||
log_lvl: log_lvl
|
|
||||||
})
|
|
||||||
.then(function (country_li_get_result) {
|
|
||||||
if (country_li_get_result) {
|
|
||||||
// handle_db_save_ae_obj_li__country({obj_type: 'country', obj_li: country_li_get_result});
|
|
||||||
return country_li_get_result;
|
|
||||||
} else {
|
|
||||||
return [];
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.catch(function (error: any) {
|
|
||||||
console.log('No results returned or failed.', error);
|
|
||||||
});
|
});
|
||||||
|
if (result?.length) {
|
||||||
console.log('ae_promises.load__country_li:', ae_promises.load__country_li);
|
await db_lookups.lu_country.clear();
|
||||||
return ae_promises.load__country_li;
|
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`);
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('lu_country refresh failed:', error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function load_ae_obj_li__country({
|
||||||
|
api_cfg,
|
||||||
|
log_lvl = 0
|
||||||
|
}: {
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
|
api_cfg: any;
|
||||||
|
log_lvl?: number;
|
||||||
|
}) {
|
||||||
|
if (log_lvl) console.log('*** load_ae_obj_li__country() ***');
|
||||||
|
|
||||||
|
const count = await db_lookups.lu_country.count();
|
||||||
|
const meta = await db_lookups.lu_cache_meta.get('country');
|
||||||
|
const is_stale = !meta || Date.now() - meta.refreshed_at > LOOKUP_TTL_MS;
|
||||||
|
|
||||||
|
if (count === 0 || is_stale) {
|
||||||
|
// 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`);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,68 +1,66 @@
|
|||||||
import type { key_val } from '$lib/stores/ae_stores';
|
|
||||||
import { api } from '$lib/api/api';
|
import { api } from '$lib/api/api';
|
||||||
|
import { db_lookups, LOOKUP_TTL_MS } from '$lib/ae_core/db_lookups';
|
||||||
|
|
||||||
import { db_core } from '$lib/ae_core/db_core';
|
/**
|
||||||
|
* Country subdivision lookup — IDB-backed SWR helper.
|
||||||
|
*
|
||||||
|
* Calling this function triggers a background API refresh if IDB is empty or
|
||||||
|
* older than 24 hours. Components subscribe to db_lookups.lu_country_subdivision
|
||||||
|
* via liveQuery and receive automatic updates when the refresh completes.
|
||||||
|
*
|
||||||
|
* Updated 2026-03-23 — replaced localStorage pattern with IDB + 24h TTL
|
||||||
|
*/
|
||||||
|
|
||||||
const ae_promises: key_val = {};
|
async function _refresh_lu_country_subdivision_background({
|
||||||
|
|
||||||
// Updated 2024-10-14
|
|
||||||
export async function load_ae_obj_li__country_subdivision({
|
|
||||||
api_cfg,
|
api_cfg,
|
||||||
// account_id,
|
|
||||||
enabled = 'enabled',
|
|
||||||
hidden = 'not_hidden',
|
|
||||||
limit = 3500, // There are roughly 3434 as of 2026-02
|
|
||||||
offset = 0,
|
|
||||||
order_by_li = { sort: 'DESC', name: 'ASC', code: 'ASC' } as const,
|
|
||||||
params = {},
|
|
||||||
try_cache = true,
|
|
||||||
log_lvl = 0
|
log_lvl = 0
|
||||||
}: {
|
}: {
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
api_cfg: any;
|
api_cfg: any;
|
||||||
// account_id: string,
|
|
||||||
enabled?: 'enabled' | 'all' | 'not_enabled' | undefined;
|
|
||||||
hidden?: 'hidden' | 'all' | 'not_hidden' | undefined;
|
|
||||||
limit?: number;
|
|
||||||
offset?: number;
|
|
||||||
order_by_li?: key_val;
|
|
||||||
params?: key_val;
|
|
||||||
try_cache?: boolean;
|
|
||||||
log_lvl?: number;
|
log_lvl?: number;
|
||||||
}) {
|
}) {
|
||||||
if (log_lvl) {
|
if (log_lvl) console.log('*** _refresh_lu_country_subdivision_background() ***');
|
||||||
console.log(`*** load_ae_obj_li__country_subdivision() ***`);
|
try {
|
||||||
}
|
const result = await api.get_ae_obj_li_for_lu({
|
||||||
|
api_cfg,
|
||||||
const params_json: key_val = {};
|
|
||||||
|
|
||||||
// console.log('params_json:', params_json);
|
|
||||||
|
|
||||||
ae_promises.load__country_subdivision_li = await api
|
|
||||||
.get_ae_obj_li_for_lu({
|
|
||||||
api_cfg: api_cfg,
|
|
||||||
for_lu_type: 'country_subdivision',
|
for_lu_type: 'country_subdivision',
|
||||||
enabled: enabled,
|
enabled: 'enabled',
|
||||||
hidden: hidden,
|
hidden: 'not_hidden',
|
||||||
limit: limit,
|
limit: 3500,
|
||||||
offset: offset,
|
log_lvl
|
||||||
params: params,
|
|
||||||
log_lvl: log_lvl
|
|
||||||
})
|
|
||||||
.then(function (country_subdivision_li_get_result) {
|
|
||||||
if (country_subdivision_li_get_result) {
|
|
||||||
// handle_db_save_ae_obj_li__country_subdivision({obj_type: 'country_subdivision', obj_li: country_subdivision_li_get_result});
|
|
||||||
return country_subdivision_li_get_result;
|
|
||||||
} else {
|
|
||||||
return [];
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.catch(function (error: any) {
|
|
||||||
console.log('No results returned or failed.', error);
|
|
||||||
});
|
});
|
||||||
|
if (result?.length) {
|
||||||
console.log(
|
await db_lookups.lu_country_subdivision.clear();
|
||||||
'ae_promises.load__country_subdivision_li:',
|
await db_lookups.lu_country_subdivision.bulkPut(result);
|
||||||
ae_promises.load__country_subdivision_li
|
await db_lookups.lu_cache_meta.put({
|
||||||
);
|
lu_type: 'country_subdivision',
|
||||||
return ae_promises.load__country_subdivision_li;
|
refreshed_at: Date.now()
|
||||||
|
});
|
||||||
|
if (log_lvl)
|
||||||
|
console.log(`lu_country_subdivision: saved ${result.length} records to IDB`);
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('lu_country_subdivision refresh failed:', error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function load_ae_obj_li__country_subdivision({
|
||||||
|
api_cfg,
|
||||||
|
log_lvl = 0
|
||||||
|
}: {
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
|
api_cfg: any;
|
||||||
|
log_lvl?: number;
|
||||||
|
}) {
|
||||||
|
if (log_lvl) console.log('*** load_ae_obj_li__country_subdivision() ***');
|
||||||
|
|
||||||
|
const count = await db_lookups.lu_country_subdivision.count();
|
||||||
|
const meta = await db_lookups.lu_cache_meta.get('country_subdivision');
|
||||||
|
const is_stale = !meta || Date.now() - meta.refreshed_at > LOOKUP_TTL_MS;
|
||||||
|
|
||||||
|
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`);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,70 +1,67 @@
|
|||||||
import type { key_val } from '$lib/stores/ae_stores';
|
|
||||||
import { api } from '$lib/api/api';
|
import { api } from '$lib/api/api';
|
||||||
|
import { db_lookups, LOOKUP_TTL_MS } from '$lib/ae_core/db_lookups';
|
||||||
|
|
||||||
import { db_core } from '$lib/ae_core/db_core';
|
/**
|
||||||
|
* Time zone lookup — IDB-backed SWR helper.
|
||||||
|
*
|
||||||
|
* Fetches priority timezones (only_priority=true, ~72 records). Calling this
|
||||||
|
* function triggers a background API refresh if IDB is empty or older than
|
||||||
|
* 24 hours. Components subscribe to db_lookups.lu_time_zone via liveQuery and
|
||||||
|
* receive automatic updates when the refresh completes.
|
||||||
|
*
|
||||||
|
* Updated 2026-03-23 — replaced $ae_loc + localStorage pattern with IDB + 24h TTL
|
||||||
|
*/
|
||||||
|
|
||||||
const ae_promises: key_val = {};
|
async function _refresh_lu_time_zone_background({
|
||||||
|
|
||||||
// Updated 2026-02-20
|
|
||||||
export async function load_ae_obj_li__time_zone({
|
|
||||||
api_cfg,
|
api_cfg,
|
||||||
// account_id,
|
|
||||||
enabled = 'enabled',
|
|
||||||
hidden = 'not_hidden',
|
|
||||||
limit = 1800, // There are roughly 1780 as of 2026-02
|
|
||||||
offset = 0,
|
|
||||||
// order_by_li = {'priority': 'DESC', 'group': 'ASC', 'sort': 'DESC', 'name': 'ASC'},
|
|
||||||
order_by_li = { priority: 'DESC', sort: 'DESC', name: 'ASC' } as const,
|
|
||||||
params = {},
|
|
||||||
only_priority = false,
|
|
||||||
try_cache = true,
|
|
||||||
log_lvl = 0
|
log_lvl = 0
|
||||||
}: {
|
}: {
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
api_cfg: any;
|
api_cfg: any;
|
||||||
// account_id: string,
|
|
||||||
enabled?: 'enabled' | 'all' | 'not_enabled' | undefined;
|
|
||||||
hidden?: 'hidden' | 'all' | 'not_hidden' | undefined;
|
|
||||||
limit?: number;
|
|
||||||
offset?: number;
|
|
||||||
order_by_li?: key_val;
|
|
||||||
params?: key_val;
|
|
||||||
only_priority?: boolean;
|
|
||||||
try_cache?: boolean;
|
|
||||||
log_lvl?: number;
|
log_lvl?: number;
|
||||||
}) {
|
}) {
|
||||||
if (log_lvl) {
|
if (log_lvl) console.log('*** _refresh_lu_time_zone_background() ***');
|
||||||
console.log(`*** load_ae_obj_li__time_zone() *** only_priority=${only_priority}`);
|
try {
|
||||||
}
|
const result = await api.get_ae_obj_li_for_lu({
|
||||||
|
api_cfg,
|
||||||
const params_json: key_val = {};
|
|
||||||
|
|
||||||
// console.log('params_json:', params_json);
|
|
||||||
|
|
||||||
ae_promises.load__time_zone_li = await api
|
|
||||||
.get_ae_obj_li_for_lu({
|
|
||||||
api_cfg: api_cfg,
|
|
||||||
for_lu_type: 'time_zone',
|
for_lu_type: 'time_zone',
|
||||||
enabled: enabled,
|
enabled: 'enabled',
|
||||||
hidden: hidden,
|
hidden: 'not_hidden',
|
||||||
limit: limit,
|
only_priority: true, // ~72 priority timezone records
|
||||||
offset: offset,
|
limit: 1800,
|
||||||
order_by_li: order_by_li,
|
log_lvl
|
||||||
params: params,
|
|
||||||
only_priority: only_priority,
|
|
||||||
log_lvl: log_lvl
|
|
||||||
})
|
|
||||||
.then(function (time_zone_li_get_result) {
|
|
||||||
if (time_zone_li_get_result) {
|
|
||||||
// handle_db_save_ae_obj_li__time_zone({obj_type: 'time_zone', obj_li: time_zone_li_get_result});
|
|
||||||
return time_zone_li_get_result;
|
|
||||||
} else {
|
|
||||||
return [];
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.catch(function (error: any) {
|
|
||||||
console.log('No results returned or failed.', error);
|
|
||||||
});
|
});
|
||||||
|
if (result?.length) {
|
||||||
console.log('ae_promises.load__time_zone_li:', ae_promises.load__time_zone_li);
|
await db_lookups.lu_time_zone.clear();
|
||||||
return ae_promises.load__time_zone_li;
|
await db_lookups.lu_time_zone.bulkPut(result);
|
||||||
|
await db_lookups.lu_cache_meta.put({
|
||||||
|
lu_type: 'time_zone',
|
||||||
|
refreshed_at: Date.now()
|
||||||
|
});
|
||||||
|
if (log_lvl) console.log(`lu_time_zone: saved ${result.length} records to IDB`);
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('lu_time_zone refresh failed:', error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function load_ae_obj_li__time_zone({
|
||||||
|
api_cfg,
|
||||||
|
log_lvl = 0
|
||||||
|
}: {
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
|
api_cfg: any;
|
||||||
|
log_lvl?: number;
|
||||||
|
}) {
|
||||||
|
if (log_lvl) console.log('*** load_ae_obj_li__time_zone() ***');
|
||||||
|
|
||||||
|
const count = await db_lookups.lu_time_zone.count();
|
||||||
|
const meta = await db_lookups.lu_cache_meta.get('time_zone');
|
||||||
|
const is_stale = !meta || Date.now() - meta.refreshed_at > LOOKUP_TTL_MS;
|
||||||
|
|
||||||
|
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`);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
81
src/lib/ae_core/db_lookups.ts
Normal file
81
src/lib/ae_core/db_lookups.ts
Normal file
@@ -0,0 +1,81 @@
|
|||||||
|
import Dexie, { type Table } from 'dexie';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Lookup DB — IDB-backed cache for V3 Uniform Lookup System reference data.
|
||||||
|
*
|
||||||
|
* These tables store the deduplicated, priority-ranked list returned by
|
||||||
|
* GET /v3/lookup/{lu_type}/list. Data is refreshed automatically on a 24-hour
|
||||||
|
* TTL via the core__*.ts load helpers; components subscribe via liveQuery.
|
||||||
|
*
|
||||||
|
* Updated 2026-03-23
|
||||||
|
*/
|
||||||
|
|
||||||
|
export interface LuCountry {
|
||||||
|
id: number;
|
||||||
|
group: string; // dedup key = alpha_2_code (e.g. "US")
|
||||||
|
alpha_2_code: string;
|
||||||
|
name: string;
|
||||||
|
english_short_name?: string;
|
||||||
|
name_override?: string;
|
||||||
|
enable?: number;
|
||||||
|
hide?: number;
|
||||||
|
priority?: number;
|
||||||
|
sort?: number;
|
||||||
|
account_id?: number | null;
|
||||||
|
[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")
|
||||||
|
code: string;
|
||||||
|
name: string;
|
||||||
|
country_alpha_2_code?: string;
|
||||||
|
name_override?: string;
|
||||||
|
enable?: number;
|
||||||
|
hide?: number;
|
||||||
|
priority?: number;
|
||||||
|
sort?: number;
|
||||||
|
account_id?: number | null;
|
||||||
|
[key: string]: unknown;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface LuTimeZone {
|
||||||
|
id: number;
|
||||||
|
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
|
||||||
|
enable?: number;
|
||||||
|
hide?: number;
|
||||||
|
priority?: number;
|
||||||
|
sort?: number;
|
||||||
|
account_id?: number | null;
|
||||||
|
[key: string]: unknown;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface LuCacheMeta {
|
||||||
|
lu_type: 'country' | 'country_subdivision' | 'time_zone';
|
||||||
|
refreshed_at: number; // Unix timestamp ms — used for 24h TTL check
|
||||||
|
}
|
||||||
|
|
||||||
|
class LookupsDexie extends Dexie {
|
||||||
|
lu_country!: Table<LuCountry>;
|
||||||
|
lu_country_subdivision!: Table<LuCountrySubdivision>;
|
||||||
|
lu_time_zone!: Table<LuTimeZone>;
|
||||||
|
lu_cache_meta!: Table<LuCacheMeta>;
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
super('ae_lookups_db');
|
||||||
|
this.version(1).stores({
|
||||||
|
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'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export const db_lookups = new LookupsDexie();
|
||||||
|
|
||||||
|
/** 24-hour TTL in milliseconds */
|
||||||
|
export const LOOKUP_TTL_MS = 24 * 60 * 60 * 1000;
|
||||||
@@ -7,6 +7,8 @@
|
|||||||
import type { key_val } from '$lib/stores/ae_stores';
|
import type { key_val } from '$lib/stores/ae_stores';
|
||||||
import { ae_util } from '$lib/ae_utils/ae_utils';
|
import { ae_util } from '$lib/ae_utils/ae_utils';
|
||||||
import { core_func } from '$lib/ae_core/ae_core_functions';
|
import { core_func } from '$lib/ae_core/ae_core_functions';
|
||||||
|
import { liveQuery } from 'dexie';
|
||||||
|
import { db_lookups } from '$lib/ae_core/db_lookups';
|
||||||
import {
|
import {
|
||||||
ae_snip,
|
ae_snip,
|
||||||
ae_loc,
|
ae_loc,
|
||||||
@@ -106,56 +108,21 @@
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
let lu_time_zone_list: any = $state(
|
// Timezone lookup — reactive IDB query; background refresh handled by liveQuery + TTL
|
||||||
localStorage.getItem('lu_time_zone_list')
|
// Sort: sort DESC (higher = first, NULL=0 last), then name ASC — matches Aether backend convention.
|
||||||
? JSON.parse(localStorage.getItem('lu_time_zone_list') as string)
|
const lq__lu_time_zone = liveQuery(() =>
|
||||||
: []
|
db_lookups.lu_time_zone.toArray().then(arr =>
|
||||||
|
arr.sort((a, b) => {
|
||||||
|
const s_diff = Number(b['sort'] ?? 0) - Number(a['sort'] ?? 0);
|
||||||
|
if (s_diff !== 0) return s_diff;
|
||||||
|
return (a.name ?? '').localeCompare(b.name ?? '');
|
||||||
|
})
|
||||||
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
onMount(() => {
|
onMount(() => {
|
||||||
$ae_loc.lu_time_zone_list = [];
|
// Trigger background IDB refresh if stale/empty; liveQuery reacts automatically
|
||||||
// $ae_loc.lu_time_zone_list = [];
|
core_func.load_ae_obj_li__time_zone({ api_cfg: $ae_api, log_lvl });
|
||||||
// lu_time_zone_list = [];
|
|
||||||
if (lu_time_zone_list && lu_time_zone_list.length > 0) {
|
|
||||||
// console.log('Already have time zone list!', lu_time_zone_list);
|
|
||||||
} else {
|
|
||||||
console.log('No time zone list');
|
|
||||||
|
|
||||||
let lu_time_zone_li_get_promise = core_func
|
|
||||||
.load_ae_obj_li__time_zone({
|
|
||||||
api_cfg: $ae_api,
|
|
||||||
only_priority: true,
|
|
||||||
log_lvl: log_lvl
|
|
||||||
})
|
|
||||||
.then(function (lu_time_zone_li_get_result) {
|
|
||||||
/* We need to save the time zone list to localStore */
|
|
||||||
if (lu_time_zone_li_get_result) {
|
|
||||||
lu_time_zone_list = lu_time_zone_li_get_result;
|
|
||||||
localStorage.setItem(
|
|
||||||
'lu_time_zone_list',
|
|
||||||
JSON.stringify(lu_time_zone_li_get_result)
|
|
||||||
);
|
|
||||||
if (log_lvl) {
|
|
||||||
console.log(`Time zone list:`, lu_time_zone_list);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
console.log(`No time zones returned!`);
|
|
||||||
// $ae_loc.lu_time_zone_list = [];
|
|
||||||
}
|
|
||||||
|
|
||||||
if (lu_time_zone_li_get_result) {
|
|
||||||
lu_time_zone_list = lu_time_zone_li_get_result;
|
|
||||||
console.log(`Time zone list:`, lu_time_zone_list);
|
|
||||||
console.log(lu_time_zone_list[0]);
|
|
||||||
console.log(lu_time_zone_list[10]);
|
|
||||||
} else {
|
|
||||||
console.log(`No time zones returned!`);
|
|
||||||
lu_time_zone_list = [];
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.catch(function (error: any) {
|
|
||||||
console.log('No results returned or failed.', error);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
function prevent_default<T extends Event>(fn: (event: T) => void) {
|
function prevent_default<T extends Event>(fn: (event: T) => void) {
|
||||||
@@ -873,7 +840,7 @@
|
|||||||
<fieldset class="flex_row flex_gap_md flex_justify_around">
|
<fieldset class="flex_row flex_gap_md flex_justify_around">
|
||||||
<label for="original_timezone"
|
<label for="original_timezone"
|
||||||
>Original Timezone
|
>Original Timezone
|
||||||
{#if lu_time_zone_list}
|
{#if ($lq__lu_time_zone ?? []).length}
|
||||||
<select
|
<select
|
||||||
id="original_timezone"
|
id="original_timezone"
|
||||||
name="original_timezone"
|
name="original_timezone"
|
||||||
@@ -886,7 +853,7 @@
|
|||||||
title="Select the original timezone"
|
title="Select the original timezone"
|
||||||
>
|
>
|
||||||
<option value="">-- None --</option>
|
<option value="">-- None --</option>
|
||||||
{#each lu_time_zone_list as lu_timezone (lu_timezone.name)}
|
{#each ($lq__lu_time_zone ?? []) as lu_timezone (lu_timezone.name)}
|
||||||
<option value={lu_timezone.name}>
|
<option value={lu_timezone.name}>
|
||||||
{lu_timezone.name}
|
{lu_timezone.name}
|
||||||
</option>
|
</option>
|
||||||
|
|||||||
@@ -8,6 +8,8 @@
|
|||||||
import type { key_val } from '$lib/stores/ae_stores';
|
import type { key_val } from '$lib/stores/ae_stores';
|
||||||
import { ae_util } from '$lib/ae_utils/ae_utils';
|
import { ae_util } from '$lib/ae_utils/ae_utils';
|
||||||
import { core_func } from '$lib/ae_core/ae_core_functions';
|
import { core_func } from '$lib/ae_core/ae_core_functions';
|
||||||
|
import { liveQuery } from 'dexie';
|
||||||
|
import { db_lookups } from '$lib/ae_core/db_lookups';
|
||||||
import {
|
import {
|
||||||
ae_snip,
|
ae_snip,
|
||||||
ae_loc,
|
ae_loc,
|
||||||
@@ -44,56 +46,21 @@
|
|||||||
let notes_changed = $state(false);
|
let notes_changed = $state(false);
|
||||||
let disable_submit_btn = true;
|
let disable_submit_btn = true;
|
||||||
|
|
||||||
let lu_time_zone_list: any = $state(
|
// Timezone lookup — reactive IDB query; background refresh handled by liveQuery + TTL
|
||||||
localStorage.getItem('lu_time_zone_list')
|
// Sort: sort DESC (higher = first, NULL=0 last), then name ASC — matches Aether backend convention.
|
||||||
? JSON.parse(localStorage.getItem('lu_time_zone_list') as string)
|
const lq__lu_time_zone = liveQuery(() =>
|
||||||
: []
|
db_lookups.lu_time_zone.toArray().then(arr =>
|
||||||
|
arr.sort((a, b) => {
|
||||||
|
const s_diff = Number(b['sort'] ?? 0) - Number(a['sort'] ?? 0);
|
||||||
|
if (s_diff !== 0) return s_diff;
|
||||||
|
return (a.name ?? '').localeCompare(b.name ?? '');
|
||||||
|
})
|
||||||
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
onMount(() => {
|
onMount(() => {
|
||||||
$ae_loc.lu_time_zone_list = [];
|
// Trigger background IDB refresh if stale/empty; liveQuery reacts automatically
|
||||||
// $ae_loc.lu_time_zone_list = [];
|
core_func.load_ae_obj_li__time_zone({ api_cfg: $ae_api, log_lvl });
|
||||||
// lu_time_zone_list = [];
|
|
||||||
if (lu_time_zone_list && lu_time_zone_list.length > 0) {
|
|
||||||
// console.log('Already have time zone list!', lu_time_zone_list);
|
|
||||||
} else {
|
|
||||||
console.log('No time zone list');
|
|
||||||
|
|
||||||
let lu_time_zone_li_get_promise = core_func
|
|
||||||
.load_ae_obj_li__time_zone({
|
|
||||||
api_cfg: $ae_api,
|
|
||||||
only_priority: true,
|
|
||||||
log_lvl: log_lvl
|
|
||||||
})
|
|
||||||
.then(function (lu_time_zone_li_get_result) {
|
|
||||||
/* We need to save the time zone list to localStore */
|
|
||||||
if (lu_time_zone_li_get_result) {
|
|
||||||
lu_time_zone_list = lu_time_zone_li_get_result;
|
|
||||||
localStorage.setItem(
|
|
||||||
'lu_time_zone_list',
|
|
||||||
JSON.stringify(lu_time_zone_li_get_result)
|
|
||||||
);
|
|
||||||
if (log_lvl) {
|
|
||||||
console.log(`Time zone list:`, lu_time_zone_list);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
console.log(`No time zones returned!`);
|
|
||||||
// $ae_loc.lu_time_zone_list = [];
|
|
||||||
}
|
|
||||||
|
|
||||||
if (lu_time_zone_li_get_result) {
|
|
||||||
lu_time_zone_list = lu_time_zone_li_get_result;
|
|
||||||
console.log(`Time zone list:`, lu_time_zone_list);
|
|
||||||
console.log(lu_time_zone_list[0]);
|
|
||||||
console.log(lu_time_zone_list[10]);
|
|
||||||
} else {
|
|
||||||
console.log(`No time zones returned!`);
|
|
||||||
lu_time_zone_list = [];
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.catch(function (error: any) {
|
|
||||||
console.log('No results returned or failed.', error);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
function prevent_default<T extends Event>(fn: (event: T) => void) {
|
function prevent_default<T extends Event>(fn: (event: T) => void) {
|
||||||
@@ -406,7 +373,7 @@
|
|||||||
<fieldset class="flex_row flex_gap_md flex_justify_around">
|
<fieldset class="flex_row flex_gap_md flex_justify_around">
|
||||||
<label for="original_timezone"
|
<label for="original_timezone"
|
||||||
>Original Timezone
|
>Original Timezone
|
||||||
{#if lu_time_zone_list}
|
{#if ($lq__lu_time_zone ?? []).length}
|
||||||
<select
|
<select
|
||||||
name="original_timezone"
|
name="original_timezone"
|
||||||
id="original_timezone"
|
id="original_timezone"
|
||||||
@@ -418,7 +385,7 @@
|
|||||||
title="Select the original timezone"
|
title="Select the original timezone"
|
||||||
>
|
>
|
||||||
<option value="">-- None --</option>
|
<option value="">-- None --</option>
|
||||||
{#each lu_time_zone_list as lu_timezone (lu_timezone.name)}
|
{#each ($lq__lu_time_zone ?? []) as lu_timezone (lu_timezone.name)}
|
||||||
<option value={lu_timezone.name}>
|
<option value={lu_timezone.name}>
|
||||||
{lu_timezone.name}
|
{lu_timezone.name}
|
||||||
</option>
|
</option>
|
||||||
|
|||||||
@@ -190,14 +190,35 @@
|
|||||||
// Lookup lists — reactive IDB queries (SWR via db_lookups + liveQuery)
|
// Lookup lists — reactive IDB queries (SWR via db_lookups + liveQuery)
|
||||||
// Data persists in IndexedDB with a 24h TTL; onMount triggers a background
|
// Data persists in IndexedDB with a 24h TTL; onMount triggers a background
|
||||||
// refresh if IDB is empty or stale. No localStorage or $ae_loc involved.
|
// refresh if IDB is empty or stale. No localStorage or $ae_loc involved.
|
||||||
|
// Note: orderBy() requires a declared Dexie index. For fields not in the schema index,
|
||||||
|
// use toArray() + in-memory sort instead to avoid a silent liveQuery error.
|
||||||
|
// Sort convention matches the Aether backend: sort DESC (higher = first, NULL=0 last), then name ASC.
|
||||||
const lq__lu_country = liveQuery(() =>
|
const lq__lu_country = liveQuery(() =>
|
||||||
db_lookups.lu_country.orderBy('english_short_name').toArray()
|
db_lookups.lu_country.toArray().then(arr =>
|
||||||
|
arr.sort((a, b) => {
|
||||||
|
const s_diff = Number(b['sort'] ?? 0) - Number(a['sort'] ?? 0);
|
||||||
|
if (s_diff !== 0) return s_diff;
|
||||||
|
return (a.english_short_name ?? a.name ?? '').localeCompare(b.english_short_name ?? b.name ?? '');
|
||||||
|
})
|
||||||
|
)
|
||||||
);
|
);
|
||||||
const lq__lu_country_subdivision = liveQuery(() =>
|
const lq__lu_country_subdivision = liveQuery(() =>
|
||||||
db_lookups.lu_country_subdivision.orderBy('name').toArray()
|
db_lookups.lu_country_subdivision.toArray().then(arr =>
|
||||||
|
arr.sort((a, b) => {
|
||||||
|
const s_diff = Number(b['sort'] ?? 0) - Number(a['sort'] ?? 0);
|
||||||
|
if (s_diff !== 0) return s_diff;
|
||||||
|
return (a.name ?? '').localeCompare(b.name ?? '');
|
||||||
|
})
|
||||||
|
)
|
||||||
);
|
);
|
||||||
const lq__lu_time_zone = liveQuery(() =>
|
const lq__lu_time_zone = liveQuery(() =>
|
||||||
db_lookups.lu_time_zone.orderBy('name').toArray()
|
db_lookups.lu_time_zone.toArray().then(arr =>
|
||||||
|
arr.sort((a, b) => {
|
||||||
|
const s_diff = Number(b['sort'] ?? 0) - Number(a['sort'] ?? 0);
|
||||||
|
if (s_diff !== 0) return s_diff;
|
||||||
|
return (a.name ?? '').localeCompare(b.name ?? '');
|
||||||
|
})
|
||||||
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
onMount(() => {
|
onMount(() => {
|
||||||
|
|||||||
Reference in New Issue
Block a user