Files
OSIT-AE-App-Svelte/src/lib/ae_journals/ae_journals__journal.ts
2026-03-24 10:54:40 -04:00

875 lines
24 KiB
TypeScript

import { marked } from 'marked';
import type { key_val } from '$lib/stores/ae_stores';
import { api } from '$lib/api/api';
import { db_save_ae_obj_li__ae_obj } from '$lib/ae_core/core__idb_dexie';
import { db_journals } from '$lib/ae_journals/db_journals';
import type { ae_Journal } from '$lib/types/ae_types';
import { load_ae_obj_li__journal_entry } from '$lib/ae_journals/ae_journals__journal_entry';
const ae_promises: key_val = {};
// Updated 2026-01-26 (SWR Optimization)
export async function load_ae_obj_id__journal({
api_cfg,
journal_id,
inc_entry_li = false,
enabled = 'enabled',
hidden = 'not_hidden',
view = 'default',
limit = 99,
offset = 0,
order_by_li = {
priority: 'DESC',
sort: 'DESC',
name: 'ASC',
updated_on: 'DESC',
created_on: 'DESC'
},
params = {},
try_cache = true,
log_lvl = 0
}: {
api_cfg: any;
journal_id: string;
inc_entry_li?: boolean;
enabled?: 'enabled' | 'all' | 'not_enabled' | undefined; // all, disabled, enabled
hidden?: 'hidden' | 'all' | 'not_hidden' | undefined; // all, hidden, not_hidden
view?: string;
limit?: number;
offset?: number;
order_by_li?: key_val; // Order by fields for the journal entries
params?: key_val;
try_cache?: boolean;
log_lvl?: number;
}): Promise<ae_Journal | null> {
if (log_lvl) {
console.log(
`*** load_ae_obj_id__journal() *** journal_id=${journal_id} (SWR)`
);
}
// 1. FAST PATH: Return cached data immediately
if (try_cache) {
try {
const cached = await db_journals.journal.get(journal_id);
if (cached) {
if (log_lvl)
console.log(
'JOURNAL LOAD: Cache hit. Returning stale data.'
);
_refresh_journal_id_background({
api_cfg,
journal_id,
view,
params,
try_cache,
inc_entry_li,
enabled,
hidden,
limit,
offset,
order_by_li,
log_lvl: 0
});
if (inc_entry_li && !cached.journal_entry_li) {
cached.journal_entry_li =
await load_ae_obj_li__journal_entry({
api_cfg,
for_obj_type: 'journal',
for_obj_id: journal_id,
enabled,
hidden,
limit,
offset,
order_by_li,
params,
try_cache,
log_lvl
});
}
return cached;
}
} catch (e) {}
}
// 2. SLOW PATH: Wait for API
return await _refresh_journal_id_background({
api_cfg,
journal_id,
view,
params,
try_cache,
inc_entry_li,
enabled,
hidden,
limit,
offset,
order_by_li,
log_lvl
});
}
/**
* Internal background refresh for a single journal
*/
async function _refresh_journal_id_background({
api_cfg,
journal_id,
view,
params,
try_cache,
inc_entry_li,
enabled,
hidden,
limit,
offset,
order_by_li,
log_lvl
}: any) {
if (typeof navigator !== 'undefined' && !navigator.onLine) return null;
try {
const result = await api.get_ae_obj({
api_cfg,
obj_type: 'journal',
obj_id: journal_id,
view,
params,
log_lvl
});
if (result) {
if (try_cache) {
const processed = await process_ae_obj__journal_props({
obj_li: [result],
log_lvl
});
await db_save_ae_obj_li__ae_obj({
db_instance: db_journals,
table_name: 'journal',
obj_li: processed,
properties_to_save,
log_lvl
});
// Yield to microtask queue so Dexie liveQuery observers fire before we return
await Promise.resolve();
}
if (inc_entry_li) {
result.journal_entry_li = await load_ae_obj_li__journal_entry({
api_cfg,
for_obj_type: 'journal',
for_obj_id: journal_id,
enabled,
hidden,
limit,
offset,
order_by_li,
params,
try_cache,
log_lvl
});
}
return result;
}
} catch (e) {}
return null;
}
// Updated 2026-01-26 (SWR Optimization)
export async function load_ae_obj_li__journal({
api_cfg,
for_obj_type = 'account',
for_obj_id,
qry_person_id = null,
inc_entry_li = false,
enabled = 'enabled',
hidden = 'not_hidden',
limit = 99,
offset = 0,
order_by_li = {
priority: 'DESC',
sort: 'DESC',
name: 'ASC',
updated_on: 'DESC',
created_on: 'DESC'
},
params = {},
try_cache = true,
log_lvl = 0
}: {
api_cfg: any;
for_obj_type: string;
for_obj_id: string;
qry_person_id?: string | null;
inc_entry_li?: boolean;
enabled?: 'enabled' | 'all' | 'not_enabled' | undefined; // all, disabled, enabled
hidden?: 'hidden' | 'all' | 'not_hidden' | undefined; // all, hidden, not_hidden
limit?: number;
offset?: number;
order_by_li?: key_val; // Order by fields for the journal entries
params?: key_val;
try_cache?: boolean;
log_lvl?: number;
}): Promise<ae_Journal[]> {
if (log_lvl) {
console.log(
`*** load_ae_obj_li__journal() *** for=${for_obj_type}:${for_obj_id} (SWR)`
);
}
// 1. FAST PATH: Check cache
if (try_cache) {
try {
const cached_li = await db_journals.journal
.where('for_id')
.equals(for_obj_id)
.toArray();
if (cached_li && cached_li.length > 0) {
if (log_lvl)
console.log(
`JOURNAL LIST: Cache hit (${cached_li.length}).`
);
_refresh_journal_li_background({
api_cfg,
for_obj_type,
for_obj_id,
qry_person_id,
inc_entry_li,
enabled,
hidden,
limit,
offset,
order_by_li,
params,
try_cache,
log_lvl: 0
});
return cached_li;
}
} catch (e) {}
}
// 2. SLOW PATH: API
return await _refresh_journal_li_background({
api_cfg,
for_obj_type,
for_obj_id,
qry_person_id,
inc_entry_li,
enabled,
hidden,
limit,
offset,
order_by_li,
params,
try_cache,
log_lvl
});
}
async function _refresh_journal_li_background({
api_cfg,
for_obj_type,
for_obj_id,
qry_person_id,
inc_entry_li,
enabled,
hidden,
limit,
offset,
order_by_li,
params,
try_cache,
log_lvl
}: any) {
if (typeof navigator !== 'undefined' && !navigator.onLine) return [];
let promise;
if (qry_person_id) {
const search_query: any = {
and: [{ field: 'person_id', op: 'eq', value: qry_person_id }]
};
if (for_obj_id)
search_query.and.push({
field: `${for_obj_type}_id`,
op: 'eq',
value: for_obj_id
});
if (enabled === 'enabled')
search_query.and.push({ field: 'enable', op: 'eq', value: true });
if (hidden === 'hidden')
search_query.and.push({ field: 'hide', op: 'eq', value: true });
promise = api.search_ae_obj({
api_cfg,
obj_type: 'journal',
search_query,
order_by_li,
limit,
offset,
log_lvl
});
} else {
promise = api.get_ae_obj_li({
api_cfg,
obj_type: 'journal',
for_obj_type,
for_obj_id,
enabled,
hidden,
limit,
offset,
order_by_li,
log_lvl
});
}
try {
const results = await promise;
if (results) {
if (try_cache) {
const processed = await process_ae_obj__journal_props({
obj_li: results,
log_lvl
});
await db_save_ae_obj_li__ae_obj({
db_instance: db_journals,
table_name: 'journal',
obj_li: processed,
properties_to_save,
log_lvl
});
// Yield to microtask queue so Dexie liveQuery observers fire before we return
await Promise.resolve();
}
if (inc_entry_li) {
for (const journal of results) {
load_ae_obj_li__journal_entry({
api_cfg,
for_obj_type: 'journal',
for_obj_id: journal.journal_id,
enabled,
hidden,
limit,
offset,
order_by_li,
params,
try_cache,
log_lvl: 0
});
}
}
return results;
}
} catch (e) {}
return [];
}
// Updated 2026-01-05
export async function create_ae_obj__journal({
api_cfg,
account_id,
data_kv,
params = {},
try_cache = true,
log_lvl = 0
}: {
api_cfg: any;
account_id: string;
data_kv: key_val;
params?: key_val;
try_cache?: boolean;
log_lvl?: number;
}): Promise<ae_Journal | null> {
if (log_lvl) {
console.log(
`*** create_ae_obj__journal() *** account_id=${account_id}`
);
}
if (!account_id) {
console.log(
`ERROR: Journals - Journal - account_id required to create`
);
return null;
}
ae_promises.create__journal = await api
.create_ae_obj_v3({
api_cfg: api_cfg,
obj_type: 'journal',
fields: {
account_id: account_id,
...data_kv
},
params: params,
log_lvl: log_lvl
})
.then(async function (journal_obj_create_result) {
if (journal_obj_create_result) {
if (try_cache) {
// Process the results first
const processed_obj_li =
await process_ae_obj__journal_props({
obj_li: [journal_obj_create_result],
log_lvl: log_lvl
});
if (log_lvl) {
console.log('Processed object list:', processed_obj_li);
}
// Save the updated results list to the database
await db_save_ae_obj_li__ae_obj({
db_instance: db_journals,
table_name: 'journal',
obj_li: processed_obj_li,
properties_to_save: properties_to_save,
log_lvl: log_lvl
});
}
return journal_obj_create_result;
} else {
return null;
}
})
.catch(function (error: any) {
console.log('No results returned or failed.', error);
});
return ae_promises.create__journal;
}
// Updated 2026-01-05
export async function delete_ae_obj_id__journal({
api_cfg,
journal_id,
method = 'delete', // 'delete', 'disable', 'hide'
params = {},
try_cache = true,
log_lvl = 0
}: {
api_cfg: any;
journal_id: string;
method?: 'delete' | 'soft_delete' | 'disable' | 'hide';
params?: key_val;
try_cache?: boolean;
log_lvl?: number;
}) {
if (log_lvl) {
console.log(
`*** delete_ae_obj_id__journal() *** journal_id=${journal_id}`
);
}
ae_promises.delete__journal_obj = await api
.delete_ae_obj({
api_cfg: api_cfg,
obj_type: 'journal',
obj_id: journal_id,
params: params,
method: method,
log_lvl: log_lvl
})
.catch(function (error: any) {
console.log('No results returned or failed.', error);
})
.finally(async function () {
if (try_cache) {
if (log_lvl) {
console.log(
`Attempting to remove IDB entry for journal_id=${journal_id}`
);
}
await db_journals.journal.delete(journal_id);
}
});
return ae_promises.delete__journal_obj;
}
// Updated 2026-01-05
export async function update_ae_obj__journal({
api_cfg,
journal_id,
data_kv,
params = {},
try_cache = true,
log_lvl = 0
}: {
api_cfg: any;
journal_id: string;
data_kv: key_val;
params?: key_val;
try_cache?: boolean;
log_lvl?: number;
}): Promise<ae_Journal | null> {
if (log_lvl) {
console.log(
`*** update_ae_obj__journal() *** journal_id=${journal_id}`,
data_kv
);
}
// Perform the API update
const result = await api.update_ae_obj_v3({
api_cfg: api_cfg,
obj_type: 'journal',
obj_id: journal_id,
fields: data_kv,
params: params,
log_lvl: log_lvl
});
// Handle the result
if (result) {
if (try_cache) {
// Process the results first
const processed_obj_li = await process_ae_obj__journal_props({
obj_li: [result],
log_lvl: log_lvl
});
if (log_lvl) {
console.log('Processed object list:', processed_obj_li);
}
// Save the updated results list to the database
await db_save_ae_obj_li__ae_obj({
db_instance: db_journals,
table_name: 'journal',
obj_li: processed_obj_li,
properties_to_save: properties_to_save,
log_lvl: log_lvl
});
}
return result;
} else {
console.error('Failed to update journal.');
return null;
}
}
// This new function is using CRUD V3 Search.
// Updated 2026-01-02
export async function qry__journal({
api_cfg,
journal_id,
qry_str,
qry_files,
qry_start_datetime, // Example greater than: '2024-10-24'
enabled = 'enabled',
hidden = 'not_hidden',
limit = 50,
offset = 0,
order_by_li = {
priority: 'DESC',
sort: 'DESC',
start_datetime: 'ASC',
name: 'ASC',
updated_on: 'DESC',
created_on: 'DESC'
} as const,
params = {},
try_cache = true,
log_lvl = 0
}: {
api_cfg: any;
journal_id: any;
qry_str?: string;
qry_files?: null | boolean;
qry_start_datetime?: null | string; // Greater than this datetime
enabled?: 'enabled' | 'all' | 'not_enabled' | undefined; // all, disabled, enabled
hidden?: 'hidden' | 'all' | 'not_hidden' | undefined; // all, hidden, not_hidden
limit?: number;
offset?: number;
order_by_li?:
| Record<string, 'ASC' | 'DESC'>
| Record<string, 'ASC' | 'DESC'>[];
params?: any;
try_cache?: boolean;
log_lvl?: number;
}): Promise<ae_Journal[]> {
if (log_lvl) {
console.log(
`*** qry__journal() *** journal_id=${journal_id} enabled=${enabled} hidden=${hidden} limit=${limit} offset=${offset}`
);
}
const search_query: any = { and: [] };
if (qry_files === true) {
search_query.and.push({ field: 'file_count_all', op: 'gt', value: 0 });
} else if (qry_files === false) {
search_query.and.push({
field: 'file_count_all',
op: 'is',
value: null
});
}
if (qry_start_datetime) {
search_query.and.push({
field: 'start_datetime',
op: 'gt',
value: qry_start_datetime
});
}
// Add for_obj_id context (Account ID)
if (journal_id) {
// Assuming journal_id here is actually the account_id as per original usage context
search_query.and.push({
field: 'account_id',
op: 'eq',
value: journal_id
});
}
// Add enabled/hidden 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 (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.load__journal_obj_li = await api
.search_ae_obj({
api_cfg: api_cfg,
obj_type: 'journal',
search_query,
order_by_li,
limit,
offset,
log_lvl
})
.then(async function (journal_obj_li_get_result) {
if (journal_obj_li_get_result) {
if (try_cache) {
// Process the results first
const processed_obj_li =
await process_ae_obj__journal_props({
obj_li: journal_obj_li_get_result,
log_lvl: log_lvl
});
if (log_lvl) {
console.log('Processed object list:', processed_obj_li);
}
// Save the updated results list to the database
if (log_lvl) {
console.log('Saving to DB...');
}
db_save_ae_obj_li__ae_obj({
db_instance: db_journals,
table_name: 'journal',
obj_li: processed_obj_li,
properties_to_save: properties_to_save,
log_lvl: log_lvl
});
if (log_lvl) {
console.log('DB save completed.');
}
}
return journal_obj_li_get_result;
} else {
return [];
}
});
if (log_lvl) {
console.log(
'ae_promises.load__journal_obj_li:',
ae_promises.load__journal_obj_li
);
}
return ae_promises.load__journal_obj_li;
}
// Updated 2025-05-09
const properties_to_save = [
'id',
'journal_id',
'code',
'for_type',
'for_id',
'type_code',
'account_id',
'person_id',
'name',
'short_name',
'summary',
'outline',
'description',
'description_md_html', // Use the markdown parser to generate HTML
'description_html',
'description_json',
// start_datetime: obj.start_datetime,
// end_datetime: obj.end_datetime,
'timezone',
'alert',
'alert_msg',
'sort_by',
'sort_by_desc',
'cfg_json',
// ux_mode: obj.ux_mode,
// This only allows for basic access to the data.
'passcode_read', // For LLM (AI) generated summary...???
'passcode_read_expire',
'passcode_write',
'passcode_write_expire',
'passcode', // For Journal Entry encryption password
'passcode_timeout',
'private_passcode', // Combine with Journal passcode to encrypt and decrypt Entries
'auth_key', // For Journal authorization without sign in
'enable',
'hide',
'priority',
'sort',
'group',
'notes',
'created_on',
'updated_on',
// Generated fields for sorting locally only
'tmp_sort_1',
'tmp_sort_2',
'tmp_sort_3',
// tmp_sort_1: `${obj.original_datetime}_${obj.group}_${obj.priority}_${obj.sort}`,
// tmp_sort_2: `${obj.group}_${obj.original_datetime}_${obj.priority}_${obj.sort}`,
// 'tmp_sort_a',
// 'tmp_sort_b',
'combined_passcode',
// From SQL view
'journal_entry_count'
// A key value list of the others
// journal_other_kv
// journal_other_li
];
/**
* NON-EXPORTED LOCAL HELPER
* Processes a list of Aether objects by applying common and specific transformations.
*/
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 (log_lvl > 0) {
console.log(
`*** _process_generic_props: Processing ${obj_li.length} objects of type "${obj_type}" ***`
);
}
if (!obj_li || obj_li.length === 0) {
if (log_lvl > 0) console.log('No objects to process.');
return [];
}
const processed_obj_li: T[] = [];
for (const original_obj of obj_li) {
let processed_obj = { ...original_obj };
// --- Common Transformations ---
// 1. Standardize ID and other '_random' fields
for (const key in processed_obj) {
if (key.endsWith('_random')) {
const newKey = key.slice(0, -7); // Remove '_random' suffix
(processed_obj as any)[newKey] = processed_obj[key];
}
}
// Ensure 'id' is set from '[obj_type]_id_random'
const randomIdKey = `${obj_type}_id`;
if (processed_obj[randomIdKey]) {
(processed_obj as any).id = processed_obj[randomIdKey];
}
// 2. Create common computed properties for client-side sorting.
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 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}`;
// --- Specific Transformations ---
if (specific_processor) {
processed_obj = await Promise.resolve(
specific_processor(processed_obj)
);
}
processed_obj_li.push(processed_obj as T);
}
return processed_obj_li;
}
// Updated 2025-11-13
export async function process_ae_obj__journal_props({
obj_li,
log_lvl = 0
}: {
obj_li: any[];
log_lvl?: number;
}) {
return _process_generic_props({
obj_li,
obj_type: 'journal',
log_lvl,
specific_processor: async (obj) => {
const description = obj.description ?? '';
const description_cleaned: string = description.replace(
/^[\u200B\u200C\u200D\u200E\u200F\uFEFF]/,
''
);
obj.description_md_html =
(await marked.parse(description_cleaned ?? '')) ?? null;
obj.cfg_json = obj.cfg_json ?? {};
obj.data_json = obj.data_json ?? {};
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}`;
obj.combined_passcode = `${obj.passcode ?? ''}:${obj.private_passcode ?? ''}`;
return obj;
}
});
}