Prettier for Journals

This commit is contained in:
Scott Idem
2026-03-24 12:25:22 -04:00
parent e1338b1a72
commit b74c6d0e9c
25 changed files with 3983 additions and 4238 deletions

View File

@@ -8,7 +8,13 @@
import { goto } from '$app/navigation'; import { goto } from '$app/navigation';
// *** Import other supporting libraries // *** Import other supporting libraries
import { ArrowDownUp, ArrowRight, House, RefreshCw, Satellite } from '@lucide/svelte'; import {
ArrowDownUp,
ArrowRight,
House,
RefreshCw,
Satellite
} from '@lucide/svelte';
// *** Import Aether specific variables and functions // *** Import Aether specific variables and functions
import { ae_loc, ae_sess, ae_api, slct } from '$lib/stores/ae_stores'; import { ae_loc, ae_sess, ae_api, slct } from '$lib/stores/ae_stores';
@@ -44,7 +50,10 @@
if ($journals_slct.journal_id !== ae_acct.slct.journal_id) { if ($journals_slct.journal_id !== ae_acct.slct.journal_id) {
$journals_slct.journal_id = ae_acct.slct.journal_id; $journals_slct.journal_id = ae_acct.slct.journal_id;
} }
if (JSON.stringify($journals_slct.journal_obj_li) !== JSON.stringify(ae_acct.slct.journal_obj_li)) { if (
JSON.stringify($journals_slct.journal_obj_li) !==
JSON.stringify(ae_acct.slct.journal_obj_li)
) {
$journals_slct.journal_obj_li = ae_acct.slct.journal_obj_li; $journals_slct.journal_obj_li = ae_acct.slct.journal_obj_li;
} }
}); });
@@ -97,15 +106,14 @@
class:iframe={$ae_loc?.iframe} class:iframe={$ae_loc?.iframe}
class=" class="
ae_journals ae_journals
h-full max-h-full max-w-7xl m-auto flex h-full
max-h-full
max-w-7xl flex-col gap-1
overflow-auto overflow-auto
flex flex-col gap-1
m-auto
bg-gray-50 dark:bg-gray-900 bg-gray-50 text-gray-800
text-gray-800 dark:text-gray-200 dark:bg-gray-900 dark:text-gray-200
" ">
>
<!-- class:hidden={yTop > 200} --> <!-- class:hidden={yTop > 200} -->
<nav <nav
bind:clientHeight={nav_y_height} bind:clientHeight={nav_y_height}
@@ -113,36 +121,33 @@
class:opacity-0={yTop > 250} class:opacity-0={yTop > 250}
class=" class="
submenu submenu
z-20 absolute
hover:opacity-100 top-0
absolute top-0 left-0 right-0 right-0 left-0 z-20 m-auto
w-full max-w-7xl flex min-h-12
min-h-12 w-full
p-1 px-2 pb-2 m-auto max-w-7xl flex-row flex-wrap items-center
flex flex-row flex-wrap justify-around gap-1 rounded-b-lg
items-center justify-around sm:justify-between border-b-2 bg-gray-200 p-1
gap-1 px-2
border-b-2 rounded-b-lg pb-2 transition-all
bg-gray-200 dark:bg-gray-800 duration-1000 hover:opacity-100
transition-all duration-1000 sm:justify-between dark:bg-gray-800
" ">
>
<span class="justify-self-start"> <span class="justify-self-start">
<!-- Be sure to explain what &AElig; (Aether) means in the title text or similar! --> <!-- Be sure to explain what &AElig; (Aether) means in the title text or similar! -->
<Satellite <Satellite
size="1.5em" size="1.5em"
class="mx-1 inline-block text-gray-500" class="mx-1 inline-block text-gray-500" />
/>
<abbr title="Aether - Journals Module"> Æ Journals </abbr> <abbr title="Aether - Journals Module"> Æ Journals </abbr>
</span> </span>
<a <a
href="/" href="/"
class="btn btn-sm preset-tonal-surface border border-surface-500 hover:preset-filled-success-500" class="btn btn-sm preset-tonal-surface border-surface-500 hover:preset-filled-success-500 border">
>
<House /> <House />
<span class="hidden md:inline"> Home </span> <span class="hidden md:inline"> Home </span>
</a> </a>
@@ -213,9 +218,8 @@
// window.location.reload(true); // true only works with Firefox // window.location.reload(true); // true only works with Firefox
// alert('Local and Session Storage cleared and Indexed DBs deleted. You will probably want to refresh the page.'); // alert('Local and Session Storage cleared and Indexed DBs deleted. You will probably want to refresh the page.');
}} }}
class="btn btn-sm preset-tonal-surface border border-surface-500 hover:preset-filled-warning-500" class="btn btn-sm preset-tonal-surface border-surface-500 hover:preset-filled-warning-500 border"
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-eraser mx-1"></span> -->
<!-- <span class="fas fa-sync mx-1"></span> --> <!-- <span class="fas fa-sync mx-1"></span> -->
<RefreshCw /> <RefreshCw />
@@ -230,8 +234,7 @@
show_btn_class="btn-info" show_btn_class="btn-info"
additional_kv={{ additional_kv={{
test: true test: true
}} }}></Help_tech>
></Help_tech>
</nav> </nav>
<!-- Add overflow-auto to section element to have the main nav sort of sticky at top --> <!-- Add overflow-auto to section element to have the main nav sort of sticky at top -->
@@ -244,23 +247,21 @@
class=" class="
main_content main_content
grow grow
px-1 md:px-2 px-1 pb-48
pb-48 md:px-2
" ">
>
{@render children?.()} {@render children?.()}
</section> </section>
<div <div
class:hidden={yTop < 500} class:hidden={yTop < 500}
class=" class="
z-20 fixed
hover:opacity-100 right-1
fixed bottom-48 right-1 bottom-48 z-20 flex
flex flex-col gap-1 items-end justify-end flex-col items-end justify-end gap-1 hover:opacity-100
" ">
>
<!-- Scroll to top button --> <!-- Scroll to top button -->
<button <button
type="button" type="button"
@@ -293,8 +294,7 @@
window.parent.postMessage({ scroll_to: 0 }, '*'); window.parent.postMessage({ scroll_to: 0 }, '*');
}} }}
title="Scroll to top" title="Scroll to top">
>
<ArrowDownUp class="rotate-180" /> <ArrowDownUp class="rotate-180" />
Scroll to Top Scroll to Top
</button> </button>
@@ -321,8 +321,7 @@
window.parent.postMessage({ scroll_to: xScroll }, '*'); window.parent.postMessage({ scroll_to: xScroll }, '*');
}} }}
title="Scroll to right" title="Scroll to right">
>
<ArrowRight size="1em" class="inline" /> <ArrowRight size="1em" class="inline" />
<!-- Scroll to Right <!-- Scroll to Right
xLeft={xLeft} xScroll={xScroll} xWidth={xWidth} xScroll={xScroll} scrollLeft={scroll_container().scrollLeft} xLeft={xLeft} xScroll={xScroll} xWidth={xWidth} xScroll={xScroll} scrollLeft={scroll_container().scrollLeft}
@@ -360,8 +359,7 @@
window.parent.postMessage({ scroll_to: yScroll }, '*'); window.parent.postMessage({ scroll_to: yScroll }, '*');
}} }}
title="Scroll to bottom" title="Scroll to bottom">
>
<ArrowDownUp /> <ArrowDownUp />
Scroll to Bottom Scroll to Bottom
</button> </button>
@@ -373,38 +371,35 @@
class:opacity-0={yTop > 250} class:opacity-0={yTop > 250}
class=" class="
footer footer
z-20 absolute
hover:opacity-100 right-0
absolute bottom-0 left-0 right-0 bottom-0 left-0 z-20 m-auto
w-full max-w-7xl flex w-full
p-1 m-auto max-w-7xl flex-row
flex flex-row flex-wrap flex-wrap items-center justify-between
items-center justify-between gap-1 rounded-t-lg
sm:flex-row md:items-center md:justify-between border-t-2 border-gray-200 bg-gray-200
gap-1 p-1
border-t-2 border-gray-200 dark:border-gray-600 text-xs transition-all duration-1000
rounded-t-lg hover:text-base
bg-gray-200 dark:bg-gray-800 hover:opacity-100 sm:flex-row
text-xs hover:text-base md:items-center md:justify-between
transition-all duration-1000 dark:border-gray-600 dark:bg-gray-800
" "
class:ae_debug={$ae_loc?.debug} class:ae_debug={$ae_loc?.debug}>
>
<Element_data_store <Element_data_store
ds_code="hub__site__appshell_footer" ds_code="hub__site__appshell_footer"
ds_type="html" ds_type="html"
class_li="grow flex flex-row justify-between" class_li="grow flex flex-row justify-between" />
/>
</footer> </footer>
</div> </div>
{:else} {:else}
<section <section
class="main_content grow px-1 md:px-2 pb-28 flex flex-col gap-1 items-center" class="main_content flex grow flex-col items-center gap-1 px-1 pb-28 md:px-2">
>
<p class="text-center"> <p class="text-center">
You are not logged in as a user. You must be signed in to access the You are not logged in as a user. You must be signed in to access the
journals module. journals module.

View File

@@ -8,7 +8,14 @@
import { goto } from '$app/navigation'; import { goto } from '$app/navigation';
// *** Icons // *** Icons
import { BookPlus, FileUp, LoaderCircle, Sparkles, SquareLibrary, Wrench } from '@lucide/svelte'; import {
BookPlus,
FileUp,
LoaderCircle,
Sparkles,
SquareLibrary,
Wrench
} from '@lucide/svelte';
// *** Libraries & Stores // *** Libraries & Stores
import { liveQuery } from 'dexie'; import { liveQuery } from 'dexie';
import { Modal } from 'flowbite-svelte'; import { Modal } from 'flowbite-svelte';
@@ -91,13 +98,11 @@
</script> </script>
<div <div
class="page_container flex flex-col gap-4 sm:gap-6 md:gap-8 items-center w-full min-h-screen p-3 sm:p-4 md:p-8" class="page_container flex min-h-screen w-full flex-col items-center gap-4 p-3 sm:gap-6 sm:p-4 md:gap-8 md:p-8">
>
<!-- Header Section --> <!-- Header Section -->
<header class="text-center space-y-2 max-w-3xl"> <header class="max-w-3xl space-y-2 text-center">
<h1 <h1
class="text-3xl sm:text-4xl md:text-5xl font-black tracking-tight text-surface-900 dark:text-surface-100" class="text-surface-900 dark:text-surface-100 text-3xl font-black tracking-tight sm:text-4xl md:text-5xl">
>
<SquareLibrary size="1em" class="text-primary-500 inline-block" /> <SquareLibrary size="1em" class="text-primary-500 inline-block" />
Journals Journals
</h1> </h1>
@@ -105,7 +110,9 @@
{#if $ae_loc.person.given_name || $ae_loc.person.family_name} {#if $ae_loc.person.given_name || $ae_loc.person.family_name}
<p class="text-surface-600 dark:text-surface-400 font-medium"> <p class="text-surface-600 dark:text-surface-400 font-medium">
<span class="text-primary-500 font-semibold"> <span class="text-primary-500 font-semibold">
{[$ae_loc.person.given_name, $ae_loc.person.family_name].filter(Boolean).join(' ')} {[$ae_loc.person.given_name, $ae_loc.person.family_name]
.filter(Boolean)
.join(' ')}
</span> </span>
</p> </p>
{/if} {/if}
@@ -113,19 +120,17 @@
<!-- Quick Add Integrated Section --> <!-- Quick Add Integrated Section -->
<section class="w-full max-w-2xl"> <section class="w-full max-w-2xl">
<div class="relative group"> <div class="group relative">
<!-- Glow ring: slightly brighter in dark mode where colors need more presence --> <!-- Glow ring: slightly brighter in dark mode where colors need more presence -->
<div <div
class="absolute -inset-1 bg-linear-to-r from-primary-500 to-secondary-500 rounded-2xl blur opacity-25 dark:opacity-40 group-hover:opacity-60 dark:group-hover:opacity-70 transition duration-1000 group-hover:duration-200" class="from-primary-500 to-secondary-500 absolute -inset-1 rounded-2xl bg-linear-to-r opacity-25 blur transition duration-1000 group-hover:opacity-60 group-hover:duration-200 dark:opacity-40 dark:group-hover:opacity-70">
></div> </div>
<AE_Comp_Journal_Entry_Quick_Add <AE_Comp_Journal_Entry_Quick_Add
journals_li={$lq__journal_obj_li} journals_li={$lq__journal_obj_li}
class="relative shadow-2xl rounded-xl overflow-hidden border border-surface-500/10 bg-surface-50 dark:bg-surface-900" class="border-surface-500/10 bg-surface-50 dark:bg-surface-900 relative overflow-hidden rounded-xl border shadow-2xl" />
/>
</div> </div>
<div <div
class="mt-2 flex items-center justify-center gap-2 text-xs opacity-50 font-bold uppercase tracking-widest" class="mt-2 flex items-center justify-center gap-2 text-xs font-bold tracking-widest uppercase opacity-50">
>
<Sparkles size="1em" /> <Sparkles size="1em" />
<span>Fast Input Mode Active</span> <span>Fast Input Mode Active</span>
</div> </div>
@@ -134,33 +139,29 @@
<!-- Administrative Action Bar (Edit Mode Only) --> <!-- Administrative Action Bar (Edit Mode Only) -->
{#if $ae_loc.edit_mode} {#if $ae_loc.edit_mode}
<nav <nav
class="flex flex-row flex-wrap gap-3 items-center justify-center w-full py-4 border-y border-surface-500/10" class="border-surface-500/10 flex w-full flex-row flex-wrap items-center justify-center gap-3 border-y py-4">
>
<button <button
type="button" type="button"
class="btn preset-tonal-secondary shadow-lg hover:scale-105 transition-transform" class="btn preset-tonal-secondary shadow-lg transition-transform hover:scale-105"
onclick={() => onclick={() =>
($journals_sess.show__modal_new__journal_obj = true)} ($journals_sess.show__modal_new__journal_obj = true)}>
>
<BookPlus size="1.2em" class="mr-2" /> <BookPlus size="1.2em" class="mr-2" />
<span>New Journal</span> <span>New Journal</span>
</button> </button>
<button <button
type="button" type="button"
class="btn preset-tonal-surface shadow-lg hover:scale-105 transition-transform" class="btn preset-tonal-surface shadow-lg transition-transform hover:scale-105"
onclick={() => (show_import_modal = true)} onclick={() => (show_import_modal = true)}>
>
<FileUp size="1.2em" class="mr-2" /> <FileUp size="1.2em" class="mr-2" />
<span>Import</span> <span>Import</span>
</button> </button>
<button <button
type="button" type="button"
class="btn preset-tonal-surface shadow-lg hover:scale-105 transition-transform" class="btn preset-tonal-surface shadow-lg transition-transform hover:scale-105"
onclick={() => onclick={() =>
($journals_sess.show__modal__journals_config = true)} ($journals_sess.show__modal__journals_config = true)}>
>
<Wrench size="1.2em" class="mr-2" /> <Wrench size="1.2em" class="mr-2" />
<span>Config</span> <span>Config</span>
</button> </button>
@@ -168,11 +169,10 @@
{/if} {/if}
<!-- Main List Section --> <!-- Main List Section -->
<main class="w-full flex justify-center"> <main class="flex w-full justify-center">
{#if $lq__journal_obj_li === undefined} {#if $lq__journal_obj_li === undefined}
<div <div
class="flex flex-col items-center justify-center p-20 gap-4 opacity-50" class="flex flex-col items-center justify-center gap-4 p-20 opacity-50">
>
<LoaderCircle size="3em" class="animate-spin" /> <LoaderCircle size="3em" class="animate-spin" />
<p class="text-xl font-bold">Accessing Brain...</p> <p class="text-xl font-bold">Accessing Brain...</p>
</div> </div>
@@ -180,11 +180,10 @@
<Journal_obj_li {lq__journal_obj_li} /> <Journal_obj_li {lq__journal_obj_li} />
{:else} {:else}
<div <div
class="max-w-md text-center p-12 bg-surface-500/5 rounded-3xl border-2 border-dashed border-surface-500/20" class="bg-surface-500/5 border-surface-500/20 max-w-md rounded-3xl border-2 border-dashed p-12 text-center">
>
<SquareLibrary size="4em" class="mx-auto mb-4 opacity-20" /> <SquareLibrary size="4em" class="mx-auto mb-4 opacity-20" />
<h3 class="text-2xl font-bold mb-2">No Journals Found</h3> <h3 class="mb-2 text-2xl font-bold">No Journals Found</h3>
<p class="opacity-60 mb-6"> <p class="mb-6 opacity-60">
You haven't created any journals yet. Start by creating one You haven't created any journals yet. Start by creating one
to begin your documentation journey. to begin your documentation journey.
</p> </p>
@@ -192,8 +191,7 @@
type="button" type="button"
class="btn preset-filled-primary" class="btn preset-filled-primary"
onclick={() => onclick={() =>
($journals_sess.show__modal_new__journal_obj = true)} ($journals_sess.show__modal_new__journal_obj = true)}>
>
Create Your First Journal Create Your First Journal
</button> </button>
</div> </div>
@@ -208,32 +206,27 @@
bind:open={$journals_sess.show__modal_new__journal_obj} bind:open={$journals_sess.show__modal_new__journal_obj}
autoclose={false} autoclose={false}
size="md" size="md"
class="bg-white dark:bg-surface-900 shadow-2xl rounded-2xl" class="dark:bg-surface-900 rounded-2xl bg-white shadow-2xl">
> <div class="space-y-4 p-2">
<div class="p-2 space-y-4">
<div class="space-y-1"> <div class="space-y-1">
<!-- svelte-ignore a11y_label_has_associated_control --> <!-- svelte-ignore a11y_label_has_associated_control -->
<label class="label text-sm font-bold opacity-75" <label class="label text-sm font-bold opacity-75"
>Journal Name</label >Journal Name</label>
>
<input <input
type="text" type="text"
placeholder="e.g. My Daily Logs" placeholder="e.g. My Daily Logs"
bind:value={$journals_sess.journal.new_journal_name} bind:value={$journals_sess.journal.new_journal_name}
class="input" class="input" />
/>
</div> </div>
<div class="space-y-1"> <div class="space-y-1">
<!-- svelte-ignore a11y_label_has_associated_control --> <!-- svelte-ignore a11y_label_has_associated_control -->
<label class="label text-sm font-bold opacity-75" <label class="label text-sm font-bold opacity-75"
>Type Code</label >Type Code</label>
>
<input <input
type="text" type="text"
placeholder="e.g. diary, log, notebook" placeholder="e.g. diary, log, notebook"
bind:value={$journals_sess.journal.new_journal_type_code} bind:value={$journals_sess.journal.new_journal_type_code}
class="input" class="input" />
/>
</div> </div>
<div class="flex justify-end gap-2 pt-4"> <div class="flex justify-end gap-2 pt-4">
<button <button
@@ -241,13 +234,11 @@
class="btn preset-tonal-surface" class="btn preset-tonal-surface"
onclick={() => onclick={() =>
($journals_sess.show__modal_new__journal_obj = false)} ($journals_sess.show__modal_new__journal_obj = false)}
>Cancel</button >Cancel</button>
>
<button <button
type="button" type="button"
class="btn preset-filled-primary font-bold" class="btn preset-filled-primary font-bold"
onclick={create_journal}>Create Journal</button onclick={create_journal}>Create Journal</button>
>
</div> </div>
</div> </div>
</Modal> </Modal>
@@ -255,12 +246,10 @@
{#if $journals_sess.show__modal__journals_config} {#if $journals_sess.show__modal__journals_config}
<AE_Comp_Modal_Journal_Config <AE_Comp_Modal_Journal_Config
show={$journals_sess.show__modal__journals_config} show={$journals_sess.show__modal__journals_config} />
/>
{/if} {/if}
<AE_Comp_Modal_Journal_Import <AE_Comp_Modal_Journal_Import
bind:open={show_import_modal} bind:open={show_import_modal}
on_close={() => (show_import_modal = false)} on_close={() => (show_import_modal = false)}
on_import_complete={() => {}} on_import_complete={() => {}} />
/>

View File

@@ -85,29 +85,27 @@
class=" class="
ae_journals__journal ae_journals__journal
mx-auto mx-auto
flex flex-col grow gap-1 flex max-h-max min-h-full max-w-max
items-center
min-h-full
max-h-max
min-w-full min-w-full
max-w-max grow
flex-col
items-center
gap-1
space-y-2 space-y-2
" ">
>
<div <div
class=" class="
flex flex-row flex-wrap relative flex w-full
flex-row
flex-wrap items-center
justify-between
gap-1 gap-1
items-center justify-between
border-gray-400
border-b border-b
py-2 border-gray-400
w-full py-2 transition-all
hover:bg-slate-100 hover:dark:bg-slate-700
relative transition-all hover:bg-slate-100 hover:dark:bg-slate-700
" ">
>
<!-- If middle click then open the all journals page in a new tab. Otherwise show/hide the menu. --> <!-- If middle click then open the all journals page in a new tab. Otherwise show/hide the menu. -->
<button <button
type="button" type="button"
@@ -134,8 +132,7 @@
transition-all transition-all
" "
title={`View all journals menu: "${$ae_loc?.user?.name}" title={`View all journals menu: "${$ae_loc?.user?.name}"
Middle-click to open in new tab`} Middle-click to open in new tab`}>
>
<!-- <BookHeart /> --> <!-- <BookHeart /> -->
<!-- <Library /> --> <!-- <Library /> -->
{#if show_menu__all_journals} {#if show_menu__all_journals}
@@ -152,16 +149,15 @@ Middle-click to open in new tab`}
<div <div
class=" class="
absolute top-12 left-0 absolute top-12 left-0
p-4 z-50 w-80 z-50 w-80 max-w-fit
space-y-0.5
bg-white dark:bg-gray-800
border border-gray-500
shadow-xl rounded-lg
min-w-72 min-w-72
max-w-fit space-y-0.5 rounded-lg
border border-gray-500
bg-white p-4
shadow-xl
dark:bg-gray-800
" "
class:hidden={!show_menu__all_journals} class:hidden={!show_menu__all_journals}>
>
<a <a
href="/journals" href="/journals"
class=" class="
@@ -172,8 +168,7 @@ Middle-click to open in new tab`}
hover:preset-filled-tertiary-300-700 hover:preset-filled-tertiary-300-700
transition-all transition-all
" "
title="View all journals for this account: {$ae_loc.account_name}" title="View all journals for this account: {$ae_loc.account_name}">
>
<!-- <BookHeart /> --> <!-- <BookHeart /> -->
<!-- <Library /> --> <!-- <Library /> -->
<SquareLibrary class="text-blue-500" /> <SquareLibrary class="text-blue-500" />
@@ -195,12 +190,11 @@ Middle-click to open in new tab`}
}} }}
class=" class="
form-select form-select
border-neutral-400-600
w-full w-full
border p-1
text-sm text-sm
border border-neutral-400-600 ">
p-1
"
>
<option value="" disabled selected> <option value="" disabled selected>
{Object.keys($journals_loc.entry_view_history_kv) {Object.keys($journals_loc.entry_view_history_kv)
.length}&times; Recent Entries... .length}&times; Recent Entries...
@@ -228,8 +222,7 @@ Middle-click to open in new tab`}
hover:preset-filled-tertiary-300-700 hover:preset-filled-tertiary-300-700
transition-all transition-all
" "
title="View all journal entries for this journal: {$lq__journal_obj?.name}" title="View all journal entries for this journal: {$lq__journal_obj?.name}">
>
<Notebook /> <Notebook />
<!-- <Bookmark /> --> <!-- <Bookmark /> -->
<!-- <BookHeart class="m-1" /> --> <!-- <BookHeart class="m-1" /> -->
@@ -315,8 +308,7 @@ Middle-click to open in new tab`}
hover:preset-filled-tertiary-300-700 hover:preset-filled-tertiary-300-700
transition-all transition-all
" "
title="Create a new journal entry for this journal: {$lq__journal_obj?.name}" title="Create a new journal entry for this journal: {$lq__journal_obj?.name}">
>
<FilePlus /> <FilePlus />
<!-- <span class="fas fa-plus m-1"></span> --> <!-- <span class="fas fa-plus m-1"></span> -->
<span class="hidden sm:inline"> New Entry </span> <span class="hidden sm:inline"> New Entry </span>

View File

@@ -68,9 +68,7 @@
let lq__journal_obj = $derived( let lq__journal_obj = $derived(
liveQuery(async () => { liveQuery(async () => {
return await db_journals.journal.get( return await db_journals.journal.get($journals_slct?.journal_id ?? '');
$journals_slct?.journal_id ?? ''
);
}) })
); );
@@ -183,9 +181,7 @@
return false; return false;
if (qry_str) { if (qry_str) {
const name = (entry.name ?? '').toLowerCase(); const name = (entry.name ?? '').toLowerCase();
const content = ( const content = (entry.content ?? '').toLowerCase();
entry.content ?? ''
).toLowerCase();
return ( return (
name.includes(qry_str) || name.includes(qry_str) ||
content.includes(qry_str) content.includes(qry_str)
@@ -300,9 +296,8 @@
{#if $lq__journal_obj === undefined} {#if $lq__journal_obj === undefined}
<div <div
class="flex flex-col items-center justify-center p-20 opacity-50 text-center" class="flex flex-col items-center justify-center p-20 text-center opacity-50">
> <LoaderCircle size="3em" class="mx-auto mb-4 animate-spin" />
<LoaderCircle size="3em" class="animate-spin mb-4 mx-auto" />
<p class="text-xl">Loading Journal...</p> <p class="text-xl">Loading Journal...</p>
</div> </div>
{:else if $ae_loc.person_id == $lq__journal_obj?.person_id} {:else if $ae_loc.person_id == $lq__journal_obj?.person_id}
@@ -310,32 +305,27 @@
{lq__journal_obj} {lq__journal_obj}
{lq__journal_entry_obj_li} {lq__journal_entry_obj_li}
on_show_export={() => (show_export_modal = true)} on_show_export={() => (show_export_modal = true)}
on_show_import={() => (show_import_modal = true)} on_show_import={() => (show_import_modal = true)} />
/>
<Journal_entry_obj_li_wrapper <Journal_entry_obj_li_wrapper
{lq__journal_obj} {lq__journal_obj}
{lq__journal_entry_obj_li} {lq__journal_entry_obj_li}
show_found_header={false} show_found_header={false}
{log_lvl} {log_lvl} />
/>
<AeCompModalJournalExport <AeCompModalJournalExport
bind:open={show_export_modal} bind:open={show_export_modal}
entries={$lq__journal_entry_obj_li ?? []} entries={$lq__journal_entry_obj_li ?? []}
journal={$lq__journal_obj} journal={$lq__journal_obj}
on_close={() => (show_export_modal = false)} on_close={() => (show_export_modal = false)} />
/>
<AeCompModalJournalImport <AeCompModalJournalImport
bind:open={show_import_modal} bind:open={show_import_modal}
on_close={() => (show_import_modal = false)} on_close={() => (show_import_modal = false)}
on_import_complete={handle_import_complete} on_import_complete={handle_import_complete} />
/>
{:else} {:else}
<section <section
class="main_content grow px-1 md:px-2 pb-28 flex flex-col gap-1 items-center" class="main_content flex grow flex-col items-center gap-1 px-1 pb-28 md:px-2">
>
<p class="text-center"> <p class="text-center">
You must be logged in as the owner to view this Journal. You must be logged in as the owner to view this Journal.
</p> </p>

View File

@@ -14,12 +14,7 @@
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 { db_journals } from '$lib/ae_journals/db_journals'; import { db_journals } from '$lib/ae_journals/db_journals';
import { import { ae_loc, ae_sess, ae_api, ae_trig } from '$lib/stores/ae_stores';
ae_loc,
ae_sess,
ae_api,
ae_trig,
} from '$lib/stores/ae_stores';
import { import {
journals_loc, journals_loc,
journals_sess, journals_sess,
@@ -177,10 +172,7 @@
console.log( console.log(
`lq__journal_entry_obj: journal_entry_id = ${$journals_slct?.journal_entry_id}` `lq__journal_entry_obj: journal_entry_id = ${$journals_slct?.journal_entry_id}`
); );
console.log( console.log(`lq__journal_entry_obj: results = `, lq__journal_entry_obj);
`lq__journal_entry_obj: results = `,
lq__journal_entry_obj
);
if ($journals_slct.journal_entry_obj && lq__journal_entry_obj) { if ($journals_slct.journal_entry_obj && lq__journal_entry_obj) {
if ( if (
JSON.stringify($journals_slct.journal_entry_obj) !== JSON.stringify($journals_slct.journal_entry_obj) !==
@@ -274,22 +266,20 @@
class=" class="
ae_journals__journal_entry ae_journals__journal_entry
mx-auto mx-auto
flex flex-col grow gap-1 flex max-h-max min-h-full max-w-max
items-center
min-h-full
max-h-max
min-w-full min-w-full
max-w-max grow
flex-col
items-center
gap-1
space-y-2 space-y-2
" ">
>
<!-- {#if $lq__journal_entry_obj} --> <!-- {#if $lq__journal_entry_obj} -->
<Journal_entry_view <Journal_entry_view
{lq__journal_obj} {lq__journal_obj}
{lq__journal_obj_li} {lq__journal_obj_li}
{lq__journal_entry_obj} {lq__journal_entry_obj}
on_show_export={() => (show_export_modal = true)} on_show_export={() => (show_export_modal = true)} />
/>
<!-- {/if} --> <!-- {/if} -->
</section> </section>
@@ -297,12 +287,10 @@
bind:open={show_export_modal} bind:open={show_export_modal}
entries={$lq__journal_entry_obj ? [$lq__journal_entry_obj] : []} entries={$lq__journal_entry_obj ? [$lq__journal_entry_obj] : []}
journal={$lq__journal_obj} journal={$lq__journal_obj}
on_close={() => (show_export_modal = false)} on_close={() => (show_export_modal = false)} />
/>
{:else} {:else}
<section <section
class="main_content grow px-1 md:px-2 pb-28 flex flex-col gap-1 items-center" class="main_content flex grow flex-col items-center gap-1 px-1 pb-28 md:px-2">
>
<p class="text-center"> <p class="text-center">
You must be logged in as the owner to view this Journal Entry. You must be logged in as the owner to view this Journal Entry.
</p> </p>

View File

@@ -13,12 +13,7 @@
log_lvl?: number; log_lvl?: number;
} }
let { let { content, summary = $bindable(), on_save, log_lvl = 0 }: Props = $props();
content,
summary = $bindable(),
on_save,
log_lvl = 0
}: Props = $props();
</script> </script>
<div class="journal-entry-ai-tools absolute top-2 right-2 z-10"> <div class="journal-entry-ai-tools absolute top-2 right-2 z-10">
@@ -29,6 +24,5 @@
summary = newSummary; summary = newSummary;
on_save(); on_save();
}} }}
{log_lvl} {log_lvl} />
/>
</div> </div>

View File

@@ -41,11 +41,10 @@
</script> </script>
<div <div
class="journal-entry-editor-wrapper grow w-full flex flex-col items-center" class="journal-entry-editor-wrapper flex w-full grow flex-col items-center">
>
{#if !is_editing} {#if !is_editing}
<!-- VIEW MODE --> <!-- VIEW MODE -->
<div class="w-full max-w-6xl p-4 prose dark:prose-invert"> <div class="prose dark:prose-invert w-full max-w-6xl p-4">
{@html tmp_entry_obj?.content_md_html || ''} {@html tmp_entry_obj?.content_md_html || ''}
</div> </div>
{:else} {:else}
@@ -53,10 +52,9 @@
{#if !tmp_entry_obj?.content && tmp_entry_obj?.content_encrypted} {#if !tmp_entry_obj?.content && tmp_entry_obj?.content_encrypted}
<!-- Decryption Required Message --> <!-- Decryption Required Message -->
<div <div
class="w-full max-w-6xl p-4 bg-error-100 dark:bg-error-900/30 text-error-900 dark:text-error-100 rounded-lg border border-error-500 flex flex-col gap-4" class="bg-error-100 dark:bg-error-900/30 text-error-900 dark:text-error-100 border-error-500 flex w-full max-w-6xl flex-col gap-4 rounded-lg border p-4">
>
<div class="space-y-2"> <div class="space-y-2">
<div class="font-bold flex items-center gap-2"> <div class="flex items-center gap-2 font-bold">
<LockKeyhole size="1.25em" /> <LockKeyhole size="1.25em" />
Decryption Required Decryption Required
</div> </div>
@@ -65,16 +63,15 @@
</p> </p>
{#if tmp_entry_obj?.content === false} {#if tmp_entry_obj?.content === false}
<p <p
class="text-xs font-bold text-error-500 uppercase tracking-widest" class="text-error-500 text-xs font-bold tracking-widest uppercase">
>
Decryption failed. Incorrect passcode. Decryption failed. Incorrect passcode.
</p> </p>
{/if} {/if}
</div> </div>
{#if $ae_loc.edit_mode && on_force_reset} {#if $ae_loc.edit_mode && on_force_reset}
<div class="pt-4 border-t border-error-500/20"> <div class="border-error-500/20 border-t pt-4">
<p class="text-xs mb-2 opacity-70 italic"> <p class="mb-2 text-xs italic opacity-70">
Passcode lost? You can force a reset to plain text, Passcode lost? You can force a reset to plain text,
but all currently encrypted data will be permanently but all currently encrypted data will be permanently
deleted. deleted.
@@ -82,8 +79,7 @@
<button <button
type="button" type="button"
class="btn btn-sm preset-tonal-error hover:preset-filled-error-500 font-bold" class="btn btn-sm preset-tonal-error hover:preset-filled-error-500 font-bold"
onclick={on_force_reset} onclick={on_force_reset}>
>
<RefreshCcw size="1.1em" class="mr-2" /> Force Reset to <RefreshCcw size="1.1em" class="mr-2" /> Force Reset to
Plain Text Plain Text
</button> </button>
@@ -98,14 +94,12 @@
bind:editor_view bind:editor_view
theme_mode={$ae_loc.theme_mode} theme_mode={$ae_loc.theme_mode}
placeholder="Write using Markdown..." placeholder="Write using Markdown..."
class_li="p-2 preset-outlined-warning-300-700 shadow-lg rounded-lg w-full max-w-6xl bg-surface-50 dark:bg-surface-800" class_li="p-2 preset-outlined-warning-300-700 shadow-lg rounded-lg w-full max-w-6xl bg-surface-50 dark:bg-surface-800" />
/>
{:else} {:else}
<textarea <textarea
bind:value={tmp_entry_obj.content} bind:value={tmp_entry_obj.content}
class="textarea grow w-full max-w-6xl p-4 font-mono shadow-lg rounded-lg border-orange-500/30 h-[500px] whitespace-pre-wrap break-words" class="textarea h-[500px] w-full max-w-6xl grow rounded-lg border-orange-500/30 p-4 font-mono break-words whitespace-pre-wrap shadow-lg"
placeholder="Edit content..." placeholder="Edit content..."></textarea>
></textarea>
{/if} {/if}
<!-- Floating Save Button --> <!-- Floating Save Button -->
@@ -113,9 +107,8 @@
type="button" type="button"
onclick={on_save} onclick={on_save}
disabled={!has_changed} disabled={!has_changed}
class="btn btn-sm md:btn-md lg:btn-lg fixed top-72 right-6 min-w-32 preset-filled-success shadow-xl z-20 transition-all" class="btn btn-sm md:btn-md lg:btn-lg preset-filled-success fixed top-72 right-6 z-20 min-w-32 shadow-xl transition-all"
class:hidden={!has_changed} class:hidden={!has_changed}>
>
<Save size="1.2em" class="mr-2" /> Save <Save size="1.2em" class="mr-2" /> Save
</button> </button>
@@ -124,16 +117,14 @@
type="button" type="button"
onclick={on_save} onclick={on_save}
disabled={!has_changed} disabled={!has_changed}
class="btn preset-tonal-warning hover:preset-filled-warning-500 w-full max-w-96 mt-4" class="btn preset-tonal-warning hover:preset-filled-warning-500 mt-4 w-full max-w-96"
class:invisible={!has_changed} class:invisible={!has_changed}>
>
<Save size="1.2em" class="mr-2" /> Save Changes <Save size="1.2em" class="mr-2" /> Save Changes
</button> </button>
{#if updated_idb} {#if updated_idb}
<p <p
class="text-xs text-error-500 mt-2 font-bold animate-pulse uppercase tracking-widest" class="text-error-500 mt-2 animate-pulse text-xs font-bold tracking-widest uppercase">
>
IDB object updated since last load! IDB object updated since last load!
</p> </p>
{/if} {/if}

View File

@@ -4,7 +4,20 @@
* Standardized Journal Entry Header. * Standardized Journal Entry Header.
* Manages name, sync status, and triggers the modular config. * Manages name, sync status, and triggers the modular config.
*/ */
import { ChevronLeft, CircleCheck, CircleX, Eye, Fingerprint, LoaderCircle, LockKeyhole, LockKeyholeOpen, Pencil, RefreshCw, Save, Settings } from '@lucide/svelte'; import {
ChevronLeft,
CircleCheck,
CircleX,
Eye,
Fingerprint,
LoaderCircle,
LockKeyhole,
LockKeyholeOpen,
Pencil,
RefreshCw,
Save,
Settings
} from '@lucide/svelte';
import { ae_util } from '$lib/ae_utils/ae_utils'; import { ae_util } from '$lib/ae_utils/ae_utils';
import { import {
journals_loc, journals_loc,
@@ -38,8 +51,7 @@
}: Props = $props(); }: Props = $props();
const is_decrypted = $derived( const is_decrypted = $derived(
$journals_sess?.journal_kv[journal?.id]?.journal_passcode_decrypted === $journals_sess?.journal_kv[journal?.id]?.journal_passcode_decrypted === true
true
); );
function toggle_edit_mode() { function toggle_edit_mode() {
@@ -55,21 +67,19 @@
</script> </script>
<header <header
class="flex flex-col md:flex-row items-center justify-between gap-4 p-3 class="flex w-full flex-col items-center justify-between gap-4 rounded-xl
bg-gray-50 dark:bg-gray-800 border border-gray-200
border border-gray-200 dark:border-gray-700 bg-gray-50 p-3 shadow-sm
rounded-xl shadow-sm w-full" md:flex-row dark:border-gray-700 dark:bg-gray-800">
> <div class="flex w-full items-center gap-3 md:w-auto">
<div class="flex items-center gap-3 w-full md:w-auto">
<a <a
href="/journals/{journal.journal_id}" href="/journals/{journal.journal_id}"
class="btn-icon btn-icon-sm preset-tonal-surface" class="btn-icon btn-icon-sm preset-tonal-surface"
title="Back to Journal" title="Back to Journal">
>
<ChevronLeft size="1.2em" /> <ChevronLeft size="1.2em" />
</a> </a>
<div class="flex items-center gap-2 grow"> <div class="flex grow items-center gap-2">
<button <button
type="button" type="button"
onclick={toggle_edit_mode} onclick={toggle_edit_mode}
@@ -77,12 +87,10 @@
$journals_loc.entry.edit_kv[entry.journal_entry_id] === $journals_loc.entry.edit_kv[entry.journal_entry_id] ===
'current' 'current'
? 'preset-filled-success' ? 'preset-filled-success'
: 'preset-tonal-surface'}" : 'preset-tonal-surface'}">
>
{#if $journals_loc.entry.edit_kv[entry.journal_entry_id] === 'current'} {#if $journals_loc.entry.edit_kv[entry.journal_entry_id] === 'current'}
{#if has_changed}<Save size="1.2em" />{:else}<Eye {#if has_changed}<Save size="1.2em" />{:else}<Eye
size="1.2em" size="1.2em" />{/if}
/>{/if}
{:else} {:else}
<Pencil size="1.2em" /> <Pencil size="1.2em" />
{/if} {/if}
@@ -92,12 +100,11 @@
<input <input
type="text" type="text"
bind:value={tmp_entry_obj.name} bind:value={tmp_entry_obj.name}
class="input input-sm font-bold text-lg grow md:min-w-[300px] border-none bg-transparent focus:ring-2 focus:ring-primary-500" class="input input-sm focus:ring-primary-500 grow border-none bg-transparent text-lg font-bold focus:ring-2 md:min-w-[300px]"
placeholder="Entry Title..." placeholder="Entry Title..."
onchange={on_save} onchange={on_save} />
/>
{:else} {:else}
<h2 class="text-base md:text-lg font-bold truncate max-w-md"> <h2 class="max-w-md truncate text-base font-bold md:text-lg">
{entry.name || {entry.name ||
ae_util.iso_datetime_formatter( ae_util.iso_datetime_formatter(
entry.created_on, entry.created_on,
@@ -108,12 +115,11 @@
</div> </div>
</div> </div>
<div class="flex items-center gap-2 w-full md:w-auto justify-end"> <div class="flex w-full items-center justify-end gap-2 md:w-auto">
<!-- Auto-Save indicator --> <!-- Auto-Save indicator -->
{#if $journals_loc.entry.edit_kv[entry.journal_entry_id] === 'current'} {#if $journals_loc.entry.edit_kv[entry.journal_entry_id] === 'current'}
<div <div
class="flex items-center gap-1 px-2 border-r border-surface-500/20 mr-1" class="border-surface-500/20 mr-1 flex items-center gap-1 border-r px-2">
>
<button <button
type="button" type="button"
class="btn-icon btn-icon-sm {$journals_loc.entry.auto_save class="btn-icon btn-icon-sm {$journals_loc.entry.auto_save
@@ -122,19 +128,16 @@
onclick={() => onclick={() =>
($journals_loc.entry.auto_save = ($journals_loc.entry.auto_save =
!$journals_loc.entry.auto_save)} !$journals_loc.entry.auto_save)}
title="Toggle Auto Save" title="Toggle Auto Save">
>
<RefreshCw size="1em" /> <RefreshCw size="1em" />
</button> </button>
{#if $journals_loc.entry.auto_save} {#if $journals_loc.entry.auto_save}
{#if save_status === 'saving'}<LoaderCircle {#if save_status === 'saving'}<LoaderCircle
size="1em" size="1em"
class="animate-spin text-primary-500" class="text-primary-500 animate-spin" />
/>
{:else if save_status === 'saved'}<CircleCheck {:else if save_status === 'saved'}<CircleCheck
size="1em" size="1em"
class="text-success-500" class="text-success-500" />
/>
{:else}<CircleX size="1em" class="opacity-30" />{/if} {:else}<CircleX size="1em" class="opacity-30" />{/if}
{/if} {/if}
</div> </div>
@@ -145,25 +148,22 @@
<button <button
type="button" type="button"
class="btn-icon btn-icon-sm transition-all {is_decrypted class="btn-icon btn-icon-sm transition-all {is_decrypted
? 'preset-filled-success shadow-lg shadow-success-500/20' ? 'preset-filled-success shadow-success-500/20 shadow-lg'
: 'preset-tonal-warning'}" : 'preset-tonal-warning'}"
onclick={on_decrypt} onclick={on_decrypt}
title={is_decrypted ? 'Lock Content' : 'Decrypt Content'} title={is_decrypted ? 'Lock Content' : 'Decrypt Content'}>
>
{#if is_decrypted}<LockKeyholeOpen {#if is_decrypted}<LockKeyholeOpen
size="1.2em" size="1.2em" />{:else}<LockKeyhole size="1.2em" />{/if}
/>{:else}<LockKeyhole size="1.2em" />{/if}
</button> </button>
{/if} {/if}
<div class="w-[1px] h-6 bg-surface-500/20 mx-1"></div> <div class="bg-surface-500/20 mx-1 h-6 w-[1px]"></div>
<!-- Unified Config Button --> <!-- Unified Config Button -->
<button <button
type="button" type="button"
class="btn btn-sm preset-tonal-primary font-bold" class="btn btn-sm preset-tonal-primary font-bold"
onclick={on_show_config} onclick={on_show_config}>
>
<Settings size="1.1em" class="mr-2" /> Config <Settings size="1.1em" class="mr-2" /> Config
</button> </button>
@@ -172,8 +172,7 @@
<button <button
type="button" type="button"
class="btn btn-sm preset-filled-primary" class="btn btn-sm preset-filled-primary"
onclick={on_save} onclick={on_save}>
>
<Save size="1.1em" class="mr-2" /> Save <Save size="1.1em" class="mr-2" /> Save
</button> </button>
{/if} {/if}

View File

@@ -18,8 +18,7 @@
<section class="journal-metadata w-full space-y-4"> <section class="journal-metadata w-full space-y-4">
<!-- System Timestamps --> <!-- System Timestamps -->
<div <div
class="flex flex-col sm:flex-row justify-between items-center gap-2 px-1 text-xs text-surface-500" class="text-surface-500 flex flex-col items-center justify-between gap-2 px-1 text-xs sm:flex-row">
>
<div class="flex gap-4"> <div class="flex gap-4">
<span title="Creation date"> <span title="Creation date">
<span class="font-semibold">Created:</span> <span class="font-semibold">Created:</span>

View File

@@ -6,7 +6,15 @@
*/ */
// *** Import Lucide Icons // *** Import Lucide Icons
import { Download, ExternalLink, FileUp, LoaderCircle, Paperclip, RefreshCw, Trash2 } from '@lucide/svelte'; import {
Download,
ExternalLink,
FileUp,
LoaderCircle,
Paperclip,
RefreshCw,
Trash2
} from '@lucide/svelte';
// *** Import Aether specific variables and functions // *** Import Aether specific variables and functions
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';
@@ -80,9 +88,8 @@
if ( if (
!files.find( !files.find(
(f) => (f) =>
(f.hosted_file_id || (f.hosted_file_id || f.id || f.hosted_file_id) ===
f.id || id
f.hosted_file_id) === id
) )
) { ) {
files.push({ ...obj, id: id }); files.push({ ...obj, id: id });
@@ -125,11 +132,7 @@
// Handle "Select Existing" completion // Handle "Select Existing" completion
$effect(() => { $effect(() => {
if ( if ($lq__journal_entry_obj && slct_hosted_file_id && slct_hosted_file_obj) {
$lq__journal_entry_obj &&
slct_hosted_file_id &&
slct_hosted_file_obj
) {
const new_file = { const new_file = {
...slct_hosted_file_obj, ...slct_hosted_file_obj,
id: slct_hosted_file_id id: slct_hosted_file_id
@@ -188,18 +191,16 @@
} }
</script> </script>
<section class="ae_section journal_entry_files w-full space-y-4 my-2"> <section class="ae_section journal_entry_files my-2 w-full space-y-4">
<!-- Header --> <!-- Header -->
<div <div
class="flex items-center justify-between border-b border-surface-500/20 pb-2" class="border-surface-500/20 flex items-center justify-between border-b pb-2">
>
<h3 class="h3 flex items-center gap-2 text-lg font-bold"> <h3 class="h3 flex items-center gap-2 text-lg font-bold">
<Paperclip size="1.1em" /> <Paperclip size="1.1em" />
Attachments Attachments
{#if unified_file_li.length} {#if unified_file_li.length}
<span class="badge preset-tonal-surface text-xs" <span class="badge preset-tonal-surface text-xs"
>{unified_file_li.length}</span >{unified_file_li.length}</span>
>
{/if} {/if}
</h3> </h3>
@@ -212,8 +213,7 @@
$ae_sess.files.add_to_use_files_method === 'upload' $ae_sess.files.add_to_use_files_method === 'upload'
? 'select' ? 'select'
: 'upload'; : 'upload';
}} }}>
>
<RefreshCw size="1em" class="mr-2" /> <RefreshCw size="1em" class="mr-2" />
{$ae_sess.files.add_to_use_files_method === 'select' {$ae_sess.files.add_to_use_files_method === 'select'
? 'Switch to Upload' ? 'Switch to Upload'
@@ -224,14 +224,13 @@
<!-- File Grid --> <!-- File Grid -->
{#if unified_file_li.length} {#if unified_file_li.length}
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-3"> <div class="grid grid-cols-1 gap-3 md:grid-cols-2 lg:grid-cols-3">
{#each unified_file_li as file (file.hosted_file_id ?? file.id)} {#each unified_file_li as file (file.hosted_file_id ?? file.id)}
{@const file_id = {@const file_id =
file.hosted_file_id || file.id || file.hosted_file_id} file.hosted_file_id || file.id || file.hosted_file_id}
<div <div
class="flex items-center justify-between p-3 rounded-xl bg-surface-50-950 border border-surface-500/10 group hover:border-primary-500 transition-all shadow-sm" class="bg-surface-50-950 border-surface-500/10 group hover:border-primary-500 flex items-center justify-between rounded-xl border p-3 shadow-sm transition-all">
> <div class="flex grow items-center gap-3 overflow-hidden">
<div class="flex items-center gap-3 overflow-hidden grow">
<AE_Comp_Hosted_Files_Download_Button <AE_Comp_Hosted_Files_Download_Button
hosted_file_id={file_id} hosted_file_id={file_id}
hosted_file_obj={file} hosted_file_obj={file}
@@ -241,18 +240,16 @@
show_divider={true} show_divider={true}
max_filename={25} max_filename={25}
show_direct_download={$ae_loc.trusted_access && show_direct_download={$ae_loc.trusted_access &&
$ae_loc.edit_mode} $ae_loc.edit_mode} />
/>
</div> </div>
<div class="flex items-center gap-1 ml-2"> <div class="ml-2 flex items-center gap-1">
{#if $ae_loc.edit_mode} {#if $ae_loc.edit_mode}
<button <button
type="button" type="button"
class="btn btn-sm preset-tonal-error" class="btn btn-sm preset-tonal-error"
onclick={() => handle_remove_file(file_id)} onclick={() => handle_remove_file(file_id)}
title="Remove attachment" title="Remove attachment">
>
<Trash2 size="1.1em" /> <Trash2 size="1.1em" />
</button> </button>
{/if} {/if}
@@ -262,8 +259,7 @@
</div> </div>
{:else if !$ae_loc.edit_mode} {:else if !$ae_loc.edit_mode}
<p <p
class="text-sm text-surface-500 italic p-4 text-center bg-surface-500/5 rounded-xl" class="text-surface-500 bg-surface-500/5 rounded-xl p-4 text-center text-sm italic">
>
No files attached to this entry. No files attached to this entry.
</p> </p>
{/if} {/if}
@@ -271,8 +267,7 @@
<!-- Edit/Management Tools --> <!-- Edit/Management Tools -->
{#if $ae_loc.edit_mode} {#if $ae_loc.edit_mode}
<div <div
class="mt-4 p-4 rounded-2xl bg-surface-500/5 border-2 border-dashed border-surface-500/20" class="bg-surface-500/5 border-surface-500/20 mt-4 rounded-2xl border-2 border-dashed p-4">
>
{#if $ae_sess.files.add_to_use_files_method === 'upload'} {#if $ae_sess.files.add_to_use_files_method === 'upload'}
<Comp_hosted_files_upload <Comp_hosted_files_upload
{link_to_type} {link_to_type}
@@ -282,13 +277,11 @@
bind:hosted_file_obj_li={slct_hosted_file_obj_li} bind:hosted_file_obj_li={slct_hosted_file_obj_li}
bind:hosted_file_obj_kv={slct_hosted_file_kv} bind:hosted_file_obj_kv={slct_hosted_file_kv}
accept="*/*" accept="*/*"
class_li="!max-w-none" class_li="!max-w-none">
>
{#snippet label()} {#snippet label()}
<div class="flex flex-col items-center gap-2 py-2"> <div class="flex flex-col items-center gap-2 py-2">
<div <div
class="p-3 rounded-full bg-primary-500/10 text-primary-500" class="bg-primary-500/10 text-primary-500 rounded-full p-3">
>
<FileUp size="1.8em" /> <FileUp size="1.8em" />
</div> </div>
<div class="text-center"> <div class="text-center">
@@ -302,7 +295,7 @@
</Comp_hosted_files_upload> </Comp_hosted_files_upload>
{:else} {:else}
<div class="space-y-2"> <div class="space-y-2">
<p class="text-sm font-bold opacity-70 ml-1"> <p class="ml-1 text-sm font-bold opacity-70">
Select from existing hosted files: Select from existing hosted files:
</p> </p>
<Element_manage_hosted_file_li_wrap <Element_manage_hosted_file_li_wrap
@@ -313,8 +306,7 @@
class_li={''} class_li={''}
bind:slct_hosted_file_kv bind:slct_hosted_file_kv
bind:slct_hosted_file_id bind:slct_hosted_file_id
bind:slct_hosted_file_obj bind:slct_hosted_file_obj />
/>
</div> </div>
{/if} {/if}
</div> </div>

View File

@@ -94,13 +94,11 @@
val === null || val === undefined ? '' : String(val).trim(); val === null || val === undefined ? '' : String(val).trim();
const content_changed = const content_changed =
normalize(tmp_entry_obj.content) !== normalize(tmp_entry_obj.content) !== normalize(orig_entry_obj.content);
normalize(orig_entry_obj.content);
const name_changed = const name_changed =
normalize(tmp_entry_obj.name) !== normalize(orig_entry_obj.name); normalize(tmp_entry_obj.name) !== normalize(orig_entry_obj.name);
const private_changed = const private_changed =
(tmp_entry_obj.private ?? false) !== (tmp_entry_obj.private ?? false) !== (orig_entry_obj.private ?? false);
(orig_entry_obj.private ?? false);
if (content_changed || name_changed || private_changed) return true; if (content_changed || name_changed || private_changed) return true;
@@ -115,8 +113,7 @@
]; ];
for (const field of other_fields) { for (const field of other_fields) {
if ( if (
normalize(tmp_entry_obj[field]) !== normalize(tmp_entry_obj[field]) !== normalize(orig_entry_obj[field])
normalize(orig_entry_obj[field])
) )
return true; return true;
} }
@@ -135,15 +132,10 @@
if (!entry || !(entry.updated_on || entry.created_on)) return; if (!entry || !(entry.updated_on || entry.created_on)) return;
const session_kv = $journals_sess?.journal_kv[journal?.id]; const session_kv = $journals_sess?.journal_kv[journal?.id];
const is_decrypted = const is_decrypted = session_kv?.journal_passcode_decrypted === true;
session_kv?.journal_passcode_decrypted === true;
// Only sync if saved and not currently processing/editing // Only sync if saved and not currently processing/editing
if ( if (save_status === 'saved' && !has_unsaved_changes && !is_processing) {
save_status === 'saved' &&
!has_unsaved_changes &&
!is_processing
) {
// Prevent overwrite of recovered text if we are in a decrypted session // Prevent overwrite of recovered text if we are in a decrypted session
if (is_decrypted && tmp_entry_obj.content && !entry.content) { if (is_decrypted && tmp_entry_obj.content && !entry.content) {
return; return;
@@ -168,18 +160,13 @@
// Isolate logic from secondary dependencies // Isolate logic from secondary dependencies
const should_save = untrack( const should_save = untrack(
() => () => has_unsaved_changes && !is_processing && save_status !== 'saving'
has_unsaved_changes &&
!is_processing &&
save_status !== 'saving'
); );
if (should_save) { if (should_save) {
if (save_status !== 'saving') save_status = 'unsaved'; if (save_status !== 'saving') save_status = 'unsaved';
const auto_save_enabled = untrack( const auto_save_enabled = untrack(() => $journals_loc.entry.auto_save);
() => $journals_loc.entry.auto_save
);
if (auto_save_enabled) { if (auto_save_enabled) {
clearTimeout(auto_save_timer); clearTimeout(auto_save_timer);
auto_save_timer = setTimeout(() => { auto_save_timer = setTimeout(() => {
@@ -283,11 +270,7 @@
} }
async function update_journal_entry(fields_kv?: key_val) { async function update_journal_entry(fields_kv?: key_val) {
if ( if (!$ae_loc.trusted_access || save_status === 'saving' || is_processing)
!$ae_loc.trusted_access ||
save_status === 'saving' ||
is_processing
)
return; return;
is_processing = true; is_processing = true;
@@ -419,21 +402,20 @@
let modal_mode: 'append' | 'prepend' | 'auto' = $state('auto'); let modal_mode: 'append' | 'prepend' | 'auto' = $state('auto');
</script> </script>
<div class="relative group/entry-view w-full"> <div class="group/entry-view relative w-full">
<!-- Subtle glow — same technique as the rest of the Journals module --> <!-- Subtle glow — same technique as the rest of the Journals module -->
<div <div
class="absolute -inset-1 bg-linear-to-r from-primary-500/50 to-secondary-500/50 class="from-primary-500/50 to-secondary-500/50 pointer-events-none absolute -inset-1
rounded-2xl blur opacity-5 dark:opacity-10 rounded-2xl bg-linear-to-r opacity-5 blur
group-hover/entry-view:opacity-15 dark:group-hover/entry-view:opacity-20 transition duration-700
transition duration-700 pointer-events-none" group-hover/entry-view:opacity-15 dark:opacity-10 dark:group-hover/entry-view:opacity-20">
></div> </div>
<section <section
class="ae_view relative flex flex-col gap-2 w-full h-full p-2 class="ae_view relative flex h-full w-full flex-col gap-2 rounded-xl
bg-white dark:bg-gray-900 border border-gray-200
border border-gray-200 dark:border-gray-700 bg-white p-2 shadow-xl
rounded-xl shadow-xl" dark:border-gray-700 dark:bg-gray-900"
bind:clientHeight={$ae_loc.iframe_height_modal_body} bind:clientHeight={$ae_loc.iframe_height_modal_body}>
>
{#if $lq__journal_entry_obj && $lq__journal_obj} {#if $lq__journal_entry_obj && $lq__journal_obj}
<AE_Comp_Journal_Entry_Header <AE_Comp_Journal_Entry_Header
entry={$lq__journal_entry_obj} entry={$lq__journal_entry_obj}
@@ -445,24 +427,20 @@
on_decrypt={handle_content_decryption} on_decrypt={handle_content_decryption}
on_show_config={() => (show_config_modal = true)} on_show_config={() => (show_config_modal = true)}
{save_status} {save_status}
{log_lvl} {log_lvl} />
/>
{#if decryption_error} {#if decryption_error}
<div <div
class="w-full p-4 bg-error-500/20 border-2 border-error-500 rounded-lg flex items-center justify-between shadow-2xl z-50 animate-bounce" class="bg-error-500/20 border-error-500 z-50 flex w-full animate-bounce items-center justify-between rounded-lg border-2 p-4 shadow-2xl">
>
<div <div
class="flex items-center gap-4 text-error-700 dark:text-error-300 font-bold" class="text-error-700 dark:text-error-300 flex items-center gap-4 font-bold">
>
<CircleAlert size="2.5em" /> <CircleAlert size="2.5em" />
<span class="text-xl">{decryption_error}</span> <span class="text-xl">{decryption_error}</span>
</div> </div>
<button <button
type="button" type="button"
class="btn btn-sm preset-tonal-error hover:preset-filled-error-500 font-bold" class="btn btn-sm preset-tonal-error hover:preset-filled-error-500 font-bold"
onclick={() => (decryption_error = null)} onclick={() => (decryption_error = null)}>
>
<CircleX size="1.2em" class="mr-2" /> Dismiss <CircleX size="1.2em" class="mr-2" /> Dismiss
</button> </button>
</div> </div>
@@ -470,19 +448,20 @@
<!-- ring-2 inset indicates "edit mode" in both light and dark without a background swap --> <!-- ring-2 inset indicates "edit mode" in both light and dark without a background swap -->
<section <section
class="grow relative p-1 rounded-lg shadow-md overflow-hidden class="relative grow overflow-hidden rounded-lg border border-gray-200
bg-gray-50 dark:bg-gray-800 bg-gray-50 p-1
border border-gray-200 dark:border-gray-700 shadow-md dark:border-gray-700 dark:bg-gray-800
{$journals_loc.entry.edit_kv[$lq__journal_entry_obj?.journal_entry_id] == 'current' {$journals_loc.entry.edit_kv[
? 'ring-2 ring-inset ring-primary-500/40' : ''}" $lq__journal_entry_obj?.journal_entry_id
> ] == 'current'
? 'ring-primary-500/40 ring-2 ring-inset'
: ''}">
<div class="absolute top-2 right-2 z-10"> <div class="absolute top-2 right-2 z-10">
<AE_Comp_Journal_Entry_AiTools <AE_Comp_Journal_Entry_AiTools
content={tmp_entry_obj.content} content={tmp_entry_obj.content}
bind:summary={tmp_entry_obj.summary} bind:summary={tmp_entry_obj.summary}
on_save={() => update_journal_entry()} on_save={() => update_journal_entry()}
{log_lvl} {log_lvl} />
/>
</div> </div>
<AE_Comp_Journal_Entry_Editor <AE_Comp_Journal_Entry_Editor
@@ -493,16 +472,14 @@
has_changed={has_unsaved_changes} has_changed={has_unsaved_changes}
updated_idb={false} updated_idb={false}
on_save={() => update_journal_entry()} on_save={() => update_journal_entry()}
on_force_reset={handle_force_reset} on_force_reset={handle_force_reset} />
/>
</section> </section>
<AE_Comp_Journal_Entry_ObjFileLi <AE_Comp_Journal_Entry_ObjFileLi
{log_lvl} {log_lvl}
link_to_type="journal_entry" link_to_type="journal_entry"
link_to_id={$lq__journal_entry_obj.journal_entry_id} link_to_id={$lq__journal_entry_obj.journal_entry_id}
{lq__journal_entry_obj} {lq__journal_entry_obj} />
/>
<AE_Comp_Journal_Entry_Metadata entry={tmp_entry_obj as any} /> <AE_Comp_Journal_Entry_Metadata entry={tmp_entry_obj as any} />
@@ -515,8 +492,7 @@
on_update={() => { on_update={() => {
show_append_modal = false; show_append_modal = false;
}} }}
{log_lvl} {log_lvl} />
/>
<AE_Comp_Modal_Journal_Entry_Config <AE_Comp_Modal_Journal_Entry_Config
bind:show={show_config_modal} bind:show={show_config_modal}
@@ -534,12 +510,10 @@
modal_mode = 'prepend'; modal_mode = 'prepend';
show_append_modal = true; show_append_modal = true;
}} }}
{log_lvl} {log_lvl} />
/>
{:else} {:else}
<div <div
class="p-20 text-center opacity-50 flex flex-col items-center gap-4" class="flex flex-col items-center gap-4 p-20 text-center opacity-50">
>
<LoaderCircle class="animate-spin" size="4em" /> <LoaderCircle class="animate-spin" size="4em" />
<span class="text-2xl font-bold">Loading Journal Entry...</span> <span class="text-2xl font-bold">Loading Journal Entry...</span>
</div> </div>

View File

@@ -15,17 +15,35 @@
// *** Import Svelte specific // *** Import Svelte specific
import { goto } from '$app/navigation'; import { goto } from '$app/navigation';
import { BookOpenText, CalendarClock, Check, CodeXml, Copy, Eye, EyeOff, Files, Fingerprint, Flag, Group, ListPlus, LoaderCircle, Lock, NotebookPen, NotebookText, NotepadTextDashed, RemoveFormatting, Shapes, Siren, Tags, TypeOutline, X } from '@lucide/svelte'; import {
BookOpenText,
CalendarClock,
Check,
CodeXml,
Copy,
Eye,
EyeOff,
Files,
Fingerprint,
Flag,
Group,
ListPlus,
LoaderCircle,
Lock,
NotebookPen,
NotebookText,
NotepadTextDashed,
RemoveFormatting,
Shapes,
Siren,
Tags,
TypeOutline,
X
} from '@lucide/svelte';
// *** Import Aether specific variables and functions // *** Import Aether specific variables and functions
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 { import { ae_loc, ae_sess, ae_api, ae_trig, slct } from '$lib/stores/ae_stores';
ae_loc,
ae_sess,
ae_api,
ae_trig,
slct
} from '$lib/stores/ae_stores';
import { import {
journals_sess, journals_sess,
journals_slct, journals_slct,
@@ -43,8 +61,7 @@
$effect(() => { $effect(() => {
// Sync local boolean with store ID presence // Sync local boolean with store ID presence
show_append_modal = show_append_modal = !!$journals_sess.show__modal_append__journal_entry_id;
!!$journals_sess.show__modal_append__journal_entry_id;
}); });
function handle_modal_close() { function handle_modal_close() {
@@ -98,37 +115,35 @@
); );
</script> </script>
<div class="relative group/entries w-full"> <div class="group/entries relative w-full">
<!-- Subtle glow behind the entry list — same technique as the other journal sections --> <!-- Subtle glow behind the entry list — same technique as the other journal sections -->
<div <div
class="absolute -inset-1 bg-linear-to-r from-primary-500/50 to-secondary-500/50 class="from-primary-500/50 to-secondary-500/50 pointer-events-none absolute -inset-1
rounded-2xl blur opacity-5 dark:opacity-10 rounded-2xl bg-linear-to-r opacity-5 blur
group-hover/entries:opacity-15 dark:group-hover/entries:opacity-20 transition duration-700
transition duration-700 pointer-events-none" group-hover/entries:opacity-15 dark:opacity-10 dark:group-hover/entries:opacity-20">
></div> </div>
<section <section
class="journal_list relative flex flex-col gap-1 md:gap-2 items-center justify-center w-full" class="journal_list relative flex w-full flex-col items-center justify-center gap-1 md:gap-2">
>
{#if visible_journal_entry_obj_li === null} {#if visible_journal_entry_obj_li === null}
<!-- Loading state --> <!-- Loading state -->
<div class="flex flex-col items-center justify-center p-10 opacity-50"> <div
<LoaderCircle size="2em" class="animate-spin mb-2" /> class="flex flex-col items-center justify-center p-10 opacity-50">
<LoaderCircle size="2em" class="mb-2 animate-spin" />
<p>Loading visible entries...</p> <p>Loading visible entries...</p>
</div> </div>
{:else if visible_journal_entry_obj_li.length > 0} {:else if visible_journal_entry_obj_li.length > 0}
{#if show_found_header} {#if show_found_header}
<div class="w-full max-w-(--breakpoint-lg) mb-2"> <div class="mb-2 w-full max-w-(--breakpoint-lg)">
<h2 class="h4 flex items-center gap-2 px-2"> <h2 class="h4 flex items-center gap-2 px-2">
<span class="text-sm text-gray-500 font-normal"> <span class="text-sm font-normal text-gray-500">
Found: Found:
</span> </span>
<span <span
class="badge preset-tonal-success font-bold text-lg px-3 py-1" class="badge preset-tonal-success px-3 py-1 text-lg font-bold">
>
{visible_journal_entry_obj_li.length}<span {visible_journal_entry_obj_li.length}<span
class="text-gray-400 dark:text-gray-600" class="text-gray-400 dark:text-gray-600"
>&times;</span >&times;</span>
>
</span> </span>
</h2> </h2>
</div> </div>
@@ -137,52 +152,46 @@
{#each visible_journal_entry_obj_li as journals_journal_entry_obj, index (journals_journal_entry_obj.journal_entry_id)} {#each visible_journal_entry_obj_li as journals_journal_entry_obj, index (journals_journal_entry_obj.journal_entry_id)}
<div <div
class=" class="
container journal journal_entry_obj group/entry journal journal_entry_obj group/entry border-l-primary-500/40
border border-gray-200 dark:border-gray-700 hover:border-l-primary-500 container flex
border-l-4 border-l-primary-500/40
px-2 py-1 space-y-1
w-full max-w-(--breakpoint-lg) w-full max-w-(--breakpoint-lg)
flex flex-col items-center justify-center flex-col items-center justify-center
bg-white text-gray-900 space-y-1 rounded-lg
dark:bg-gray-800 dark:text-gray-200 border border-l-4 border-gray-200 bg-white
rounded-lg px-2 py-1
hover:bg-gray-50 dark:hover:bg-gray-700 text-gray-900 transition-all
hover:border-l-primary-500 hover:shadow-md duration-200
transition-all duration-200 ease-out ease-out hover:bg-gray-50
hover:shadow-md dark:border-gray-700
dark:bg-gray-800 dark:text-gray-200 dark:hover:bg-gray-700
" "
class:dim={!journals_journal_entry_obj.enable} class:dim={!journals_journal_entry_obj.enable}
class:bg-warning-100={!journals_journal_entry_obj?.enable} class:bg-warning-100={!journals_journal_entry_obj?.enable}>
>
<header <header
class="ae_header flex flex-row gap-2 items-center justify-between w-full" class="ae_header flex w-full flex-row items-center justify-between gap-2">
>
<span class="flex flex-row flex-wrap gap-1"> <span class="flex flex-row flex-wrap gap-1">
<span class="journal_entry__name *:hover:inline-block"> <span
class="journal_entry__name *:hover:inline-block">
{#if journals_journal_entry_obj.alert} {#if journals_journal_entry_obj.alert}
<Siren <Siren
size="1.25em" size="1.25em"
class="mx-1 inline-block text-red-500" class="mx-1 inline-block text-red-500" />
/>
{/if} {/if}
{#if journals_journal_entry_obj.priority} {#if journals_journal_entry_obj.priority}
<Flag <Flag
size="1.25em" size="1.25em"
class="mx-1 inline-block text-yellow-500" class="mx-1 inline-block text-yellow-500" />
/>
{/if} {/if}
{#if journals_journal_entry_obj.group} {#if journals_journal_entry_obj.group}
<Group <Group
size="1.25em" size="1.25em"
class="mx-1 inline-block text-green-500" class="mx-1 inline-block text-green-500" />
/> <span class="hidden text-xs text-gray-500"
<span class="text-xs text-gray-500 hidden" >Group:</span>
>Group:</span
>
<span <span
class="font-semibold text-sm text-gray-500 hidden md:inline" class="hidden text-sm font-semibold text-gray-500 md:inline">
>
{journals_journal_entry_obj.group} {journals_journal_entry_obj.group}
</span> </span>
{/if} {/if}
@@ -190,24 +199,20 @@
<h3 <h3
class:dim={journals_journal_entry_obj.hide} class:dim={journals_journal_entry_obj.hide}
class="journal__name h4" class="journal__name h4">
>
{#if journals_journal_entry_obj.template} {#if journals_journal_entry_obj.template}
<NotepadTextDashed <NotepadTextDashed
class="mx-1 inline-block text-neutral-800/60 dark:text-neutral-50/60" class="mx-1 inline-block text-neutral-800/60 dark:text-neutral-50/60" />
/>
{@html journals_journal_entry_obj.name ?? {@html journals_journal_entry_obj.name ??
'-- no name --'} '-- no name --'}
{:else if journals_journal_entry_obj.name} {:else if journals_journal_entry_obj.name}
<NotebookText <NotebookText
class="mx-1 inline-block text-neutral-800/60 dark:text-neutral-50/60" class="mx-1 inline-block text-neutral-800/60 dark:text-neutral-50/60" />
/>
{@html journals_journal_entry_obj.name} {@html journals_journal_entry_obj.name}
{:else} {:else}
<CalendarClock <CalendarClock
class="mx-1 inline-block text-neutral-800/60 dark:text-neutral-50/60" class="mx-1 inline-block text-neutral-800/60 dark:text-neutral-50/60" />
/>
{ae_util.iso_datetime_formatter( {ae_util.iso_datetime_formatter(
journals_journal_entry_obj.created_on, journals_journal_entry_obj.created_on,
'datetime_iso_12_no_seconds' 'datetime_iso_12_no_seconds'
@@ -216,8 +221,7 @@
</h3> </h3>
<span <span
class="flex flex-row flex-wrap gap-1 items-center justify-center" class="flex flex-row flex-wrap items-center justify-center gap-1">
>
{#if !journals_journal_entry_obj.private} {#if !journals_journal_entry_obj.private}
<!-- Button to copy the Markdown version --> <!-- Button to copy the Markdown version -->
<button <button
@@ -227,7 +231,9 @@
journals_journal_entry_obj; journals_journal_entry_obj;
navigator.clipboard navigator.clipboard
.writeText(tmp_entry_obj.content) .writeText(
tmp_entry_obj.content
)
.then(() => { .then(() => {
alert( alert(
'Markdown content copied to clipboard!' 'Markdown content copied to clipboard!'
@@ -245,9 +251,8 @@
}} }}
class:hidden={$lq__journal_obj?.cfg_json class:hidden={$lq__journal_obj?.cfg_json
?.hide_copy_plain_md} ?.hide_copy_plain_md}
class="btn btn-sm p-1 preset-tonal-surface hover:preset-filled-secondary-500 *:hover:inline text-xs lg:text-sm" class="btn btn-sm preset-tonal-surface hover:preset-filled-secondary-500 p-1 text-xs *:hover:inline lg:text-sm"
title="Copy the markdown content" title="Copy the markdown content">
>
<RemoveFormatting size="1.25em" /> <RemoveFormatting size="1.25em" />
<span class="hidden"> <span class="hidden">
Copy Plaintext Markdown Copy Plaintext Markdown
@@ -282,9 +287,8 @@
class:hidden={journals_journal_entry_obj.template || class:hidden={journals_journal_entry_obj.template ||
$lq__journal_obj?.cfg_json $lq__journal_obj?.cfg_json
?.hide_copy_html} ?.hide_copy_html}
class="btn btn-sm p-1 preset-tonal-surface hover:preset-filled-secondary-500 *:hover:inline lg:text-xs" class="btn btn-sm preset-tonal-surface hover:preset-filled-secondary-500 p-1 *:hover:inline lg:text-xs"
title="Copy the rendered HTML content" title="Copy the rendered HTML content">
>
<CodeXml size="1.25em" /> <CodeXml size="1.25em" />
<span class="hidden"> <span class="hidden">
Copy HTML Markup Copy HTML Markup
@@ -295,7 +299,8 @@
<button <button
type="button" type="button"
onclick={async () => { onclick={async () => {
const element = document.getElementById( const element =
document.getElementById(
`rendered_journal_entry_content_${journals_journal_entry_obj.journal_entry_id}` `rendered_journal_entry_content_${journals_journal_entry_obj.journal_entry_id}`
); );
if (!element) { if (!element) {
@@ -311,16 +316,21 @@
try { try {
const htmlContent = const htmlContent =
element.innerHTML; element.innerHTML;
await navigator.clipboard.write([ await navigator.clipboard.write(
[
new ClipboardItem({ new ClipboardItem({
'text/html': new Blob( 'text/html':
[htmlContent], new Blob(
[
htmlContent
],
{ {
type: 'text/html' type: 'text/html'
} }
) )
}) })
]); ]
);
alert( alert(
'Rendered rich content copied to clipboard!' 'Rendered rich content copied to clipboard!'
@@ -338,11 +348,11 @@
class:hidden={journals_journal_entry_obj.template || class:hidden={journals_journal_entry_obj.template ||
$lq__journal_obj?.cfg_json $lq__journal_obj?.cfg_json
?.hide_copy_rich} ?.hide_copy_rich}
class="btn btn-sm p-1 preset-tonal-surface hover:preset-filled-secondary-500 *:hover:inline lg:text-xs" class="btn btn-sm preset-tonal-surface hover:preset-filled-secondary-500 p-1 *:hover:inline lg:text-xs"
title="Copy the rich text (rendered HTML) content" title="Copy the rich text (rendered HTML) content">
>
<TypeOutline size="1.25em" /> <TypeOutline size="1.25em" />
<span class="hidden">Copy Rich Text</span> <span class="hidden"
>Copy Rich Text</span>
</button> </button>
<!-- Clone entry --> <!-- Clone entry -->
@@ -395,20 +405,18 @@
}); });
}} }}
class:hidden={!journals_journal_entry_obj.template} class:hidden={!journals_journal_entry_obj.template}
class="btn btn-sm p-1 preset-tonal-surface hover:preset-filled-secondary-500 *:hover:inline lg:text-xs" class="btn btn-sm preset-tonal-surface hover:preset-filled-secondary-500 p-1 *:hover:inline lg:text-xs"
title="Clone this journal entry" title="Clone this journal entry">
>
<Copy size="1.25em" /> <Copy size="1.25em" />
<span class="hidden md:inline">Clone</span> <span class="hidden md:inline"
>Clone</span>
</button> </button>
{:else} {:else}
<Lock <Lock
size="1.25em" size="1.25em"
class="mx-1 inline-block text-red-400 dark:text-red-600" class="mx-1 inline-block text-red-400 dark:text-red-600" />
/> <span class="hidden text-xs text-gray-500"
<span class="text-xs text-gray-500 hidden" >Private</span>
>Private</span
>
<!-- Button to copy the Markdown version --> <!-- Button to copy the Markdown version -->
<button <button
@@ -438,35 +446,35 @@
}} }}
class:hidden={$lq__journal_obj?.cfg_json class:hidden={$lq__journal_obj?.cfg_json
?.hide_copy_encrypted} ?.hide_copy_encrypted}
class="btn btn-sm p-1 preset-tonal-surface hover:preset-filled-secondary-500 *:hover:inline text-xs lg:text-sm" class="btn btn-sm preset-tonal-surface hover:preset-filled-secondary-500 p-1 text-xs *:hover:inline lg:text-sm"
title="Copy the encrypted content" title="Copy the encrypted content">
>
<Fingerprint size="1.25em" /> <Fingerprint size="1.25em" />
<span class="hidden"> Copy Encrypted </span> <span class="hidden">
Copy Encrypted
</span>
</button> </button>
{/if} {/if}
</span> </span>
</span> </span>
<div <div
class="flex flex-row flex-wrap gap-2 items-center justify-end" class="flex flex-row flex-wrap items-center justify-end gap-2">
>
<!-- Linked file count --> <!-- Linked file count -->
<div <div
class="ae_linked_file_count flex flex-row flex-wrap gap-0.5 items-center justify-start" class="ae_linked_file_count flex flex-row flex-wrap items-center justify-start gap-0.5"
class:hidden={!journals_journal_entry_obj?.data_json class:hidden={!journals_journal_entry_obj
?.hosted_file_kv} ?.data_json?.hosted_file_kv}>
>
<Files class="mx-1 inline-block" /> <Files class="mx-1 inline-block" />
<span class="text-xs text-gray-500 hidden md:inline" <span
>Linked files:</span class="hidden text-xs text-gray-500 md:inline"
> >Linked files:</span>
<span class="font-semibold text-sm text-gray-500"> <span
class="text-sm font-semibold text-gray-500">
{journals_journal_entry_obj?.data_json {journals_journal_entry_obj?.data_json
?.hosted_file_kv ?.hosted_file_kv
? Object.keys( ? Object.keys(
journals_journal_entry_obj?.data_json journals_journal_entry_obj
?.hosted_file_kv ?.data_json?.hosted_file_kv
).length ).length
: 0}× : 0}×
</span> </span>
@@ -475,19 +483,16 @@
<!-- Tags for journal entry. Comma delimited list. --> <!-- Tags for journal entry. Comma delimited list. -->
{#if journals_journal_entry_obj.tags && journals_journal_entry_obj.tags.length} {#if journals_journal_entry_obj.tags && journals_journal_entry_obj.tags.length}
<div <div
class="tags flex flex-row flex-wrap gap-0.5 items-center justify-start p-1" class="tags flex flex-row flex-wrap items-center justify-start gap-0.5 p-1">
>
<Tags class="mx-1 inline-block" /> <Tags class="mx-1 inline-block" />
<span <span
class="text-xs text-gray-500 hidden md:inline" class="hidden text-xs text-gray-500 md:inline"
>Tags:</span >Tags:</span>
>
{#each journals_journal_entry_obj.tags.split(',') as tag (tag)} {#each journals_journal_entry_obj.tags.split(',') as tag (tag)}
<span <span
class="btn btn-sm preset-tonal-tertiary hover:preset-tonal-tertiary border border-tertiary-500 transition py-1 px-2" class="btn btn-sm preset-tonal-tertiary hover:preset-tonal-tertiary border-tertiary-500 border px-2 py-1 transition"
title={`Tag: ${tag.trim()}`} title={`Tag: ${tag.trim()}`}>
>
{tag.trim()} {tag.trim()}
</span> </span>
{/each} {/each}
@@ -511,8 +516,8 @@
journals_journal_entry_obj.category_code; journals_journal_entry_obj.category_code;
} }
if ( if (
$journals_loc.entry.search_version === $journals_loc.entry
undefined .search_version === undefined
) )
$journals_loc.entry.search_version = 0; $journals_loc.entry.search_version = 0;
$journals_loc.entry.search_version++; $journals_loc.entry.search_version++;
@@ -520,9 +525,8 @@
class:bg-green-100={$journals_loc.entry class:bg-green-100={$journals_loc.entry
.qry__category_code == .qry__category_code ==
journals_journal_entry_obj.category_code} journals_journal_entry_obj.category_code}
class="btn btn-sm preset-outlined-secondary hover:preset-filled-secondary-500 transition py-1 px-2" class="btn btn-sm preset-outlined-secondary hover:preset-filled-secondary-500 px-2 py-1 transition"
title={`Filter by category: ${journals_journal_entry_obj.category_code}`} title={`Filter by category: ${journals_journal_entry_obj.category_code}`}>
>
<Shapes class="mx-1 inline-block" /> <Shapes class="mx-1 inline-block" />
{journals_journal_entry_obj.category_code ?? {journals_journal_entry_obj.category_code ??
'-- no category --'} '-- no category --'}
@@ -532,12 +536,11 @@
<a <a
href="/journals/{journals_journal_entry_obj?.journal_id ?? href="/journals/{journals_journal_entry_obj?.journal_id ??
$lq__journal_obj?.journal_id}/entry/{journals_journal_entry_obj?.journal_entry_id}" $lq__journal_obj?.journal_id}/entry/{journals_journal_entry_obj?.journal_entry_id}"
class="btn preset-tonal-primary border border-primary-500 hover:preset-filled-primary-500 transition" class="btn preset-tonal-primary border-primary-500 hover:preset-filled-primary-500 border transition"
title={`View ID: ${journals_journal_entry_obj?.id} title={`View ID: ${journals_journal_entry_obj?.id}
${journals_journal_entry_obj?.name ?? ae_util.iso_datetime_formatter(journals_journal_entry_obj.created_on, 'datetime_iso_12_no_seconds')} ${journals_journal_entry_obj?.name ?? ae_util.iso_datetime_formatter(journals_journal_entry_obj.created_on, 'datetime_iso_12_no_seconds')}
Journal ID: ${journals_journal_entry_obj?.journal_id} Journal ID: ${journals_journal_entry_obj?.journal_id}
`} `}>
>
<NotebookPen class="mx-1 inline-block" /> <NotebookPen class="mx-1 inline-block" />
<span class="hidden md:inline"> View </span> <span class="hidden md:inline"> View </span>
</a> </a>
@@ -549,15 +552,16 @@ Journal ID: ${journals_journal_entry_obj?.journal_id}
$journals_sess.show__modal_append__journal_entry_id = $journals_sess.show__modal_append__journal_entry_id =
journals_journal_entry_obj?.id; journals_journal_entry_obj?.id;
tmp_entry_obj = JSON.parse( tmp_entry_obj = JSON.parse(
JSON.stringify(journals_journal_entry_obj) JSON.stringify(
journals_journal_entry_obj
)
); );
}} }}
class="btn btn-icon btn-sm preset-tonal-surface border border-surface-500 hover:preset-filled-secondary-500 transition" class="btn btn-icon btn-sm preset-tonal-surface border-surface-500 hover:preset-filled-secondary-500 border transition"
title={$lq__journal_obj?.cfg_json?.entry_add_text == title={$lq__journal_obj?.cfg_json
'append' ?.entry_add_text == 'append'
? 'Append to Journal Entry' ? 'Append to Journal Entry'
: 'Prepend to Journal Entry'} : 'Prepend to Journal Entry'}>
>
<ListPlus /> <ListPlus />
</button> </button>
</div> </div>
@@ -576,19 +580,19 @@ Journal ID: ${journals_journal_entry_obj?.journal_id}
$journals_slct.journal_obj?.cfg_json $journals_slct.journal_obj?.cfg_json
.hide_professional)} .hide_professional)}
class="journal__content class="journal__content
w-full p-1 w-full overflow-scroll
bg-gray-100 text-gray-900 rounded-lg border
dark:bg-gray-900 dark:text-gray-100 border-gray-200 bg-gray-100
shadow-lg rounded-lg p-1 font-mono
border border-gray-200 dark:border-gray-700 text-sm text-wrap whitespace-pre-wrap
text-wrap text-sm font-mono whitespace-pre-wrap text-gray-900 shadow-lg transition-all delay-1000
transition-all duration-1000
delay-1000 hover:delay-1000 active:delay-100 ease-in-out hover:border-blue-500 hover:bg-blue-100
duration-1000 hover:duration-200 active:duration-200 hover:delay-1000 hover:duration-200 active:z-10
ease-in-out active:delay-100
active:z-10 active:duration-200
hover:bg-blue-100 dark:hover:bg-blue-950 hover:border-blue-500 dark:hover:border-blue-500 dark:border-gray-700 dark:bg-gray-900 dark:text-gray-100 dark:hover:border-blue-500
overflow-scroll dark:hover:bg-blue-950
{$journals_slct.journal_obj.cfg_json.entry_li_max_height {$journals_slct.journal_obj.cfg_json.entry_li_max_height
? `${$journals_slct.journal_obj.cfg_json.entry_li_max_height}` ? `${$journals_slct.journal_obj.cfg_json.entry_li_max_height}`
: ''} : ''}
@@ -602,15 +606,13 @@ Journal ID: ${journals_journal_entry_obj?.journal_id}
.entry_li_hover_max_height .entry_li_hover_max_height
? `${$journals_slct.journal_obj.cfg_json.entry_li_hover_max_height}` ? `${$journals_slct.journal_obj.cfg_json.entry_li_hover_max_height}`
: ''} : ''}
" ">
>
{@html journals_journal_entry_obj.content} {@html journals_journal_entry_obj.content}
</div> </div>
<article <article
class="prose hidden" class="prose hidden"
id="rendered_journal_entry_content_{journals_journal_entry_obj.journal_entry_id}" id="rendered_journal_entry_content_{journals_journal_entry_obj.journal_entry_id}">
>
{@html journals_journal_entry_obj?.content_md_html} {@html journals_journal_entry_obj?.content_md_html}
</article> </article>
{/if} {/if}
@@ -618,41 +620,35 @@ Journal ID: ${journals_journal_entry_obj?.journal_id}
<section <section
class:hidden={!journals_journal_entry_obj?.original_datetime && class:hidden={!journals_journal_entry_obj?.original_datetime &&
!journals_journal_entry_obj?.original_timezone} !journals_journal_entry_obj?.original_timezone}
class="ae_section journal_entry__entry" class="ae_section journal_entry__entry">
>
<div <div
class="ae_group" class="ae_group"
class:hidden={!journals_journal_entry_obj?.original_datetime && class:hidden={!journals_journal_entry_obj?.original_datetime &&
!journals_journal_entry_obj?.original_timezone} !journals_journal_entry_obj?.original_timezone}>
> <span class="ae_label text-sm"
<span class="ae_label text-sm">Original date/time:</span >Original date/time:</span>
>
{#if journals_journal_entry_obj.original_datetime} {#if journals_journal_entry_obj.original_datetime}
<span <span
class="ae_value ae_prop prop_original_datetime font-semibold" class="ae_value ae_prop prop_original_datetime font-semibold"
>{ae_util.iso_datetime_formatter( >{ae_util.iso_datetime_formatter(
journals_journal_entry_obj.original_datetime, journals_journal_entry_obj.original_datetime,
'datetime_12_long' 'datetime_12_long'
)}</span )}</span>
>
{/if} {/if}
{#if journals_journal_entry_obj.original_timezone} {#if journals_journal_entry_obj.original_timezone}
<span class="ae_label text-sm">Timezone:</span> <span class="ae_label text-sm">Timezone:</span>
<span class="ae_value font-semibold" <span class="ae_value font-semibold"
>{journals_journal_entry_obj.original_timezone}</span >{journals_journal_entry_obj.original_timezone}</span>
>
{/if} {/if}
</div> </div>
</section> </section>
<section <section
class="ae_meta mt-2 flex flex-col sm:flex-row gap-2 items-center justify-center text-xs text-gray-500" class="ae_meta mt-2 flex flex-col items-center justify-center gap-2 text-xs text-gray-500 sm:flex-row">
>
<span <span
class:hidden={!$ae_loc.trusted_access || class:hidden={!$ae_loc.trusted_access ||
!$ae_loc.edit_mode} !$ae_loc.edit_mode}
class="flex flex-row gap-1 items-center justify-center" class="flex flex-row items-center justify-center gap-1">
>
<span class="journal_entry__created_on"> <span class="journal_entry__created_on">
Created: Created:
{ae_util.iso_datetime_formatter( {ae_util.iso_datetime_formatter(
@@ -662,8 +658,7 @@ Journal ID: ${journals_journal_entry_obj?.journal_id}
</span> </span>
<span <span
class:hidden={!journals_journal_entry_obj.updated_on} class:hidden={!journals_journal_entry_obj.updated_on}
class="journal_entry__updated_on" class="journal_entry__updated_on">
>
Last update: Last update:
{ae_util.iso_datetime_formatter( {ae_util.iso_datetime_formatter(
journals_journal_entry_obj.updated_on, journals_journal_entry_obj.updated_on,
@@ -695,24 +690,23 @@ Journal ID: ${journals_journal_entry_obj?.journal_id}
'Error updating journal entry:', 'Error updating journal entry:',
error error
); );
alert('Failed to update journal entry.'); alert(
'Failed to update journal entry.'
);
}); });
}} }}
class:hidden={!$ae_loc.edit_mode} class:hidden={!$ae_loc.edit_mode}
class="btn btn-sm preset-tonal-surface hover:preset-filled-warning-500 transition py-1 px-2" class="btn btn-sm preset-tonal-surface hover:preset-filled-warning-500 px-2 py-1 transition"
title={`Set entry as ${journals_journal_entry_obj.hide ? 'visible' : 'hidden'}`} title={`Set entry as ${journals_journal_entry_obj.hide ? 'visible' : 'hidden'}`}>
>
{#if journals_journal_entry_obj.hide} {#if journals_journal_entry_obj.hide}
<EyeOff <EyeOff
strokeWidth="1" strokeWidth="1"
class="inline-block text-error-500/60" class="text-error-500/60 inline-block" />
/>
<span class="hidden md:inline">Hidden</span> <span class="hidden md:inline">Hidden</span>
{:else} {:else}
<Eye <Eye
strokeWidth="2.5" strokeWidth="2.5"
class="inline-block text-success-700 dark:text-success-400" class="text-success-700 dark:text-success-400 inline-block" />
/>
<span class="hidden lg:inline">Visible</span> <span class="hidden lg:inline">Visible</span>
{/if} {/if}
</button> </button>
@@ -728,14 +722,12 @@ Journal ID: ${journals_journal_entry_obj?.journal_id}
journal_config={$lq__journal_obj?.cfg_json} journal_config={$lq__journal_obj?.cfg_json}
on_close={handle_modal_close} on_close={handle_modal_close}
on_update={handle_modal_update} on_update={handle_modal_update}
{log_lvl} {log_lvl} />
/>
{/if} {/if}
{:else} {:else}
<div <div
class="flex flex-col items-center justify-center p-10 opacity-50 text-center" class="flex flex-col items-center justify-center p-10 text-center opacity-50">
> <BookOpenText size="3em" class="mx-auto mb-2 opacity-20" />
<BookOpenText size="3em" class="mb-2 opacity-20 mx-auto" />
<p> <p>
No Journal Entry available to show. Please check the query No Journal Entry available to show. Please check the query
filters or create a new Entry. filters or create a new Entry.

View File

@@ -26,11 +26,10 @@
{lq__journal_obj} {lq__journal_obj}
{lq__journal_entry_obj_li} {lq__journal_entry_obj_li}
{show_found_header} {show_found_header}
{log_lvl} {log_lvl} />
/>
{:else} {:else}
<div class="flex flex-col items-center justify-center p-10 opacity-50"> <div class="flex flex-col items-center justify-center p-10 opacity-50">
<LoaderCircle size="2em" class="animate-spin mb-2" /> <LoaderCircle size="2em" class="mb-2 animate-spin" />
<p>Loading entries...</p> <p>Loading entries...</p>
</div> </div>
{/if} {/if}

View File

@@ -85,8 +85,7 @@
</script> </script>
<div <div
class="ae_group filters_and_search flex flex-row flex-wrap items-center justify-center gap-2" class="ae_group filters_and_search flex flex-row flex-wrap items-center justify-center gap-2">
>
<!-- Search input form --> <!-- Search input form -->
<span class="flex flex-row flex-wrap items-center justify-center gap-1"> <span class="flex flex-row flex-wrap items-center justify-center gap-1">
<form <form
@@ -94,9 +93,8 @@
handle_search_trigger(); handle_search_trigger();
})} })}
autocomplete="off" autocomplete="off"
class="search_form flex flex-row flex-wrap gap-1 items-center justify-center" class="search_form flex flex-row flex-wrap items-center justify-center gap-1">
> <span class="hidden text-sm text-gray-500 lg:inline">
<span class="text-sm text-gray-500 hidden lg:inline">
Search: Search:
</span> </span>
<input <input
@@ -111,18 +109,16 @@
autocomplete="off" autocomplete="off"
class=" class="
input input-sm input input-sm
w-44 md:w-52 w-44 text-sm
text-sm md:w-52
" "
class:bg-red-200={$journals_sess.entry_li == null} class:bg-red-200={$journals_sess.entry_li == null}
class:dark:bg-red-800={$journals_sess.entry_li == null} class:dark:bg-red-800={$journals_sess.entry_li == null} />
/>
<button <button
type="submit" type="submit"
class="btn btn-sm preset-filled-primary transition" class="btn btn-sm preset-filled-primary transition"
title="Perform detailed search" title="Perform detailed search">
>
<Library size="1.25em" /> <Library size="1.25em" />
</button> </button>
@@ -142,12 +138,10 @@
hover:preset-filled-surface-500 hover:preset-filled-surface-500
transition-all transition-all
" "
title="Clear search query text" title="Clear search query text">
>
<RemoveFormatting <RemoveFormatting
size="1.25em" size="1.25em"
class="text-neutral-800/60 dark:text-neutral-50/60" class="text-neutral-800/60 dark:text-neutral-50/60" />
/>
<span class="hidden md:inline"> Clear </span> <span class="hidden md:inline"> Clear </span>
</button> </button>
</form> </form>
@@ -155,15 +149,14 @@
<!-- Give list of categories to base the new entry on --> <!-- Give list of categories to base the new entry on -->
<span class="flex flex-row items-center gap-2"> <span class="flex flex-row items-center gap-2">
<span class="text-sm text-gray-500 hidden md:inline"> Category: </span> <span class="hidden text-sm text-gray-500 md:inline"> Category: </span>
<select <select
class="select select-sm" class="select select-sm"
bind:value={$journals_loc.entry.qry__category_code} bind:value={$journals_loc.entry.qry__category_code}
onchange={(event) => { onchange={(event) => {
handle_search_trigger(); handle_search_trigger();
}} }}
title="Filter by category" title="Filter by category">
>
<option value="">All Categories</option> <option value="">All Categories</option>
{#each $lq__journal_obj?.cfg_json?.category_li as category (category.code)} {#each $lq__journal_obj?.cfg_json?.category_li as category (category.code)}
<option value={category.code}>{category.name}</option> <option value={category.code}>{category.name}</option>
@@ -173,8 +166,7 @@
<!-- Search Control Toggles --> <!-- Search Control Toggles -->
<span <span
class="flex flex-row flex-wrap items-center gap-2 border-l border-surface-300-700 pl-2" class="border-surface-300-700 flex flex-row flex-wrap items-center gap-2 border-l pl-2">
>
<!-- Global Search hidden until backend supports person_id in entries 2026-01-27 --> <!-- Global Search hidden until backend supports person_id in entries 2026-01-27 -->
<!-- <!--
<label <label
@@ -193,9 +185,8 @@
{#if $ae_loc.edit_mode} {#if $ae_loc.edit_mode}
<label <label
class="flex items-center gap-1 cursor-pointer" class="flex cursor-pointer items-center gap-1"
title="When enabled, search results are fetched directly from the server first." title="When enabled, search results are fetched directly from the server first.">
>
<span class="text-xs font-semibold text-gray-500"> <span class="text-xs font-semibold text-gray-500">
Remote First? Remote First?
</span> </span>
@@ -203,8 +194,7 @@
type="checkbox" type="checkbox"
bind:checked={$journals_loc.entry.qry__remote_first} bind:checked={$journals_loc.entry.qry__remote_first}
onchange={handle_search_trigger} onchange={handle_search_trigger}
class="checkbox checkbox-sm" class="checkbox checkbox-sm" />
/>
</label> </label>
{/if} {/if}
</span> </span>

View File

@@ -89,10 +89,9 @@
} }
</script> </script>
<div class="card p-4 space-y-4 preset-tonal-surface {className}"> <div class="card preset-tonal-surface space-y-4 p-4 {className}">
<header <header
class="flex flex-col sm:flex-row justify-between items-start sm:items-center gap-2" class="flex flex-col items-start justify-between gap-2 sm:flex-row sm:items-center">
>
<h3 class="h3 flex items-center gap-2"> <h3 class="h3 flex items-center gap-2">
<BookType size="1.2em" class="text-primary-500" /> <BookType size="1.2em" class="text-primary-500" />
Quick Add Quick Add
@@ -103,11 +102,9 @@
<select <select
class="select select-sm font-bold" class="select select-sm font-bold"
value={target_journal_id} value={target_journal_id}
onchange={handle_journal_change} onchange={handle_journal_change}>
>
<option value="" disabled selected={!target_journal_id} <option value="" disabled selected={!target_journal_id}
>Select Target Journal...</option >Select Target Journal...</option>
>
{#each journals_li as journal (journal.id)} {#each journals_li as journal (journal.id)}
<option value={journal.id}>{journal.name}</option> <option value={journal.id}>{journal.name}</option>
{/each} {/each}
@@ -124,22 +121,19 @@
bind:value={note_content} bind:value={note_content}
{placeholder} {placeholder}
onkeydown={handle_keydown} onkeydown={handle_keydown}
disabled={is_submitting} disabled={is_submitting}></textarea>
></textarea>
<div class="flex justify-between items-center"> <div class="flex items-center justify-between">
<span <span
class="text-[10px] opacity-50 font-mono uppercase tracking-tighter hidden sm:block" class="hidden font-mono text-[10px] tracking-tighter uppercase opacity-50 sm:block">
>
Press Ctrl + Enter to save Press Ctrl + Enter to save
</span> </span>
<div class="flex justify-end space-x-2 grow sm:grow-0"> <div class="flex grow justify-end space-x-2 sm:grow-0">
<button <button
type="button" type="button"
class="btn btn-sm preset-tonal-surface" class="btn btn-sm preset-tonal-surface"
onclick={() => (note_content = '')} onclick={() => (note_content = '')}
disabled={is_submitting || note_content.length === 0} disabled={is_submitting || note_content.length === 0}>
>
Clear Clear
</button> </button>
<button <button
@@ -148,8 +142,7 @@
onclick={handle_submit} onclick={handle_submit}
disabled={is_submitting || disabled={is_submitting ||
!target_journal_id || !target_journal_id ||
note_content.length === 0} note_content.length === 0}>
>
{#if is_submitting}Saving...{:else}Add Note{/if} {#if is_submitting}Saving...{:else}Add Note{/if}
</button> </button>
</div> </div>

View File

@@ -5,7 +5,41 @@
* Standardized Journal-level configuration. * Standardized Journal-level configuration.
* Restored missing visibility and button toggles. * Restored missing visibility and button toggles.
*/ */
import { BetweenVerticalEnd, BetweenVerticalStart, BookHeart, BookOpenText, BriefcaseBusiness, CalendarClock, Check, CodeXml, Copy, Database, Expand, Eye, EyeOff, FileDown, FilePlus, FileUp, Fingerprint, Globe, Layout, LockKeyhole, MessageSquareWarning, Minus, MonitorPlay, MousePointerClick, Palette, Plus, Settings, ShieldCheck, Siren, Target, Trash2, X, Zap } from '@lucide/svelte'; import {
BetweenVerticalEnd,
BetweenVerticalStart,
BookHeart,
BookOpenText,
BriefcaseBusiness,
CalendarClock,
Check,
CodeXml,
Copy,
Database,
Expand,
Eye,
EyeOff,
FileDown,
FilePlus,
FileUp,
Fingerprint,
Globe,
Layout,
LockKeyhole,
MessageSquareWarning,
Minus,
MonitorPlay,
MousePointerClick,
Palette,
Plus,
Settings,
ShieldCheck,
Siren,
Target,
Trash2,
X,
Zap
} from '@lucide/svelte';
import { Modal } from 'flowbite-svelte'; import { Modal } from 'flowbite-svelte';
import { goto } from '$app/navigation'; import { goto } from '$app/navigation';
@@ -38,8 +72,7 @@
}: Props = $props(); }: Props = $props();
// *** Internal State // *** Internal State
let tab: 'actions' | 'general' | 'security' | 'ui' | 'json' = let tab: 'actions' | 'general' | 'security' | 'ui' | 'json' = $state('actions');
$state('actions');
let tmp__journal_obj: any = $state({}); let tmp__journal_obj: any = $state({});
// Deep copy on mount or when lq changes to ensure we have a working copy // Deep copy on mount or when lq changes to ensure we have a working copy
@@ -51,12 +84,9 @@
!tmp__journal_obj.journal_id || !tmp__journal_obj.journal_id ||
tmp__journal_obj.journal_id !== source_id tmp__journal_obj.journal_id !== source_id
) { ) {
tmp__journal_obj = JSON.parse( tmp__journal_obj = JSON.parse(JSON.stringify($lq__journal_obj));
JSON.stringify($lq__journal_obj)
);
// Ensure cfg_json exists // Ensure cfg_json exists
if (!tmp__journal_obj.cfg_json) if (!tmp__journal_obj.cfg_json) tmp__journal_obj.cfg_json = {};
tmp__journal_obj.cfg_json = {};
} }
}); });
} }
@@ -142,32 +172,32 @@
dismissable={false} dismissable={false}
placement="top-center" placement="top-center"
size="xl" size="xl"
class="relative flex flex-col mx-auto w-full bg-white dark:bg-gray-800 text-gray-800 dark:text-gray-200 border border-orange-300 dark:border-orange-700 rounded-lg shadow-xl" class="relative mx-auto flex w-full flex-col rounded-lg border border-orange-300 bg-white text-gray-800 shadow-xl dark:border-orange-700 dark:bg-gray-800 dark:text-gray-200"
headerClass="flex flex-row gap-2 items-center justify-between w-full bg-orange-100 dark:bg-orange-900 p-4 rounded-t-lg border-b border-orange-200 dark:border-orange-800" headerClass="flex flex-row gap-2 items-center justify-between w-full bg-orange-100 dark:bg-orange-900 p-4 rounded-t-lg border-b border-orange-200 dark:border-orange-800"
footerClass="flex flex-row gap-2 items-center justify-center w-full bg-orange-100 dark:bg-orange-900 p-4 rounded-b-lg border-t border-orange-200 dark:border-orange-800" footerClass="flex flex-row gap-2 items-center justify-center w-full bg-orange-100 dark:bg-orange-900 p-4 rounded-b-lg border-t border-orange-200 dark:border-orange-800">
>
{#snippet header()} {#snippet header()}
<h3 class="flex-1 flex items-center gap-2 text-lg font-bold"> <h3 class="flex flex-1 items-center gap-2 text-lg font-bold">
<Settings class="text-primary-500" /> <Settings class="text-primary-500" />
<span>Journal Config: {$lq__journal_obj?.name ?? '--'}</span> <span>Journal Config: {$lq__journal_obj?.name ?? '--'}</span>
</h3> </h3>
<button type="button" class="btn-icon btn-icon-sm preset-tonal-surface ml-2" onclick={() => (show = false)}> <button
type="button"
class="btn-icon btn-icon-sm preset-tonal-surface ml-2"
onclick={() => (show = false)}>
<X size="1.1em" /> <X size="1.1em" />
</button> </button>
{/snippet} {/snippet}
<div class="space-y-6 py-2 h-[75vh] overflow-y-auto px-4"> <div class="h-[75vh] space-y-6 overflow-y-auto px-4 py-2">
<!-- Navigation Tabs --> <!-- Navigation Tabs -->
<div <div
class="flex justify-center gap-1 mb-4 p-1 bg-surface-500/10 rounded-lg max-w-fit mx-auto sticky top-0 z-10 backdrop-blur-sm" class="bg-surface-500/10 sticky top-0 z-10 mx-auto mb-4 flex max-w-fit justify-center gap-1 rounded-lg p-1 backdrop-blur-sm">
>
<button <button
type="button" type="button"
class="btn btn-sm transition-all {tab === 'actions' class="btn btn-sm transition-all {tab === 'actions'
? 'preset-filled-primary' ? 'preset-filled-primary'
: 'preset-tonal-surface'}" : 'preset-tonal-surface'}"
onclick={() => (tab = 'actions')} onclick={() => (tab = 'actions')}>
>
<Zap size="1.1em" class="mr-1" /> Quick Actions <Zap size="1.1em" class="mr-1" /> Quick Actions
</button> </button>
<button <button
@@ -175,8 +205,7 @@
class="btn btn-sm transition-all {tab === 'general' class="btn btn-sm transition-all {tab === 'general'
? 'preset-filled-primary' ? 'preset-filled-primary'
: 'preset-tonal-surface'}" : 'preset-tonal-surface'}"
onclick={() => (tab = 'general')} onclick={() => (tab = 'general')}>
>
<Layout size="1.1em" class="mr-1" /> General <Layout size="1.1em" class="mr-1" /> General
</button> </button>
<button <button
@@ -184,8 +213,7 @@
class="btn btn-sm transition-all {tab === 'security' class="btn btn-sm transition-all {tab === 'security'
? 'preset-filled-primary' ? 'preset-filled-primary'
: 'preset-tonal-surface'}" : 'preset-tonal-surface'}"
onclick={() => (tab = 'security')} onclick={() => (tab = 'security')}>
>
<ShieldCheck size="1.1em" class="mr-1" /> Status & Security <ShieldCheck size="1.1em" class="mr-1" /> Status & Security
</button> </button>
<button <button
@@ -193,8 +221,7 @@
class="btn btn-sm transition-all {tab === 'ui' class="btn btn-sm transition-all {tab === 'ui'
? 'preset-filled-primary' ? 'preset-filled-primary'
: 'preset-tonal-surface'}" : 'preset-tonal-surface'}"
onclick={() => (tab = 'ui')} onclick={() => (tab = 'ui')}>
>
<Palette size="1.1em" class="mr-1" /> UI/Visuals <Palette size="1.1em" class="mr-1" /> UI/Visuals
</button> </button>
<button <button
@@ -202,23 +229,21 @@
class="btn btn-sm transition-all {tab === 'json' class="btn btn-sm transition-all {tab === 'json'
? 'preset-filled-primary' ? 'preset-filled-primary'
: 'preset-tonal-surface'}" : 'preset-tonal-surface'}"
onclick={() => (tab = 'json')} onclick={() => (tab = 'json')}>
>
<CodeXml size="1.1em" class="mr-1" /> JSON <CodeXml size="1.1em" class="mr-1" /> JSON
</button> </button>
</div> </div>
{#if tab === 'actions'} {#if tab === 'actions'}
<div class="space-y-6 animate-in fade-in duration-300"> <div class="animate-in fade-in space-y-6 duration-300">
<section class="grid grid-cols-1 md:grid-cols-2 gap-4"> <section class="grid grid-cols-1 gap-4 md:grid-cols-2">
<button <button
type="button" type="button"
class="btn preset-tonal-secondary w-full py-4 text-lg font-bold" class="btn preset-tonal-secondary w-full py-4 text-lg font-bold"
onclick={() => { onclick={() => {
show = false; show = false;
on_new_entry?.(); on_new_entry?.();
}} }}>
>
<FilePlus size="1.5em" class="mr-2" /> New Journal Entry <FilePlus size="1.5em" class="mr-2" /> New Journal Entry
</button> </button>
<button <button
@@ -227,8 +252,7 @@
onclick={() => { onclick={() => {
show = false; show = false;
on_show_export?.(); on_show_export?.();
}} }}>
>
<FileDown size="1.5em" class="mr-2" /> Export Entries <FileDown size="1.5em" class="mr-2" /> Export Entries
</button> </button>
<button <button
@@ -237,31 +261,27 @@
onclick={() => { onclick={() => {
show = false; show = false;
on_show_import?.(); on_show_import?.();
}} }}>
>
<FileUp size="1.5em" class="mr-2" /> Import Entries <FileUp size="1.5em" class="mr-2" /> Import Entries
</button> </button>
</section> </section>
</div> </div>
{:else if tab === 'general'} {:else if tab === 'general'}
<div class="space-y-6 animate-in fade-in duration-300"> <div class="animate-in fade-in space-y-6 duration-300">
<!-- Core Meta --> <!-- Core Meta -->
<section class="grid grid-cols-1 gap-4 p-2"> <section class="grid grid-cols-1 gap-4 p-2">
<label class="label"> <label class="label">
<span class="text-sm font-bold opacity-70" <span class="text-sm font-bold opacity-70"
>Journal Name</span >Journal Name</span>
>
<input <input
type="text" type="text"
bind:value={tmp__journal_obj.name} bind:value={tmp__journal_obj.name}
class="input" class="input"
placeholder="e.g. Personal Log" placeholder="e.g. Personal Log" />
/>
</label> </label>
<label class="label"> <label class="label">
<span class="text-sm font-bold opacity-70" <span class="text-sm font-bold opacity-70"
>Description (Markdown)</span >Description (Markdown)</span>
>
<textarea <textarea
bind:value={tmp__journal_obj.description} bind:value={tmp__journal_obj.description}
class="textarea h-32" class="textarea h-32"
@@ -271,29 +291,24 @@
<div class="grid grid-cols-2 gap-4"> <div class="grid grid-cols-2 gap-4">
<label class="label"> <label class="label">
<span class="text-sm font-bold opacity-70" <span class="text-sm font-bold opacity-70"
>Type</span >Type</span>
>
<select <select
bind:value={tmp__journal_obj.type_code} bind:value={tmp__journal_obj.type_code}
class="select" class="select">
>
{#each $journals_loc.journal.type_code_li as type (type.code)} {#each $journals_loc.journal.type_code_li as type (type.code)}
<option value={type.code} <option value={type.code}
>{type.name}</option >{type.name}</option>
>
{/each} {/each}
</select> </select>
</label> </label>
<label class="label"> <label class="label">
<span class="text-sm font-bold opacity-70" <span class="text-sm font-bold opacity-70"
>Journal Group (text)</span >Journal Group (text)</span>
>
<input <input
type="text" type="text"
bind:value={tmp__journal_obj.group} bind:value={tmp__journal_obj.group}
class="input" class="input"
placeholder="Standard" placeholder="Standard" />
/>
</label> </label>
</div> </div>
</section> </section>
@@ -301,28 +316,24 @@
<!-- Categories --> <!-- Categories -->
<section class="space-y-4 p-2"> <section class="space-y-4 p-2">
<h2 <h2
class="text-lg font-bold flex items-center gap-2 border-b border-surface-500/30 pb-2" class="border-surface-500/30 flex items-center gap-2 border-b pb-2 text-lg font-bold">
>
<MonitorPlay size="1em" class="text-primary-500" /> <MonitorPlay size="1em" class="text-primary-500" />
Journal Categories Journal Categories
</h2> </h2>
<div <div
class="space-y-2 bg-surface-500/5 p-4 rounded-lg border border-surface-500/10" class="bg-surface-500/5 border-surface-500/10 space-y-2 rounded-lg border p-4">
>
{#each tmp__journal_obj?.cfg_json?.category_li ?? [] as category, i (category.code ?? i)} {#each tmp__journal_obj?.cfg_json?.category_li ?? [] as category, i (category.code ?? i)}
<div class="flex gap-2 items-center"> <div class="flex items-center gap-2">
<input <input
type="text" type="text"
bind:value={category.code} bind:value={category.code}
class="input input-sm w-32" class="input input-sm w-32"
placeholder="Code" placeholder="Code" />
/>
<input <input
type="text" type="text"
bind:value={category.name} bind:value={category.name}
class="input input-sm grow" class="input input-sm grow"
placeholder="Display Name" placeholder="Display Name" />
/>
<button <button
type="button" type="button"
class="btn-icon btn-icon-sm preset-tonal-error" class="btn-icon btn-icon-sm preset-tonal-error"
@@ -331,15 +342,14 @@
i, i,
1 1
); );
}} }}>
>
<Minus size="1em" /> <Minus size="1em" />
</button> </button>
</div> </div>
{/each} {/each}
<button <button
type="button" type="button"
class="btn btn-sm preset-tonal-primary w-full mt-2" class="btn btn-sm preset-tonal-primary mt-2 w-full"
onclick={() => { onclick={() => {
if (!tmp__journal_obj.cfg_json.category_li) if (!tmp__journal_obj.cfg_json.category_li)
tmp__journal_obj.cfg_json.category_li = []; tmp__journal_obj.cfg_json.category_li = [];
@@ -347,81 +357,68 @@
code: '', code: '',
name: '' name: ''
}); });
}} }}>
>
<Plus size="1em" class="mr-1" /> Add Category <Plus size="1em" class="mr-1" /> Add Category
</button> </button>
</div> </div>
</section> </section>
</div> </div>
{:else if tab === 'security'} {:else if tab === 'security'}
<div class="space-y-6 animate-in fade-in duration-300"> <div class="animate-in fade-in space-y-6 duration-300">
<!-- Status & Lifecycle --> <!-- Status & Lifecycle -->
<section class="space-y-4 p-2"> <section class="space-y-4 p-2">
<h2 <h2
class="text-lg font-bold flex items-center gap-2 border-b border-surface-500/30 pb-2" class="border-surface-500/30 flex items-center gap-2 border-b pb-2 text-lg font-bold">
>
<Fingerprint size="1.2em" class="text-primary-500" /> <Fingerprint size="1.2em" class="text-primary-500" />
Status & Lifecycle Status & Lifecycle
</h2> </h2>
<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 <label
class="flex items-center space-x-3 cursor-pointer p-3 rounded-lg bg-surface-500/5 border border-surface-500/10 transition-colors hover:bg-surface-500/10" class="bg-surface-500/5 border-surface-500/10 hover:bg-surface-500/10 flex cursor-pointer items-center space-x-3 rounded-lg border p-3 transition-colors">
>
<input <input
type="checkbox" type="checkbox"
bind:checked={tmp__journal_obj.enable} bind:checked={tmp__journal_obj.enable}
onchange={() => handle_update_journal(false)} onchange={() => handle_update_journal(false)}
class="checkbox checkbox-primary" class="checkbox checkbox-primary" />
/>
<div class="flex flex-col"> <div class="flex flex-col">
<span class="font-bold">Enabled</span> <span class="font-bold">Enabled</span>
<span class="text-xs opacity-60" <span class="text-xs opacity-60"
>Allow access to this journal</span >Allow access to this journal</span>
>
</div> </div>
</label> </label>
<label <label
class="flex items-center space-x-3 cursor-pointer p-3 rounded-lg bg-surface-500/5 border border-surface-500/10 transition-colors hover:bg-surface-500/10" class="bg-surface-500/5 border-surface-500/10 hover:bg-surface-500/10 flex cursor-pointer items-center space-x-3 rounded-lg border p-3 transition-colors">
>
<input <input
type="checkbox" type="checkbox"
bind:checked={tmp__journal_obj.hide} bind:checked={tmp__journal_obj.hide}
onchange={() => handle_update_journal(false)} onchange={() => handle_update_journal(false)}
class="checkbox checkbox-primary" class="checkbox checkbox-primary" />
/>
<div class="flex flex-col"> <div class="flex flex-col">
<span class="font-bold">Hidden</span> <span class="font-bold">Hidden</span>
<span class="text-xs opacity-60" <span class="text-xs opacity-60"
>Hide from standard lists</span >Hide from standard lists</span>
>
</div> </div>
</label> </label>
<label <label
class="flex items-center space-x-3 cursor-pointer p-3 rounded-lg bg-surface-500/5 border border-surface-500/10 transition-colors hover:bg-surface-500/10" class="bg-surface-500/5 border-surface-500/10 hover:bg-surface-500/10 flex cursor-pointer items-center space-x-3 rounded-lg border p-3 transition-colors">
>
<input <input
type="checkbox" type="checkbox"
bind:checked={tmp__journal_obj.priority} bind:checked={tmp__journal_obj.priority}
onchange={() => handle_update_journal(false)} onchange={() => handle_update_journal(false)}
class="checkbox checkbox-primary" class="checkbox checkbox-primary" />
/>
<div class="flex flex-col"> <div class="flex flex-col">
<span class="font-bold">Priority Journal</span> <span class="font-bold">Priority Journal</span>
<span class="text-xs opacity-60" <span class="text-xs opacity-60"
>Star or pin to top</span >Star or pin to top</span>
>
</div> </div>
</label> </label>
<div <div
class="flex items-center justify-between p-3 rounded-lg bg-surface-500/5 border border-surface-500/10" class="bg-surface-500/5 border-surface-500/10 flex items-center justify-between rounded-lg border p-3">
>
<div class="flex flex-col"> <div class="flex flex-col">
<span class="font-bold text-sm">Sort Order</span <span class="text-sm font-bold"
> >Sort Order</span>
<span class="text-xs opacity-60" <span class="text-xs opacity-60"
>Manual list position</span >Manual list position</span>
>
</div> </div>
<div class="flex items-center gap-2"> <div class="flex items-center gap-2">
<button <button
@@ -431,12 +428,10 @@
tmp__journal_obj.sort = tmp__journal_obj.sort =
(tmp__journal_obj.sort ?? 0) - 1; (tmp__journal_obj.sort ?? 0) - 1;
handle_update_journal(false); handle_update_journal(false);
}}><Minus size="1em" /></button }}><Minus size="1em" /></button>
>
<span <span
class="font-mono font-bold w-8 text-center text-lg" class="w-8 text-center font-mono text-lg font-bold"
>{tmp__journal_obj.sort ?? 0}</span >{tmp__journal_obj.sort ?? 0}</span>
>
<button <button
type="button" type="button"
class="btn-icon btn-icon-sm preset-tonal-surface" class="btn-icon btn-icon-sm preset-tonal-surface"
@@ -444,8 +439,7 @@
tmp__journal_obj.sort = tmp__journal_obj.sort =
(tmp__journal_obj.sort ?? 0) + 1; (tmp__journal_obj.sort ?? 0) + 1;
handle_update_journal(false); handle_update_journal(false);
}}><Plus size="1em" /></button }}><Plus size="1em" /></button>
>
</div> </div>
</div> </div>
</div> </div>
@@ -454,34 +448,29 @@
<!-- Encryption Passcodes --> <!-- Encryption Passcodes -->
<section class="space-y-4 p-2"> <section class="space-y-4 p-2">
<h2 <h2
class="text-lg font-bold flex items-center gap-2 border-b border-surface-500/30 pb-2" class="border-surface-500/30 flex items-center gap-2 border-b pb-2 text-lg font-bold">
>
<LockKeyhole size="1.2em" class="text-primary-500" /> <LockKeyhole size="1.2em" class="text-primary-500" />
Encryption Passcodes Encryption Passcodes
</h2> </h2>
<div <div
class="bg-warning-500/10 border border-warning-500/30 p-4 rounded-lg space-y-4 shadow-inner" class="bg-warning-500/10 border-warning-500/30 space-y-4 rounded-lg border p-4 shadow-inner">
>
<label class="label"> <label class="label">
<span <span
class="text-xs font-bold uppercase tracking-wider opacity-70" class="text-xs font-bold tracking-wider uppercase opacity-70"
>Primary Passcode (Stored)</span >Primary Passcode (Stored)</span>
>
<div class="flex gap-2"> <div class="flex gap-2">
<input <input
type="password" type="password"
bind:value={tmp__journal_obj.passcode} bind:value={tmp__journal_obj.passcode}
class="input grow" class="input grow"
placeholder="Module-level passcode" placeholder="Module-level passcode" />
/>
<Fingerprint class="opacity-30" /> <Fingerprint class="opacity-30" />
</div> </div>
</label> </label>
<label class="label"> <label class="label">
<span <span
class="text-xs font-bold uppercase tracking-wider opacity-70" class="text-xs font-bold tracking-wider uppercase opacity-70"
>Private Passcode (Double Encryption)</span >Private Passcode (Double Encryption)</span>
>
<div class="flex gap-2"> <div class="flex gap-2">
<input <input
type="password" type="password"
@@ -489,14 +478,12 @@
tmp__journal_obj.private_passcode tmp__journal_obj.private_passcode
} }
class="input grow" class="input grow"
placeholder="User-level secret" placeholder="User-level secret" />
/>
<ShieldCheck class="opacity-30" /> <ShieldCheck class="opacity-30" />
</div> </div>
</label> </label>
<div <div
class="text-[10px] text-warning-700 dark:text-warning-300 italic" class="text-warning-700 dark:text-warning-300 text-[10px] italic">
>
* Note: Passcodes are used locally for E2EE. Primary * Note: Passcodes are used locally for E2EE. Primary
is often stored in the DB, while Private should is often stored in the DB, while Private should
remain known only to you. remain known only to you.
@@ -508,71 +495,58 @@
<button <button
type="button" type="button"
class="btn btn-sm preset-tonal-error hover:preset-filled-error-500 w-full shadow-lg" class="btn btn-sm preset-tonal-error hover:preset-filled-error-500 w-full shadow-lg"
onclick={delete_journal} onclick={delete_journal}>
>
<Trash2 size="1.1em" class="mr-2" /> Delete Entire Journal <Trash2 size="1.1em" class="mr-2" /> Delete Entire Journal
</button> </button>
</section> </section>
</div> </div>
{:else if tab === 'ui'} {:else if tab === 'ui'}
<div class="space-y-8 animate-in fade-in duration-300"> <div class="animate-in fade-in space-y-8 duration-300">
<section class="space-y-4 p-2"> <section class="space-y-4 p-2">
<h2 <h2
class="text-lg font-bold flex items-center gap-2 border-b border-surface-500/30 pb-2" class="border-surface-500/30 flex items-center gap-2 border-b pb-2 text-lg font-bold">
>
<MonitorPlay size="1.2em" class="text-primary-500" /> <MonitorPlay size="1.2em" class="text-primary-500" />
Default View Modes Default View Modes
</h2> </h2>
<div <div
class="grid grid-cols-1 md:grid-cols-2 gap-6 bg-surface-500/5 p-4 rounded-lg border border-surface-500/10" class="bg-surface-500/5 border-surface-500/10 grid grid-cols-1 gap-6 rounded-lg border p-4 md:grid-cols-2">
>
<label class="label"> <label class="label">
<span class="text-sm font-bold opacity-70" <span class="text-sm font-bold opacity-70"
>Preferred Viewer</span >Preferred Viewer</span>
>
<select <select
bind:value={ bind:value={
tmp__journal_obj.cfg_json.pref_viewer tmp__journal_obj.cfg_json.pref_viewer
} }
class="select" class="select">
>
<option value="rendered" <option value="rendered"
>Rendered HTML (Default)</option >Rendered HTML (Default)</option>
>
<option value="plain">Plain Text</option> <option value="plain">Plain Text</option>
<option value="codemirror" <option value="codemirror"
>CodeMirror (Syntax)</option >CodeMirror (Syntax)</option>
>
</select> </select>
</label> </label>
<label class="label"> <label class="label">
<span class="text-sm font-bold opacity-70" <span class="text-sm font-bold opacity-70"
>Preferred Editor</span >Preferred Editor</span>
>
<select <select
bind:value={ bind:value={
tmp__journal_obj.cfg_json.pref_editor tmp__journal_obj.cfg_json.pref_editor
} }
class="select" class="select">
>
<option value="textarea" <option value="textarea"
>Standard Textarea</option >Standard Textarea</option>
>
<option value="codemirror" <option value="codemirror"
>CodeMirror (Advanced)</option >CodeMirror (Advanced)</option>
>
</select> </select>
</label> </label>
<label class="label"> <label class="label">
<span class="text-sm font-bold opacity-70" <span class="text-sm font-bold opacity-70"
>Color Scheme</span >Color Scheme</span>
>
<select <select
bind:value={ bind:value={
tmp__journal_obj.cfg_json.color_scheme tmp__journal_obj.cfg_json.color_scheme
} }
class="select" class="select">
>
<option value="">Default (Slate)</option> <option value="">Default (Slate)</option>
<option value="blue">Deep Blue</option> <option value="blue">Deep Blue</option>
<option value="green">Nature Green</option> <option value="green">Nature Green</option>
@@ -583,19 +557,16 @@
</label> </label>
<label class="label"> <label class="label">
<span class="text-sm font-bold opacity-70" <span class="text-sm font-bold opacity-70"
>Quick Add Placement</span >Quick Add Placement</span>
>
<select <select
bind:value={ bind:value={
tmp__journal_obj.cfg_json.entry_add_text tmp__journal_obj.cfg_json.entry_add_text
} }
class="select" class="select">
>
<option value="append" <option value="append"
>Append to End (Default)</option >Append to End (Default)</option>
> <option value="prepend"
<option value="prepend">Prepend to Start</option >Prepend to Start</option>
>
</select> </select>
</label> </label>
</div> </div>
@@ -604,51 +575,42 @@
<!-- Visibility Toggles (Restored) --> <!-- Visibility Toggles (Restored) -->
<section class="space-y-4 p-2"> <section class="space-y-4 p-2">
<h2 <h2
class="text-lg font-bold flex items-center gap-2 border-b border-surface-500/30 pb-2" class="border-surface-500/30 flex items-center gap-2 border-b pb-2 text-lg font-bold">
>
<Eye size="1.2em" class="text-primary-500" /> <Eye size="1.2em" class="text-primary-500" />
Entry Visibility (Global) Entry Visibility (Global)
</h2> </h2>
<div <div
class="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-2" class="grid grid-cols-1 gap-2 sm:grid-cols-2 lg:grid-cols-3">
>
<label <label
class="flex items-center space-x-2 cursor-pointer p-2 bg-surface-500/5 rounded border border-surface-500/10" class="bg-surface-500/5 border-surface-500/10 flex cursor-pointer items-center space-x-2 rounded border p-2">
>
<input <input
type="checkbox" type="checkbox"
bind:checked={ bind:checked={
tmp__journal_obj.cfg_json.hide_private tmp__journal_obj.cfg_json.hide_private
} }
class="checkbox checkbox-sm" class="checkbox checkbox-sm" />
/>
<span class="text-xs font-bold">Hide Private</span> <span class="text-xs font-bold">Hide Private</span>
</label> </label>
<label <label
class="flex items-center space-x-2 cursor-pointer p-2 bg-surface-500/5 rounded border border-surface-500/10" class="bg-surface-500/5 border-surface-500/10 flex cursor-pointer items-center space-x-2 rounded border p-2">
>
<input <input
type="checkbox" type="checkbox"
bind:checked={ bind:checked={
tmp__journal_obj.cfg_json.hide_personal tmp__journal_obj.cfg_json.hide_personal
} }
class="checkbox checkbox-sm" class="checkbox checkbox-sm" />
/>
<span class="text-xs font-bold">Hide Personal</span> <span class="text-xs font-bold">Hide Personal</span>
</label> </label>
<label <label
class="flex items-center space-x-2 cursor-pointer p-2 bg-surface-500/5 rounded border border-surface-500/10" class="bg-surface-500/5 border-surface-500/10 flex cursor-pointer items-center space-x-2 rounded border p-2">
>
<input <input
type="checkbox" type="checkbox"
bind:checked={ bind:checked={
tmp__journal_obj.cfg_json.hide_professional tmp__journal_obj.cfg_json.hide_professional
} }
class="checkbox checkbox-sm" class="checkbox checkbox-sm" />
/>
<span class="text-xs font-bold" <span class="text-xs font-bold"
>Hide Professional</span >Hide Professional</span>
>
</label> </label>
</div> </div>
</section> </section>
@@ -656,193 +618,157 @@
<!-- Button Toggles (Restored) --> <!-- Button Toggles (Restored) -->
<section class="space-y-4 p-2"> <section class="space-y-4 p-2">
<h2 <h2
class="text-lg font-bold flex items-center gap-2 border-b border-surface-500/30 pb-2" class="border-surface-500/30 flex items-center gap-2 border-b pb-2 text-lg font-bold">
>
<Settings size="1.2em" class="text-primary-500" /> <Settings size="1.2em" class="text-primary-500" />
Entry Feature Buttons Entry Feature Buttons
</h2> </h2>
<div <div
class="grid grid-cols-2 sm:grid-cols-3 lg:grid-cols-4 gap-2" class="grid grid-cols-2 gap-2 sm:grid-cols-3 lg:grid-cols-4">
>
<label <label
class="flex items-center gap-2 p-2 bg-surface-500/5 rounded border border-surface-500/10" class="bg-surface-500/5 border-surface-500/10 flex items-center gap-2 rounded border p-2">
>
<input <input
type="checkbox" type="checkbox"
bind:checked={ bind:checked={
tmp__journal_obj.cfg_json.hide_btn_alert tmp__journal_obj.cfg_json.hide_btn_alert
} }
class="checkbox checkbox-sm" class="checkbox checkbox-sm" />
/> <span class="text-[10px] font-bold uppercase"
<span class="text-[10px] uppercase font-bold" >Hide Alert</span>
>Hide Alert</span
>
</label> </label>
<label <label
class="flex items-center gap-2 p-2 bg-surface-500/5 rounded border border-surface-500/10" class="bg-surface-500/5 border-surface-500/10 flex items-center gap-2 rounded border p-2">
>
<input <input
type="checkbox" type="checkbox"
bind:checked={ bind:checked={
tmp__journal_obj.cfg_json.hide_btn_private tmp__journal_obj.cfg_json.hide_btn_private
} }
class="checkbox checkbox-sm" class="checkbox checkbox-sm" />
/> <span class="text-[10px] font-bold uppercase"
<span class="text-[10px] uppercase font-bold" >Hide Private</span>
>Hide Private</span
>
</label> </label>
<label <label
class="flex items-center gap-2 p-2 bg-surface-500/5 rounded border border-surface-500/10" class="bg-surface-500/5 border-surface-500/10 flex items-center gap-2 rounded border p-2">
>
<input <input
type="checkbox" type="checkbox"
bind:checked={ bind:checked={
tmp__journal_obj.cfg_json.hide_btn_public tmp__journal_obj.cfg_json.hide_btn_public
} }
class="checkbox checkbox-sm" class="checkbox checkbox-sm" />
/> <span class="text-[10px] font-bold uppercase"
<span class="text-[10px] uppercase font-bold" >Hide Public</span>
>Hide Public</span
>
</label> </label>
<label <label
class="flex items-center gap-2 p-2 bg-surface-500/5 rounded border border-surface-500/10" class="bg-surface-500/5 border-surface-500/10 flex items-center gap-2 rounded border p-2">
>
<input <input
type="checkbox" type="checkbox"
bind:checked={ bind:checked={
tmp__journal_obj.cfg_json.hide_btn_personal tmp__journal_obj.cfg_json.hide_btn_personal
} }
class="checkbox checkbox-sm" class="checkbox checkbox-sm" />
/> <span class="text-[10px] font-bold uppercase"
<span class="text-[10px] uppercase font-bold" >Hide Pers.</span>
>Hide Pers.</span
>
</label> </label>
<label <label
class="flex items-center gap-2 p-2 bg-surface-500/5 rounded border border-surface-500/10" class="bg-surface-500/5 border-surface-500/10 flex items-center gap-2 rounded border p-2">
>
<input <input
type="checkbox" type="checkbox"
bind:checked={ bind:checked={
tmp__journal_obj.cfg_json tmp__journal_obj.cfg_json
.hide_btn_professional .hide_btn_professional
} }
class="checkbox checkbox-sm" class="checkbox checkbox-sm" />
/> <span class="text-[10px] font-bold uppercase"
<span class="text-[10px] uppercase font-bold" >Hide Prof.</span>
>Hide Prof.</span
>
</label> </label>
<label <label
class="flex items-center gap-2 p-2 bg-surface-500/5 rounded border border-surface-500/10" class="bg-surface-500/5 border-surface-500/10 flex items-center gap-2 rounded border p-2">
>
<input <input
type="checkbox" type="checkbox"
bind:checked={ bind:checked={
tmp__journal_obj.cfg_json.hide_btn_template tmp__journal_obj.cfg_json.hide_btn_template
} }
class="checkbox checkbox-sm" class="checkbox checkbox-sm" />
/> <span class="text-[10px] font-bold uppercase"
<span class="text-[10px] uppercase font-bold" >Hide Templ.</span>
>Hide Templ.</span
>
</label> </label>
<label <label
class="flex items-center gap-2 p-2 bg-surface-500/5 rounded border border-surface-500/10" class="bg-surface-500/5 border-surface-500/10 flex items-center gap-2 rounded border p-2">
>
<input <input
type="checkbox" type="checkbox"
bind:checked={ bind:checked={
tmp__journal_obj.cfg_json.hide_copy_plain_md tmp__journal_obj.cfg_json.hide_copy_plain_md
} }
class="checkbox checkbox-sm" class="checkbox checkbox-sm" />
/> <span class="text-[10px] font-bold uppercase"
<span class="text-[10px] uppercase font-bold" >Hide Copy MD</span>
>Hide Copy MD</span
>
</label> </label>
<label <label
class="flex items-center gap-2 p-2 bg-surface-500/5 rounded border border-surface-500/10" class="bg-surface-500/5 border-surface-500/10 flex items-center gap-2 rounded border p-2">
>
<input <input
type="checkbox" type="checkbox"
bind:checked={ bind:checked={
tmp__journal_obj.cfg_json.hide_clone tmp__journal_obj.cfg_json.hide_clone
} }
class="checkbox checkbox-sm" class="checkbox checkbox-sm" />
/> <span class="text-[10px] font-bold uppercase"
<span class="text-[10px] uppercase font-bold" >Hide Clone</span>
>Hide Clone</span
>
</label> </label>
</div> </div>
</section> </section>
<section class="space-y-4 p-2"> <section class="space-y-4 p-2">
<h2 <h2
class="text-lg font-bold flex items-center gap-2 border-b border-surface-500/30 pb-2" class="border-surface-500/30 flex items-center gap-2 border-b pb-2 text-lg font-bold">
>
<Layout size="1.2em" class="text-primary-500" /> <Layout size="1.2em" class="text-primary-500" />
List Interactions List Interactions
</h2> </h2>
<div class="bg-surface-500/5 p-4 rounded-lg space-y-4"> <div class="bg-surface-500/5 space-y-4 rounded-lg p-4">
<label class="label"> <label class="label">
<span class="text-xs font-bold opacity-70" <span class="text-xs font-bold opacity-70"
>Expansion Trigger</span >Expansion Trigger</span>
>
<select <select
bind:value={ bind:value={
tmp__journal_obj.cfg_json.expand_li_content tmp__journal_obj.cfg_json.expand_li_content
} }
class="select" class="select">
>
<option value="click">Click to Expand</option> <option value="click">Click to Expand</option>
<option value="hover">Hover to Expand</option> <option value="hover">Hover to Expand</option>
</select> </select>
</label> </label>
<div class="grid grid-cols-1 sm:grid-cols-2 gap-4"> <div class="grid grid-cols-1 gap-4 sm:grid-cols-2">
<label class="label"> <label class="label">
<span class="text-xs font-bold opacity-70" <span class="text-xs font-bold opacity-70"
>List Max Height</span >List Max Height</span>
>
<select <select
bind:value={ bind:value={
tmp__journal_obj.cfg_json tmp__journal_obj.cfg_json
.entry_li_max_height .entry_li_max_height
} }
class="select select-sm" class="select select-sm">
>
<option value="">Default</option> <option value="">Default</option>
<option value="max-h-16">Small (16)</option> <option value="max-h-16">Small (16)</option>
<option value="max-h-32">Medium (32)</option <option value="max-h-32"
> >Medium (32)</option>
<option value="max-h-64">Large (64)</option> <option value="max-h-64">Large (64)</option>
<option value="max-h-full">Full</option> <option value="max-h-full">Full</option>
</select> </select>
</label> </label>
<label class="label"> <label class="label">
<span class="text-xs font-bold opacity-70" <span class="text-xs font-bold opacity-70"
>Active Max Height</span >Active Max Height</span>
>
<select <select
bind:value={ bind:value={
tmp__journal_obj.cfg_json tmp__journal_obj.cfg_json
.entry_li_click_max_height .entry_li_click_max_height
} }
class="select select-sm" class="select select-sm">
>
<option value="">Default</option> <option value="">Default</option>
<option value="active:max-h-64" <option value="active:max-h-64"
>Large (64)</option >Large (64)</option>
>
<option value="active:max-h-96" <option value="active:max-h-96"
>X-Large (96)</option >X-Large (96)</option>
>
<option value="active:max-h-full" <option value="active:max-h-full"
>Full</option >Full</option>
>
</select> </select>
</label> </label>
</div> </div>
@@ -855,8 +781,7 @@
readonly={true} readonly={true}
content={JSON.stringify(tmp__journal_obj, null, 2)} content={JSON.stringify(tmp__journal_obj, null, 2)}
theme_mode={$ae_loc.theme_mode} theme_mode={$ae_loc.theme_mode}
class_li="rounded-lg border border-surface-500/30" class_li="rounded-lg border border-surface-500/30" />
/>
</div> </div>
{/if} {/if}
</div> </div>
@@ -865,16 +790,14 @@
<div class="flex gap-4"> <div class="flex gap-4">
<button <button
type="button" type="button"
class="btn preset-tonal-surface font-bold min-w-[100px]" class="btn preset-tonal-surface min-w-[100px] font-bold"
onclick={() => (show = false)} onclick={() => (show = false)}>
>
<X size="1.2em" class="mr-2" /> Cancel <X size="1.2em" class="mr-2" /> Cancel
</button> </button>
<button <button
type="button" type="button"
class="btn preset-filled-primary font-bold min-w-[120px]" class="btn preset-filled-primary min-w-[120px] font-bold"
onclick={() => handle_update_journal(true)} onclick={() => handle_update_journal(true)}>
>
<Check size="1.2em" class="mr-2" /> Save Changes <Check size="1.2em" class="mr-2" /> Save Changes
</button> </button>
</div> </div>

View File

@@ -3,7 +3,17 @@
import { goto } from '$app/navigation'; import { goto } from '$app/navigation';
// *** Import other supporting libraries // *** Import other supporting libraries
import { BookOpenText, BookPlus, FileDown, FilePlus, FileUp, LoaderCircle, Menu, Pencil, Settings } from '@lucide/svelte'; import {
BookOpenText,
BookPlus,
FileDown,
FilePlus,
FileUp,
LoaderCircle,
Menu,
Pencil,
Settings
} from '@lucide/svelte';
// *** Import Aether specific variables and functions // *** Import Aether specific variables and functions
import { ae_util } from '$lib/ae_utils/ae_utils'; import { ae_util } from '$lib/ae_utils/ae_utils';
import { import {
@@ -146,8 +156,7 @@
}); });
if (results?.journal_entry_id) { if (results?.journal_entry_id) {
$journals_slct.journal_entry_id = $journals_slct.journal_entry_id = results.journal_entry_id;
results.journal_entry_id;
$journals_loc.entry.edit_kv[$journals_slct.journal_entry_id] = $journals_loc.entry.edit_kv[$journals_slct.journal_entry_id] =
'current'; 'current';
goto( goto(
@@ -161,60 +170,53 @@
} }
</script> </script>
<div class="relative group/view w-full mx-2 my-1"> <div class="group/view relative mx-2 my-1 w-full">
<!-- Glow ring — mirrors Quick Add and Journal List --> <!-- Glow ring — mirrors Quick Add and Journal List -->
<div <div
class="absolute -inset-1 bg-linear-to-r from-primary-500 to-secondary-500 class="from-primary-500 to-secondary-500 pointer-events-none absolute -inset-1
rounded-2xl blur opacity-10 dark:opacity-20 rounded-2xl bg-linear-to-r opacity-10 blur
group-hover/view:opacity-25 dark:group-hover/view:opacity-35 transition duration-700
transition duration-700 pointer-events-none" group-hover/view:opacity-25 dark:opacity-20 dark:group-hover/view:opacity-35">
></div> </div>
<section <section
class="relative rounded-xl p-3 w-full class="relative flex w-full flex-col
flex flex-col gap-2 items-center justify-center items-center justify-center gap-2 rounded-xl border
bg-white dark:bg-gray-900 border-gray-200 bg-white
border border-gray-200 dark:border-gray-700 p-3 text-gray-900 shadow-xl
text-gray-900 dark:text-gray-100 dark:border-gray-700 dark:bg-gray-900
shadow-xl" dark:text-gray-100"
bind:clientHeight={$ae_loc.iframe_height_modal_body} bind:clientHeight={$ae_loc.iframe_height_modal_body}>
>
<header <header
class="ae_header journal__header flex flex-row flex-wrap gap-2 items-center justify-between w-full" 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"> <h2 class="journal__name h3 text-center">
<BookOpenText class="inline-block text-primary-500/80" /> <BookOpenText class="text-primary-500/80 inline-block" />
{@html $lq__journal_obj?.name ?? 'Loading...'} {@html $lq__journal_obj?.name ?? 'Loading...'}
{#if $ae_loc.edit_mode} {#if $ae_loc.edit_mode}
<span <span
class="badge preset-tonal-success font-bold text-lg px-2 ml-2" class="badge preset-tonal-success ml-2 px-2 text-lg font-bold"
title="Entries matching current filters" title="Entries matching current filters">
>
{$lq__journal_entry_obj_li?.length ?? '0'}<span {$lq__journal_entry_obj_li?.length ?? '0'}<span
class="text-xs opacity-50 ml-0.5">&times;</span class="ml-0.5 text-xs opacity-50">&times;</span>
>
</span> </span>
{/if} {/if}
{#await $journals_prom.load__journal_entry_obj_li} {#await $journals_prom.load__journal_entry_obj_li}
<LoaderCircle <LoaderCircle
size="1em" size="1em"
class="inline-block animate-spin ml-1 text-primary-500" class="text-primary-500 ml-1 inline-block animate-spin" />
/>
{/await} {/await}
</h2> </h2>
<div <div
class="grow flex flex-row flex-wrap gap-2 items-center justify-end" class="flex grow flex-row flex-wrap items-center justify-end gap-2">
>
<!-- Simplified Config Trigger --> <!-- Simplified Config Trigger -->
<button <button
type="button" type="button"
onclick={() => onclick={() =>
($journals_sess.show__modal_edit__journal_obj = true)} ($journals_sess.show__modal_edit__journal_obj = true)}
class="btn preset-tonal-secondary py-1 px-3 shadow-md" class="btn preset-tonal-secondary px-3 py-1 shadow-md"
title="Journal Config & Actions" title="Journal Config & Actions">
>
<Settings size="1.2em" class="mr-2" /> <Settings size="1.2em" class="mr-2" />
<span class="hidden md:inline">Config</span> <span class="hidden md:inline">Config</span>
</button> </button>
@@ -227,8 +229,7 @@
type="text" type="text"
bind:value={typed_journal_passcode} bind:value={typed_journal_passcode}
placeholder="Passcode" placeholder="Passcode"
class="input input-sm w-32" class="input input-sm w-32" />
/>
</div> </div>
{/if} {/if}
</div> </div>
@@ -239,24 +240,23 @@
<div <div
class=" class="
prose prose
space-y-1 word-break
p-2 prose-p:m-0
w-full max-w-(--breakpoint-sm) md:max-w-(--breakpoint-md) prose-p:p-0 prose-h1:underline prose-h1:decoration-double
font-mono
bg-gray-50 text-gray-900
dark:bg-gray-800 dark:text-gray-100
shadow-md rounded-lg
text-sm font-normal text-wrap word-break
prose-p:m-0 prose-p:p-0
prose-h1:underline prose-h1:decoration-double
prose-h2:underline prose-h2:underline
prose-h1:text-2xl prose-h2:text-xl prose-h3:text-lg prose-h1:text-2xl prose-h2:text-xl
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-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:m-0 prose-li:p-0 prose-li:line-height-none 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} {@html $lq__journal_obj.description_md_html}
</div> </div>
{/if} {/if}
@@ -270,5 +270,4 @@
bind:show={$journals_sess.show__modal_edit__journal_obj} bind:show={$journals_sess.show__modal_edit__journal_obj}
on_new_entry={handle_new_entry} on_new_entry={handle_new_entry}
{on_show_export} {on_show_export}
{on_show_import} {on_show_import} />
/>

View File

@@ -22,83 +22,106 @@
Uses plain Tailwind gray scale with explicit dark: variants — no Skeleton paired utilities — Uses plain Tailwind gray scale with explicit dark: variants — no Skeleton paired utilities —
so behavior is predictable regardless of which Skeleton theme is active. so behavior is predictable regardless of which Skeleton theme is active.
--> -->
<div class="w-full max-w-2xl relative group/list"> <div class="group/list relative w-full max-w-2xl">
<!-- Glow ring behind the list, same technique as Quick Add --> <!-- Glow ring behind the list, same technique as Quick Add -->
<div <div
class="absolute -inset-1 bg-linear-to-r from-primary-500 to-secondary-500 class="from-primary-500 to-secondary-500 pointer-events-none absolute -inset-1
rounded-2xl blur opacity-10 dark:opacity-20 rounded-2xl bg-linear-to-r opacity-10 blur
group-hover/list:opacity-25 dark:group-hover/list:opacity-35 transition duration-700
transition duration-700 pointer-events-none" group-hover/list:opacity-25 dark:opacity-20 dark:group-hover/list:opacity-35">
></div> </div>
<section class="journal_list relative w-full space-y-1.5 p-2 sm:p-3 <section
bg-white dark:bg-gray-900 class="journal_list relative w-full space-y-1.5 rounded-2xl border
border border-gray-200 dark:border-gray-700 border-gray-200 bg-white
rounded-2xl shadow-xl"> p-2 shadow-xl sm:p-3
dark:border-gray-700 dark:bg-gray-900">
{#if $lq__journal_obj_li && $lq__journal_obj_li.length} {#if $lq__journal_obj_li && $lq__journal_obj_li.length}
{#each $lq__journal_obj_li as journal (journal.journal_id)} {#each $lq__journal_obj_li as journal (journal.journal_id)}
<a <a
href="/journals/{journal?.journal_id}" href="/journals/{journal?.journal_id}"
class="journal_card group relative class="journal_card group border-l-primary-500/60
flex items-center gap-3 px-4 py-3 hover:border-l-primary-500 relative flex items-center gap-3
bg-gray-50 dark:bg-gray-800 rounded-xl border
border border-gray-200 dark:border-gray-700 border-l-4 border-gray-200 bg-gray-50
border-l-4 border-l-primary-500/60 px-4 py-3
rounded-xl shadow-sm shadow-sm transition-all
hover:shadow-md hover:border-l-primary-500 duration-150 ease-in-out
hover:bg-gray-100 dark:hover:bg-gray-700 hover:bg-gray-100 hover:shadow-md
active:scale-[0.99] active:scale-[0.99]
transition-all duration-150 ease-in-out" dark:border-gray-700 dark:bg-gray-800 dark:hover:bg-gray-700"
class:hidden={(journal?.hide || !journal?.enable) && !$ae_loc.trusted_access} class:hidden={(journal?.hide || !journal?.enable) &&
!$ae_loc.trusted_access}
class:opacity-60={journal.hide} class:opacity-60={journal.hide}
class:!border-l-warning-500={!journal?.enable} class:!border-l-warning-500={!journal?.enable}>
>
<!-- Icon: fixed size, never squashed --> <!-- Icon: fixed size, never squashed -->
<div class="shrink-0 p-2 bg-primary-500/10 rounded-lg text-primary-500 <div
group-hover:bg-primary-500 group-hover:text-white transition-colors"> class="bg-primary-500/10 text-primary-500 group-hover:bg-primary-500 shrink-0 rounded-lg
p-2 transition-colors group-hover:text-white">
<BookType size="1.3em" /> <BookType size="1.3em" />
</div> </div>
<!-- Name + badge: min-w-0 flex-1 lets text shrink and wrap --> <!-- Name + badge: min-w-0 flex-1 lets text shrink and wrap -->
<div class="min-w-0 flex-1"> <div class="min-w-0 flex-1">
<div class="text-sm sm:text-base font-bold <div
text-gray-900 dark:text-gray-100 class="text-sm leading-snug font-bold
leading-snug break-words"> break-words text-gray-900
sm:text-base dark:text-gray-100">
{journal.name} {journal.name}
</div> </div>
<div class="flex flex-wrap items-center gap-2 mt-0.5"> <div class="mt-0.5 flex flex-wrap items-center gap-2">
{#if journal.type_code} {#if journal.type_code}
<span class="badge preset-tonal-warning text-[10px] uppercase tracking-wider font-bold"> <span
class="badge preset-tonal-warning text-[10px] font-bold tracking-wider uppercase">
{journal.type_code} {journal.type_code}
</span> </span>
{/if} {/if}
<!-- Description snippet: edit mode only --> <!-- Description snippet: edit mode only -->
{#if journal.description && $ae_loc.edit_mode} {#if journal.description && $ae_loc.edit_mode}
<span class="text-[11px] text-gray-500 dark:text-gray-400 font-mono truncate max-w-[16rem]"> <span
{ae_util.strip_html ? ae_util.strip_html(journal.description).slice(0, 60) : journal.description.slice(0, 60)}&hellip; class="max-w-[16rem] truncate font-mono text-[11px] text-gray-500 dark:text-gray-400">
{ae_util.strip_html
? ae_util
.strip_html(journal.description)
.slice(0, 60)
: journal.description.slice(
0,
60
)}&hellip;
</span> </span>
{/if} {/if}
</div> </div>
</div> </div>
<!-- Stats: right-aligned, compact --> <!-- Stats: right-aligned, compact -->
<div class="shrink-0 flex flex-col items-end gap-0.5 <div
class="flex shrink-0 flex-col items-end gap-0.5
text-xs text-gray-500 dark:text-gray-400"> text-xs text-gray-500 dark:text-gray-400">
<div class="flex items-center gap-1" title="Entry Count"> <div
class="flex items-center gap-1"
title="Entry Count">
<Hash size="0.85em" /> <Hash size="0.85em" />
<span class="font-bold tabular-nums">{journal.journal_entry_count ?? 0}&times;</span> <span class="font-bold tabular-nums"
>{journal.journal_entry_count ??
0}&times;</span>
</div> </div>
<div class="flex items-center gap-1" title="Last Updated"> <div
class="flex items-center gap-1"
title="Last Updated">
<Clock size="0.85em" /> <Clock size="0.85em" />
<span class="tabular-nums">{ae_util.iso_datetime_formatter( <span class="tabular-nums"
>{ae_util.iso_datetime_formatter(
journal.updated_on || journal.created_on, journal.updated_on || journal.created_on,
'date_short' 'date_short'
)}</span> )}</span>
</div> </div>
{#if $ae_loc.edit_mode && $ae_loc.administrator_access} {#if $ae_loc.edit_mode && $ae_loc.administrator_access}
<div class="flex items-center gap-1 opacity-40" title="Created"> <div
class="flex items-center gap-1 opacity-40"
title="Created">
<Calendar size="0.85em" /> <Calendar size="0.85em" />
<span class="font-mono text-[10px] tabular-nums">{ae_util.iso_datetime_formatter( <span class="font-mono text-[10px] tabular-nums"
>{ae_util.iso_datetime_formatter(
journal.created_on, journal.created_on,
'date_short' 'date_short'
)}</span> )}</span>
@@ -107,24 +130,30 @@
</div> </div>
<!-- Chevron hint: desktop only --> <!-- Chevron hint: desktop only -->
<BookOpenText size="0.95em" <BookOpenText
class="shrink-0 opacity-0 group-hover:opacity-30 transition-opacity hidden sm:block" /> size="0.95em"
class="hidden shrink-0 opacity-0 transition-opacity group-hover:opacity-30 sm:block" />
<!-- Status overlays: edit mode only --> <!-- Status overlays: edit mode only -->
{#if $ae_loc.edit_mode} {#if $ae_loc.edit_mode}
<div class="absolute -top-1.5 -right-1.5 flex gap-1"> <div class="absolute -top-1.5 -right-1.5 flex gap-1">
{#if journal.hide} {#if journal.hide}
<span class="badge-icon preset-tonal-surface shadow-sm" title="Hidden">🚫</span> <span
class="badge-icon preset-tonal-surface shadow-sm"
title="Hidden">🚫</span>
{/if} {/if}
{#if !journal.enable} {#if !journal.enable}
<span class="badge-icon preset-tonal-warning shadow-sm" title="Disabled">⚠️</span> <span
class="badge-icon preset-tonal-warning shadow-sm"
title="Disabled">⚠️</span>
{/if} {/if}
</div> </div>
{/if} {/if}
</a> </a>
{/each} {/each}
{:else} {:else}
<div class="p-20 text-center text-gray-400 dark:text-gray-500 flex flex-col items-center gap-4"> <div
class="flex flex-col items-center gap-4 p-20 text-center text-gray-400 dark:text-gray-500">
<BookType size="4em" /> <BookType size="4em" />
<p class="text-xl">No journals found in this view.</p> <p class="text-xl">No journals found in this view.</p>
</div> </div>

View File

@@ -5,7 +5,20 @@
* Standardized Module-level settings for Journals. * Standardized Module-level settings for Journals.
* Fixed Svelte 5 state placement and reactivity loops. * Fixed Svelte 5 state placement and reactivity loops.
*/ */
import { CalendarClock, Check, CodeXml, Database, Layout, MonitorPlay, MousePointerClick, Palette, Settings, ShieldCheck, Wrench, X } from '@lucide/svelte'; import {
CalendarClock,
Check,
CodeXml,
Database,
Layout,
MonitorPlay,
MousePointerClick,
Palette,
Settings,
ShieldCheck,
Wrench,
X
} from '@lucide/svelte';
import { Modal } from 'flowbite-svelte'; import { Modal } from 'flowbite-svelte';
// *** Import Aether specific variables and functions // *** Import Aether specific variables and functions
@@ -53,32 +66,32 @@
dismissable={false} dismissable={false}
placement="top-center" placement="top-center"
size="xl" size="xl"
class="relative flex flex-col mx-auto w-full bg-white dark:bg-gray-800 text-gray-800 dark:text-gray-200 border border-orange-300 dark:border-orange-700 rounded-lg shadow-xl" class="relative mx-auto flex w-full flex-col rounded-lg border border-orange-300 bg-white text-gray-800 shadow-xl dark:border-orange-700 dark:bg-gray-800 dark:text-gray-200"
headerClass="flex flex-row gap-2 items-center justify-between w-full bg-orange-100 dark:bg-orange-900 p-4 rounded-t-lg border-b border-orange-200 dark:border-orange-800" headerClass="flex flex-row gap-2 items-center justify-between w-full bg-orange-100 dark:bg-orange-900 p-4 rounded-t-lg border-b border-orange-200 dark:border-orange-800"
footerClass="flex flex-row gap-2 items-center justify-center w-full bg-orange-100 dark:bg-orange-900 p-4 rounded-b-lg border-t border-orange-200 dark:border-orange-800" footerClass="flex flex-row gap-2 items-center justify-center w-full bg-orange-100 dark:bg-orange-900 p-4 rounded-b-lg border-t border-orange-200 dark:border-orange-800">
>
{#snippet header()} {#snippet header()}
<h3 class="flex-1 flex items-center gap-2 text-lg font-bold"> <h3 class="flex flex-1 items-center gap-2 text-lg font-bold">
<Wrench class="text-primary-500" /> <Wrench class="text-primary-500" />
<span>&AElig; Journals Module Config</span> <span>&AElig; Journals Module Config</span>
</h3> </h3>
<button type="button" class="btn-icon btn-icon-sm preset-tonal-surface ml-2" onclick={() => (show = false)}> <button
type="button"
class="btn-icon btn-icon-sm preset-tonal-surface ml-2"
onclick={() => (show = false)}>
<X size="1.1em" /> <X size="1.1em" />
</button> </button>
{/snippet} {/snippet}
<div class="space-y-6 py-2 h-[60vh] overflow-y-auto px-4"> <div class="h-[60vh] space-y-6 overflow-y-auto px-4 py-2">
<!-- Navigation Tabs --> <!-- Navigation Tabs -->
<div <div
class="flex justify-center gap-1 mb-4 p-1 bg-surface-500/10 rounded-lg max-w-fit mx-auto sticky top-0 z-10 backdrop-blur-sm" class="bg-surface-500/10 sticky top-0 z-10 mx-auto mb-4 flex max-w-fit justify-center gap-1 rounded-lg p-1 backdrop-blur-sm">
>
<button <button
type="button" type="button"
class="btn btn-sm transition-all {tab === 'form' class="btn btn-sm transition-all {tab === 'form'
? 'preset-filled-primary' ? 'preset-filled-primary'
: 'preset-tonal-surface'}" : 'preset-tonal-surface'}"
onclick={() => (tab = 'form')} onclick={() => (tab = 'form')}>
>
<Settings size="1.1em" class="mr-1" /> Config <Settings size="1.1em" class="mr-1" /> Config
</button> </button>
<button <button
@@ -86,8 +99,7 @@
class="btn btn-sm transition-all {tab === 'local_json' class="btn btn-sm transition-all {tab === 'local_json'
? 'preset-filled-primary' ? 'preset-filled-primary'
: 'preset-tonal-surface'}" : 'preset-tonal-surface'}"
onclick={() => (tab = 'local_json')} onclick={() => (tab = 'local_json')}>
>
<Database size="1.1em" class="mr-1" /> Local JSON <Database size="1.1em" class="mr-1" /> Local JSON
</button> </button>
<button <button
@@ -95,71 +107,55 @@
class="btn btn-sm transition-all {tab === 'session_json' class="btn btn-sm transition-all {tab === 'session_json'
? 'preset-filled-primary' ? 'preset-filled-primary'
: 'preset-tonal-surface'}" : 'preset-tonal-surface'}"
onclick={() => (tab = 'session_json')} onclick={() => (tab = 'session_json')}>
>
<CodeXml size="1.1em" class="mr-1" /> Session JSON <CodeXml size="1.1em" class="mr-1" /> Session JSON
</button> </button>
</div> </div>
{#if tab === 'form'} {#if tab === 'form'}
<div class="space-y-8 animate-in fade-in duration-300"> <div class="animate-in fade-in space-y-8 duration-300">
<!-- Date/Time Section --> <!-- Date/Time Section -->
<section class="space-y-4"> <section class="space-y-4">
<h2 <h2
class="text-xl font-bold flex items-center gap-2 border-b border-surface-500/30 pb-2" class="border-surface-500/30 flex items-center gap-2 border-b pb-2 text-xl font-bold">
>
<CalendarClock size="1.2em" class="text-primary-500" /> <CalendarClock size="1.2em" class="text-primary-500" />
Date and Time Display Date and Time Display
</h2> </h2>
<div class="grid grid-cols-1 md:grid-cols-2 gap-6 p-2"> <div class="grid grid-cols-1 gap-6 p-2 md:grid-cols-2">
<label class="label"> <label class="label">
<span class="text-sm font-bold opacity-70" <span class="text-sm font-bold opacity-70"
>DateTime Format</span >DateTime Format</span>
>
<select <select
bind:value={tmp_config.datetime_format} bind:value={tmp_config.datetime_format}
class="select select-sm" class="select select-sm">
>
<option value="datetime_12_short" <option value="datetime_12_short"
>MMM D, YY hh:mm A</option >MMM D, YY hh:mm A</option>
>
<option value="datetime_12_long" <option value="datetime_12_long"
>MMMM D, YYYY hh:mm A</option >MMMM D, YYYY hh:mm A</option>
>
<option value="datetime_short" <option value="datetime_short"
>MMM D, YY HH:mm</option >MMM D, YY HH:mm</option>
>
<option value="datetime_long" <option value="datetime_long"
>MMMM D, YYYY HH:mm</option >MMMM D, YYYY HH:mm</option>
>
<option value="datetime_us" <option value="datetime_us"
>US (MM/DD/YYYY hh:mm:ss A)</option >US (MM/DD/YYYY hh:mm:ss A)</option>
>
<option value="datetime_iso" <option value="datetime_iso"
>ISO (YYYY-MM-DD HH:mm:ss)</option >ISO (YYYY-MM-DD HH:mm:ss)</option>
>
</select> </select>
</label> </label>
<label class="label"> <label class="label">
<span class="text-sm font-bold opacity-70" <span class="text-sm font-bold opacity-70"
>Time-Only Format</span >Time-Only Format</span>
>
<select <select
bind:value={tmp_config.time_format} bind:value={tmp_config.time_format}
class="select select-sm" class="select select-sm">
>
<option value="time_12_short" <option value="time_12_short"
>12-hour short (3:30 PM)</option >12-hour short (3:30 PM)</option>
>
<option value="time_12_long" <option value="time_12_long"
>12-hour long (3:30:45 PM)</option >12-hour long (3:30:45 PM)</option>
>
<option value="time_short" <option value="time_short"
>24-hour short (15:30)</option >24-hour short (15:30)</option>
>
<option value="time_long" <option value="time_long"
>24-hour long (15:30:45)</option >24-hour long (15:30:45)</option>
>
</select> </select>
</label> </label>
</div> </div>
@@ -168,23 +164,19 @@
<!-- UI Section --> <!-- UI Section -->
<section class="space-y-4"> <section class="space-y-4">
<h2 <h2
class="text-xl font-bold flex items-center gap-2 border-b border-surface-500/30 pb-2" class="border-surface-500/30 flex items-center gap-2 border-b pb-2 text-xl font-bold">
>
<MousePointerClick <MousePointerClick
size="1.2em" size="1.2em"
class="text-primary-500" class="text-primary-500" />
/>
User Interface Preferences User Interface Preferences
</h2> </h2>
<div class="grid grid-cols-1 md:grid-cols-2 gap-6 p-2"> <div class="grid grid-cols-1 gap-6 p-2 md:grid-cols-2">
<label <label
class="flex items-center space-x-3 cursor-pointer p-2 rounded-lg bg-surface-500/5 border border-surface-500/10" class="bg-surface-500/5 border-surface-500/10 flex cursor-pointer items-center space-x-3 rounded-lg border p-2">
>
<input <input
type="checkbox" type="checkbox"
bind:checked={tmp_config.entry.auto_save} bind:checked={tmp_config.entry.auto_save}
class="checkbox" class="checkbox" />
/>
<div class="space-y-0.5"> <div class="space-y-0.5">
<span class="font-bold">Enable Auto-Save</span> <span class="font-bold">Enable Auto-Save</span>
<p class="text-xs opacity-60"> <p class="text-xs opacity-60">
@@ -193,16 +185,14 @@
</div> </div>
</label> </label>
<label <label
class="flex items-center space-x-3 cursor-pointer p-2 rounded-lg bg-surface-500/5 border border-surface-500/10" class="bg-surface-500/5 border-surface-500/10 flex cursor-pointer items-center space-x-3 rounded-lg border p-2">
>
<input <input
type="checkbox" type="checkbox"
bind:checked={tmp_config.show_id_random} bind:checked={tmp_config.show_id_random}
class="checkbox" class="checkbox" />
/>
<div class="space-y-0.5"> <div class="space-y-0.5">
<span class="font-bold">Show Technical IDs</span <span class="font-bold"
> >Show Technical IDs</span>
<p class="text-xs opacity-60"> <p class="text-xs opacity-60">
Display UUIDs in metadata footers Display UUIDs in metadata footers
</p> </p>
@@ -214,52 +204,42 @@
<!-- Journal Query Filters Section --> <!-- Journal Query Filters Section -->
<section class="space-y-4"> <section class="space-y-4">
<h2 <h2
class="text-xl font-bold flex items-center gap-2 border-b border-surface-500/30 pb-2" class="border-surface-500/30 flex items-center gap-2 border-b pb-2 text-xl font-bold">
>
<Database size="1.2em" class="text-primary-500" /> <Database size="1.2em" class="text-primary-500" />
Journal Query Filters Journal Query Filters
</h2> </h2>
<div class="grid grid-cols-1 md:grid-cols-3 gap-6 p-2"> <div class="grid grid-cols-1 gap-6 p-2 md:grid-cols-3">
<label class="label"> <label class="label">
<span class="text-sm font-bold opacity-70" <span class="text-sm font-bold opacity-70"
>Enabled Status</span >Enabled Status</span>
>
<select <select
bind:value={tmp_config.journal.qry__enabled} bind:value={tmp_config.journal.qry__enabled}
class="select select-sm" class="select select-sm">
>
<option value="enabled">Enabled Only</option> <option value="enabled">Enabled Only</option>
<option value="not_enabled" <option value="not_enabled"
>Disabled Only</option >Disabled Only</option>
>
<option value="all" <option value="all"
>All (Enabled & Disabled & NULL)</option >All (Enabled & Disabled & NULL)</option>
>
</select> </select>
</label> </label>
<label class="label"> <label class="label">
<span class="text-sm font-bold opacity-70" <span class="text-sm font-bold opacity-70"
>Hidden Status</span >Hidden Status</span>
>
<select <select
bind:value={tmp_config.journal.qry__hidden} bind:value={tmp_config.journal.qry__hidden}
class="select select-sm" class="select select-sm">
>
<option value="not_hidden">Visible Only</option> <option value="not_hidden">Visible Only</option>
<option value="hidden">Hidden Only</option> <option value="hidden">Hidden Only</option>
<option value="all" <option value="all"
>All (Visible & Hidden & NULL)</option >All (Visible & Hidden & NULL)</option>
>
</select> </select>
</label> </label>
<label class="label"> <label class="label">
<span class="text-sm font-bold opacity-70" <span class="text-sm font-bold opacity-70"
>Query Limit</span >Query Limit</span>
>
<select <select
bind:value={tmp_config.journal.qry__limit} bind:value={tmp_config.journal.qry__limit}
class="select select-sm" class="select select-sm">
>
<option value={10}>10</option> <option value={10}>10</option>
<option value={20}>20</option> <option value={20}>20</option>
<option value={50}>50</option> <option value={50}>50</option>
@@ -275,52 +255,42 @@
<!-- Entry Query Filters Section --> <!-- Entry Query Filters Section -->
<section class="space-y-4"> <section class="space-y-4">
<h2 <h2
class="text-xl font-bold flex items-center gap-2 border-b border-surface-500/30 pb-2" class="border-surface-500/30 flex items-center gap-2 border-b pb-2 text-xl font-bold">
>
<Database size="1.2em" class="text-primary-500" /> <Database size="1.2em" class="text-primary-500" />
Entry Query Filters Entry Query Filters
</h2> </h2>
<div class="grid grid-cols-1 md:grid-cols-3 gap-6 p-2"> <div class="grid grid-cols-1 gap-6 p-2 md:grid-cols-3">
<label class="label"> <label class="label">
<span class="text-sm font-bold opacity-70" <span class="text-sm font-bold opacity-70"
>Enabled Status</span >Enabled Status</span>
>
<select <select
bind:value={tmp_config.entry.qry__enabled} bind:value={tmp_config.entry.qry__enabled}
class="select select-sm" class="select select-sm">
>
<option value="enabled">Enabled Only</option> <option value="enabled">Enabled Only</option>
<option value="not_enabled" <option value="not_enabled"
>Disabled Only</option >Disabled Only</option>
>
<option value="all" <option value="all"
>All (Enabled & Disabled & NULL)</option >All (Enabled & Disabled & NULL)</option>
>
</select> </select>
</label> </label>
<label class="label"> <label class="label">
<span class="text-sm font-bold opacity-70" <span class="text-sm font-bold opacity-70"
>Hidden Status</span >Hidden Status</span>
>
<select <select
bind:value={tmp_config.entry.qry__hidden} bind:value={tmp_config.entry.qry__hidden}
class="select select-sm" class="select select-sm">
>
<option value="not_hidden">Visible Only</option> <option value="not_hidden">Visible Only</option>
<option value="hidden">Hidden Only</option> <option value="hidden">Hidden Only</option>
<option value="all" <option value="all"
>All (Visible & Hidden & NULL)</option >All (Visible & Hidden & NULL)</option>
>
</select> </select>
</label> </label>
<label class="label"> <label class="label">
<span class="text-sm font-bold opacity-70" <span class="text-sm font-bold opacity-70"
>Query Limit</span >Query Limit</span>
>
<select <select
bind:value={tmp_config.entry.qry__limit} bind:value={tmp_config.entry.qry__limit}
class="select select-sm" class="select select-sm">
>
<option value={10}>10</option> <option value={10}>10</option>
<option value={20}>20</option> <option value={20}>20</option>
<option value={50}>50</option> <option value={50}>50</option>
@@ -336,30 +306,25 @@
<!-- Security Section --> <!-- Security Section -->
<section class="space-y-4"> <section class="space-y-4">
<h2 <h2
class="text-xl font-bold flex items-center gap-2 border-b border-surface-500/30 pb-2" class="border-surface-500/30 flex items-center gap-2 border-b pb-2 text-xl font-bold">
>
<ShieldCheck size="1.2em" class="text-primary-500" /> <ShieldCheck size="1.2em" class="text-primary-500" />
Security & Encryption Security & Encryption
</h2> </h2>
<div <div
class="p-4 bg-orange-500/5 rounded-lg border border-orange-500/20 space-y-4" class="space-y-4 rounded-lg border border-orange-500/20 bg-orange-500/5 p-4">
> <div class="text-sm italic opacity-80">
<div class="text-sm opacity-80 italic">
Global security overrides for the journal module. Global security overrides for the journal module.
</div> </div>
<label <label
class="flex items-center space-x-3 cursor-pointer" class="flex cursor-pointer items-center space-x-3">
>
<input <input
type="checkbox" type="checkbox"
bind:checked={ bind:checked={
$journals_sess.enable_session_passcode_cache $journals_sess.enable_session_passcode_cache
} }
class="checkbox checkbox-primary" class="checkbox checkbox-primary" />
/>
<span class="text-sm font-bold" <span class="text-sm font-bold"
>Cache Passcodes in Session</span >Cache Passcodes in Session</span>
>
</label> </label>
</div> </div>
</section> </section>
@@ -370,8 +335,7 @@
readonly={true} readonly={true}
content={JSON.stringify(tmp_config, null, 2)} content={JSON.stringify(tmp_config, null, 2)}
theme_mode={$ae_loc.theme_mode} theme_mode={$ae_loc.theme_mode}
class_li="rounded-lg border border-surface-500/30" class_li="rounded-lg border border-surface-500/30" />
/>
</div> </div>
{:else if tab === 'session_json'} {:else if tab === 'session_json'}
<div class="h-full min-h-[400px]"> <div class="h-full min-h-[400px]">
@@ -379,8 +343,7 @@
readonly={true} readonly={true}
content={JSON.stringify($journals_sess, null, 2)} content={JSON.stringify($journals_sess, null, 2)}
theme_mode={$ae_loc.theme_mode} theme_mode={$ae_loc.theme_mode}
class_li="rounded-lg border border-surface-500/30" class_li="rounded-lg border border-surface-500/30" />
/>
</div> </div>
{/if} {/if}
</div> </div>
@@ -389,16 +352,14 @@
<div class="flex gap-4"> <div class="flex gap-4">
<button <button
type="button" type="button"
class="btn preset-tonal-surface font-bold min-w-[100px]" class="btn preset-tonal-surface min-w-[100px] font-bold"
onclick={() => (show = false)} onclick={() => (show = false)}>
>
<X size="1.2em" class="mr-2" /> Cancel <X size="1.2em" class="mr-2" /> Cancel
</button> </button>
<button <button
type="button" type="button"
class="btn preset-filled-primary font-bold shadow-lg min-w-[120px]" class="btn preset-filled-primary min-w-[120px] font-bold shadow-lg"
onclick={handle_save} onclick={handle_save}>
>
<Check size="1.2em" class="mr-2" /> Save Changes <Check size="1.2em" class="mr-2" /> Save Changes
</button> </button>
</div> </div>

View File

@@ -60,9 +60,7 @@
'datetime_iso_12_no_seconds' 'datetime_iso_12_no_seconds'
); );
let day_of_week_str = add_timestamp_header_w_day_of_week let day_of_week_str = add_timestamp_header_w_day_of_week
? ' (' + ? ' (' + ae_util.iso_datetime_formatter(new Date(), 'week_long') + ')'
ae_util.iso_datetime_formatter(new Date(), 'week_long') +
')'
: ''; : '';
if (add_timestamp_header && add_text_header) { if (add_timestamp_header && add_text_header) {
@@ -113,8 +111,7 @@
let data_kv = { content: new_content }; let data_kv = { content: new_content };
try { try {
let update_result = let update_result = await journals_func.update_ae_obj__journal_entry({
await journals_func.update_ae_obj__journal_entry({
api_cfg: $ae_api, api_cfg: $ae_api,
journal_entry_id: tmp_entry_obj?.journal_entry_id, journal_entry_id: tmp_entry_obj?.journal_entry_id,
data_kv: data_kv, data_kv: data_kv,
@@ -145,8 +142,7 @@
autoclose={false} autoclose={false}
placement="top-center" placement="top-center"
size="xl" size="xl"
class="top-center bg-white dark:bg-gray-800 text-gray-800 dark:text-gray-200 rounded-lg border-gray-200 dark:border-gray-700 divide-gray-200 dark:divide-gray-700 shadow-md relative flex flex-col gap-1 mx-auto w-full" class="top-center relative mx-auto flex w-full flex-col gap-1 divide-gray-200 rounded-lg border-gray-200 bg-white text-gray-800 shadow-md dark:divide-gray-700 dark:border-gray-700 dark:bg-gray-800 dark:text-gray-200">
>
<div class="flex flex-col gap-3"> <div class="flex flex-col gap-3">
<!-- Checkbox Options --> <!-- Checkbox Options -->
<div class="flex flex-wrap gap-4"> <div class="flex flex-wrap gap-4">
@@ -155,8 +151,7 @@
type="checkbox" type="checkbox"
id="append_timestamp_header" id="append_timestamp_header"
bind:checked={add_timestamp_header} bind:checked={add_timestamp_header}
class="checkbox" class="checkbox" />
/>
<span>Use timestamp as Markdown header</span> <span>Use timestamp as Markdown header</span>
</label> </label>
@@ -165,8 +160,7 @@
type="checkbox" type="checkbox"
id="append_timestamp_header_w_day_of_week" id="append_timestamp_header_w_day_of_week"
bind:checked={add_timestamp_header_w_day_of_week} bind:checked={add_timestamp_header_w_day_of_week}
class="checkbox" class="checkbox" />
/>
<span>Include day of week</span> <span>Include day of week</span>
</label> </label>
</div> </div>
@@ -176,8 +170,7 @@
type="text" type="text"
placeholder="Markdown header for content (Optional)" placeholder="Markdown header for content (Optional)"
bind:value={add_text_header} bind:value={add_text_header}
class="input" class="input" />
/>
<!-- Main Content Area --> <!-- Main Content Area -->
<textarea <textarea
@@ -185,26 +178,23 @@
class="textarea min-h-48" class="textarea min-h-48"
placeholder="Content to {mode === 'auto' placeholder="Content to {mode === 'auto'
? journal_config?.entry_add_text ? journal_config?.entry_add_text
: mode}..." : mode}...">
>
</textarea> </textarea>
<div class="flex justify-end gap-2 mt-2"> <div class="mt-2 flex justify-end gap-2">
<button <button
type="button" type="button"
disabled={!has_changes} disabled={!has_changes}
onclick={handle_save} onclick={handle_save}
class="btn preset-filled-primary" class="btn preset-filled-primary"
class:opacity-50={!has_changes} class:opacity-50={!has_changes}>
>
<Check class="mr-1" /> <Check class="mr-1" />
Update Update
</button> </button>
<button <button
type="button" type="button"
onclick={on_close} onclick={on_close}
class="btn preset-tonal-surface" class="btn preset-tonal-surface">
>
<X class="mr-1" /> <X class="mr-1" />
Cancel Cancel
</button> </button>

View File

@@ -3,7 +3,27 @@
* ae_comp__modal_journal_entry_config.svelte * ae_comp__modal_journal_entry_config.svelte
* Standardized Journal Entry-level configuration. * Standardized Journal Entry-level configuration.
*/ */
import { ArrowDownToLine, ArrowUpToLine, Check, Clock, CodeXml, Copy, Database, FileDown, Fingerprint, Minus, Plus, RefreshCcw, Settings, Shapes, ShieldCheck, Tag, Trash2, X, Zap } from '@lucide/svelte'; import {
ArrowDownToLine,
ArrowUpToLine,
Check,
Clock,
CodeXml,
Copy,
Database,
FileDown,
Fingerprint,
Minus,
Plus,
RefreshCcw,
Settings,
Shapes,
ShieldCheck,
Tag,
Trash2,
X,
Zap
} from '@lucide/svelte';
import { Modal } from 'flowbite-svelte'; import { Modal } from 'flowbite-svelte';
import { ae_loc, ae_api } from '$lib/stores/ae_stores'; import { ae_loc, ae_api } from '$lib/stores/ae_stores';
import { import {
@@ -42,8 +62,7 @@
let tab: 'actions' | 'meta' | 'security' | 'json' = $state('actions'); let tab: 'actions' | 'meta' | 'security' | 'json' = $state('actions');
const normalize_date = (val: string | null) => const normalize_date = (val: string | null) => (val ? val.slice(0, 16) : null);
val ? val.slice(0, 16) : null;
async function handle_update_entry() { async function handle_update_entry() {
try { try {
@@ -92,32 +111,32 @@
dismissable={false} dismissable={false}
placement="top-center" placement="top-center"
size="lg" size="lg"
class="relative flex flex-col mx-auto w-full bg-white dark:bg-gray-800 text-gray-800 dark:text-gray-200 border border-orange-300 dark:border-orange-700 rounded-lg shadow-xl" class="relative mx-auto flex w-full flex-col rounded-lg border border-orange-300 bg-white text-gray-800 shadow-xl dark:border-orange-700 dark:bg-gray-800 dark:text-gray-200"
headerClass="flex flex-row gap-2 items-center justify-between w-full bg-orange-100 dark:bg-orange-900 p-4 rounded-t-lg border-b border-orange-200 dark:border-orange-800" headerClass="flex flex-row gap-2 items-center justify-between w-full bg-orange-100 dark:bg-orange-900 p-4 rounded-t-lg border-b border-orange-200 dark:border-orange-800"
footerClass="flex flex-row gap-2 items-center justify-center w-full bg-orange-100 dark:bg-orange-900 p-4 rounded-b-lg border-t border-orange-200 dark:border-orange-800" footerClass="flex flex-row gap-2 items-center justify-center w-full bg-orange-100 dark:bg-orange-900 p-4 rounded-b-lg border-t border-orange-200 dark:border-orange-800">
>
{#snippet header()} {#snippet header()}
<h3 class="flex-1 flex items-center gap-2 text-lg font-bold"> <h3 class="flex flex-1 items-center gap-2 text-lg font-bold">
<Settings class="text-primary-500" /> <Settings class="text-primary-500" />
<span>Entry Config: {tmp_entry_obj.name || '--'}</span> <span>Entry Config: {tmp_entry_obj.name || '--'}</span>
</h3> </h3>
<button type="button" class="btn-icon btn-icon-sm preset-tonal-surface ml-2" onclick={() => (show = false)}> <button
type="button"
class="btn-icon btn-icon-sm preset-tonal-surface ml-2"
onclick={() => (show = false)}>
<X size="1.1em" /> <X size="1.1em" />
</button> </button>
{/snippet} {/snippet}
<div class="space-y-6 py-2 h-[60vh] overflow-y-auto px-4"> <div class="h-[60vh] space-y-6 overflow-y-auto px-4 py-2">
<!-- Navigation Tabs --> <!-- Navigation Tabs -->
<div <div
class="flex justify-center gap-1 mb-4 p-1 bg-surface-500/10 rounded-lg max-w-fit mx-auto sticky top-0 z-10 backdrop-blur-sm" class="bg-surface-500/10 sticky top-0 z-10 mx-auto mb-4 flex max-w-fit justify-center gap-1 rounded-lg p-1 backdrop-blur-sm">
>
<button <button
type="button" type="button"
class="btn btn-sm transition-all {tab === 'actions' class="btn btn-sm transition-all {tab === 'actions'
? 'preset-filled-primary' ? 'preset-filled-primary'
: 'preset-tonal-surface'}" : 'preset-tonal-surface'}"
onclick={() => (tab = 'actions')} onclick={() => (tab = 'actions')}>
>
<Zap size="1.1em" class="mr-1" /> Quick Actions <Zap size="1.1em" class="mr-1" /> Quick Actions
</button> </button>
<button <button
@@ -125,8 +144,7 @@
class="btn btn-sm transition-all {tab === 'meta' class="btn btn-sm transition-all {tab === 'meta'
? 'preset-filled-primary' ? 'preset-filled-primary'
: 'preset-tonal-surface'}" : 'preset-tonal-surface'}"
onclick={() => (tab = 'meta')} onclick={() => (tab = 'meta')}>
>
<Shapes size="1.1em" class="mr-1" /> Metadata <Shapes size="1.1em" class="mr-1" /> Metadata
</button> </button>
<button <button
@@ -134,8 +152,7 @@
class="btn btn-sm transition-all {tab === 'security' class="btn btn-sm transition-all {tab === 'security'
? 'preset-filled-primary' ? 'preset-filled-primary'
: 'preset-tonal-surface'}" : 'preset-tonal-surface'}"
onclick={() => (tab = 'security')} onclick={() => (tab = 'security')}>
>
<ShieldCheck size="1.1em" class="mr-1" /> Status & Security <ShieldCheck size="1.1em" class="mr-1" /> Status & Security
</button> </button>
<button <button
@@ -143,23 +160,21 @@
class="btn btn-sm transition-all {tab === 'json' class="btn btn-sm transition-all {tab === 'json'
? 'preset-filled-primary' ? 'preset-filled-primary'
: 'preset-tonal-surface'}" : 'preset-tonal-surface'}"
onclick={() => (tab = 'json')} onclick={() => (tab = 'json')}>
>
<CodeXml size="1.1em" class="mr-1" /> JSON <CodeXml size="1.1em" class="mr-1" /> JSON
</button> </button>
</div> </div>
{#if tab === 'actions'} {#if tab === 'actions'}
<div class="space-y-6 animate-in fade-in duration-300"> <div class="animate-in fade-in space-y-6 duration-300">
<section class="grid grid-cols-1 md:grid-cols-2 gap-4"> <section class="grid grid-cols-1 gap-4 md:grid-cols-2">
<button <button
type="button" type="button"
class="btn preset-tonal-secondary w-full" class="btn preset-tonal-secondary w-full"
onclick={() => { onclick={() => {
show = false; show = false;
on_prepend?.(); on_prepend?.();
}} }}>
>
<ArrowUpToLine size="1.2em" class="mr-2" /> Prepend Content <ArrowUpToLine size="1.2em" class="mr-2" /> Prepend Content
</button> </button>
<button <button
@@ -168,8 +183,7 @@
onclick={() => { onclick={() => {
show = false; show = false;
on_append?.(); on_append?.();
}} }}>
>
<ArrowDownToLine size="1.2em" class="mr-2" /> Append Content <ArrowDownToLine size="1.2em" class="mr-2" /> Append Content
</button> </button>
<button <button
@@ -178,8 +192,7 @@
onclick={() => { onclick={() => {
show = false; show = false;
on_show_export?.(); on_show_export?.();
}} }}>
>
<FileDown size="1.2em" class="mr-2" /> Export Entry <FileDown size="1.2em" class="mr-2" /> Export Entry
</button> </button>
<button <button
@@ -189,13 +202,12 @@
/* Clone logic here */ alert( /* Clone logic here */ alert(
'Clone not yet implemented in modal' 'Clone not yet implemented in modal'
); );
}} }}>
>
<Copy size="1.2em" class="mr-2" /> Clone Entry <Copy size="1.2em" class="mr-2" /> Clone Entry
</button> </button>
</section> </section>
<section class="space-y-4 pt-4 border-t border-surface-500/20"> <section class="border-surface-500/20 space-y-4 border-t pt-4">
<h4 class="text-xs font-bold uppercase opacity-50"> <h4 class="text-xs font-bold uppercase opacity-50">
Quick Category Quick Category
</h4> </h4>
@@ -211,8 +223,7 @@
tmp_entry_obj.category_code = cat.code; tmp_entry_obj.category_code = cat.code;
handle_update_entry(); handle_update_entry();
on_save(); on_save();
}} }}>
>
{cat.name} {cat.name}
</button> </button>
{/each} {/each}
@@ -220,7 +231,7 @@
</section> </section>
</div> </div>
{:else if tab === 'meta'} {:else if tab === 'meta'}
<div class="space-y-6 animate-in fade-in duration-300"> <div class="animate-in fade-in space-y-6 duration-300">
<label class="label"> <label class="label">
<span class="text-sm font-bold opacity-70">Category</span> <span class="text-sm font-bold opacity-70">Category</span>
<select <select
@@ -229,8 +240,7 @@
onchange={() => { onchange={() => {
handle_update_entry(); handle_update_entry();
on_save(); on_save();
}} }}>
>
<option value="">None</option> <option value="">None</option>
{#each journal?.cfg_json?.category_li ?? [] as cat (cat.code)} {#each journal?.cfg_json?.category_li ?? [] as cat (cat.code)}
<option value={cat.code}>{cat.name}</option> <option value={cat.code}>{cat.name}</option>
@@ -240,9 +250,8 @@
<label class="label"> <label class="label">
<span class="text-sm font-bold opacity-70" <span class="text-sm font-bold opacity-70"
>Tags (Comma separated)</span >Tags (Comma separated)</span>
> <div class="flex items-center gap-2">
<div class="flex gap-2 items-center">
<Tag size="1.2em" class="opacity-30" /> <Tag size="1.2em" class="opacity-30" />
<input <input
type="text" type="text"
@@ -252,16 +261,14 @@
onchange={() => { onchange={() => {
handle_update_entry(); handle_update_entry();
on_save(); on_save();
}} }} />
/>
</div> </div>
</label> </label>
<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"> <label class="label">
<span class="text-sm font-bold opacity-70" <span class="text-sm font-bold opacity-70"
>Archive On</span >Archive On</span>
>
<input <input
type="datetime-local" type="datetime-local"
value={normalize_date(tmp_entry_obj.archive_on)} value={normalize_date(tmp_entry_obj.archive_on)}
@@ -271,13 +278,11 @@
handle_update_entry(); handle_update_entry();
on_save(); on_save();
}} }}
class="input" class="input" />
/>
</label> </label>
<label class="label"> <label class="label">
<span class="text-sm font-bold opacity-70" <span class="text-sm font-bold opacity-70"
>Sort Priority</span >Sort Priority</span>
>
<div class="flex items-center gap-2"> <div class="flex items-center gap-2">
<button <button
type="button" type="button"
@@ -287,11 +292,9 @@
(tmp_entry_obj.sort ?? 0) - 1; (tmp_entry_obj.sort ?? 0) - 1;
handle_update_entry(); handle_update_entry();
on_save(); on_save();
}}><Minus size="1em" /></button }}><Minus size="1em" /></button>
> <span class="w-8 text-center font-mono font-bold"
<span class="font-mono font-bold w-8 text-center" >{tmp_entry_obj.sort ?? 0}</span>
>{tmp_entry_obj.sort ?? 0}</span
>
<button <button
type="button" type="button"
class="btn-icon btn-icon-sm preset-tonal-surface" class="btn-icon btn-icon-sm preset-tonal-surface"
@@ -300,25 +303,22 @@
(tmp_entry_obj.sort ?? 0) + 1; (tmp_entry_obj.sort ?? 0) + 1;
handle_update_entry(); handle_update_entry();
on_save(); on_save();
}}><Plus size="1em" /></button }}><Plus size="1em" /></button>
>
</div> </div>
</label> </label>
</div> </div>
</div> </div>
{:else if tab === 'security'} {:else if tab === 'security'}
<div class="space-y-6 animate-in fade-in duration-300"> <div class="animate-in fade-in space-y-6 duration-300">
<section class="space-y-4"> <section class="space-y-4">
<h2 <h2
class="text-lg font-bold flex items-center gap-2 border-b border-surface-500/30 pb-2" class="border-surface-500/30 flex items-center gap-2 border-b pb-2 text-lg font-bold">
>
<Fingerprint size="1.2em" class="text-primary-500" /> <Fingerprint size="1.2em" class="text-primary-500" />
Status & Security Status & Security
</h2> </h2>
<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 <label
class="flex items-center space-x-3 cursor-pointer p-3 rounded-lg bg-surface-500/5 border border-surface-500/10 transition-colors hover:bg-surface-500/10" class="bg-surface-500/5 border-surface-500/10 hover:bg-surface-500/10 flex cursor-pointer items-center space-x-3 rounded-lg border p-3 transition-colors">
>
<input <input
type="checkbox" type="checkbox"
bind:checked={tmp_entry_obj.enable} bind:checked={tmp_entry_obj.enable}
@@ -326,18 +326,15 @@
handle_update_entry(); handle_update_entry();
on_save(); on_save();
}} }}
class="checkbox checkbox-primary" class="checkbox checkbox-primary" />
/>
<div class="flex flex-col"> <div class="flex flex-col">
<span class="font-bold">Enabled</span> <span class="font-bold">Enabled</span>
<span class="text-xs opacity-60" <span class="text-xs opacity-60"
>Allow access to this entry</span >Allow access to this entry</span>
>
</div> </div>
</label> </label>
<label <label
class="flex items-center space-x-3 cursor-pointer p-3 rounded-lg bg-surface-500/5 border border-surface-500/10 transition-colors hover:bg-surface-500/10" class="bg-surface-500/5 border-surface-500/10 hover:bg-surface-500/10 flex cursor-pointer items-center space-x-3 rounded-lg border p-3 transition-colors">
>
<input <input
type="checkbox" type="checkbox"
bind:checked={tmp_entry_obj.hide} bind:checked={tmp_entry_obj.hide}
@@ -345,18 +342,15 @@
handle_update_entry(); handle_update_entry();
on_save(); on_save();
}} }}
class="checkbox checkbox-primary" class="checkbox checkbox-primary" />
/>
<div class="flex flex-col"> <div class="flex flex-col">
<span class="font-bold">Hidden</span> <span class="font-bold">Hidden</span>
<span class="text-xs opacity-60" <span class="text-xs opacity-60"
>Hide from standard lists</span >Hide from standard lists</span>
>
</div> </div>
</label> </label>
<label <label
class="flex items-center space-x-3 cursor-pointer p-3 rounded-lg bg-surface-500/5 border border-surface-500/10 transition-colors hover:bg-surface-500/10" class="bg-surface-500/5 border-surface-500/10 hover:bg-surface-500/10 flex cursor-pointer items-center space-x-3 rounded-lg border p-3 transition-colors">
>
<input <input
type="checkbox" type="checkbox"
bind:checked={tmp_entry_obj.priority} bind:checked={tmp_entry_obj.priority}
@@ -364,24 +358,20 @@
handle_update_entry(); handle_update_entry();
on_save(); on_save();
}} }}
class="checkbox checkbox-primary" class="checkbox checkbox-primary" />
/>
<div class="flex flex-col"> <div class="flex flex-col">
<span class="font-bold">Priority Entry</span> <span class="font-bold">Priority Entry</span>
<span class="text-xs opacity-60" <span class="text-xs opacity-60"
>Star or pin to top</span >Star or pin to top</span>
>
</div> </div>
</label> </label>
<div <div
class="flex items-center justify-between p-3 rounded-lg bg-surface-500/5 border border-surface-500/10" class="bg-surface-500/5 border-surface-500/10 flex items-center justify-between rounded-lg border p-3">
>
<div class="flex flex-col"> <div class="flex flex-col">
<span class="font-bold text-sm">Sort Order</span <span class="text-sm font-bold"
> >Sort Order</span>
<span class="text-xs opacity-60" <span class="text-xs opacity-60"
>Manual list position</span >Manual list position</span>
>
</div> </div>
<div class="flex items-center gap-2"> <div class="flex items-center gap-2">
<button <button
@@ -392,12 +382,10 @@
(tmp_entry_obj.sort ?? 0) - 1; (tmp_entry_obj.sort ?? 0) - 1;
handle_update_entry(); handle_update_entry();
on_save(); on_save();
}}><Minus size="1em" /></button }}><Minus size="1em" /></button>
>
<span <span
class="font-mono font-bold w-8 text-center text-lg" class="w-8 text-center font-mono text-lg font-bold"
>{tmp_entry_obj.sort ?? 0}</span >{tmp_entry_obj.sort ?? 0}</span>
>
<button <button
type="button" type="button"
class="btn-icon btn-icon-sm preset-tonal-surface" class="btn-icon btn-icon-sm preset-tonal-surface"
@@ -406,8 +394,7 @@
(tmp_entry_obj.sort ?? 0) + 1; (tmp_entry_obj.sort ?? 0) + 1;
handle_update_entry(); handle_update_entry();
on_save(); on_save();
}}><Plus size="1em" /></button }}><Plus size="1em" /></button>
>
</div> </div>
</div> </div>
</div> </div>
@@ -429,21 +416,18 @@
hide_personal={journal?.cfg_json?.hide_btn_personal} hide_personal={journal?.cfg_json?.hide_btn_personal}
hide_professional={journal?.cfg_json hide_professional={journal?.cfg_json
?.hide_btn_professional} ?.hide_btn_professional}
hide_template={journal?.cfg_json?.hide_btn_template} hide_template={journal?.cfg_json?.hide_btn_template} />
/>
</section> </section>
{#if tmp_entry_obj.private && !tmp_entry_obj.content && tmp_entry_obj.content_encrypted} {#if tmp_entry_obj.private && !tmp_entry_obj.content && tmp_entry_obj.content_encrypted}
<section class="pt-8 border-t border-error-500/20"> <section class="border-error-500/20 border-t pt-8">
<div <div
class="bg-error-500/10 p-4 rounded-lg border border-error-500/30" class="bg-error-500/10 border-error-500/30 rounded-lg border p-4">
>
<h4 <h4
class="text-error-500 font-bold flex items-center gap-2 mb-2" class="text-error-500 mb-2 flex items-center gap-2 font-bold">
>
<RefreshCcw size="1.2em" /> Disaster Recovery <RefreshCcw size="1.2em" /> Disaster Recovery
</h4> </h4>
<p class="text-xs opacity-70 mb-4 italic"> <p class="mb-4 text-xs italic opacity-70">
If the encryption passcode is lost, the data is If the encryption passcode is lost, the data is
unrecoverable. You can force a reset to plain unrecoverable. You can force a reset to plain
text to reuse this entry ID. text to reuse this entry ID.
@@ -454,8 +438,7 @@
onclick={() => { onclick={() => {
show = false; show = false;
on_force_reset?.(); on_force_reset?.();
}} }}>
>
Force Reset to Plain Text Force Reset to Plain Text
</button> </button>
</div> </div>
@@ -468,8 +451,7 @@
class="btn btn-sm preset-tonal-error w-full" class="btn btn-sm preset-tonal-error w-full"
onclick={() => { onclick={() => {
alert('Delete logic handled in parent component'); alert('Delete logic handled in parent component');
}} }}>
>
<Trash2 size="1.1em" class="mr-2" /> Delete Entry <Trash2 size="1.1em" class="mr-2" /> Delete Entry
</button> </button>
</section> </section>
@@ -480,8 +462,7 @@
readonly={true} readonly={true}
content={JSON.stringify(tmp_entry_obj, null, 2)} content={JSON.stringify(tmp_entry_obj, null, 2)}
theme_mode={$ae_loc.theme_mode} theme_mode={$ae_loc.theme_mode}
class_li="rounded-lg border border-surface-500/30" class_li="rounded-lg border border-surface-500/30" />
/>
</div> </div>
{/if} {/if}
</div> </div>
@@ -489,9 +470,8 @@
{#snippet footer()} {#snippet footer()}
<button <button
type="button" type="button"
class="btn preset-filled-primary font-bold min-w-[120px]" class="btn preset-filled-primary min-w-[120px] font-bold"
onclick={() => (show = false)} onclick={() => (show = false)}>
>
<Check size="1.2em" class="mr-2" /> <Check size="1.2em" class="mr-2" />
Done Done
</button> </button>

View File

@@ -7,7 +7,14 @@
*/ */
import { Modal } from 'flowbite-svelte'; import { Modal } from 'flowbite-svelte';
import { Code, Copy, Download, FileJson, FileType, Settings2 } from '@lucide/svelte'; import {
Code,
Copy,
Download,
FileJson,
FileType,
Settings2
} from '@lucide/svelte';
import { ae_util } from '$lib/ae_utils/ae_utils'; import { ae_util } from '$lib/ae_utils/ae_utils';
import type { ae_JournalEntry, ae_Journal } from '$lib/types/ae_types'; import type { ae_JournalEntry, ae_Journal } from '$lib/types/ae_types';
import { import {
@@ -36,9 +43,8 @@
const templates = Object.values(EXPORT_TEMPLATES); const templates = Object.values(EXPORT_TEMPLATES);
const selected_template = $derived( const selected_template = $derived(
EXPORT_TEMPLATES[ EXPORT_TEMPLATES[selected_template_id as keyof typeof EXPORT_TEMPLATES] ||
selected_template_id as keyof typeof EXPORT_TEMPLATES EXPORT_TEMPLATES.standard_markdown
] || EXPORT_TEMPLATES.standard_markdown
); );
// Auto-select template based on journal type when modal opens // Auto-select template based on journal type when modal opens
@@ -109,12 +115,10 @@
bind:open bind:open
autoclose={false} autoclose={false}
size="xl" size="xl"
class="w-full" class="w-full">
>
<div class="space-y-4"> <div class="space-y-4">
<div <div
class="flex items-center justify-between p-4 bg-blue-50 dark:bg-blue-900/20 text-blue-800 dark:text-blue-200 rounded-lg" class="flex items-center justify-between rounded-lg bg-blue-50 p-4 text-blue-800 dark:bg-blue-900/20 dark:text-blue-200">
>
<div> <div>
<strong>Ready to Export:</strong> <strong>Ready to Export:</strong>
{export_count} entries. {export_count} entries.
@@ -122,8 +126,7 @@
{#if journal} {#if journal}
<div class="text-xs opacity-70"> <div class="text-xs opacity-70">
Journal Type: <span class="font-mono" Journal Type: <span class="font-mono"
>{journal.type_code || 'standard'}</span >{journal.type_code || 'standard'}</span>
>
</div> </div>
{/if} {/if}
</div> </div>
@@ -134,19 +137,18 @@
<Settings2 size="1em" /> <Settings2 size="1em" />
<span class="label-text font-bold">Select Export Template</span> <span class="label-text font-bold">Select Export Template</span>
</label> </label>
<div class="grid grid-cols-2 md:grid-cols-3 lg:grid-cols-5 gap-2"> <div class="grid grid-cols-2 gap-2 md:grid-cols-3 lg:grid-cols-5">
{#each templates as template (template.id)} {#each templates as template (template.id)}
<button <button
type="button" type="button"
class="btn preset-outlined-surface flex flex-col gap-1 p-2 h-24 items-center justify-center border-2 text-center {selected_template_id === class="btn preset-outlined-surface flex h-24 flex-col items-center justify-center gap-1 border-2 p-2 text-center {selected_template_id ===
template.id template.id
? 'border-primary-500 bg-primary-50 dark:bg-primary-900/20' ? 'border-primary-500 bg-primary-50 dark:bg-primary-900/20'
: 'opacity-60'}" : 'opacity-60'}"
onclick={() => { onclick={() => {
selected_template_id = template.id; selected_template_id = template.id;
generate_preview(); generate_preview();
}} }}>
>
{#if template.extension === 'json'} {#if template.extension === 'json'}
<FileJson size="1.5em" /> <FileJson size="1.5em" />
{:else if template.extension === 'html'} {:else if template.extension === 'html'}
@@ -154,12 +156,10 @@
{:else} {:else}
<FileType size="1.5em" /> <FileType size="1.5em" />
{/if} {/if}
<span class="text-xs font-bold leading-tight" <span class="text-xs leading-tight font-bold"
>{template.name}</span >{template.name}</span>
>
<span class="text-[10px] opacity-70" <span class="text-[10px] opacity-70"
>.{template.extension}</span >.{template.extension}</span>
>
</button> </button>
{/each} {/each}
</div> </div>
@@ -167,43 +167,38 @@
<!-- Preview Area --> <!-- Preview Area -->
<div class="form-control"> <div class="form-control">
<div class="label flex justify-between items-center"> <div class="label flex items-center justify-between">
<span class="label-text">Preview</span> <span class="label-text">Preview</span>
<span class="text-[10px] opacity-50 uppercase tracking-widest" <span class="text-[10px] tracking-widest uppercase opacity-50"
>{selected_template.name}</span >{selected_template.name}</span>
>
</div> </div>
<textarea <textarea
class="textarea h-64 font-mono text-xs bg-gray-50 dark:bg-gray-900" class="textarea h-64 bg-gray-50 font-mono text-xs dark:bg-gray-900"
readonly readonly
value={export_preview.substring(0, 5000) + value={export_preview.substring(0, 5000) +
(export_preview.length > 5000 (export_preview.length > 5000
? '\n... (truncated for preview)' ? '\n... (truncated for preview)'
: '')} : '')}></textarea>
></textarea>
</div> </div>
<!-- Actions --> <!-- Actions -->
<div class="modal-action flex justify-between items-center"> <div class="modal-action flex items-center justify-between">
<button <button
type="button" type="button"
class="btn preset-tonal-secondary" class="btn preset-tonal-secondary"
onclick={on_close}>Close</button onclick={on_close}>Close</button>
>
<div class="flex gap-2"> <div class="flex gap-2">
<button <button
type="button" type="button"
class="btn preset-tonal-primary" class="btn preset-tonal-primary"
onclick={handle_copy} onclick={handle_copy}>
>
<Copy class="mr-2" size="1.2em" /> Copy to Clipboard <Copy class="mr-2" size="1.2em" /> Copy to Clipboard
</button> </button>
<button <button
type="button" type="button"
class="btn preset-filled-primary" class="btn preset-filled-primary"
onclick={handle_download} onclick={handle_download}>
>
<Download class="mr-2" size="1.2em" /> Download File <Download class="mr-2" size="1.2em" /> Download File
</button> </button>
</div> </div>

View File

@@ -1,6 +1,13 @@
<script lang="ts"> <script lang="ts">
import { Modal } from 'flowbite-svelte'; import { Modal } from 'flowbite-svelte';
import { Check, CircleAlert, FileText, RefreshCw, Upload, X } from '@lucide/svelte'; import {
Check,
CircleAlert,
FileText,
RefreshCw,
Upload,
X
} from '@lucide/svelte';
import { import {
PARSERS, PARSERS,
type AeJournalEntryInput type AeJournalEntryInput
@@ -15,11 +22,7 @@
on_import_complete: () => void; on_import_complete: () => void;
} }
let { let { open = $bindable(false), on_close, on_import_complete }: Props = $props();
open = $bindable(false),
on_close,
on_import_complete
}: Props = $props();
let files: FileList | null = $state(null); let files: FileList | null = $state(null);
let selected_parser: keyof typeof PARSERS = $state('standard'); let selected_parser: keyof typeof PARSERS = $state('standard');
@@ -144,21 +147,18 @@
bind:open bind:open
autoclose={false} autoclose={false}
size="xl" size="xl"
class="w-full" class="w-full">
>
<div class="space-y-4"> <div class="space-y-4">
<!-- Configuration --> <!-- Configuration -->
<div class="grid grid-cols-1 md:grid-cols-2 gap-4"> <div class="grid grid-cols-1 gap-4 md:grid-cols-2">
<div> <div>
<label class="label"> <label class="label">
<span>Parser Strategy</span> <span>Parser Strategy</span>
<select class="select" bind:value={selected_parser}> <select class="select" bind:value={selected_parser}>
<option value="standard" <option value="standard"
>Standard (1 File = 1 Entry)</option >Standard (1 File = 1 Entry)</option>
>
<option value="personal_log" <option value="personal_log"
>Personal Log (Split by Date)</option >Personal Log (Split by Date)</option>
>
<option value="amazon_vine">Amazon Vine Reviews</option> <option value="amazon_vine">Amazon Vine Reviews</option>
</select> </select>
</label> </label>
@@ -173,25 +173,23 @@
<!-- svelte-ignore a11y_no_static_element_interactions --> <!-- svelte-ignore a11y_no_static_element_interactions -->
<div <div
class=" class="
border-2 border-dashed rounded-lg p-8 text-center cursor-pointer transition-all duration-200 flex cursor-pointer flex-col items-center justify-center gap-2 rounded-lg border-2
flex flex-col items-center justify-center gap-2 border-dashed p-8 text-center transition-all duration-200
{is_dragging {is_dragging
? 'border-primary-500 bg-primary-50 dark:bg-primary-900/20' ? 'border-primary-500 bg-primary-50 dark:bg-primary-900/20'
: 'border-gray-300 dark:border-gray-600 hover:border-gray-400 dark:hover:border-gray-500'} : 'border-gray-300 hover:border-gray-400 dark:border-gray-600 dark:hover:border-gray-500'}
" "
ondragenter={handle_drag_enter} ondragenter={handle_drag_enter}
ondragleave={handle_drag_leave} ondragleave={handle_drag_leave}
ondragover={handle_drag_over} ondragover={handle_drag_over}
ondrop={handle_drop} ondrop={handle_drop}
onclick={() => onclick={() =>
document.getElementById('file_import_input')?.click()} document.getElementById('file_import_input')?.click()}>
>
<Upload class="h-10 w-10 text-gray-400" /> <Upload class="h-10 w-10 text-gray-400" />
<p class="text-sm text-gray-500"> <p class="text-sm text-gray-500">
<span <span
class="font-semibold text-primary-600 hover:text-primary-500" class="text-primary-600 hover:text-primary-500 font-semibold"
>Click to upload</span >Click to upload</span>
>
or drag and drop or drag and drop
</p> </p>
<p class="text-xs text-gray-400"> <p class="text-xs text-gray-400">
@@ -204,17 +202,15 @@
class="hidden" class="hidden"
multiple multiple
accept=".md,.txt" accept=".md,.txt"
onchange={(e) => (files = e.currentTarget.files)} onchange={(e) => (files = e.currentTarget.files)} />
/>
</div> </div>
</div> </div>
</div> </div>
<!-- Preview --> <!-- Preview -->
<div <div
class="border rounded-lg p-2 bg-gray-50 dark:bg-gray-900 max-h-64 overflow-y-auto" class="max-h-64 overflow-y-auto rounded-lg border bg-gray-50 p-2 dark:bg-gray-900">
> <h4 class="mb-2 flex justify-between font-bold">
<h4 class="font-bold mb-2 flex justify-between">
<span>Preview ({parsed_entries.length} entries)</span> <span>Preview ({parsed_entries.length} entries)</span>
{#if is_parsing} {#if is_parsing}
<RefreshCw class="animate-spin" /> <RefreshCw class="animate-spin" />
@@ -222,7 +218,7 @@
</h4> </h4>
{#if parsed_entries.length > 0} {#if parsed_entries.length > 0}
<table class="table table-compact w-full text-xs"> <table class="table-compact table w-full text-xs">
<thead> <thead>
<tr> <tr>
<th>Name</th> <th>Name</th>
@@ -234,9 +230,8 @@
{#each parsed_entries as entry, i (i)} {#each parsed_entries as entry, i (i)}
<tr> <tr>
<td <td
class="truncate max-w-[200px]" class="max-w-[200px] truncate"
title={entry.name}>{entry.name}</td title={entry.name}>{entry.name}</td>
>
<td>{entry.created_on?.substring(0, 10)}</td> <td>{entry.created_on?.substring(0, 10)}</td>
<td>{entry.tags.join(', ')}</td> <td>{entry.tags.join(', ')}</td>
</tr> </tr>
@@ -244,11 +239,11 @@
</tbody> </tbody>
</table> </table>
{:else if files && files.length > 0 && !is_parsing} {:else if files && files.length > 0 && !is_parsing}
<div class="text-center text-gray-500 py-4"> <div class="py-4 text-center text-gray-500">
No entries found in selected files. No entries found in selected files.
</div> </div>
{:else if !files} {:else if !files}
<div class="text-center text-gray-500 py-4"> <div class="py-4 text-center text-gray-500">
Select files to preview import. Select files to preview import.
</div> </div>
{/if} {/if}
@@ -257,8 +252,7 @@
<!-- Import Log --> <!-- Import Log -->
{#if import_log.length > 0} {#if import_log.length > 0}
<div <div
class="bg-black text-green-400 p-2 rounded text-xs font-mono max-h-32 overflow-y-auto" class="max-h-32 overflow-y-auto rounded bg-black p-2 font-mono text-xs text-green-400">
>
{#each import_log as log, i (i)} {#each import_log as log, i (i)}
<div>{log}</div> <div>{log}</div>
{/each} {/each}
@@ -269,14 +263,12 @@
<button <button
type="button" type="button"
class="btn preset-tonal-secondary" class="btn preset-tonal-secondary"
onclick={on_close}>Cancel</button onclick={on_close}>Cancel</button>
>
<button <button
type="button" type="button"
class="btn preset-filled-primary" class="btn preset-filled-primary"
disabled={parsed_entries.length === 0 || is_importing} disabled={parsed_entries.length === 0 || is_importing}
onclick={handle_import} onclick={handle_import}>
>
{#if is_importing} {#if is_importing}
Importing... Importing...
{:else} {:else}

View File

@@ -98,8 +98,7 @@
</script> </script>
<section <section
class="ae_meta flex flex-row flex-wrap gap-1 items-center justify-between w-full" class="ae_meta flex w-full flex-row flex-wrap items-center justify-between gap-1">
>
<!-- {lq__journal_entry_obj?.priority} <!-- {lq__journal_entry_obj?.priority}
{lq__journal_entry_obj?.sort} {lq__journal_entry_obj?.sort}
{lq__journal_entry_obj?.group} {lq__journal_entry_obj?.group}
@@ -113,9 +112,8 @@
obj_priority = !obj_priority; obj_priority = !obj_priority;
// update_journal_entry(); // update_journal_entry();
}} }}
class="btn-icon btn-icon-sm md:btn-icon-base preset-tonal-tertiary transition hover:preset-filled-tertiary-500" class="btn-icon btn-icon-sm md:btn-icon-base preset-tonal-tertiary hover:preset-filled-tertiary-500 transition"
title="Toggle priority of this journal entry" title="Toggle priority of this journal entry">
>
{#if obj_priority} {#if obj_priority}
<Flag strokeWidth="2.5" color="green" class="inline-block" /> <Flag strokeWidth="2.5" color="green" class="inline-block" />
{:else} {:else}
@@ -127,17 +125,15 @@
<!-- Set sort order (number) --> <!-- Set sort order (number) -->
<span <span
class:hidden={!$ae_loc.edit_mode} class:hidden={!$ae_loc.edit_mode}
class="flex flex-row flex-wrap items-center justify-center border border-gray-300 rounded-lg" class="flex flex-row flex-wrap items-center justify-center rounded-lg border border-gray-300">
>
<button <button
type="button" type="button"
onclick={() => { onclick={() => {
obj_sort = obj_sort ? obj_sort + 1 : 1; obj_sort = obj_sort ? obj_sort + 1 : 1;
// update_journal_entry(); // update_journal_entry();
}} }}
class="btn-icon btn-icon-sm preset-tonal-tertiary transition hover:preset-filled-tertiary-500" class="btn-icon btn-icon-sm preset-tonal-tertiary hover:preset-filled-tertiary-500 transition"
title="Increment sort order of this journal entry" title="Increment sort order of this journal entry">
>
<Plus strokeWidth="2.5" color="blue" /> <Plus strokeWidth="2.5" color="blue" />
</button> </button>
<span class="mx-1"> <span class="mx-1">
@@ -154,9 +150,8 @@
obj_sort = obj_sort ? obj_sort - 1 : 0; obj_sort = obj_sort ? obj_sort - 1 : 0;
// update_journal_entry(); // update_journal_entry();
}} }}
class="btn-icon btn-icon-sm preset-tonal-tertiary transition hover:preset-filled-tertiary-500" class="btn-icon btn-icon-sm preset-tonal-tertiary hover:preset-filled-tertiary-500 transition"
title="Decrement sort order of this journal entry" title="Decrement sort order of this journal entry">
>
<Minus strokeWidth="2.5" color="blue" /> <Minus strokeWidth="2.5" color="blue" />
</button> </button>
</span> </span>
@@ -171,13 +166,11 @@
}} }}
class:hidden={!$ae_loc.edit_mode} class:hidden={!$ae_loc.edit_mode}
class="input input-sm input-bordered w-24" class="input input-sm input-bordered w-24"
title="Set group (for sorting) of this journal entry" title="Set group (for sorting) of this journal entry" />
/>
<!-- Set archive datetime (string) --> <!-- Set archive datetime (string) -->
<span <span
class="flex flex-row flex-wrap items-center justify-center border border-gray-200 rounded-lg" class="flex flex-row flex-wrap items-center justify-center rounded-lg border border-gray-200">
>
<input <input
type="datetime-local" type="datetime-local"
bind:value={obj_archive_on} bind:value={obj_archive_on}
@@ -187,8 +180,7 @@
}} }}
class:hidden={!$ae_loc.edit_mode} class:hidden={!$ae_loc.edit_mode}
class="input input-sm input-bordered w-auto border-none" class="input input-sm input-bordered w-auto border-none"
title="Set archive on datetime for archiving this journal entry" title="Set archive on datetime for archiving this journal entry" />
/>
{#if obj_archive_on} {#if obj_archive_on}
<!-- Button to clear the datetime --> <!-- Button to clear the datetime -->
@@ -200,8 +192,7 @@
}} }}
class:hidden={!$ae_loc.edit_mode} class:hidden={!$ae_loc.edit_mode}
class="btn-icon btn-icon-sm preset-tonal-warning hover:preset-filled-warning-500 transition" class="btn-icon btn-icon-sm preset-tonal-warning hover:preset-filled-warning-500 transition"
title="Clear the archive on datetime for this journal entry" title="Clear the archive on datetime for this journal entry">
>
<X strokeWidth="2.5" color="red" /> <X strokeWidth="2.5" color="red" />
<!-- <span class="hidden">Clear Archive</span> --> <!-- <span class="hidden">Clear Archive</span> -->
</button> </button>
@@ -221,8 +212,7 @@
}} }}
class:hidden={!$ae_loc.edit_mode} class:hidden={!$ae_loc.edit_mode}
class="btn-icon btn-icon-sm preset-tonal-warning hover:preset-filled-warning-500 transition" class="btn-icon btn-icon-sm preset-tonal-warning hover:preset-filled-warning-500 transition"
title="Set the archive on datetime for this journal entry" title="Set the archive on datetime for this journal entry">
>
<Clock strokeWidth="2.5" color="blue" /> <Clock strokeWidth="2.5" color="blue" />
<!-- <span class="hidden">Set Archive</span> --> <!-- <span class="hidden">Set Archive</span> -->
</button> </button>
@@ -237,8 +227,7 @@
update_journal_entry(); update_journal_entry();
}} }}
class="btn btn-sm md:btn-md preset-tonal-warning hover:preset-filled-warning-500 transition" class="btn btn-sm md:btn-md preset-tonal-warning hover:preset-filled-warning-500 transition"
title="Toggle visibility of this journal entry" title="Toggle visibility of this journal entry">
>
{#if obj_hide} {#if obj_hide}
<EyeOff strokeWidth="1" color="red" class="inline-block" /> <EyeOff strokeWidth="1" color="red" class="inline-block" />
<span class="hidden md:inline">Hidden</span> <span class="hidden md:inline">Hidden</span>
@@ -257,8 +246,7 @@
}} }}
class:hidden={!$ae_loc.administrator_access || !$ae_loc.edit_mode} class:hidden={!$ae_loc.administrator_access || !$ae_loc.edit_mode}
class="btn btn-sm md:btn-md preset-tonal-error hover:preset-filled-error-500 transition" class="btn btn-sm md:btn-md preset-tonal-error hover:preset-filled-error-500 transition"
title="Toggle enable status of this journal entry" title="Toggle enable status of this journal entry">
>
{#if obj_enable} {#if obj_enable}
<ShieldCheck strokeWidth="2.5" color="green" class="inline-block" /> <ShieldCheck strokeWidth="2.5" color="green" class="inline-block" />
<span class="hidden md:inline">Enabled</span> <span class="hidden md:inline">Enabled</span>
@@ -304,8 +292,7 @@
}} }}
class:hidden={!$ae_loc.edit_mode} class:hidden={!$ae_loc.edit_mode}
class="btn btn-sm md:btn-md preset-tonal-error hover:preset-filled-error-500 transition" class="btn btn-sm md:btn-md preset-tonal-error hover:preset-filled-error-500 transition"
title="Delete this journal entry" title="Delete this journal entry">
>
{#if $ae_loc.administrator_access && $ae_loc.edit_mode} {#if $ae_loc.administrator_access && $ae_loc.edit_mode}
<FileX strokeWidth="2.5" color="red" class="inline-block" /> <FileX strokeWidth="2.5" color="red" class="inline-block" />
<span class="hidden md:inline">Delete</span> <span class="hidden md:inline">Delete</span>
@@ -316,8 +303,7 @@
</button> </button>
<span <span
class="flex flex-row items-center justify-center text-sm text-gray-500" class="flex flex-row items-center justify-center text-sm text-gray-500">
>
{#if !$ae_loc.edit_mode} {#if !$ae_loc.edit_mode}
<span class=""> <span class="">
{ae_util.iso_datetime_formatter( {ae_util.iso_datetime_formatter(
@@ -344,19 +330,18 @@
<!-- Select option list of Journals to choose from. This is used to assign the Journal Entry to a different Journal ID. --> <!-- Select option list of Journals to choose from. This is used to assign the Journal Entry to a different Journal ID. -->
{#if $ae_loc.edit_mode && lq__journal_obj_li?.length} {#if $ae_loc.edit_mode && lq__journal_obj_li?.length}
<div <div
class="flex flex-row flex-wrap gap-2 items-center justify-start border border-gray-200 rounded-lg" class="flex flex-row flex-wrap items-center justify-start gap-2 rounded-lg border border-gray-200">
>
<SquareLibrary size="1em" class="mx-1" /> <SquareLibrary size="1em" class="mx-1" />
<span class="text-sm text-gray-500 hidden sm:inline"> <span class="hidden text-sm text-gray-500 sm:inline">
Journal: Journal:
</span> </span>
<select <select
class="novi_btn btn btn-secondary btn-sm class="novi_btn btn btn-secondary btn-sm
preset-tonal-primary preset-tonal-primary
hover:preset-filled-primary-500 hover:preset-filled-primary-500
transition
text-xs
border-none border-none
text-xs
transition
" "
bind:value={tmp_entry_obj.journal_id} bind:value={tmp_entry_obj.journal_id}
onchange={(event) => { onchange={(event) => {
@@ -372,14 +357,12 @@
change_journal_id(); change_journal_id();
} }
}} }}
title="Select a different journal for this entry" title="Select a different journal for this entry">
>
<option value="">Select Journal</option> <option value="">Select Journal</option>
{#each lq__journal_obj_li as journal (journal.journal_id)} {#each lq__journal_obj_li as journal (journal.journal_id)}
<option <option
value={journal.journal_id} value={journal.journal_id}
title={`Journal: ${journal.name}`} title={`Journal: ${journal.name}`}>
>
{journal.name} {journal.name}
</option> </option>
{/each} {/each}

View File

@@ -30,33 +30,49 @@ describe('Journal Entry Visibility Filtering', () => {
{ id: '1', name: 'Normal Entry', hide: false, enable: true }, { id: '1', name: 'Normal Entry', hide: false, enable: true },
{ id: '2', name: 'Hidden Entry', hide: true, enable: true }, { id: '2', name: 'Hidden Entry', hide: true, enable: true },
{ id: '3', name: 'Disabled Entry', hide: false, enable: false }, { id: '3', name: 'Disabled Entry', hide: false, enable: false },
{ id: '4', name: 'Hidden & Disabled', hide: true, enable: false }, { id: '4', name: 'Hidden & Disabled', hide: true, enable: false }
]; ];
it('should show only normal entries when Edit Mode is OFF (Manager)', () => { it('should show only normal entries when Edit Mode is OFF (Manager)', () => {
const ae_loc = { edit_mode: false, trusted_access: true, administrator_access: true }; const ae_loc = {
edit_mode: false,
trusted_access: true,
administrator_access: true
};
const result = filterEntries(mockEntries, ae_loc); const result = filterEntries(mockEntries, ae_loc);
expect(result?.length).toBe(1); expect(result?.length).toBe(1);
expect(result?.[0].id).toBe('1'); expect(result?.[0].id).toBe('1');
}); });
it('should show hidden entries to Trusted users when Edit Mode is ON', () => { it('should show hidden entries to Trusted users when Edit Mode is ON', () => {
const ae_loc = { edit_mode: true, trusted_access: true, administrator_access: false }; const ae_loc = {
edit_mode: true,
trusted_access: true,
administrator_access: false
};
const result = filterEntries(mockEntries, ae_loc); const result = filterEntries(mockEntries, ae_loc);
// Should see Normal (1) and Hidden (2). Should NOT see Disabled (3, 4) // Should see Normal (1) and Hidden (2). Should NOT see Disabled (3, 4)
expect(result?.length).toBe(2); expect(result?.length).toBe(2);
expect(result?.map(r => r.id)).toContain('1'); expect(result?.map((r) => r.id)).toContain('1');
expect(result?.map(r => r.id)).toContain('2'); expect(result?.map((r) => r.id)).toContain('2');
}); });
it('should show everything to Administrators when Edit Mode is ON', () => { it('should show everything to Administrators when Edit Mode is ON', () => {
const ae_loc = { edit_mode: true, trusted_access: true, administrator_access: true }; const ae_loc = {
edit_mode: true,
trusted_access: true,
administrator_access: true
};
const result = filterEntries(mockEntries, ae_loc); const result = filterEntries(mockEntries, ae_loc);
expect(result?.length).toBe(4); expect(result?.length).toBe(4);
}); });
it('should hide everything sensitive to Public users even if Edit Mode is ON (Safety Check)', () => { it('should hide everything sensitive to Public users even if Edit Mode is ON (Safety Check)', () => {
const ae_loc = { edit_mode: true, trusted_access: false, administrator_access: false }; const ae_loc = {
edit_mode: true,
trusted_access: false,
administrator_access: false
};
const result = filterEntries(mockEntries, ae_loc); const result = filterEntries(mockEntries, ae_loc);
expect(result?.length).toBe(1); expect(result?.length).toBe(1);
expect(result?.[0].id).toBe('1'); expect(result?.[0].id).toBe('1');