From 54707a00e3fcf9c9e35749b16c3264ab55b688ee Mon Sep 17 00:00:00 2001 From: Scott Idem Date: Tue, 5 May 2026 17:14:20 -0400 Subject: [PATCH] Refine journal entry config Polish the Journal Entry Config modal to match the desired section outline, hide alert messaging unless enabled, update the shared draft typing for entry flows, and replace deprecated privacy icons. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- documentation/AE__Permissions_and_Security.md | 2 +- ...JECT__AE_UI_Journals_module_update_2026.md | 4 +- src/lib/ae_elements/AE_AITools.svelte | 4 +- src/lib/ae_elements/AE_Object_Flags.svelte | 10 +- .../elements/element_editor_codemirror.svelte | 2 +- src/lib/types/ae_types.ts | 24 ++ .../ae_comp__journal_entry_ai_tools.svelte | 2 +- .../ae_comp__journal_entry_editor.svelte | 13 +- .../ae_comp__journal_entry_obj_id_view.svelte | 35 +- .../ae_comp__journal_entry_obj_li.svelte | 3 +- ...ae_comp__modal_journal_entry_append.svelte | 15 +- ...ae_comp__modal_journal_entry_config.svelte | 305 ++++++++++-------- 12 files changed, 244 insertions(+), 175 deletions(-) diff --git a/documentation/AE__Permissions_and_Security.md b/documentation/AE__Permissions_and_Security.md index b9141d34..150532c1 100644 --- a/documentation/AE__Permissions_and_Security.md +++ b/documentation/AE__Permissions_and_Security.md @@ -191,7 +191,7 @@ This pattern lives in `ae_comp__badge_obj_li.svelte` — move to `ae_utils` if n - Entry configuration admin controls are gated to `trusted_access` and above. - `manager_access` and `administrator_access` see the Delete action, which performs a hard delete. - `trusted_access` users see Remove instead, which follows disable semantics rather than a hard delete. -- The Admin section is the place for staff notes, enabled/default access state, and destructive entry actions; visibility/privacy flags remain separate. +- The Admin section is the place for staff notes, enabled/default access state, and destructive entry actions; the template toggle belongs in Metadata, while visibility/audience flags remain separate. ### Events — Badges diff --git a/documentation/PROJECT__AE_UI_Journals_module_update_2026.md b/documentation/PROJECT__AE_UI_Journals_module_update_2026.md index a34cdbf7..c97730c6 100644 --- a/documentation/PROJECT__AE_UI_Journals_module_update_2026.md +++ b/documentation/PROJECT__AE_UI_Journals_module_update_2026.md @@ -151,9 +151,9 @@ Svelte 5 state is backed by Proxies. ### 5. Journal Entry Config Layout Notes The Entry Config modal now follows a stricter section grammar: -* `Metadata` contains category, tags, summary, and archive date. +* `Metadata` contains category, tags, summary, archive date, and template. * `Status & Security` contains enabled/hidden/priority/sort. -* `Privacy Flags` contains only visibility/audience toggles. +* `Visibility & Audience` contains only visibility/audience toggles. * `Alerts & Messaging` contains alert flag + alert message. * `Admin` is gated to trusted access and above, and is the only place for notes plus delete/remove actions. diff --git a/src/lib/ae_elements/AE_AITools.svelte b/src/lib/ae_elements/AE_AITools.svelte index 33649c45..491ddbeb 100644 --- a/src/lib/ae_elements/AE_AITools.svelte +++ b/src/lib/ae_elements/AE_AITools.svelte @@ -24,8 +24,8 @@ import AE_Comp_Editor_CodeMirror from '$lib/elements/element_editor_codemirror.s interface Props { // Core Props - content: string; // The text to summarize/analyze - summary: string; // The result (bindable) + content: string | null | undefined; // The text to summarize/analyze + summary: string | null | undefined; // The result (bindable) // Configuration (Bindable for global settings persistence) model?: string; diff --git a/src/lib/ae_elements/AE_Object_Flags.svelte b/src/lib/ae_elements/AE_Object_Flags.svelte index b29cd15a..cc15e80d 100644 --- a/src/lib/ae_elements/AE_Object_Flags.svelte +++ b/src/lib/ae_elements/AE_Object_Flags.svelte @@ -6,7 +6,7 @@ */ import { Siren, - Fingerprint, + FingerprintPattern, Globe, BookHeart, BriefcaseBusiness, @@ -14,11 +14,11 @@ import { Settings } from '@lucide/svelte'; import { ae_loc } from '$lib/stores/ae_stores'; -import type { ae_JournalEntry } from '$lib/types/ae_types'; +import type { ae_JournalEntryDraft } from '$lib/types/ae_types'; interface Props { // The object containing the flags (bindable) - obj: ae_JournalEntry; + obj: ae_JournalEntryDraft; // Visibility configuration (optional overrides) show_labels?: boolean; @@ -109,8 +109,8 @@ function toggle_template() { onclick={toggle_private} class="btn btn-sm flex items-center gap-2 px-3 transition preset-tonal-secondary hover:preset-filled-secondary-500" title="Toggle private or encrypted visibility"> - - Private + + Private or Encrypt {/if} diff --git a/src/lib/elements/element_editor_codemirror.svelte b/src/lib/elements/element_editor_codemirror.svelte index 050e57e5..eed4433e 100644 --- a/src/lib/elements/element_editor_codemirror.svelte +++ b/src/lib/elements/element_editor_codemirror.svelte @@ -13,7 +13,7 @@ import { ensure_CodeMirror_modules } from './codemirror_modules'; // Icons (Standardized to Lucide where possible, or FontAwesome placeholders) import { Bold, Code, Italic, List } from '@lucide/svelte'; interface Props { - content?: string; + content?: string | null; new_content?: string; placeholder?: string; theme_mode?: 'light' | 'dark'; diff --git a/src/lib/types/ae_types.ts b/src/lib/types/ae_types.ts index fc22b857..a8f291d3 100644 --- a/src/lib/types/ae_types.ts +++ b/src/lib/types/ae_types.ts @@ -230,6 +230,30 @@ export interface ae_JournalEntry extends ae_BaseObj { file_count?: number; } +export type ae_JournalEntryDraft = Omit< + Partial, + | 'journal_entry_id' + | 'journal_id' + | 'name' + | 'summary' + | 'content' + | 'content_md_html' + | 'content_encrypted' + | 'history' + | 'history_encrypted' +> & { + journal_entry_id?: string; + journal_id?: string; + name?: string | null; + summary?: string | null; + content?: string | null; + content_md_html?: string | null; + content_encrypted?: string | null; + history?: string | null; + history_encrypted?: string | null; + [key: string]: unknown; +}; + /** * Person - A human entity */ diff --git a/src/routes/journals/ae_comp__journal_entry_ai_tools.svelte b/src/routes/journals/ae_comp__journal_entry_ai_tools.svelte index 9295792e..ba9dd782 100644 --- a/src/routes/journals/ae_comp__journal_entry_ai_tools.svelte +++ b/src/routes/journals/ae_comp__journal_entry_ai_tools.svelte @@ -9,7 +9,7 @@ import { journals_loc } from '$lib/ae_journals/ae_journals_stores'; interface Props { content: string; - summary: string; // Bindable + summary: string | null | undefined; // Bindable on_save: () => void; log_lvl?: number; } diff --git a/src/routes/journals/ae_comp__journal_entry_editor.svelte b/src/routes/journals/ae_comp__journal_entry_editor.svelte index b8365f8d..e642b84a 100644 --- a/src/routes/journals/ae_comp__journal_entry_editor.svelte +++ b/src/routes/journals/ae_comp__journal_entry_editor.svelte @@ -8,17 +8,16 @@ import { LockKeyhole, RefreshCcw, Save } from '@lucide/svelte'; import { ae_loc } from '$lib/stores/ae_stores'; import { journals_loc } from '$lib/ae_journals/ae_journals_stores'; import AE_Comp_Editor_CodeMirror from '$lib/elements/element_editor_codemirror.svelte'; -import type { ae_JournalEntry, ae_Journal } from '$lib/types/ae_types'; - -type JournalEntryDraft = Partial & { - content?: string | false; - [key: string]: unknown; -}; +import type { + ae_JournalEntry, + ae_Journal, + ae_JournalEntryDraft +} from '$lib/types/ae_types'; interface Props { entry: ae_JournalEntry; journal: ae_Journal; - tmp_entry_obj: JournalEntryDraft; // Bindable + tmp_entry_obj: ae_JournalEntryDraft; // Bindable editor_view?: unknown; // Bindable has_changed: boolean; updated_idb: boolean; diff --git a/src/routes/journals/ae_comp__journal_entry_obj_id_view.svelte b/src/routes/journals/ae_comp__journal_entry_obj_id_view.svelte index 98725240..70f6bb3d 100644 --- a/src/routes/journals/ae_comp__journal_entry_obj_id_view.svelte +++ b/src/routes/journals/ae_comp__journal_entry_obj_id_view.svelte @@ -31,6 +31,7 @@ import AE_Comp_Journal_Entry_AiTools from './ae_comp__journal_entry_ai_tools.sve 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_Config from './ae_comp__modal_journal_entry_config.svelte'; +import type { ae_JournalEntryDraft } from '$lib/types/ae_types'; // Icons import { CircleAlert, CircleX, LoaderCircle } from '@lucide/svelte'; @@ -53,8 +54,8 @@ let { // *** State let editor_view: any = $state(); -let orig_entry_obj: key_val | null = $state(null); -let tmp_entry_obj: key_val = $state({}); +let orig_entry_obj: ae_JournalEntryDraft | null = $state(null); +let tmp_entry_obj: ae_JournalEntryDraft = $state({}); let save_status: 'saved' | 'unsaved' | 'saving' = $state('saved'); let decryption_error: string | null = $state(null); let auto_save_timer: ReturnType; @@ -65,7 +66,7 @@ let show_config_modal = $state(false); function deep_copy(obj: any) { if (!obj) return null; try { - const copy: key_val = {}; + const copy: ae_JournalEntryDraft = {}; for (const key in obj) { const val = obj[key]; if (val instanceof Date) { @@ -240,9 +241,12 @@ async function run_decryption_workflow() { } // SUCCESS - const content = result.content || ''; + const content = + typeof result.content === 'string' + ? result.content + : await (result.content ?? ''); tmp_entry_obj.content = content; - tmp_entry_obj.content_md_html = handle_marked(content); + tmp_entry_obj.content_md_html = await handle_marked(content); tmp_entry_obj.content_encrypted = null; tmp_entry_obj.history_encrypted = null; @@ -303,8 +307,12 @@ async function update_journal_entry(fields_kv?: key_val) { if (!fields_kv) { if (tmp_entry_obj.private) { if (tmp_entry_obj.content) { + const content_to_encrypt = + typeof tmp_entry_obj.content === 'string' + ? tmp_entry_obj.content + : ''; data_kv.content_encrypted = await ae_util.encrypt_wrapper( - tmp_entry_obj.content, + content_to_encrypt, decrypt_key ); data_kv.content = null; @@ -316,9 +324,15 @@ async function update_journal_entry(fields_kv?: key_val) { } try { + const journal_entry_id = $lq__journal_entry_obj?.journal_entry_id; + if (!journal_entry_id) { + console.error('Journal entry ID missing for update.'); + return; + } + await journals_func.update_ae_obj__journal_entry({ api_cfg: $ae_api, - journal_entry_id: $lq__journal_entry_obj?.journal_entry_id, + journal_entry_id, data_kv: data_kv, log_lvl: 1 }); @@ -468,7 +482,11 @@ let modal_mode: 'append' | 'prepend' | 'auto' = $state('auto'); ? 'ring-primary-500/40 ring-2 ring-inset' : ''}"> update_journal_entry()} {log_lvl} /> @@ -509,7 +527,6 @@ let modal_mode: 'append' | 'prepend' | 'auto' = $state('auto'); update_journal_entry()} diff --git a/src/routes/journals/ae_comp__journal_entry_obj_li.svelte b/src/routes/journals/ae_comp__journal_entry_obj_li.svelte index 5578685a..20fe7bb8 100644 --- a/src/routes/journals/ae_comp__journal_entry_obj_li.svelte +++ b/src/routes/journals/ae_comp__journal_entry_obj_li.svelte @@ -52,8 +52,9 @@ import { } from '$lib/ae_journals/ae_journals_stores'; import { journals_func } from '$lib/ae_journals/ae_journals_functions'; import AeCompModalJournalEntryAppend from './ae_comp__modal_journal_entry_append.svelte'; +import type { ae_JournalEntryDraft } from '$lib/types/ae_types'; -let tmp_entry_obj: key_val = $state({}); +let tmp_entry_obj: ae_JournalEntryDraft = $state({}); // 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 diff --git a/src/routes/journals/ae_comp__modal_journal_entry_append.svelte b/src/routes/journals/ae_comp__modal_journal_entry_append.svelte index ff5143be..7aa3b54e 100644 --- a/src/routes/journals/ae_comp__modal_journal_entry_append.svelte +++ b/src/routes/journals/ae_comp__modal_journal_entry_append.svelte @@ -5,10 +5,11 @@ import { ae_util } from '$lib/ae_utils/ae_utils'; import { journals_func } from '$lib/ae_journals/ae_journals_functions'; import { ae_api } from '$lib/stores/ae_stores'; import type { key_val } from '$lib/stores/ae_stores'; +import type { ae_JournalEntryDraft } from '$lib/types/ae_types'; interface Props { open: boolean; - journal_entry: key_val; + journal_entry: ae_JournalEntryDraft; journal_config: key_val; // The cfg_json from the journal object mode?: 'append' | 'prepend' | 'auto'; on_close: () => void; @@ -26,7 +27,7 @@ let { log_lvl = 0 }: Props = $props(); // Local State -let tmp_entry_obj: key_val = $state({}); +let tmp_entry_obj: ae_JournalEntryDraft = $state({}); // Header Options let add_timestamp_header: boolean = $state(true); @@ -50,7 +51,8 @@ $effect(() => { }); async function handle_save() { - let current_entry_content = tmp_entry_obj?.content || ''; + let current_entry_content = + typeof tmp_entry_obj?.content === 'string' ? tmp_entry_obj.content : ''; let add_content = ''; let new_content = current_entry_content; @@ -109,11 +111,16 @@ async function handle_save() { new_content = new_content.trim() + '\n'; let data_kv = { content: new_content }; + const journal_entry_id = tmp_entry_obj.journal_entry_id; + if (!journal_entry_id) { + console.error('Journal entry ID missing for append save.'); + return; + } try { let update_result = await journals_func.update_ae_obj__journal_entry({ api_cfg: $ae_api, - journal_entry_id: tmp_entry_obj?.journal_entry_id, + journal_entry_id, data_kv: data_kv, log_lvl: log_lvl }); diff --git a/src/routes/journals/ae_comp__modal_journal_entry_config.svelte b/src/routes/journals/ae_comp__modal_journal_entry_config.svelte index bf2bfbf6..fa255485 100644 --- a/src/routes/journals/ae_comp__modal_journal_entry_config.svelte +++ b/src/routes/journals/ae_comp__modal_journal_entry_config.svelte @@ -7,13 +7,11 @@ import { ArrowDownToLine, ArrowUpToLine, Check, - CircleAlert, CodeXml, Copy, FileDown, - Fingerprint, + FingerprintPattern, Minus, - Plus, RefreshCcw, Settings, Shapes, @@ -29,13 +27,16 @@ import { ae_loc, ae_api } from '$lib/stores/ae_stores'; import { journals_func } from '$lib/ae_journals/ae_journals_functions'; 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 type { ae_Journal, ae_JournalEntry } from '$lib/types/ae_types'; +import type { + ae_Journal, + ae_JournalEntryDraft +} from '$lib/types/ae_types'; interface Props { log_lvl?: number; show?: boolean; journal: ae_Journal; - tmp_entry_obj: ae_JournalEntry; // Bindable + tmp_entry_obj: ae_JournalEntryDraft; // Bindable on_save: () => void; on_force_reset?: () => void; on_show_export?: () => void; @@ -77,6 +78,12 @@ function tab_button_class(is_active: boolean): string { async function handle_update_entry() { try { // WHITELISTED BASE TABLE COLUMNS ONLY + const journal_entry_id = tmp_entry_obj.journal_entry_id; + if (!journal_entry_id) { + console.error('Journal entry ID missing for update.'); + return; + } + const data_kv = { name: tmp_entry_obj.name, short_name: tmp_entry_obj.short_name, @@ -86,6 +93,7 @@ async function handle_update_entry() { type_code: tmp_entry_obj.type_code, topic_code: tmp_entry_obj.topic_code, tags: tmp_entry_obj.tags, + passcode_hash: tmp_entry_obj.passcode_hash, private: tmp_entry_obj.private, public: tmp_entry_obj.public, personal: tmp_entry_obj.personal, @@ -105,7 +113,7 @@ async function handle_update_entry() { await journals_func.update_ae_obj__journal_entry({ api_cfg: $ae_api, - journal_entry_id: tmp_entry_obj.journal_entry_id, + journal_entry_id, data_kv: data_kv, log_lvl: log_lvl }); @@ -119,6 +127,12 @@ async function handle_admin_delete_action() { const delete_method = can_delete ? 'delete' : 'disable'; const action_label = can_delete ? 'delete' : 'remove'; const confirm_label = can_delete ? 'delete' : 'remove'; + const journal_entry_id = tmp_entry_obj.journal_entry_id; + + if (!journal_entry_id) { + console.error('Journal entry ID missing for delete action.'); + return; + } if (!confirm(`Are you sure you want to ${confirm_label} this journal entry?`)) { return; @@ -127,7 +141,7 @@ async function handle_admin_delete_action() { try { await journals_func.delete_ae_obj_id__journal_entry({ api_cfg: $ae_api, - journal_entry_id: tmp_entry_obj.journal_entry_id, + journal_entry_id, method: delete_method, log_lvl }); @@ -149,7 +163,7 @@ async function handle_admin_delete_action() { size="lg" class="relative mx-auto flex h-[calc(100dvh-2rem)] max-h-[calc(100dvh-2rem)] w-full flex-col rounded-xl border border-surface-200-800 bg-surface-50-900 text-surface-950-50 shadow-xl" headerClass="flex w-full flex-row items-center justify-between gap-2 rounded-t-xl border-b border-surface-200-800 bg-surface-100-900 p-4" - footerClass="flex w-full flex-row items-center justify-center gap-2 rounded-b-xl border-t border-surface-200-800 bg-surface-100-900 p-4"> + footerClass="mt-auto flex w-full shrink-0 flex-row items-center justify-center gap-2 rounded-b-xl border-t border-surface-200-800 bg-surface-100-900 p-4"> {#snippet header()}

@@ -338,6 +352,25 @@ async function handle_admin_delete_action() { }} class="input" /> + + {#if !journal?.cfg_json?.hide_btn_template} + + {/if} @@ -346,118 +379,27 @@ async function handle_admin_delete_action() {

- Status & Security + Status

- - - -
-
- Sort Order - Manual list position -
-
-
{/if} - {#if $ae_loc.trusted_access || $ae_loc.manager_access || $ae_loc.administrator_access} -
-

- - Admin -

+
+ + + Admin + +

- Trusted access and above only. Notes are for staff - use; managers and admins see Delete while trusted - access sees Remove. + Trusted access and above only. Notes are for staff use; managers and admins see Delete while trusted access sees Remove.