Files
OSIT-AE-App-Svelte/src/routes/journals/ae_comp__journal_obj_id_view.svelte
2026-03-24 12:25:22 -04:00

274 lines
8.9 KiB
Svelte

<script lang="ts">
// *** Import Svelte specific
import { goto } from '$app/navigation';
// *** Import other supporting libraries
import {
BookOpenText,
BookPlus,
FileDown,
FilePlus,
FileUp,
LoaderCircle,
Menu,
Pencil,
Settings
} from '@lucide/svelte';
// *** Import Aether specific variables and functions
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 {
journals_loc,
journals_sess,
journals_slct,
journals_trig,
journals_prom
} from '$lib/ae_journals/ae_journals_stores';
import { journals_func } from '$lib/ae_journals/ae_journals_functions';
import Journal_obj_id_edit from './ae_comp__journal_obj_id_edit.svelte';
interface Props {
log_lvl?: number;
lq__journal_obj: any;
lq__journal_entry_obj_li: any;
on_show_export?: () => void;
on_show_import?: () => void;
}
let {
log_lvl = 0,
lq__journal_obj,
lq__journal_entry_obj_li,
on_show_export,
on_show_import
}: Props = $props();
// let ae_promises: key_val = {};
// let ae_tmp: key_val = {};
// let ae_trigger: any = null;
// let ae_triggers: key_val = {};
let typed_journal_passcode: string = $state('');
let passcode_timer: any = $state(null);
$effect(() => {
if (typed_journal_passcode?.length > 4) {
if (!$journals_sess?.journal_kv) {
$journals_sess.journal_kv = {};
}
if (!$journals_sess.journal_kv[$lq__journal_obj?.id]) {
$journals_sess.journal_kv[$lq__journal_obj?.id] = {};
}
verify_journal_passcode();
}
// We need to set a timeout to force the user to re-enter their private passcode
if (
$lq__journal_obj?.id &&
$journals_sess?.journal_kv[$lq__journal_obj?.id] &&
$journals_sess?.journal_kv[$lq__journal_obj?.id]
?.journal_passcode_verified
) {
if (passcode_timer) {
if (log_lvl) {
console.log('Passcode timer already set');
}
return;
}
// Use journal.passcode_timeout (assuming it's in minutes, default to 5)
const timeout_minutes = $lq__journal_obj?.passcode_timeout ?? 5;
const timeout_ms = 1000 * 60 * timeout_minutes;
if (log_lvl) {
console.log(
`Setting passcode timer for ${timeout_minutes} minutes (${timeout_ms}ms)`
);
}
passcode_timer = setTimeout(() => {
if (log_lvl) {
console.log('Passcode timer expired');
}
typed_journal_passcode = '';
if (!$journals_sess?.journal_kv[$lq__journal_obj?.id]) {
$journals_sess.journal_kv[$lq__journal_obj?.id] = {};
}
// Reset verification and decryption flags
$journals_sess.journal_kv[
$lq__journal_obj?.id
].journal_passcode_verified = false;
$journals_sess.journal_kv[
$lq__journal_obj?.id
].journal_passcode_decrypted = false;
passcode_timer = null;
}, timeout_ms);
}
});
function verify_journal_passcode() {
if (log_lvl) {
console.log(
`verify_journal_passcode: typed_journal_passcode = ${typed_journal_passcode} journal private passcode = ${$lq__journal_obj?.private_passcode}`
);
}
if (typed_journal_passcode === $lq__journal_obj?.private_passcode) {
console.log('Matched journal private passcode');
if (!$journals_sess?.journal_kv[$lq__journal_obj?.id]) {
$journals_sess.journal_kv[$lq__journal_obj?.id] = {};
}
$journals_sess.journal_kv[$lq__journal_obj?.id] = {
typed_journal_passcode: typed_journal_passcode,
journal_passcode_verified: true
};
typed_journal_passcode = '';
} else {
}
}
async function handle_new_entry() {
let data_kv = {
category_code: null
};
if ($journals_loc.entry.qry__category_code) {
data_kv.category_code = $journals_loc.entry.qry__category_code;
}
try {
const results = await journals_func.create_ae_obj__journal_entry({
api_cfg: $ae_api,
journal_id: $lq__journal_obj?.journal_id,
data_kv: data_kv,
log_lvl: log_lvl
});
if (results?.journal_entry_id) {
$journals_slct.journal_entry_id = results.journal_entry_id;
$journals_loc.entry.edit_kv[$journals_slct.journal_entry_id] =
'current';
goto(
`/journals/${$lq__journal_obj?.journal_id}/entry/${results.journal_entry_id}`
);
}
} catch (error) {
console.error('Error creating journal entry:', error);
alert('Failed to create new journal entry.');
}
}
</script>
<div class="group/view relative mx-2 my-1 w-full">
<!-- Glow ring — mirrors Quick Add and Journal List -->
<div
class="from-primary-500 to-secondary-500 pointer-events-none absolute -inset-1
rounded-2xl bg-linear-to-r opacity-10 blur
transition duration-700
group-hover/view:opacity-25 dark:opacity-20 dark:group-hover/view:opacity-35">
</div>
<section
class="relative flex w-full flex-col
items-center justify-center gap-2 rounded-xl border
border-gray-200 bg-white
p-3 text-gray-900 shadow-xl
dark:border-gray-700 dark:bg-gray-900
dark:text-gray-100"
bind:clientHeight={$ae_loc.iframe_height_modal_body}>
<header
class="ae_header journal__header flex w-full flex-row flex-wrap items-center justify-between gap-2">
<h2 class="journal__name h3 text-center">
<BookOpenText class="text-primary-500/80 inline-block" />
{@html $lq__journal_obj?.name ?? 'Loading...'}
{#if $ae_loc.edit_mode}
<span
class="badge preset-tonal-success ml-2 px-2 text-lg font-bold"
title="Entries matching current filters">
{$lq__journal_entry_obj_li?.length ?? '0'}<span
class="ml-0.5 text-xs opacity-50">&times;</span>
</span>
{/if}
{#await $journals_prom.load__journal_entry_obj_li}
<LoaderCircle
size="1em"
class="text-primary-500 ml-1 inline-block animate-spin" />
{/await}
</h2>
<div
class="flex grow flex-row flex-wrap items-center justify-end gap-2">
<!-- Simplified Config Trigger -->
<button
type="button"
onclick={() =>
($journals_sess.show__modal_edit__journal_obj = true)}
class="btn preset-tonal-secondary px-3 py-1 shadow-md"
title="Journal Config & Actions">
<Settings size="1.2em" class="mr-2" />
<span class="hidden md:inline">Config</span>
</button>
<!-- Passcode Verification (Condensed) -->
{#if !$journals_sess?.journal_kv[$lq__journal_obj?.id]?.journal_passcode_verified}
<div class="flex gap-1">
<input
autocomplete="off"
type="text"
bind:value={typed_journal_passcode}
placeholder="Passcode"
class="input input-sm w-32" />
</div>
{/if}
</div>
</header>
<!-- Show Journal description -->
{#if $lq__journal_obj?.description && $ae_loc.edit_mode}
<div
class="
prose
word-break
prose-p:m-0
prose-p:p-0 prose-h1:underline prose-h1:decoration-double
prose-h2:underline
prose-h1:text-2xl prose-h2:text-xl
prose-h3:text-lg prose-h1:m-0
prose-h2:m-0 prose-h3:m-0
prose-h4:m-0 prose-h5:m-0 prose-h6:m-0 prose-li:m-0
prose-li:p-0 prose-li:line-height-none
w-full max-w-(--breakpoint-sm)
space-y-1
rounded-lg bg-gray-50 p-2
font-mono text-sm font-normal text-wrap text-gray-900 shadow-md
md:max-w-(--breakpoint-md) dark:bg-gray-800 dark:text-gray-100
">
{@html $lq__journal_obj.description_md_html}
</div>
{/if}
</section>
</div>
<!-- Standardized Journal Action/Config Modal -->
<Journal_obj_id_edit
{log_lvl}
{lq__journal_obj}
bind:show={$journals_sess.show__modal_edit__journal_obj}
on_new_entry={handle_new_entry}
{on_show_export}
{on_show_import} />