274 lines
8.9 KiB
Svelte
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">×</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} />
|