Prettier for Journals
This commit is contained in:
@@ -1,67 +1,76 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
/** @type {import('./$types').LayoutProps} */
|
/** @type {import('./$types').LayoutProps} */
|
||||||
let log_lvl = $state(0);
|
let log_lvl = $state(0);
|
||||||
|
|
||||||
// *** Import Svelte specific
|
// *** Import Svelte specific
|
||||||
import { untrack } from 'svelte';
|
import { untrack } from 'svelte';
|
||||||
// import { browser } from '$app/environment';
|
// import { browser } from '$app/environment';
|
||||||
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';
|
||||||
import {
|
import {
|
||||||
journals_loc,
|
journals_loc,
|
||||||
journals_slct,
|
journals_slct,
|
||||||
journals_trig
|
journals_trig
|
||||||
} from '$lib/ae_journals/ae_journals_stores';
|
} from '$lib/ae_journals/ae_journals_stores';
|
||||||
import Element_data_store from '$lib/elements/element_data_store.svelte';
|
import Element_data_store from '$lib/elements/element_data_store.svelte';
|
||||||
import Help_tech from '$lib/app_components/e_app_help_tech.svelte';
|
import Help_tech from '$lib/app_components/e_app_help_tech.svelte';
|
||||||
|
|
||||||
// *** Setup Svelte properties
|
// *** Setup Svelte properties
|
||||||
interface Props {
|
interface Props {
|
||||||
data: any;
|
data: any;
|
||||||
children: any;
|
children: any;
|
||||||
}
|
}
|
||||||
let { data, children }: Props = $props();
|
let { data, children }: Props = $props();
|
||||||
|
|
||||||
// Use effects for store initializations to prevent render-phase updates
|
// Use effects for store initializations to prevent render-phase updates
|
||||||
$effect(() => {
|
$effect(() => {
|
||||||
untrack(() => {
|
untrack(() => {
|
||||||
if ($slct.account_id !== data.account_id) {
|
if ($slct.account_id !== data.account_id) {
|
||||||
$slct.account_id = data.account_id;
|
$slct.account_id = data.account_id;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
let ae_acct = $derived(data[data.account_id]);
|
let ae_acct = $derived(data[data.account_id]);
|
||||||
|
|
||||||
$effect(() => {
|
$effect(() => {
|
||||||
if (ae_acct) {
|
if (ae_acct) {
|
||||||
untrack(() => {
|
untrack(() => {
|
||||||
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;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
let nav_y_height = $state(0);
|
let nav_y_height = $state(0);
|
||||||
|
|
||||||
let box: any = $state(null);
|
let box: any = $state(null);
|
||||||
let xLeft = $state(0);
|
let xLeft = $state(0);
|
||||||
let xScroll = $state(0);
|
let xScroll = $state(0);
|
||||||
let xWidth = $state(0);
|
let xWidth = $state(0);
|
||||||
let yTop = $state(0);
|
let yTop = $state(0);
|
||||||
let yScroll = $state(0);
|
let yScroll = $state(0);
|
||||||
let yHeight = $state(0);
|
let yHeight = $state(0);
|
||||||
|
|
||||||
function handle_scroll() {
|
function handle_scroll() {
|
||||||
// console.log(`handle_scroll() called`);
|
// console.log(`handle_scroll() called`);
|
||||||
if (box) {
|
if (box) {
|
||||||
xLeft = box.scrollLeft;
|
xLeft = box.scrollLeft;
|
||||||
@@ -72,15 +81,15 @@
|
|||||||
yScroll = box.scrollHeight;
|
yScroll = box.scrollHeight;
|
||||||
// console.log(`handle_scroll() called: ${yTop}`);
|
// console.log(`handle_scroll() called: ${yTop}`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function scroll_container() {
|
function scroll_container() {
|
||||||
return (
|
return (
|
||||||
document.getElementById('ae_main_content') ||
|
document.getElementById('ae_main_content') ||
|
||||||
document.documentElement ||
|
document.documentElement ||
|
||||||
document.body
|
document.body
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<svelte:head>
|
<svelte:head>
|
||||||
@@ -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 Æ (Aether) means in the title text or similar! -->
|
<!-- Be sure to explain what Æ (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.
|
||||||
|
|||||||
@@ -1,53 +1,60 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
/**
|
/**
|
||||||
* src/routes/journals/+page.svelte
|
* src/routes/journals/+page.svelte
|
||||||
* Modernized Journals Index View
|
* Modernized Journals Index View
|
||||||
* Focus: Simplicity for regular users, power tools for edit mode.
|
* Focus: Simplicity for regular users, power tools for edit mode.
|
||||||
*/
|
*/
|
||||||
// import { onMount } from 'svelte';
|
// import { onMount } from 'svelte';
|
||||||
import { goto } from '$app/navigation';
|
import { goto } from '$app/navigation';
|
||||||
|
|
||||||
// *** Icons
|
// *** Icons
|
||||||
import { BookPlus, FileUp, LoaderCircle, Sparkles, SquareLibrary, Wrench } from '@lucide/svelte';
|
import {
|
||||||
// *** Libraries & Stores
|
BookPlus,
|
||||||
import { liveQuery } from 'dexie';
|
FileUp,
|
||||||
import { Modal } from 'flowbite-svelte';
|
LoaderCircle,
|
||||||
import { db_core } from '$lib/ae_core/db_core';
|
Sparkles,
|
||||||
import { db_journals } from '$lib/ae_journals/db_journals';
|
SquareLibrary,
|
||||||
import { journals_func } from '$lib/ae_journals/ae_journals_functions';
|
Wrench
|
||||||
import { ae_loc, ae_api, slct } from '$lib/stores/ae_stores';
|
} from '@lucide/svelte';
|
||||||
import {
|
// *** Libraries & Stores
|
||||||
|
import { liveQuery } from 'dexie';
|
||||||
|
import { Modal } from 'flowbite-svelte';
|
||||||
|
import { db_core } from '$lib/ae_core/db_core';
|
||||||
|
import { db_journals } from '$lib/ae_journals/db_journals';
|
||||||
|
import { journals_func } from '$lib/ae_journals/ae_journals_functions';
|
||||||
|
import { ae_loc, ae_api, slct } from '$lib/stores/ae_stores';
|
||||||
|
import {
|
||||||
journals_loc,
|
journals_loc,
|
||||||
journals_sess,
|
journals_sess,
|
||||||
journals_slct,
|
journals_slct,
|
||||||
journals_trig
|
journals_trig
|
||||||
} from '$lib/ae_journals/ae_journals_stores';
|
} from '$lib/ae_journals/ae_journals_stores';
|
||||||
|
|
||||||
// *** Components
|
// *** Components
|
||||||
import AE_Comp_Modal_Journal_Config from './ae_comp__modal_journal_config.svelte';
|
import AE_Comp_Modal_Journal_Config from './ae_comp__modal_journal_config.svelte';
|
||||||
import Journal_obj_li from './ae_comp__journal_obj_li.svelte';
|
import Journal_obj_li from './ae_comp__journal_obj_li.svelte';
|
||||||
import AE_Comp_Journal_Entry_Quick_Add from './ae_comp__journal_entry_quick_add.svelte';
|
import AE_Comp_Journal_Entry_Quick_Add from './ae_comp__journal_entry_quick_add.svelte';
|
||||||
import AE_Comp_Modal_Journal_Import from './ae_comp__modal_journal_import.svelte';
|
import AE_Comp_Modal_Journal_Import from './ae_comp__modal_journal_import.svelte';
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
data: any;
|
data: any;
|
||||||
}
|
}
|
||||||
|
|
||||||
let { data }: Props = $props();
|
let { data }: Props = $props();
|
||||||
|
|
||||||
// *** State
|
// *** State
|
||||||
let show_import_modal = $state(false);
|
let show_import_modal = $state(false);
|
||||||
let log_lvl = 0;
|
let log_lvl = 0;
|
||||||
|
|
||||||
// *** LiveQueries
|
// *** LiveQueries
|
||||||
let lq__account = $derived(
|
let lq__account = $derived(
|
||||||
liveQuery(async () => {
|
liveQuery(async () => {
|
||||||
if (!$slct.account_id) return null;
|
if (!$slct.account_id) return null;
|
||||||
return await db_core.account.get($slct.account_id);
|
return await db_core.account.get($slct.account_id);
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
|
||||||
let lq__journal_obj_li = $derived(
|
let lq__journal_obj_li = $derived(
|
||||||
liveQuery(async () => {
|
liveQuery(async () => {
|
||||||
return await db_journals.journal
|
return await db_journals.journal
|
||||||
.where('person_id')
|
.where('person_id')
|
||||||
@@ -55,9 +62,9 @@
|
|||||||
.reverse()
|
.reverse()
|
||||||
.sortBy('tmp_sort_3');
|
.sortBy('tmp_sort_3');
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
|
||||||
async function create_journal() {
|
async function create_journal() {
|
||||||
if (!confirm('Create a new journal?')) return;
|
if (!confirm('Create a new journal?')) return;
|
||||||
|
|
||||||
const name = $journals_sess.journal.new_journal_name;
|
const name = $journals_sess.journal.new_journal_name;
|
||||||
@@ -87,17 +94,15 @@
|
|||||||
} else {
|
} else {
|
||||||
alert('Please provide a name and type.');
|
alert('Please provide a name and type.');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</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={() => {}} />
|
||||||
/>
|
|
||||||
|
|||||||
@@ -1,41 +1,41 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
/** @type {import('./$types').LayoutProps} */
|
/** @type {import('./$types').LayoutProps} */
|
||||||
let log_lvl: number = $state(0);
|
let log_lvl: number = $state(0);
|
||||||
|
|
||||||
let { data, children } = $props();
|
let { data, children } = $props();
|
||||||
|
|
||||||
// *** Import Svelte specific
|
// *** Import Svelte specific
|
||||||
import { goto } from '$app/navigation';
|
import { goto } from '$app/navigation';
|
||||||
|
|
||||||
// *** Import other supporting libraries
|
// *** Import other supporting libraries
|
||||||
import { FilePlus, Notebook, SquareLibrary, X } from '@lucide/svelte';
|
import { FilePlus, Notebook, SquareLibrary, X } from '@lucide/svelte';
|
||||||
|
|
||||||
import { liveQuery } from 'dexie';
|
import { liveQuery } from 'dexie';
|
||||||
|
|
||||||
import { db_journals } from '$lib/ae_journals/db_journals';
|
import { db_journals } from '$lib/ae_journals/db_journals';
|
||||||
import { ae_loc, ae_api, slct } from '$lib/stores/ae_stores';
|
import { ae_loc, ae_api, slct } from '$lib/stores/ae_stores';
|
||||||
import {
|
import {
|
||||||
journals_loc,
|
journals_loc,
|
||||||
journals_sess,
|
journals_sess,
|
||||||
journals_slct
|
journals_slct
|
||||||
} from '$lib/ae_journals/ae_journals_stores';
|
} from '$lib/ae_journals/ae_journals_stores';
|
||||||
import { journals_func } from '$lib/ae_journals/ae_journals_functions';
|
import { journals_func } from '$lib/ae_journals/ae_journals_functions';
|
||||||
import type { ae_JournalEntry } from '$lib/types/ae_types';
|
import type { ae_JournalEntry } from '$lib/types/ae_types';
|
||||||
|
|
||||||
import Journal_entry_obj_qry from './../ae_comp__journal_entry_obj_qry.svelte';
|
import Journal_entry_obj_qry from './../ae_comp__journal_entry_obj_qry.svelte';
|
||||||
|
|
||||||
// NOTE: Derived from data.account_id (prop) instead of $slct.account_id (store)
|
// NOTE: Derived from data.account_id (prop) instead of $slct.account_id (store)
|
||||||
// to prevent circular dependency loops during hydration.
|
// to prevent circular dependency loops during hydration.
|
||||||
let ae_acct = $derived(data[data.account_id]);
|
let ae_acct = $derived(data[data.account_id]);
|
||||||
$effect(() => {
|
$effect(() => {
|
||||||
if (log_lvl) {
|
if (log_lvl) {
|
||||||
console.log(`ae_acct = `, ae_acct);
|
console.log(`ae_acct = `, ae_acct);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
let show_menu__all_journals: boolean = $state(false);
|
let show_menu__all_journals: boolean = $state(false);
|
||||||
|
|
||||||
let lq__journal_obj = $derived(
|
let lq__journal_obj = $derived(
|
||||||
liveQuery(async () => {
|
liveQuery(async () => {
|
||||||
let results = await db_journals.journal.get(
|
let results = await db_journals.journal.get(
|
||||||
$journals_slct?.journal_id ?? ''
|
$journals_slct?.journal_id ?? ''
|
||||||
@@ -53,9 +53,9 @@
|
|||||||
|
|
||||||
return results;
|
return results;
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
|
||||||
$effect(() => {
|
$effect(() => {
|
||||||
if (log_lvl) {
|
if (log_lvl) {
|
||||||
console.log(
|
console.log(
|
||||||
`lq__journal_obj: journal_id = ${$journals_slct?.journal_id}`
|
`lq__journal_obj: journal_id = ${$journals_slct?.journal_id}`
|
||||||
@@ -77,7 +77,7 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<!-- Svelte layout for a Journal ID page and children -->
|
<!-- Svelte layout for a Journal ID page and children -->
|
||||||
@@ -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}× Recent Entries...
|
.length}× 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>
|
||||||
|
|||||||
@@ -1,23 +1,23 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
/** @type {import('./$types').PageData} */
|
/** @type {import('./$types').PageData} */
|
||||||
let log_lvl: number = $state(0);
|
let log_lvl: number = $state(0);
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
data: any;
|
data: any;
|
||||||
}
|
}
|
||||||
|
|
||||||
let { data }: Props = $props();
|
let { data }: Props = $props();
|
||||||
|
|
||||||
// *** Import Svelte specific
|
// *** Import Svelte specific
|
||||||
import { browser } from '$app/environment';
|
import { browser } from '$app/environment';
|
||||||
import { untrack } from 'svelte';
|
import { untrack } from 'svelte';
|
||||||
|
|
||||||
// *** Import other supporting libraries
|
// *** Import other supporting libraries
|
||||||
import { liveQuery } from 'dexie';
|
import { liveQuery } from 'dexie';
|
||||||
|
|
||||||
// *** 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 {
|
||||||
ae_snip,
|
ae_snip,
|
||||||
ae_loc,
|
ae_loc,
|
||||||
ae_sess,
|
ae_sess,
|
||||||
@@ -25,63 +25,61 @@
|
|||||||
ae_trig,
|
ae_trig,
|
||||||
slct,
|
slct,
|
||||||
slct_trigger
|
slct_trigger
|
||||||
} from '$lib/stores/ae_stores';
|
} from '$lib/stores/ae_stores';
|
||||||
import { db_journals } from '$lib/ae_journals/db_journals';
|
import { db_journals } from '$lib/ae_journals/db_journals';
|
||||||
import {
|
import {
|
||||||
journals_loc,
|
journals_loc,
|
||||||
journals_sess,
|
journals_sess,
|
||||||
journals_slct,
|
journals_slct,
|
||||||
journals_prom,
|
journals_prom,
|
||||||
journals_trig
|
journals_trig
|
||||||
} from '$lib/ae_journals/ae_journals_stores';
|
} from '$lib/ae_journals/ae_journals_stores';
|
||||||
import { journals_func } from '$lib/ae_journals/ae_journals_functions';
|
import { journals_func } from '$lib/ae_journals/ae_journals_functions';
|
||||||
|
|
||||||
import AeCompJournalObjIdView from './../ae_comp__journal_obj_id_view.svelte';
|
import AeCompJournalObjIdView from './../ae_comp__journal_obj_id_view.svelte';
|
||||||
import Journal_entry_obj_li_wrapper from './../ae_comp__journal_entry_obj_li_wrapper.svelte';
|
import Journal_entry_obj_li_wrapper from './../ae_comp__journal_entry_obj_li_wrapper.svelte';
|
||||||
import AeCompModalJournalExport from '../ae_comp__modal_journal_export.svelte';
|
import AeCompModalJournalExport from '../ae_comp__modal_journal_export.svelte';
|
||||||
import AeCompModalJournalImport from '../ae_comp__modal_journal_import.svelte';
|
import AeCompModalJournalImport from '../ae_comp__modal_journal_import.svelte';
|
||||||
|
|
||||||
// Variables
|
// Variables
|
||||||
let ae_acct = $derived(data[data.account_id]);
|
let ae_acct = $derived(data[data.account_id]);
|
||||||
let show_export_modal = $state(false);
|
let show_export_modal = $state(false);
|
||||||
let show_import_modal = $state(false);
|
let show_import_modal = $state(false);
|
||||||
|
|
||||||
let search_id_li: Array<string> = $state([]);
|
let search_id_li: Array<string> = $state([]);
|
||||||
let search_debounce_timer: any = null;
|
let search_debounce_timer: any = null;
|
||||||
let last_search_id = 0;
|
let last_search_id = 0;
|
||||||
let last_executed_key = ''; // Search Guard Key
|
let last_executed_key = ''; // Search Guard Key
|
||||||
|
|
||||||
function handle_import_complete() {
|
function handle_import_complete() {
|
||||||
// Trigger a refresh of the journal entry list
|
// Trigger a refresh of the journal entry list
|
||||||
if ($journals_loc.entry.search_version === undefined)
|
if ($journals_loc.entry.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++;
|
||||||
}
|
}
|
||||||
|
|
||||||
$effect(() => {
|
$effect(() => {
|
||||||
if (!ae_acct) return;
|
if (!ae_acct) return;
|
||||||
untrack(() => {
|
untrack(() => {
|
||||||
$journals_slct.journal_id = ae_acct.slct.journal_id;
|
$journals_slct.journal_id = ae_acct.slct.journal_id;
|
||||||
$journals_slct.journal_entry_id = null;
|
$journals_slct.journal_entry_id = null;
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
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 ?? ''
|
|
||||||
);
|
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
|
||||||
// Stable LiveQuery Pattern (Aether UI V3)
|
// Stable LiveQuery Pattern (Aether UI V3)
|
||||||
// Re-wrapped in $derived to ensure the observable instance remains stable
|
// Re-wrapped in $derived to ensure the observable instance remains stable
|
||||||
// unless the underlying dependencies (ids, search context) change.
|
// unless the underlying dependencies (ids, search context) change.
|
||||||
// Important: keep the `liveQuery` closure free of transient reactive
|
// Important: keep the `liveQuery` closure free of transient reactive
|
||||||
// references — capture stable values (ids, search keys) so the observable
|
// references — capture stable values (ids, search keys) so the observable
|
||||||
// isn't recreated unnecessarily on every render. Use `search_id_li` or
|
// isn't recreated unnecessarily on every render. Use `search_id_li` or
|
||||||
// other plain arrays/values as explicit dependencies.
|
// other plain arrays/values as explicit dependencies.
|
||||||
let lq__journal_entry_obj_li = $derived(
|
let lq__journal_entry_obj_li = $derived(
|
||||||
liveQuery(async () => {
|
liveQuery(async () => {
|
||||||
const ids = search_id_li;
|
const ids = search_id_li;
|
||||||
const journal_id = $lq__journal_obj?.journal_id;
|
const journal_id = $lq__journal_obj?.journal_id;
|
||||||
@@ -111,11 +109,11 @@
|
|||||||
|
|
||||||
return [];
|
return [];
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
|
||||||
// Standardized Reactive Search Pattern (Aether UI V3)
|
// Standardized Reactive Search Pattern (Aether UI V3)
|
||||||
// 1. Isolate dependencies into a stable derived object
|
// 1. Isolate dependencies into a stable derived object
|
||||||
let search_params = $derived({
|
let search_params = $derived({
|
||||||
v: $journals_loc.entry.search_version,
|
v: $journals_loc.entry.search_version,
|
||||||
str: ($journals_loc.entry.qry__search_text ?? '').toLowerCase().trim(),
|
str: ($journals_loc.entry.qry__search_text ?? '').toLowerCase().trim(),
|
||||||
cat: $journals_loc.entry.qry__category_code,
|
cat: $journals_loc.entry.qry__category_code,
|
||||||
@@ -125,10 +123,10 @@
|
|||||||
journal_id: $journals_slct.journal_id,
|
journal_id: $journals_slct.journal_id,
|
||||||
person_id: $ae_loc.person_id,
|
person_id: $ae_loc.person_id,
|
||||||
remote_first: $journals_loc.entry.qry__remote_first
|
remote_first: $journals_loc.entry.qry__remote_first
|
||||||
});
|
});
|
||||||
|
|
||||||
// 2. Controlled effect for triggering searches
|
// 2. Controlled effect for triggering searches
|
||||||
$effect(() => {
|
$effect(() => {
|
||||||
// Establishes reactive dependency on search_params
|
// Establishes reactive dependency on search_params
|
||||||
const params = search_params;
|
const params = search_params;
|
||||||
|
|
||||||
@@ -143,9 +141,9 @@
|
|||||||
return () => {
|
return () => {
|
||||||
if (search_debounce_timer) clearTimeout(search_debounce_timer);
|
if (search_debounce_timer) clearTimeout(search_debounce_timer);
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
async function handle_search_refresh(params: any) {
|
async function handle_search_refresh(params: any) {
|
||||||
// 1. Guard: Check if criteria actually changed
|
// 1. Guard: Check if criteria actually changed
|
||||||
const qry_key = JSON.stringify(params);
|
const qry_key = JSON.stringify(params);
|
||||||
if (qry_key === last_executed_key) return;
|
if (qry_key === last_executed_key) return;
|
||||||
@@ -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)
|
||||||
@@ -280,16 +276,16 @@
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (browser) {
|
if (browser) {
|
||||||
window.parent.postMessage(
|
window.parent.postMessage(
|
||||||
{ journal_id: $journals_slct?.journal_id ?? null },
|
{ journal_id: $journals_slct?.journal_id ?? null },
|
||||||
'*'
|
'*'
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
import { LoaderCircle } from '@lucide/svelte';
|
import { LoaderCircle } from '@lucide/svelte';
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<svelte:head>
|
<svelte:head>
|
||||||
@@ -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>
|
||||||
|
|||||||
@@ -1,60 +1,55 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
/** @type {import('./$types').PageData} */
|
/** @type {import('./$types').PageData} */
|
||||||
let log_lvl: number = $state(0);
|
let log_lvl: number = $state(0);
|
||||||
|
|
||||||
// *** Import Svelte specific
|
// *** Import Svelte specific
|
||||||
import { untrack } from 'svelte';
|
import { untrack } from 'svelte';
|
||||||
import { browser } from '$app/environment';
|
import { browser } from '$app/environment';
|
||||||
|
|
||||||
// *** Import other supporting libraries
|
// *** Import other supporting libraries
|
||||||
import { liveQuery } from 'dexie';
|
import { liveQuery } from 'dexie';
|
||||||
|
|
||||||
// *** Import Aether specific variables and functions
|
// *** Import Aether specific variables and functions
|
||||||
// import type { key_val } from '$lib/ae_stores';
|
// import type { key_val } from '$lib/ae_stores';
|
||||||
import { ae_util } from '$lib/ae_utils/ae_utils';
|
import { ae_util } from '$lib/ae_utils/ae_utils';
|
||||||
// import { core_func } from '$lib/ae_core/ae_core_functions';
|
// import { core_func } from '$lib/ae_core/ae_core_functions';
|
||||||
import { 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,
|
import {
|
||||||
ae_sess,
|
|
||||||
ae_api,
|
|
||||||
ae_trig,
|
|
||||||
} from '$lib/stores/ae_stores';
|
|
||||||
import {
|
|
||||||
journals_loc,
|
journals_loc,
|
||||||
journals_sess,
|
journals_sess,
|
||||||
journals_slct,
|
journals_slct,
|
||||||
journals_prom,
|
journals_prom,
|
||||||
journals_trig
|
journals_trig
|
||||||
} from '$lib/ae_journals/ae_journals_stores';
|
} from '$lib/ae_journals/ae_journals_stores';
|
||||||
|
|
||||||
import Journal_entry_view from './../../../ae_comp__journal_entry_obj_id_view.svelte';
|
import Journal_entry_view from './../../../ae_comp__journal_entry_obj_id_view.svelte';
|
||||||
// import Element_data_store from '$lib/elements/element_data_store.svelte';
|
// import Element_data_store from '$lib/elements/element_data_store.svelte';
|
||||||
import AeCompModalJournalExport from '../../../ae_comp__modal_journal_export.svelte';
|
import AeCompModalJournalExport from '../../../ae_comp__modal_journal_export.svelte';
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
data: any;
|
data: any;
|
||||||
}
|
}
|
||||||
|
|
||||||
let { data }: Props = $props();
|
let { data }: Props = $props();
|
||||||
|
|
||||||
// let ae_promises: key_val = {};
|
// let ae_promises: key_val = {};
|
||||||
// let ae_tmp: key_val = {};
|
// let ae_tmp: key_val = {};
|
||||||
// let ae_triggers: key_val = {};
|
// let ae_triggers: key_val = {};
|
||||||
|
|
||||||
// Variables
|
// Variables
|
||||||
// *** Quickly pull out data from parent(s)
|
// *** Quickly pull out data from parent(s)
|
||||||
let ae_acct = $derived(data[data.account_id]);
|
let ae_acct = $derived(data[data.account_id]);
|
||||||
let show_export_modal = $state(false);
|
let show_export_modal = $state(false);
|
||||||
|
|
||||||
$effect(() => {
|
$effect(() => {
|
||||||
if (!ae_acct) return;
|
if (!ae_acct) return;
|
||||||
if (log_lvl) console.log(`ae_acct = `, ae_acct);
|
if (log_lvl) console.log(`ae_acct = `, ae_acct);
|
||||||
untrack(() => {
|
untrack(() => {
|
||||||
$journals_slct.journal_id = ae_acct.slct.journal_id;
|
$journals_slct.journal_id = ae_acct.slct.journal_id;
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
let lq__journal_obj = $derived(
|
let lq__journal_obj = $derived(
|
||||||
liveQuery(async () => {
|
liveQuery(async () => {
|
||||||
let results = await db_journals.journal.get(
|
let results = await db_journals.journal.get(
|
||||||
$journals_slct?.journal_id ?? ''
|
$journals_slct?.journal_id ?? ''
|
||||||
@@ -72,9 +67,9 @@
|
|||||||
|
|
||||||
return results;
|
return results;
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
|
||||||
$effect(() => {
|
$effect(() => {
|
||||||
if (log_lvl) {
|
if (log_lvl) {
|
||||||
console.log(
|
console.log(
|
||||||
`lq__journal_obj: journal_id = ${$journals_slct?.journal_id}`
|
`lq__journal_obj: journal_id = ${$journals_slct?.journal_id}`
|
||||||
@@ -96,9 +91,9 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
let lq__journal_obj_li = $derived(
|
let lq__journal_obj_li = $derived(
|
||||||
liveQuery(async () => {
|
liveQuery(async () => {
|
||||||
let results = await db_journals.journal
|
let results = await db_journals.journal
|
||||||
.where('person_id')
|
.where('person_id')
|
||||||
@@ -117,9 +112,9 @@
|
|||||||
|
|
||||||
return results;
|
return results;
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
|
||||||
$effect(() => {
|
$effect(() => {
|
||||||
if (log_lvl) {
|
if (log_lvl) {
|
||||||
console.log(`lq__journal_obj_li: person_id = ${$ae_loc.person_id}`);
|
console.log(`lq__journal_obj_li: person_id = ${$ae_loc.person_id}`);
|
||||||
console.log(`lq__journal_obj_li: results = `, lq__journal_obj_li);
|
console.log(`lq__journal_obj_li: results = `, lq__journal_obj_li);
|
||||||
@@ -140,19 +135,19 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// For some reason data.params.journal_entry_id (or whatever param) is not being passed to this page when loaded by a link from another page. This seems to be a bug with Svelte or SvelteKit. Hopefully fixed in a future version 5? 2024-11-06
|
// For some reason data.params.journal_entry_id (or whatever param) is not being passed to this page when loaded by a link from another page. This seems to be a bug with Svelte or SvelteKit. Hopefully fixed in a future version 5? 2024-11-06
|
||||||
// NOTE: This must remain reactive (in an effect) so it updates on same-route navigation.
|
// NOTE: This must remain reactive (in an effect) so it updates on same-route navigation.
|
||||||
$effect(() => {
|
$effect(() => {
|
||||||
if (!ae_acct) return;
|
if (!ae_acct) return;
|
||||||
untrack(() => {
|
untrack(() => {
|
||||||
$journals_slct.journal_entry_id = ae_acct.slct.journal_entry_id;
|
$journals_slct.journal_entry_id = ae_acct.slct.journal_entry_id;
|
||||||
// $journals_slct.journal_entry_obj = ae_acct.slct.journal_entry_obj;
|
// $journals_slct.journal_entry_obj = ae_acct.slct.journal_entry_obj;
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
let lq__journal_entry_obj = $derived(
|
let lq__journal_entry_obj = $derived(
|
||||||
liveQuery(async () => {
|
liveQuery(async () => {
|
||||||
let results = await db_journals.journal_entry.get(
|
let results = await db_journals.journal_entry.get(
|
||||||
$journals_slct.journal_entry_id ?? ''
|
$journals_slct.journal_entry_id ?? ''
|
||||||
@@ -170,17 +165,14 @@
|
|||||||
|
|
||||||
return results;
|
return results;
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
|
||||||
$effect(() => {
|
$effect(() => {
|
||||||
if (log_lvl) {
|
if (log_lvl) {
|
||||||
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) !==
|
||||||
@@ -197,9 +189,9 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
$effect(() => {
|
$effect(() => {
|
||||||
if (browser && $lq__journal_entry_obj?.journal_entry_id) {
|
if (browser && $lq__journal_entry_obj?.journal_entry_id) {
|
||||||
// Start with the current KV or convert the LI to a KV if needed
|
// Start with the current KV or convert the LI to a KV if needed
|
||||||
let history_kv = {
|
let history_kv = {
|
||||||
@@ -258,7 +250,7 @@
|
|||||||
|
|
||||||
// log_lvl = 1;
|
// log_lvl = 1;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<!-- <svelte:head>
|
<!-- <svelte:head>
|
||||||
@@ -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>
|
||||||
|
|||||||
@@ -1,24 +1,19 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
/**
|
/**
|
||||||
* ae_comp__journal_entry_ai_tools.svelte
|
* ae_comp__journal_entry_ai_tools.svelte
|
||||||
* Journal-specific wrapper for the generic AE_AITools.
|
* Journal-specific wrapper for the generic AE_AITools.
|
||||||
* Handles layout/positioning and specific save behavior for journal entries.
|
* Handles layout/positioning and specific save behavior for journal entries.
|
||||||
*/
|
*/
|
||||||
import AE_AITools from '$lib/ae_elements/AE_AITools.svelte';
|
import AE_AITools from '$lib/ae_elements/AE_AITools.svelte';
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
content: string;
|
content: string;
|
||||||
summary: string; // Bindable
|
summary: string; // Bindable
|
||||||
on_save: () => void;
|
on_save: () => void;
|
||||||
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>
|
||||||
|
|||||||
@@ -1,19 +1,19 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
/**
|
/**
|
||||||
* ae_comp__journal_entry_editor.svelte
|
* ae_comp__journal_entry_editor.svelte
|
||||||
* Extracted 2026-01-08 to modularize the massive Journal Entry view.
|
* Extracted 2026-01-08 to modularize the massive Journal Entry view.
|
||||||
* Handles: CodeMirror vs Plain vs Rendered HTML for both View and Edit modes.
|
* Handles: CodeMirror vs Plain vs Rendered HTML for both View and Edit modes.
|
||||||
*/
|
*/
|
||||||
import { LockKeyhole, RefreshCcw, Save } from '@lucide/svelte';
|
import { LockKeyhole, RefreshCcw, Save } from '@lucide/svelte';
|
||||||
import { ae_loc } from '$lib/stores/ae_stores';
|
import { ae_loc } from '$lib/stores/ae_stores';
|
||||||
import {
|
import {
|
||||||
journals_loc,
|
journals_loc,
|
||||||
journals_sess
|
journals_sess
|
||||||
} from '$lib/ae_journals/ae_journals_stores';
|
} from '$lib/ae_journals/ae_journals_stores';
|
||||||
import AE_Comp_Editor_CodeMirror from '$lib/elements/element_editor_codemirror.svelte';
|
import AE_Comp_Editor_CodeMirror from '$lib/elements/element_editor_codemirror.svelte';
|
||||||
import type { ae_JournalEntry, ae_Journal } from '$lib/types/ae_types';
|
import type { ae_JournalEntry, ae_Journal } from '$lib/types/ae_types';
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
entry: ae_JournalEntry;
|
entry: ae_JournalEntry;
|
||||||
journal: ae_Journal;
|
journal: ae_Journal;
|
||||||
tmp_entry_obj: any; // Bindable
|
tmp_entry_obj: any; // Bindable
|
||||||
@@ -22,9 +22,9 @@
|
|||||||
updated_idb: boolean;
|
updated_idb: boolean;
|
||||||
on_save: () => void;
|
on_save: () => void;
|
||||||
on_force_reset?: () => void;
|
on_force_reset?: () => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
let {
|
let {
|
||||||
entry,
|
entry,
|
||||||
journal,
|
journal,
|
||||||
tmp_entry_obj = $bindable(),
|
tmp_entry_obj = $bindable(),
|
||||||
@@ -33,19 +33,18 @@
|
|||||||
updated_idb,
|
updated_idb,
|
||||||
on_save,
|
on_save,
|
||||||
on_force_reset
|
on_force_reset
|
||||||
}: Props = $props();
|
}: Props = $props();
|
||||||
|
|
||||||
const is_editing = $derived(
|
const is_editing = $derived(
|
||||||
$journals_loc.entry.edit_kv[entry.journal_entry_id] === 'current'
|
$journals_loc.entry.edit_kv[entry.journal_entry_id] === 'current'
|
||||||
);
|
);
|
||||||
</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}
|
||||||
|
|||||||
@@ -1,18 +1,31 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
/**
|
/**
|
||||||
* ae_comp__journal_entry_header.svelte
|
* ae_comp__journal_entry_header.svelte
|
||||||
* 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 {
|
||||||
import { ae_util } from '$lib/ae_utils/ae_utils';
|
ChevronLeft,
|
||||||
import {
|
CircleCheck,
|
||||||
|
CircleX,
|
||||||
|
Eye,
|
||||||
|
Fingerprint,
|
||||||
|
LoaderCircle,
|
||||||
|
LockKeyhole,
|
||||||
|
LockKeyholeOpen,
|
||||||
|
Pencil,
|
||||||
|
RefreshCw,
|
||||||
|
Save,
|
||||||
|
Settings
|
||||||
|
} from '@lucide/svelte';
|
||||||
|
import { ae_util } from '$lib/ae_utils/ae_utils';
|
||||||
|
import {
|
||||||
journals_loc,
|
journals_loc,
|
||||||
journals_sess
|
journals_sess
|
||||||
} from '$lib/ae_journals/ae_journals_stores';
|
} from '$lib/ae_journals/ae_journals_stores';
|
||||||
import type { ae_JournalEntry, ae_Journal } from '$lib/types/ae_types';
|
import type { ae_JournalEntry, ae_Journal } from '$lib/types/ae_types';
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
entry: ae_JournalEntry;
|
entry: ae_JournalEntry;
|
||||||
journal: ae_Journal;
|
journal: ae_Journal;
|
||||||
journals_li?: ae_Journal[];
|
journals_li?: ae_Journal[];
|
||||||
@@ -23,9 +36,9 @@
|
|||||||
on_decrypt: () => void;
|
on_decrypt: () => void;
|
||||||
on_show_config: () => void;
|
on_show_config: () => void;
|
||||||
log_lvl?: number;
|
log_lvl?: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
let {
|
let {
|
||||||
entry,
|
entry,
|
||||||
journal,
|
journal,
|
||||||
tmp_entry_obj = $bindable(),
|
tmp_entry_obj = $bindable(),
|
||||||
@@ -35,14 +48,13 @@
|
|||||||
on_decrypt,
|
on_decrypt,
|
||||||
on_show_config,
|
on_show_config,
|
||||||
log_lvl = 0
|
log_lvl = 0
|
||||||
}: 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() {
|
||||||
const isEditing =
|
const isEditing =
|
||||||
$journals_loc.entry.edit_kv[entry.journal_entry_id] === 'current';
|
$journals_loc.entry.edit_kv[entry.journal_entry_id] === 'current';
|
||||||
if (isEditing) {
|
if (isEditing) {
|
||||||
@@ -51,25 +63,23 @@
|
|||||||
} else {
|
} else {
|
||||||
$journals_loc.entry.edit_kv[entry.journal_entry_id] = 'current';
|
$journals_loc.entry.edit_kv[entry.journal_entry_id] = 'current';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</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}
|
||||||
|
|||||||
@@ -1,25 +1,24 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
/**
|
/**
|
||||||
* JournalEntry_Metadata.svelte
|
* JournalEntry_Metadata.svelte
|
||||||
* Extracted 2026-01-08 to modularize the massive Journal Entry God Component.
|
* Extracted 2026-01-08 to modularize the massive Journal Entry God Component.
|
||||||
* Displays creation, update, and original datetime/timezone information.
|
* Displays creation, update, and original datetime/timezone information.
|
||||||
*/
|
*/
|
||||||
import { ae_util } from '$lib/ae_utils/ae_utils';
|
import { ae_util } from '$lib/ae_utils/ae_utils';
|
||||||
import { ae_loc } from '$lib/stores/ae_stores';
|
import { ae_loc } from '$lib/stores/ae_stores';
|
||||||
import type { ae_JournalEntry } from '$lib/types/ae_types';
|
import type { ae_JournalEntry } from '$lib/types/ae_types';
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
entry: ae_JournalEntry;
|
entry: ae_JournalEntry;
|
||||||
}
|
}
|
||||||
|
|
||||||
let { entry }: Props = $props();
|
let { entry }: Props = $props();
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<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>
|
||||||
|
|||||||
@@ -1,18 +1,26 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
/**
|
/**
|
||||||
* ae_comp__journal_entry_obj_file_li.svelte
|
* ae_comp__journal_entry_obj_file_li.svelte
|
||||||
* Manages and displays file attachments for Journal Entries.
|
* Manages and displays file attachments for Journal Entries.
|
||||||
* Ported/Refactored 2026-01-26 to include View mode and strictly snake_case.
|
* Ported/Refactored 2026-01-26 to include View mode and strictly snake_case.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
// *** Import Lucide Icons
|
// *** Import Lucide Icons
|
||||||
import { Download, ExternalLink, FileUp, LoaderCircle, Paperclip, RefreshCw, Trash2 } from '@lucide/svelte';
|
import {
|
||||||
// *** Import Aether specific variables and functions
|
Download,
|
||||||
import type { key_val } from '$lib/stores/ae_stores';
|
ExternalLink,
|
||||||
import { ae_util } from '$lib/ae_utils/ae_utils';
|
FileUp,
|
||||||
import { core_func } from '$lib/ae_core/ae_core_functions';
|
LoaderCircle,
|
||||||
import { api } from '$lib/api/api';
|
Paperclip,
|
||||||
import {
|
RefreshCw,
|
||||||
|
Trash2
|
||||||
|
} from '@lucide/svelte';
|
||||||
|
// *** Import Aether specific variables and functions
|
||||||
|
import type { key_val } from '$lib/stores/ae_stores';
|
||||||
|
import { ae_util } from '$lib/ae_utils/ae_utils';
|
||||||
|
import { core_func } from '$lib/ae_core/ae_core_functions';
|
||||||
|
import { api } from '$lib/api/api';
|
||||||
|
import {
|
||||||
ae_snip,
|
ae_snip,
|
||||||
ae_loc,
|
ae_loc,
|
||||||
ae_sess,
|
ae_sess,
|
||||||
@@ -20,49 +28,49 @@
|
|||||||
ae_trig,
|
ae_trig,
|
||||||
slct,
|
slct,
|
||||||
slct_trigger
|
slct_trigger
|
||||||
} from '$lib/stores/ae_stores';
|
} from '$lib/stores/ae_stores';
|
||||||
import {
|
import {
|
||||||
journals_loc,
|
journals_loc,
|
||||||
journals_sess,
|
journals_sess,
|
||||||
journals_slct,
|
journals_slct,
|
||||||
journals_trig,
|
journals_trig,
|
||||||
journals_prom
|
journals_prom
|
||||||
} from '$lib/ae_journals/ae_journals_stores';
|
} from '$lib/ae_journals/ae_journals_stores';
|
||||||
import { journals_func } from '$lib/ae_journals/ae_journals_functions';
|
import { journals_func } from '$lib/ae_journals/ae_journals_functions';
|
||||||
|
|
||||||
import Comp_hosted_files_upload from '$lib/ae_core/ae_comp__hosted_files_upload.svelte';
|
import Comp_hosted_files_upload from '$lib/ae_core/ae_comp__hosted_files_upload.svelte';
|
||||||
import Element_manage_hosted_file_li_wrap from '$lib/elements/element_manage_hosted_file_li_all.svelte';
|
import Element_manage_hosted_file_li_wrap from '$lib/elements/element_manage_hosted_file_li_all.svelte';
|
||||||
import AE_Comp_Hosted_Files_Download_Button from '$lib/ae_core/ae_comp__hosted_files_download_button.svelte';
|
import AE_Comp_Hosted_Files_Download_Button from '$lib/ae_core/ae_comp__hosted_files_download_button.svelte';
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
log_lvl?: number;
|
log_lvl?: number;
|
||||||
link_to_type: string;
|
link_to_type: string;
|
||||||
link_to_id: string;
|
link_to_id: string;
|
||||||
lq__journal_entry_obj: any;
|
lq__journal_entry_obj: any;
|
||||||
}
|
}
|
||||||
|
|
||||||
let {
|
let {
|
||||||
log_lvl = 0,
|
log_lvl = 0,
|
||||||
link_to_type,
|
link_to_type,
|
||||||
link_to_id,
|
link_to_id,
|
||||||
lq__journal_entry_obj
|
lq__journal_entry_obj
|
||||||
}: Props = $props();
|
}: Props = $props();
|
||||||
|
|
||||||
// *** State
|
// *** State
|
||||||
let ae_promises: Record<string, any> = $state({});
|
let ae_promises: Record<string, any> = $state({});
|
||||||
let upload_complete: boolean = $state(false);
|
let upload_complete: boolean = $state(false);
|
||||||
|
|
||||||
// Selection state for "Select Existing" mode
|
// Selection state for "Select Existing" mode
|
||||||
let slct_hosted_file_kv: key_val = $state({});
|
let slct_hosted_file_kv: key_val = $state({});
|
||||||
let slct_hosted_file_id: string | null = $state(null);
|
let slct_hosted_file_id: string | null = $state(null);
|
||||||
let slct_hosted_file_obj: any = $state(null);
|
let slct_hosted_file_obj: any = $state(null);
|
||||||
|
|
||||||
// Selection state for "Upload" mode
|
// Selection state for "Upload" mode
|
||||||
let slct_hosted_file_id_li: string[] = $state([]);
|
let slct_hosted_file_id_li: string[] = $state([]);
|
||||||
let slct_hosted_file_obj_li: any[] = $state([]);
|
let slct_hosted_file_obj_li: any[] = $state([]);
|
||||||
|
|
||||||
// Derived: Unified file list from both legacy data_json and new linked_li_json
|
// Derived: Unified file list from both legacy data_json and new linked_li_json
|
||||||
let unified_file_li = $derived.by(() => {
|
let unified_file_li = $derived.by(() => {
|
||||||
const entry = $lq__journal_entry_obj;
|
const entry = $lq__journal_entry_obj;
|
||||||
if (!entry) return [];
|
if (!entry) return [];
|
||||||
|
|
||||||
@@ -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 });
|
||||||
@@ -92,9 +99,9 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
return files;
|
return files;
|
||||||
});
|
});
|
||||||
|
|
||||||
async function update_journal_entry(updated_files: any[]) {
|
async function update_journal_entry(updated_files: any[]) {
|
||||||
let data_kv: key_val = {
|
let data_kv: key_val = {
|
||||||
linked_li_json: JSON.stringify(updated_files),
|
linked_li_json: JSON.stringify(updated_files),
|
||||||
// Maintain data_json.hosted_file_kv for backward compatibility
|
// Maintain data_json.hosted_file_kv for backward compatibility
|
||||||
@@ -119,17 +126,13 @@
|
|||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Error updating journal entry files:', error);
|
console.error('Error updating journal entry files:', error);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// *** Effects for File Management
|
// *** Effects for File Management
|
||||||
|
|
||||||
// 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
|
||||||
@@ -141,10 +144,10 @@
|
|||||||
|
|
||||||
update_journal_entry(updated_li);
|
update_journal_entry(updated_li);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// Handle "Upload" completion
|
// Handle "Upload" completion
|
||||||
$effect(() => {
|
$effect(() => {
|
||||||
if (
|
if (
|
||||||
$lq__journal_entry_obj &&
|
$lq__journal_entry_obj &&
|
||||||
upload_complete &&
|
upload_complete &&
|
||||||
@@ -161,9 +164,9 @@
|
|||||||
|
|
||||||
update_journal_entry(updated_li);
|
update_journal_entry(updated_li);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
async function handle_remove_file(file_id: string) {
|
async function handle_remove_file(file_id: string) {
|
||||||
if (!confirm('Are you sure you want to remove this file attachment?'))
|
if (!confirm('Are you sure you want to remove this file attachment?'))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
@@ -185,21 +188,19 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
await update_journal_entry(updated_li);
|
await update_journal_entry(updated_li);
|
||||||
}
|
}
|
||||||
</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>
|
||||||
|
|||||||
@@ -1,69 +1,69 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { untrack } from 'svelte';
|
import { untrack } from 'svelte';
|
||||||
/**
|
/**
|
||||||
* ae_comp__journal_entry_obj_id_view.svelte
|
* ae_comp__journal_entry_obj_id_view.svelte
|
||||||
* Reference Implementation for Journal Entry View/Edit
|
* Reference Implementation for Journal Entry View/Edit
|
||||||
* Corrected for V3 API Strictness & Robust Decryption
|
* Corrected for V3 API Strictness & Robust Decryption
|
||||||
* Uses strictly snake_case.
|
* Uses strictly snake_case.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
// *** Import Svelte core
|
// *** Import Svelte core
|
||||||
// import { goto } from '$app/navigation';
|
// import { goto } from '$app/navigation';
|
||||||
|
|
||||||
// *** Import secondary libraries
|
// *** Import secondary libraries
|
||||||
import { marked } from 'marked';
|
import { marked } from 'marked';
|
||||||
|
|
||||||
// *** Import Aether components and helpers
|
// *** Import Aether components and helpers
|
||||||
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 { ae_loc, ae_api } from '$lib/stores/ae_stores';
|
import { ae_loc, ae_api } from '$lib/stores/ae_stores';
|
||||||
import {
|
import {
|
||||||
journals_loc,
|
journals_loc,
|
||||||
journals_sess,
|
journals_sess,
|
||||||
journals_slct
|
journals_slct
|
||||||
} from '$lib/ae_journals/ae_journals_stores';
|
} from '$lib/ae_journals/ae_journals_stores';
|
||||||
import { journals_func } from '$lib/ae_journals/ae_journals_functions';
|
import { journals_func } from '$lib/ae_journals/ae_journals_functions';
|
||||||
import { decrypt_journal_entry } from '$lib/ae_journals/ae_journals_decryption';
|
import { decrypt_journal_entry } from '$lib/ae_journals/ae_journals_decryption';
|
||||||
|
|
||||||
import AE_Comp_Journal_Entry_Editor from './ae_comp__journal_entry_editor.svelte';
|
import AE_Comp_Journal_Entry_Editor from './ae_comp__journal_entry_editor.svelte';
|
||||||
import AE_Comp_Journal_Entry_Header from './ae_comp__journal_entry_header.svelte';
|
import AE_Comp_Journal_Entry_Header from './ae_comp__journal_entry_header.svelte';
|
||||||
import AE_Comp_Journal_Entry_Metadata from './ae_comp__journal_entry_metadata.svelte';
|
import AE_Comp_Journal_Entry_Metadata from './ae_comp__journal_entry_metadata.svelte';
|
||||||
import AE_Comp_Journal_Entry_AiTools from './ae_comp__journal_entry_ai_tools.svelte';
|
import AE_Comp_Journal_Entry_AiTools from './ae_comp__journal_entry_ai_tools.svelte';
|
||||||
import AE_Comp_Journal_Entry_ObjFileLi from './ae_comp__journal_entry_obj_file_li.svelte';
|
import AE_Comp_Journal_Entry_ObjFileLi from './ae_comp__journal_entry_obj_file_li.svelte';
|
||||||
import AE_Comp_Modal_Journal_Entry_Append from './ae_comp__modal_journal_entry_append.svelte';
|
import AE_Comp_Modal_Journal_Entry_Append from './ae_comp__modal_journal_entry_append.svelte';
|
||||||
import AE_Comp_Modal_Journal_Entry_Config from './ae_comp__modal_journal_entry_config.svelte';
|
import AE_Comp_Modal_Journal_Entry_Config from './ae_comp__modal_journal_entry_config.svelte';
|
||||||
|
|
||||||
// Icons
|
// Icons
|
||||||
import { CircleAlert, CircleX, LoaderCircle } from '@lucide/svelte';
|
import { CircleAlert, CircleX, LoaderCircle } from '@lucide/svelte';
|
||||||
// *** Props
|
// *** Props
|
||||||
interface Props {
|
interface Props {
|
||||||
log_lvl?: number;
|
log_lvl?: number;
|
||||||
lq__journal_obj: any;
|
lq__journal_obj: any;
|
||||||
lq__journal_obj_li: any;
|
lq__journal_obj_li: any;
|
||||||
lq__journal_entry_obj: any;
|
lq__journal_entry_obj: any;
|
||||||
on_show_export?: () => void;
|
on_show_export?: () => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
let {
|
let {
|
||||||
log_lvl = 0,
|
log_lvl = 0,
|
||||||
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
|
on_show_export
|
||||||
}: Props = $props();
|
}: Props = $props();
|
||||||
|
|
||||||
// *** State
|
// *** State
|
||||||
let editor_view: any = $state();
|
let editor_view: any = $state();
|
||||||
let orig_entry_obj: key_val | null = $state(null);
|
let orig_entry_obj: key_val | null = $state(null);
|
||||||
let tmp_entry_obj: key_val = $state({});
|
let tmp_entry_obj: key_val = $state({});
|
||||||
let save_status: 'saved' | 'unsaved' | 'saving' = $state('saved');
|
let save_status: 'saved' | 'unsaved' | 'saving' = $state('saved');
|
||||||
let decryption_error: string | null = $state(null);
|
let decryption_error: string | null = $state(null);
|
||||||
let auto_save_timer: ReturnType<typeof setTimeout>;
|
let auto_save_timer: ReturnType<typeof setTimeout>;
|
||||||
let is_processing = $state(false);
|
let is_processing = $state(false);
|
||||||
let show_config_modal = $state(false);
|
let show_config_modal = $state(false);
|
||||||
|
|
||||||
// *** Helpers
|
// *** Helpers
|
||||||
function deep_copy(obj: any) {
|
function deep_copy(obj: any) {
|
||||||
if (!obj) return null;
|
if (!obj) return null;
|
||||||
try {
|
try {
|
||||||
const copy: key_val = {};
|
const copy: key_val = {};
|
||||||
@@ -84,23 +84,21 @@
|
|||||||
console.error('deep_copy failed:', e);
|
console.error('deep_copy failed:', e);
|
||||||
return { ...obj };
|
return { ...obj };
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// *** Derived
|
// *** Derived
|
||||||
let has_unsaved_changes = $derived.by(() => {
|
let has_unsaved_changes = $derived.by(() => {
|
||||||
if (!tmp_entry_obj || !orig_entry_obj || is_processing) return false;
|
if (!tmp_entry_obj || !orig_entry_obj || is_processing) return false;
|
||||||
|
|
||||||
const normalize = (val: any) =>
|
const normalize = (val: any) =>
|
||||||
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,19 +113,18 @@
|
|||||||
];
|
];
|
||||||
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;
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
});
|
});
|
||||||
|
|
||||||
// *** Effects
|
// *** Effects
|
||||||
|
|
||||||
// 1. Initial Load & Background Sync
|
// 1. Initial Load & Background Sync
|
||||||
$effect(() => {
|
$effect(() => {
|
||||||
const entry = $lq__journal_entry_obj; // Track only entry
|
const entry = $lq__journal_entry_obj; // Track only entry
|
||||||
|
|
||||||
untrack(() => {
|
untrack(() => {
|
||||||
@@ -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;
|
||||||
@@ -158,28 +150,23 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
// 2. Auto-Save Debounce
|
// 2. Auto-Save Debounce
|
||||||
$effect(() => {
|
$effect(() => {
|
||||||
// Track core properties
|
// Track core properties
|
||||||
const _content = tmp_entry_obj.content;
|
const _content = tmp_entry_obj.content;
|
||||||
const _private = tmp_entry_obj.private;
|
const _private = tmp_entry_obj.private;
|
||||||
|
|
||||||
// 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(() => {
|
||||||
@@ -198,10 +185,10 @@
|
|||||||
} else if (save_status === 'unsaved' && !has_unsaved_changes) {
|
} else if (save_status === 'unsaved' && !has_unsaved_changes) {
|
||||||
save_status = 'saved';
|
save_status = 'saved';
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// 3. Auto-Decryption Workflow
|
// 3. Auto-Decryption Workflow
|
||||||
$effect(() => {
|
$effect(() => {
|
||||||
const journal = $lq__journal_obj;
|
const journal = $lq__journal_obj;
|
||||||
const entry = $lq__journal_entry_obj;
|
const entry = $lq__journal_entry_obj;
|
||||||
if (!journal?.id || !entry) return;
|
if (!journal?.id || !entry) return;
|
||||||
@@ -220,11 +207,11 @@
|
|||||||
) {
|
) {
|
||||||
untrack(() => run_decryption_workflow());
|
untrack(() => run_decryption_workflow());
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// *** Actions
|
// *** Actions
|
||||||
|
|
||||||
async function run_decryption_workflow() {
|
async function run_decryption_workflow() {
|
||||||
const journal = $lq__journal_obj;
|
const journal = $lq__journal_obj;
|
||||||
if (!journal?.id || is_processing) return;
|
if (!journal?.id || is_processing) return;
|
||||||
|
|
||||||
@@ -280,14 +267,10 @@
|
|||||||
return s;
|
return s;
|
||||||
});
|
});
|
||||||
is_processing = false;
|
is_processing = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
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;
|
||||||
@@ -356,9 +339,9 @@
|
|||||||
} finally {
|
} finally {
|
||||||
is_processing = false;
|
is_processing = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async function handle_content_decryption() {
|
async function handle_content_decryption() {
|
||||||
const journal = $lq__journal_obj;
|
const journal = $lq__journal_obj;
|
||||||
const entry = $lq__journal_entry_obj;
|
const entry = $lq__journal_entry_obj;
|
||||||
if (!journal?.id || !entry || is_processing) return;
|
if (!journal?.id || !entry || is_processing) return;
|
||||||
@@ -379,14 +362,14 @@
|
|||||||
});
|
});
|
||||||
is_processing = false;
|
is_processing = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function handle_marked(text: string) {
|
function handle_marked(text: string) {
|
||||||
if (!text) return '';
|
if (!text) return '';
|
||||||
return marked.parse(text.replace(/^[]/, ''));
|
return marked.parse(text.replace(/^[]/, ''));
|
||||||
}
|
}
|
||||||
|
|
||||||
async function handle_force_reset() {
|
async function handle_force_reset() {
|
||||||
if (
|
if (
|
||||||
!confirm(
|
!confirm(
|
||||||
'WARNING: This will permanently DELETE the encrypted content and history for this entry and reset it to plain text. This cannot be undone. Proceed?'
|
'WARNING: This will permanently DELETE the encrypted content and history for this entry and reset it to plain text. This cannot be undone. Proceed?'
|
||||||
@@ -413,27 +396,26 @@
|
|||||||
|
|
||||||
await update_journal_entry();
|
await update_journal_entry();
|
||||||
is_processing = false;
|
is_processing = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
let show_append_modal = $state(false);
|
let show_append_modal = $state(false);
|
||||||
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,15 +510,13 @@
|
|||||||
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>
|
||||||
{/if}
|
{/if}
|
||||||
</section>
|
</section>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,64 +1,81 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
interface Props {
|
interface Props {
|
||||||
log_lvl?: number;
|
log_lvl?: number;
|
||||||
lq__journal_obj: any;
|
lq__journal_obj: any;
|
||||||
lq__journal_entry_obj_li: any;
|
lq__journal_entry_obj_li: any;
|
||||||
show_found_header?: boolean;
|
show_found_header?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
let {
|
let {
|
||||||
log_lvl = $bindable(0),
|
log_lvl = $bindable(0),
|
||||||
lq__journal_obj,
|
lq__journal_obj,
|
||||||
lq__journal_entry_obj_li,
|
lq__journal_entry_obj_li,
|
||||||
show_found_header = true
|
show_found_header = true
|
||||||
}: Props = $props();
|
}: Props = $props();
|
||||||
|
|
||||||
// *** 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 {
|
||||||
// *** Import Aether specific variables and functions
|
BookOpenText,
|
||||||
import type { key_val } from '$lib/stores/ae_stores';
|
CalendarClock,
|
||||||
import { ae_util } from '$lib/ae_utils/ae_utils';
|
Check,
|
||||||
import {
|
CodeXml,
|
||||||
ae_loc,
|
Copy,
|
||||||
ae_sess,
|
Eye,
|
||||||
ae_api,
|
EyeOff,
|
||||||
ae_trig,
|
Files,
|
||||||
slct
|
Fingerprint,
|
||||||
} from '$lib/stores/ae_stores';
|
Flag,
|
||||||
import {
|
Group,
|
||||||
|
ListPlus,
|
||||||
|
LoaderCircle,
|
||||||
|
Lock,
|
||||||
|
NotebookPen,
|
||||||
|
NotebookText,
|
||||||
|
NotepadTextDashed,
|
||||||
|
RemoveFormatting,
|
||||||
|
Shapes,
|
||||||
|
Siren,
|
||||||
|
Tags,
|
||||||
|
TypeOutline,
|
||||||
|
X
|
||||||
|
} from '@lucide/svelte';
|
||||||
|
// *** Import Aether specific variables and functions
|
||||||
|
import type { key_val } from '$lib/stores/ae_stores';
|
||||||
|
import { ae_util } from '$lib/ae_utils/ae_utils';
|
||||||
|
import { ae_loc, ae_sess, ae_api, ae_trig, slct } from '$lib/stores/ae_stores';
|
||||||
|
import {
|
||||||
journals_sess,
|
journals_sess,
|
||||||
journals_slct,
|
journals_slct,
|
||||||
journals_trig,
|
journals_trig,
|
||||||
journals_loc
|
journals_loc
|
||||||
} from '$lib/ae_journals/ae_journals_stores';
|
} from '$lib/ae_journals/ae_journals_stores';
|
||||||
import { journals_func } from '$lib/ae_journals/ae_journals_functions';
|
import { journals_func } from '$lib/ae_journals/ae_journals_functions';
|
||||||
import AeCompModalJournalEntryAppend from './ae_comp__modal_journal_entry_append.svelte';
|
import AeCompModalJournalEntryAppend from './ae_comp__modal_journal_entry_append.svelte';
|
||||||
|
|
||||||
let tmp_entry_obj: key_val = $state({});
|
let tmp_entry_obj: key_val = $state({});
|
||||||
|
|
||||||
// Derived state for modal visibility
|
// Derived state for modal visibility
|
||||||
// We cast to boolean for the prop, but we need to handle the close event to clear the store ID
|
// We cast to boolean for the prop, but we need to handle the close event to clear the store ID
|
||||||
let show_append_modal = $state(false);
|
let show_append_modal = $state(false);
|
||||||
|
|
||||||
$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() {
|
||||||
$journals_sess.show__modal_append__journal_entry_id = null;
|
$journals_sess.show__modal_append__journal_entry_id = null;
|
||||||
show_append_modal = false;
|
show_append_modal = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
function handle_modal_update() {
|
function handle_modal_update() {
|
||||||
handle_modal_close();
|
handle_modal_close();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Derived list of visible items (Standardized Search Pattern 2026-01-27)
|
// Derived list of visible items (Standardized Search Pattern 2026-01-27)
|
||||||
// Ensures count matches exactly what is rendered to the user
|
// Ensures count matches exactly what is rendered to the user
|
||||||
let visible_journal_entry_obj_li = $derived(
|
let visible_journal_entry_obj_li = $derived(
|
||||||
(() => {
|
(() => {
|
||||||
// Subscribe to the observable
|
// Subscribe to the observable
|
||||||
const list = $lq__journal_entry_obj_li;
|
const list = $lq__journal_entry_obj_li;
|
||||||
@@ -95,40 +112,38 @@
|
|||||||
|
|
||||||
return filtered;
|
return filtered;
|
||||||
})()
|
})()
|
||||||
);
|
);
|
||||||
</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"
|
||||||
>×</span
|
>×</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,19 +722,17 @@ 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.
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
{/if}
|
{/if}
|
||||||
</section>
|
</section>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,24 +1,24 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
interface Props {
|
interface Props {
|
||||||
container_class_li?: string | Array<string>;
|
container_class_li?: string | Array<string>;
|
||||||
lq__journal_obj: any;
|
lq__journal_obj: any;
|
||||||
lq__journal_entry_obj_li: any;
|
lq__journal_entry_obj_li: any;
|
||||||
show_found_header?: boolean;
|
show_found_header?: boolean;
|
||||||
log_lvl?: number;
|
log_lvl?: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
let {
|
let {
|
||||||
container_class_li = '',
|
container_class_li = '',
|
||||||
lq__journal_obj,
|
lq__journal_obj,
|
||||||
lq__journal_entry_obj_li,
|
lq__journal_entry_obj_li,
|
||||||
show_found_header = true,
|
show_found_header = true,
|
||||||
log_lvl = 0
|
log_lvl = 0
|
||||||
}: Props = $props();
|
}: Props = $props();
|
||||||
|
|
||||||
// *** Import other supporting libraries
|
// *** Import other supporting libraries
|
||||||
import { LoaderCircle } from '@lucide/svelte';
|
import { LoaderCircle } from '@lucide/svelte';
|
||||||
// *** Import Aether specific components
|
// *** Import Aether specific components
|
||||||
import Journal_entry_obj_li from './ae_comp__journal_entry_obj_li.svelte';
|
import Journal_entry_obj_li from './ae_comp__journal_entry_obj_li.svelte';
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
{#if $lq__journal_entry_obj_li}
|
{#if $lq__journal_entry_obj_li}
|
||||||
@@ -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}
|
||||||
|
|||||||
@@ -1,12 +1,12 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
interface Props {
|
interface Props {
|
||||||
log_lvl?: number;
|
log_lvl?: number;
|
||||||
lq__journal_obj: any;
|
lq__journal_obj: any;
|
||||||
}
|
}
|
||||||
|
|
||||||
let { log_lvl = $bindable(0), lq__journal_obj }: Props = $props();
|
let { log_lvl = $bindable(0), lq__journal_obj }: Props = $props();
|
||||||
|
|
||||||
import {
|
import {
|
||||||
ArrowDown01,
|
ArrowDown01,
|
||||||
ArrowDown10,
|
ArrowDown10,
|
||||||
ArrowDownUp,
|
ArrowDownUp,
|
||||||
@@ -48,9 +48,9 @@
|
|||||||
Trash2,
|
Trash2,
|
||||||
TypeOutline,
|
TypeOutline,
|
||||||
X
|
X
|
||||||
} from '@lucide/svelte';
|
} from '@lucide/svelte';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
ae_snip,
|
ae_snip,
|
||||||
ae_loc,
|
ae_loc,
|
||||||
ae_sess,
|
ae_sess,
|
||||||
@@ -58,35 +58,34 @@
|
|||||||
ae_trig,
|
ae_trig,
|
||||||
slct,
|
slct,
|
||||||
slct_trigger
|
slct_trigger
|
||||||
} from '$lib/stores/ae_stores';
|
} from '$lib/stores/ae_stores';
|
||||||
import {
|
import {
|
||||||
journals_loc,
|
journals_loc,
|
||||||
journals_sess,
|
journals_sess,
|
||||||
journals_slct,
|
journals_slct,
|
||||||
journals_prom,
|
journals_prom,
|
||||||
journals_trig
|
journals_trig
|
||||||
} from '$lib/ae_journals/ae_journals_stores';
|
} from '$lib/ae_journals/ae_journals_stores';
|
||||||
import { journals_func } from '$lib/ae_journals/ae_journals_functions';
|
import { journals_func } from '$lib/ae_journals/ae_journals_functions';
|
||||||
|
|
||||||
// *** Functions and Logic
|
// *** Functions and Logic
|
||||||
function handle_search_trigger() {
|
function handle_search_trigger() {
|
||||||
if ($journals_loc.entry.search_version === undefined) {
|
if ($journals_loc.entry.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++;
|
||||||
}
|
}
|
||||||
|
|
||||||
function prevent_default<T extends Event>(fn: (event: T) => void) {
|
function prevent_default<T extends Event>(fn: (event: T) => void) {
|
||||||
return function (event: T) {
|
return function (event: T) {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
fn(event);
|
fn(event);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
</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>
|
||||||
|
|||||||
@@ -1,34 +1,34 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { api } from '$lib/api/api';
|
import { api } from '$lib/api/api';
|
||||||
import { ae_api } from '$lib/stores/ae_stores';
|
import { ae_api } from '$lib/stores/ae_stores';
|
||||||
import {
|
import {
|
||||||
journals_slct,
|
journals_slct,
|
||||||
journals_loc,
|
journals_loc,
|
||||||
journals_trig
|
journals_trig
|
||||||
} from '$lib/ae_journals/ae_journals_stores';
|
} from '$lib/ae_journals/ae_journals_stores';
|
||||||
import { journals_func } from '$lib/ae_journals/ae_journals_functions';
|
import { journals_func } from '$lib/ae_journals/ae_journals_functions';
|
||||||
import { BookType } from '@lucide/svelte';
|
import { BookType } from '@lucide/svelte';
|
||||||
// Props
|
// Props
|
||||||
let {
|
let {
|
||||||
class: className = '',
|
class: className = '',
|
||||||
placeholder = 'Type your quick note... (First line = Title)',
|
placeholder = 'Type your quick note... (First line = Title)',
|
||||||
journals_li = [] // Optional list of journals to select from
|
journals_li = [] // Optional list of journals to select from
|
||||||
} = $props();
|
} = $props();
|
||||||
|
|
||||||
// State
|
// State
|
||||||
let note_content = $state('');
|
let note_content = $state('');
|
||||||
let is_submitting = $state(false);
|
let is_submitting = $state(false);
|
||||||
|
|
||||||
// Derived / Local target
|
// Derived / Local target
|
||||||
// We prefer the persisted 'qry__journal_id' if we are on the main landing page
|
// We prefer the persisted 'qry__journal_id' if we are on the main landing page
|
||||||
let selected_journal_id = $state($journals_loc.entry.qry__journal_id);
|
let selected_journal_id = $state($journals_loc.entry.qry__journal_id);
|
||||||
|
|
||||||
// If a journal is explicitly selected via slct (e.g. we are in a journal view), use that
|
// If a journal is explicitly selected via slct (e.g. we are in a journal view), use that
|
||||||
let target_journal_id = $derived(
|
let target_journal_id = $derived(
|
||||||
$journals_slct.journal_id || selected_journal_id
|
$journals_slct.journal_id || selected_journal_id
|
||||||
);
|
);
|
||||||
|
|
||||||
async function handle_submit() {
|
async function handle_submit() {
|
||||||
if (!note_content.trim()) return;
|
if (!note_content.trim()) return;
|
||||||
if (!target_journal_id) {
|
if (!target_journal_id) {
|
||||||
alert('Please select a target journal first.');
|
alert('Please select a target journal first.');
|
||||||
@@ -74,25 +74,24 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
is_submitting = false;
|
is_submitting = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
function handle_keydown(e: KeyboardEvent) {
|
function handle_keydown(e: KeyboardEvent) {
|
||||||
if (e.ctrlKey && e.key === 'Enter') {
|
if (e.ctrlKey && e.key === 'Enter') {
|
||||||
handle_submit();
|
handle_submit();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function handle_journal_change(e: Event) {
|
function handle_journal_change(e: Event) {
|
||||||
const val = (e.target as HTMLSelectElement).value;
|
const val = (e.target as HTMLSelectElement).value;
|
||||||
selected_journal_id = val;
|
selected_journal_id = val;
|
||||||
$journals_loc.entry.qry__journal_id = val; // Persist choice
|
$journals_loc.entry.qry__journal_id = val; // Persist choice
|
||||||
}
|
}
|
||||||
</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>
|
||||||
|
|||||||
@@ -1,49 +1,82 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { untrack } from 'svelte';
|
import { untrack } from 'svelte';
|
||||||
/**
|
/**
|
||||||
* ae_comp__journal_obj_id_edit.svelte
|
* ae_comp__journal_obj_id_edit.svelte
|
||||||
* 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 {
|
||||||
import { Modal } from 'flowbite-svelte';
|
BetweenVerticalEnd,
|
||||||
import { goto } from '$app/navigation';
|
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 { goto } from '$app/navigation';
|
||||||
|
|
||||||
// *** Import Aether specific variables and functions
|
// *** Import Aether specific variables and functions
|
||||||
import { ae_loc, ae_api } from '$lib/stores/ae_stores';
|
import { ae_loc, ae_api } from '$lib/stores/ae_stores';
|
||||||
import {
|
import {
|
||||||
journals_loc,
|
journals_loc,
|
||||||
journals_sess,
|
journals_sess,
|
||||||
journals_slct
|
journals_slct
|
||||||
} from '$lib/ae_journals/ae_journals_stores';
|
} from '$lib/ae_journals/ae_journals_stores';
|
||||||
import { journals_func } from '$lib/ae_journals/ae_journals_functions';
|
import { journals_func } from '$lib/ae_journals/ae_journals_functions';
|
||||||
import AE_Comp_Editor_CodeMirror from '$lib/elements/element_editor_codemirror.svelte';
|
import AE_Comp_Editor_CodeMirror from '$lib/elements/element_editor_codemirror.svelte';
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
log_lvl?: number;
|
log_lvl?: number;
|
||||||
lq__journal_obj: any;
|
lq__journal_obj: any;
|
||||||
show?: boolean;
|
show?: boolean;
|
||||||
on_new_entry?: () => void;
|
on_new_entry?: () => void;
|
||||||
on_show_export?: () => void;
|
on_show_export?: () => void;
|
||||||
on_show_import?: () => void;
|
on_show_import?: () => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
let {
|
let {
|
||||||
log_lvl = $bindable(0),
|
log_lvl = $bindable(0),
|
||||||
lq__journal_obj,
|
lq__journal_obj,
|
||||||
show = $bindable(false),
|
show = $bindable(false),
|
||||||
on_new_entry,
|
on_new_entry,
|
||||||
on_show_export,
|
on_show_export,
|
||||||
on_show_import
|
on_show_import
|
||||||
}: 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
|
||||||
$effect(() => {
|
$effect(() => {
|
||||||
if (show && $lq__journal_obj) {
|
if (show && $lq__journal_obj) {
|
||||||
const source_id = $lq__journal_obj.journal_id;
|
const source_id = $lq__journal_obj.journal_id;
|
||||||
untrack(() => {
|
untrack(() => {
|
||||||
@@ -51,18 +84,15 @@
|
|||||||
!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 = {};
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
async function handle_update_journal(close_modal: boolean = true) {
|
async function handle_update_journal(close_modal: boolean = true) {
|
||||||
if (!tmp__journal_obj.name || !tmp__journal_obj.type_code) {
|
if (!tmp__journal_obj.name || !tmp__journal_obj.type_code) {
|
||||||
alert('Please provide both name and type for the journal.');
|
alert('Please provide both name and type for the journal.');
|
||||||
return;
|
return;
|
||||||
@@ -112,9 +142,9 @@
|
|||||||
console.error('Error updating journal:', error);
|
console.error('Error updating journal:', error);
|
||||||
if (close_modal) alert('Failed to update journal.');
|
if (close_modal) alert('Failed to update journal.');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async function delete_journal() {
|
async function delete_journal() {
|
||||||
if (
|
if (
|
||||||
confirm(
|
confirm(
|
||||||
`CRITICAL WARNING: Are you sure you want to delete the journal "${$lq__journal_obj.name}"? This will delete all entries associated with it.`
|
`CRITICAL WARNING: Are you sure you want to delete the journal "${$lq__journal_obj.name}"? This will delete all entries associated with it.`
|
||||||
@@ -133,7 +163,7 @@
|
|||||||
alert('Failed to delete journal.');
|
alert('Failed to delete journal.');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<Modal
|
<Modal
|
||||||
@@ -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>
|
||||||
|
|||||||
@@ -1,12 +1,22 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
// *** Import Svelte specific
|
// *** Import Svelte specific
|
||||||
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 {
|
||||||
// *** Import Aether specific variables and functions
|
BookOpenText,
|
||||||
import { ae_util } from '$lib/ae_utils/ae_utils';
|
BookPlus,
|
||||||
import {
|
FileDown,
|
||||||
|
FilePlus,
|
||||||
|
FileUp,
|
||||||
|
LoaderCircle,
|
||||||
|
Menu,
|
||||||
|
Pencil,
|
||||||
|
Settings
|
||||||
|
} from '@lucide/svelte';
|
||||||
|
// *** Import Aether specific variables and functions
|
||||||
|
import { ae_util } from '$lib/ae_utils/ae_utils';
|
||||||
|
import {
|
||||||
ae_snip,
|
ae_snip,
|
||||||
ae_loc,
|
ae_loc,
|
||||||
ae_sess,
|
ae_sess,
|
||||||
@@ -14,42 +24,42 @@
|
|||||||
ae_trig,
|
ae_trig,
|
||||||
slct,
|
slct,
|
||||||
slct_trigger
|
slct_trigger
|
||||||
} from '$lib/stores/ae_stores';
|
} from '$lib/stores/ae_stores';
|
||||||
import {
|
import {
|
||||||
journals_loc,
|
journals_loc,
|
||||||
journals_sess,
|
journals_sess,
|
||||||
journals_slct,
|
journals_slct,
|
||||||
journals_trig,
|
journals_trig,
|
||||||
journals_prom
|
journals_prom
|
||||||
} from '$lib/ae_journals/ae_journals_stores';
|
} from '$lib/ae_journals/ae_journals_stores';
|
||||||
import { journals_func } from '$lib/ae_journals/ae_journals_functions';
|
import { journals_func } from '$lib/ae_journals/ae_journals_functions';
|
||||||
import Journal_obj_id_edit from './ae_comp__journal_obj_id_edit.svelte';
|
import Journal_obj_id_edit from './ae_comp__journal_obj_id_edit.svelte';
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
log_lvl?: number;
|
log_lvl?: number;
|
||||||
lq__journal_obj: any;
|
lq__journal_obj: any;
|
||||||
lq__journal_entry_obj_li: any;
|
lq__journal_entry_obj_li: any;
|
||||||
on_show_export?: () => void;
|
on_show_export?: () => void;
|
||||||
on_show_import?: () => void;
|
on_show_import?: () => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
let {
|
let {
|
||||||
log_lvl = 0,
|
log_lvl = 0,
|
||||||
lq__journal_obj,
|
lq__journal_obj,
|
||||||
lq__journal_entry_obj_li,
|
lq__journal_entry_obj_li,
|
||||||
on_show_export,
|
on_show_export,
|
||||||
on_show_import
|
on_show_import
|
||||||
}: Props = $props();
|
}: Props = $props();
|
||||||
|
|
||||||
// let ae_promises: key_val = {};
|
// let ae_promises: key_val = {};
|
||||||
// let ae_tmp: key_val = {};
|
// let ae_tmp: key_val = {};
|
||||||
// let ae_trigger: any = null;
|
// let ae_trigger: any = null;
|
||||||
// let ae_triggers: key_val = {};
|
// let ae_triggers: key_val = {};
|
||||||
|
|
||||||
let typed_journal_passcode: string = $state('');
|
let typed_journal_passcode: string = $state('');
|
||||||
let passcode_timer: any = $state(null);
|
let passcode_timer: any = $state(null);
|
||||||
|
|
||||||
$effect(() => {
|
$effect(() => {
|
||||||
if (typed_journal_passcode?.length > 4) {
|
if (typed_journal_passcode?.length > 4) {
|
||||||
if (!$journals_sess?.journal_kv) {
|
if (!$journals_sess?.journal_kv) {
|
||||||
$journals_sess.journal_kv = {};
|
$journals_sess.journal_kv = {};
|
||||||
@@ -105,9 +115,9 @@
|
|||||||
passcode_timer = null;
|
passcode_timer = null;
|
||||||
}, timeout_ms);
|
}, timeout_ms);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
function verify_journal_passcode() {
|
function verify_journal_passcode() {
|
||||||
if (log_lvl) {
|
if (log_lvl) {
|
||||||
console.log(
|
console.log(
|
||||||
`verify_journal_passcode: typed_journal_passcode = ${typed_journal_passcode} journal private passcode = ${$lq__journal_obj?.private_passcode}`
|
`verify_journal_passcode: typed_journal_passcode = ${typed_journal_passcode} journal private passcode = ${$lq__journal_obj?.private_passcode}`
|
||||||
@@ -127,9 +137,9 @@
|
|||||||
typed_journal_passcode = '';
|
typed_journal_passcode = '';
|
||||||
} else {
|
} else {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async function handle_new_entry() {
|
async function handle_new_entry() {
|
||||||
let data_kv = {
|
let data_kv = {
|
||||||
category_code: null
|
category_code: null
|
||||||
};
|
};
|
||||||
@@ -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(
|
||||||
@@ -158,63 +167,56 @@
|
|||||||
console.error('Error creating journal entry:', error);
|
console.error('Error creating journal entry:', error);
|
||||||
alert('Failed to create new journal entry.');
|
alert('Failed to create new journal entry.');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</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">×</span
|
class="ml-0.5 text-xs opacity-50">×</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} />
|
||||||
/>
|
|
||||||
|
|||||||
@@ -1,19 +1,19 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
/**
|
/**
|
||||||
* ae_comp__journal_obj_li.svelte
|
* ae_comp__journal_obj_li.svelte
|
||||||
* Modernized Journal List Component
|
* Modernized Journal List Component
|
||||||
* Layout: Responsive Grid (1 col mobile, 2 col tablet, 3 col desktop)
|
* Layout: Responsive Grid (1 col mobile, 2 col tablet, 3 col desktop)
|
||||||
* Style: Tailwind 4 + Skeleton UI Reference Standard
|
* Style: Tailwind 4 + Skeleton UI Reference Standard
|
||||||
*/
|
*/
|
||||||
import { BookOpenText, BookType, Calendar, Clock, Hash } from '@lucide/svelte';
|
import { BookOpenText, BookType, Calendar, Clock, Hash } from '@lucide/svelte';
|
||||||
import { ae_util } from '$lib/ae_utils/ae_utils';
|
import { ae_util } from '$lib/ae_utils/ae_utils';
|
||||||
import { ae_loc } from '$lib/stores/ae_stores';
|
import { ae_loc } from '$lib/stores/ae_stores';
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
lq__journal_obj_li: any;
|
lq__journal_obj_li: any;
|
||||||
}
|
}
|
||||||
|
|
||||||
let { lq__journal_obj_li }: Props = $props();
|
let { lq__journal_obj_li }: Props = $props();
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<!--
|
<!--
|
||||||
@@ -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)}…
|
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
|
||||||
|
)}…
|
||||||
</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}×</span>
|
<span class="font-bold tabular-nums"
|
||||||
|
>{journal.journal_entry_count ??
|
||||||
|
0}×</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>
|
||||||
|
|||||||
@@ -1,36 +1,49 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { untrack } from 'svelte';
|
import { untrack } from 'svelte';
|
||||||
/**
|
/**
|
||||||
* ae_comp__modal_journal_config.svelte
|
* ae_comp__modal_journal_config.svelte
|
||||||
* 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 {
|
||||||
import { Modal } from 'flowbite-svelte';
|
CalendarClock,
|
||||||
|
Check,
|
||||||
|
CodeXml,
|
||||||
|
Database,
|
||||||
|
Layout,
|
||||||
|
MonitorPlay,
|
||||||
|
MousePointerClick,
|
||||||
|
Palette,
|
||||||
|
Settings,
|
||||||
|
ShieldCheck,
|
||||||
|
Wrench,
|
||||||
|
X
|
||||||
|
} from '@lucide/svelte';
|
||||||
|
import { Modal } from 'flowbite-svelte';
|
||||||
|
|
||||||
// *** Import Aether specific variables and functions
|
// *** Import Aether specific variables and functions
|
||||||
import { ae_loc, ae_api } from '$lib/stores/ae_stores';
|
import { ae_loc, ae_api } from '$lib/stores/ae_stores';
|
||||||
import {
|
import {
|
||||||
journals_loc,
|
journals_loc,
|
||||||
journals_sess
|
journals_sess
|
||||||
} from '$lib/ae_journals/ae_journals_stores';
|
} from '$lib/ae_journals/ae_journals_stores';
|
||||||
import AE_Comp_Editor_CodeMirror from '$lib/elements/element_editor_codemirror.svelte';
|
import AE_Comp_Editor_CodeMirror from '$lib/elements/element_editor_codemirror.svelte';
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
log_lvl?: number;
|
log_lvl?: number;
|
||||||
show?: boolean;
|
show?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
let { log_lvl = 0, show = $bindable(false) }: Props = $props();
|
let { log_lvl = 0, show = $bindable(false) }: Props = $props();
|
||||||
|
|
||||||
// Internal State
|
// Internal State
|
||||||
let tab: 'form' | 'local_json' | 'session_json' = $state('form');
|
let tab: 'form' | 'local_json' | 'session_json' = $state('form');
|
||||||
let tmp_config: any = $state({
|
let tmp_config: any = $state({
|
||||||
journal: {},
|
journal: {},
|
||||||
entry: {}
|
entry: {}
|
||||||
});
|
});
|
||||||
|
|
||||||
$effect(() => {
|
$effect(() => {
|
||||||
if (show) {
|
if (show) {
|
||||||
untrack(() => {
|
untrack(() => {
|
||||||
const fresh_config = JSON.parse(JSON.stringify($journals_loc));
|
const fresh_config = JSON.parse(JSON.stringify($journals_loc));
|
||||||
@@ -39,12 +52,12 @@
|
|||||||
tmp_config = fresh_config;
|
tmp_config = fresh_config;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
function handle_save() {
|
function handle_save() {
|
||||||
journals_loc.set(tmp_config);
|
journals_loc.set(tmp_config);
|
||||||
show = false;
|
show = false;
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<Modal
|
<Modal
|
||||||
@@ -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>Æ Journals Module Config</span>
|
<span>Æ 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>
|
||||||
|
|||||||
@@ -1,12 +1,12 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { Modal } from 'flowbite-svelte';
|
import { Modal } from 'flowbite-svelte';
|
||||||
import { Check, X } from '@lucide/svelte';
|
import { Check, X } from '@lucide/svelte';
|
||||||
import { ae_util } from '$lib/ae_utils/ae_utils';
|
import { ae_util } from '$lib/ae_utils/ae_utils';
|
||||||
import { journals_func } from '$lib/ae_journals/ae_journals_functions';
|
import { journals_func } from '$lib/ae_journals/ae_journals_functions';
|
||||||
import { ae_api } from '$lib/stores/ae_stores';
|
import { ae_api } from '$lib/stores/ae_stores';
|
||||||
import type { key_val } from '$lib/stores/ae_stores';
|
import type { key_val } from '$lib/stores/ae_stores';
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
open: boolean;
|
open: boolean;
|
||||||
journal_entry: key_val;
|
journal_entry: key_val;
|
||||||
journal_config: key_val; // The cfg_json from the journal object
|
journal_config: key_val; // The cfg_json from the journal object
|
||||||
@@ -14,9 +14,9 @@
|
|||||||
on_close: () => void;
|
on_close: () => void;
|
||||||
on_update: () => void;
|
on_update: () => void;
|
||||||
log_lvl?: number;
|
log_lvl?: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
let {
|
let {
|
||||||
open = $bindable(false),
|
open = $bindable(false),
|
||||||
journal_entry,
|
journal_entry,
|
||||||
journal_config,
|
journal_config,
|
||||||
@@ -24,32 +24,32 @@
|
|||||||
on_close,
|
on_close,
|
||||||
on_update,
|
on_update,
|
||||||
log_lvl = 0
|
log_lvl = 0
|
||||||
}: Props = $props();
|
}: Props = $props();
|
||||||
// Local State
|
// Local State
|
||||||
let tmp_entry_obj: key_val = $state({});
|
let tmp_entry_obj: key_val = $state({});
|
||||||
|
|
||||||
// Header Options
|
// Header Options
|
||||||
let add_timestamp_header: boolean = $state(true);
|
let add_timestamp_header: boolean = $state(true);
|
||||||
let add_timestamp_header_w_day_of_week: boolean = $state(true);
|
let add_timestamp_header_w_day_of_week: boolean = $state(true);
|
||||||
let add_text_header: string = $state('');
|
let add_text_header: string = $state('');
|
||||||
let add_text: string = $state('');
|
let add_text: string = $state('');
|
||||||
|
|
||||||
// Change detection
|
// Change detection
|
||||||
let has_changes: boolean = $derived(
|
let has_changes: boolean = $derived(
|
||||||
add_text_header.length > 0 || add_text.length > 0
|
add_text_header.length > 0 || add_text.length > 0
|
||||||
);
|
);
|
||||||
|
|
||||||
// Initialize tmp object when entry changes or modal opens
|
// Initialize tmp object when entry changes or modal opens
|
||||||
$effect(() => {
|
$effect(() => {
|
||||||
if (open && journal_entry) {
|
if (open && journal_entry) {
|
||||||
tmp_entry_obj = JSON.parse(JSON.stringify(journal_entry));
|
tmp_entry_obj = JSON.parse(JSON.stringify(journal_entry));
|
||||||
// Reset fields
|
// Reset fields
|
||||||
add_text_header = '';
|
add_text_header = '';
|
||||||
add_text = '';
|
add_text = '';
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
async function handle_save() {
|
async function handle_save() {
|
||||||
let current_entry_content = tmp_entry_obj?.content || '';
|
let current_entry_content = tmp_entry_obj?.content || '';
|
||||||
let add_content = '';
|
let add_content = '';
|
||||||
let new_content = current_entry_content;
|
let new_content = current_entry_content;
|
||||||
@@ -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,
|
||||||
@@ -132,7 +129,7 @@
|
|||||||
console.error('Error updating journal entry:', error);
|
console.error('Error updating journal entry:', error);
|
||||||
alert('Failed to update journal entry.');
|
alert('Failed to update journal entry.');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<Modal
|
<Modal
|
||||||
@@ -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>
|
||||||
|
|||||||
@@ -1,20 +1,40 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
/**
|
/**
|
||||||
* 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 {
|
||||||
import { Modal } from 'flowbite-svelte';
|
ArrowDownToLine,
|
||||||
import { ae_loc, ae_api } from '$lib/stores/ae_stores';
|
ArrowUpToLine,
|
||||||
import {
|
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 { ae_loc, ae_api } from '$lib/stores/ae_stores';
|
||||||
|
import {
|
||||||
journals_loc,
|
journals_loc,
|
||||||
journals_sess
|
journals_sess
|
||||||
} from '$lib/ae_journals/ae_journals_stores';
|
} from '$lib/ae_journals/ae_journals_stores';
|
||||||
import { journals_func } from '$lib/ae_journals/ae_journals_functions';
|
import { journals_func } from '$lib/ae_journals/ae_journals_functions';
|
||||||
import AE_Comp_Editor_CodeMirror from '$lib/elements/element_editor_codemirror.svelte';
|
import AE_Comp_Editor_CodeMirror from '$lib/elements/element_editor_codemirror.svelte';
|
||||||
import AE_Object_Flags from '$lib/ae_elements/AE_Object_Flags.svelte';
|
import AE_Object_Flags from '$lib/ae_elements/AE_Object_Flags.svelte';
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
log_lvl?: number;
|
log_lvl?: number;
|
||||||
show?: boolean;
|
show?: boolean;
|
||||||
entry: any;
|
entry: any;
|
||||||
@@ -25,9 +45,9 @@
|
|||||||
on_show_export?: () => void;
|
on_show_export?: () => void;
|
||||||
on_append?: () => void;
|
on_append?: () => void;
|
||||||
on_prepend?: () => void;
|
on_prepend?: () => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
let {
|
let {
|
||||||
log_lvl = $bindable(0),
|
log_lvl = $bindable(0),
|
||||||
show = $bindable(false),
|
show = $bindable(false),
|
||||||
entry,
|
entry,
|
||||||
@@ -38,14 +58,13 @@
|
|||||||
on_show_export,
|
on_show_export,
|
||||||
on_append,
|
on_append,
|
||||||
on_prepend
|
on_prepend
|
||||||
}: Props = $props();
|
}: Props = $props();
|
||||||
|
|
||||||
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 {
|
||||||
// WHITELISTED BASE TABLE COLUMNS ONLY
|
// WHITELISTED BASE TABLE COLUMNS ONLY
|
||||||
const data_kv = {
|
const data_kv = {
|
||||||
@@ -83,7 +102,7 @@
|
|||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Error updating journal entry:', error);
|
console.error('Error updating journal entry:', error);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<Modal
|
<Modal
|
||||||
@@ -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>
|
||||||
|
|||||||
@@ -1,48 +1,54 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
/**
|
/**
|
||||||
* @file ae_comp__modal_journal_export.svelte
|
* @file ae_comp__modal_journal_export.svelte
|
||||||
* @description Modal component for bulk exporting journal entries.
|
* @description Modal component for bulk exporting journal entries.
|
||||||
* Allows exporting entries using various templates (Markdown, HTML, JSON).
|
* Allows exporting entries using various templates (Markdown, HTML, JSON).
|
||||||
* @author One Sky IT
|
* @author One Sky IT
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { Modal } from 'flowbite-svelte';
|
import { Modal } from 'flowbite-svelte';
|
||||||
import { Code, Copy, Download, FileJson, FileType, Settings2 } from '@lucide/svelte';
|
import {
|
||||||
import { ae_util } from '$lib/ae_utils/ae_utils';
|
Code,
|
||||||
import type { ae_JournalEntry, ae_Journal } from '$lib/types/ae_types';
|
Copy,
|
||||||
import {
|
Download,
|
||||||
|
FileJson,
|
||||||
|
FileType,
|
||||||
|
Settings2
|
||||||
|
} from '@lucide/svelte';
|
||||||
|
import { ae_util } from '$lib/ae_utils/ae_utils';
|
||||||
|
import type { ae_JournalEntry, ae_Journal } from '$lib/types/ae_types';
|
||||||
|
import {
|
||||||
EXPORT_TEMPLATES,
|
EXPORT_TEMPLATES,
|
||||||
type ExportTemplate
|
type ExportTemplate
|
||||||
} from '$lib/ae_journals/ae_journals_export_templates';
|
} from '$lib/ae_journals/ae_journals_export_templates';
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
open: boolean;
|
open: boolean;
|
||||||
entries: ae_JournalEntry[];
|
entries: ae_JournalEntry[];
|
||||||
journal?: ae_Journal;
|
journal?: ae_Journal;
|
||||||
on_close: () => void;
|
on_close: () => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
let {
|
let {
|
||||||
open = $bindable(false),
|
open = $bindable(false),
|
||||||
entries = [],
|
entries = [],
|
||||||
journal,
|
journal,
|
||||||
on_close
|
on_close
|
||||||
}: Props = $props();
|
}: Props = $props();
|
||||||
|
|
||||||
// State
|
// State
|
||||||
let selected_template_id: string = $state('standard_markdown');
|
let selected_template_id: string = $state('standard_markdown');
|
||||||
let export_preview: string = $state('');
|
let export_preview: string = $state('');
|
||||||
let export_count: number = $derived(entries.length);
|
let export_count: number = $derived(entries.length);
|
||||||
|
|
||||||
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
|
||||||
$effect(() => {
|
$effect(() => {
|
||||||
if (open && journal?.type_code) {
|
if (open && journal?.type_code) {
|
||||||
if (
|
if (
|
||||||
journal.type_code === 'personal_log' &&
|
journal.type_code === 'personal_log' &&
|
||||||
@@ -56,24 +62,24 @@
|
|||||||
selected_template_id = 'amazon_vine';
|
selected_template_id = 'amazon_vine';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// Re-generate preview when entries or template changes
|
// Re-generate preview when entries or template changes
|
||||||
$effect(() => {
|
$effect(() => {
|
||||||
if (open && entries.length > 0) {
|
if (open && entries.length > 0) {
|
||||||
generate_preview();
|
generate_preview();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
function generate_preview() {
|
function generate_preview() {
|
||||||
if (!selected_template) return;
|
if (!selected_template) return;
|
||||||
export_preview = selected_template.formatter(entries);
|
export_preview = selected_template.formatter(entries);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Downloads the generated content as a file.
|
* Downloads the generated content as a file.
|
||||||
*/
|
*/
|
||||||
function handle_download() {
|
function handle_download() {
|
||||||
const timestamp = new Date().toISOString().replace(/[:.]/g, '-');
|
const timestamp = new Date().toISOString().replace(/[:.]/g, '-');
|
||||||
const filename = `journal_export_${timestamp}.${selected_template.extension}`;
|
const filename = `journal_export_${timestamp}.${selected_template.extension}`;
|
||||||
const blob = new Blob([export_preview], {
|
const blob = new Blob([export_preview], {
|
||||||
@@ -88,12 +94,12 @@
|
|||||||
link.click();
|
link.click();
|
||||||
document.body.removeChild(link);
|
document.body.removeChild(link);
|
||||||
URL.revokeObjectURL(url);
|
URL.revokeObjectURL(url);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Copies the generated content to the clipboard.
|
* Copies the generated content to the clipboard.
|
||||||
*/
|
*/
|
||||||
async function handle_copy() {
|
async function handle_copy() {
|
||||||
try {
|
try {
|
||||||
await navigator.clipboard.writeText(export_preview);
|
await navigator.clipboard.writeText(export_preview);
|
||||||
alert('Content copied to clipboard!');
|
alert('Content copied to clipboard!');
|
||||||
@@ -101,7 +107,7 @@
|
|||||||
console.error('Failed to copy:', err);
|
console.error('Failed to copy:', err);
|
||||||
alert('Failed to copy to clipboard.');
|
alert('Failed to copy to clipboard.');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<Modal
|
<Modal
|
||||||
@@ -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>
|
||||||
|
|||||||
@@ -1,60 +1,63 @@
|
|||||||
<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 {
|
||||||
import {
|
Check,
|
||||||
|
CircleAlert,
|
||||||
|
FileText,
|
||||||
|
RefreshCw,
|
||||||
|
Upload,
|
||||||
|
X
|
||||||
|
} from '@lucide/svelte';
|
||||||
|
import {
|
||||||
PARSERS,
|
PARSERS,
|
||||||
type AeJournalEntryInput
|
type AeJournalEntryInput
|
||||||
} from '$lib/ae_journals/ae_journals_parsers';
|
} from '$lib/ae_journals/ae_journals_parsers';
|
||||||
import { journals_func } from '$lib/ae_journals/ae_journals_functions';
|
import { journals_func } from '$lib/ae_journals/ae_journals_functions';
|
||||||
import { ae_api } from '$lib/stores/ae_stores';
|
import { ae_api } from '$lib/stores/ae_stores';
|
||||||
import { journals_slct } from '$lib/ae_journals/ae_journals_stores';
|
import { journals_slct } from '$lib/ae_journals/ae_journals_stores';
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
open: boolean;
|
open: boolean;
|
||||||
on_close: () => void;
|
on_close: () => void;
|
||||||
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');
|
||||||
let parsed_entries: AeJournalEntryInput[] = $state([]);
|
let parsed_entries: AeJournalEntryInput[] = $state([]);
|
||||||
let is_parsing = $state(false);
|
let is_parsing = $state(false);
|
||||||
let is_importing = $state(false);
|
let is_importing = $state(false);
|
||||||
let import_log: string[] = $state([]);
|
let import_log: string[] = $state([]);
|
||||||
let is_dragging = $state(false);
|
let is_dragging = $state(false);
|
||||||
|
|
||||||
// Watch for file selection or parser change to trigger parsing
|
// Watch for file selection or parser change to trigger parsing
|
||||||
$effect(() => {
|
$effect(() => {
|
||||||
if (files && files.length > 0) {
|
if (files && files.length > 0) {
|
||||||
parse_files();
|
parse_files();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
function handle_drag_enter(e: DragEvent) {
|
function handle_drag_enter(e: DragEvent) {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
is_dragging = true;
|
is_dragging = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
function handle_drag_leave(e: DragEvent) {
|
function handle_drag_leave(e: DragEvent) {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
is_dragging = false;
|
is_dragging = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
function handle_drag_over(e: DragEvent) {
|
function handle_drag_over(e: DragEvent) {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
is_dragging = true;
|
is_dragging = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
function handle_drop(e: DragEvent) {
|
function handle_drop(e: DragEvent) {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
is_dragging = false;
|
is_dragging = false;
|
||||||
@@ -62,9 +65,9 @@
|
|||||||
if (e.dataTransfer?.files && e.dataTransfer.files.length > 0) {
|
if (e.dataTransfer?.files && e.dataTransfer.files.length > 0) {
|
||||||
files = e.dataTransfer.files;
|
files = e.dataTransfer.files;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async function parse_files() {
|
async function parse_files() {
|
||||||
if (!files) return;
|
if (!files) return;
|
||||||
is_parsing = true;
|
is_parsing = true;
|
||||||
parsed_entries = [];
|
parsed_entries = [];
|
||||||
@@ -82,9 +85,9 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
is_parsing = false;
|
is_parsing = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
async function handle_import() {
|
async function handle_import() {
|
||||||
if (parsed_entries.length === 0) return;
|
if (parsed_entries.length === 0) return;
|
||||||
is_importing = true;
|
is_importing = true;
|
||||||
import_log = [];
|
import_log = [];
|
||||||
@@ -136,7 +139,7 @@
|
|||||||
);
|
);
|
||||||
on_import_complete();
|
on_import_complete();
|
||||||
open = false;
|
open = false;
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<Modal
|
<Modal
|
||||||
@@ -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}
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
// *** Import Svelte specific
|
// *** Import Svelte specific
|
||||||
|
|
||||||
import {
|
import {
|
||||||
ArrowDown01,
|
ArrowDown01,
|
||||||
ArrowDown10,
|
ArrowDown10,
|
||||||
ArrowDownUp,
|
ArrowDownUp,
|
||||||
@@ -44,12 +44,12 @@
|
|||||||
Trash2,
|
Trash2,
|
||||||
TypeOutline,
|
TypeOutline,
|
||||||
X
|
X
|
||||||
} from '@lucide/svelte';
|
} 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_snip,
|
ae_snip,
|
||||||
ae_loc,
|
ae_loc,
|
||||||
ae_sess,
|
ae_sess,
|
||||||
@@ -57,12 +57,12 @@
|
|||||||
ae_trig,
|
ae_trig,
|
||||||
slct,
|
slct,
|
||||||
slct_trigger
|
slct_trigger
|
||||||
} from '$lib/stores/ae_stores';
|
} from '$lib/stores/ae_stores';
|
||||||
import { journals_func } from '$lib/ae_journals/ae_journals_functions';
|
import { journals_func } from '$lib/ae_journals/ae_journals_functions';
|
||||||
import { journals_slct } from '$lib/ae_journals/ae_journals_stores';
|
import { journals_slct } from '$lib/ae_journals/ae_journals_stores';
|
||||||
import { goto } from '$app/navigation';
|
import { goto } from '$app/navigation';
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
log_lvl?: number;
|
log_lvl?: number;
|
||||||
obj_priority: any;
|
obj_priority: any;
|
||||||
obj_sort: any;
|
obj_sort: any;
|
||||||
@@ -76,9 +76,9 @@
|
|||||||
lq__journal_entry_obj: any;
|
lq__journal_entry_obj: any;
|
||||||
change_journal_id: any;
|
change_journal_id: any;
|
||||||
lq__journal_obj_li: any;
|
lq__journal_obj_li: any;
|
||||||
}
|
}
|
||||||
|
|
||||||
let {
|
let {
|
||||||
log_lvl = 0,
|
log_lvl = 0,
|
||||||
obj_priority,
|
obj_priority,
|
||||||
obj_sort,
|
obj_sort,
|
||||||
@@ -92,14 +92,13 @@
|
|||||||
lq__journal_entry_obj,
|
lq__journal_entry_obj,
|
||||||
change_journal_id,
|
change_journal_id,
|
||||||
lq__journal_obj_li
|
lq__journal_obj_li
|
||||||
}: Props = $props();
|
}: Props = $props();
|
||||||
|
|
||||||
let ae_promises: key_val = $state({});
|
let ae_promises: key_val = $state({});
|
||||||
</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}
|
||||||
|
|||||||
@@ -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');
|
||||||
|
|||||||
Reference in New Issue
Block a user