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>
This commit is contained in:
@@ -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.
|
- 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.
|
- `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.
|
- `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
|
### Events — Badges
|
||||||
|
|
||||||
|
|||||||
@@ -151,9 +151,9 @@ Svelte 5 state is backed by Proxies.
|
|||||||
|
|
||||||
### 5. Journal Entry Config Layout Notes
|
### 5. Journal Entry Config Layout Notes
|
||||||
The Entry Config modal now follows a stricter section grammar:
|
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.
|
* `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.
|
* `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.
|
* `Admin` is gated to trusted access and above, and is the only place for notes plus delete/remove actions.
|
||||||
|
|
||||||
|
|||||||
@@ -24,8 +24,8 @@ import AE_Comp_Editor_CodeMirror from '$lib/elements/element_editor_codemirror.s
|
|||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
// Core Props
|
// Core Props
|
||||||
content: string; // The text to summarize/analyze
|
content: string | null | undefined; // The text to summarize/analyze
|
||||||
summary: string; // The result (bindable)
|
summary: string | null | undefined; // The result (bindable)
|
||||||
|
|
||||||
// Configuration (Bindable for global settings persistence)
|
// Configuration (Bindable for global settings persistence)
|
||||||
model?: string;
|
model?: string;
|
||||||
|
|||||||
@@ -6,7 +6,7 @@
|
|||||||
*/
|
*/
|
||||||
import {
|
import {
|
||||||
Siren,
|
Siren,
|
||||||
Fingerprint,
|
FingerprintPattern,
|
||||||
Globe,
|
Globe,
|
||||||
BookHeart,
|
BookHeart,
|
||||||
BriefcaseBusiness,
|
BriefcaseBusiness,
|
||||||
@@ -14,11 +14,11 @@ import {
|
|||||||
Settings
|
Settings
|
||||||
} from '@lucide/svelte';
|
} from '@lucide/svelte';
|
||||||
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_JournalEntryDraft } from '$lib/types/ae_types';
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
// The object containing the flags (bindable)
|
// The object containing the flags (bindable)
|
||||||
obj: ae_JournalEntry;
|
obj: ae_JournalEntryDraft;
|
||||||
|
|
||||||
// Visibility configuration (optional overrides)
|
// Visibility configuration (optional overrides)
|
||||||
show_labels?: boolean;
|
show_labels?: boolean;
|
||||||
@@ -109,8 +109,8 @@ function toggle_template() {
|
|||||||
onclick={toggle_private}
|
onclick={toggle_private}
|
||||||
class="btn btn-sm flex items-center gap-2 px-3 transition preset-tonal-secondary hover:preset-filled-secondary-500"
|
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">
|
title="Toggle private or encrypted visibility">
|
||||||
<Fingerprint size="1.2em" class={obj?.private ? 'text-success-500' : 'opacity-40'} />
|
<FingerprintPattern size="1.2em" class={obj?.private ? 'text-success-500' : 'opacity-40'} />
|
||||||
<span class="whitespace-nowrap text-[10px] font-bold uppercase tracking-wider">Private</span>
|
<span class="whitespace-nowrap text-[10px] font-bold uppercase tracking-wider">Private or Encrypt</span>
|
||||||
</button>
|
</button>
|
||||||
{/if}
|
{/if}
|
||||||
|
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ import { ensure_CodeMirror_modules } from './codemirror_modules';
|
|||||||
// Icons (Standardized to Lucide where possible, or FontAwesome placeholders)
|
// Icons (Standardized to Lucide where possible, or FontAwesome placeholders)
|
||||||
import { Bold, Code, Italic, List } from '@lucide/svelte';
|
import { Bold, Code, Italic, List } from '@lucide/svelte';
|
||||||
interface Props {
|
interface Props {
|
||||||
content?: string;
|
content?: string | null;
|
||||||
new_content?: string;
|
new_content?: string;
|
||||||
placeholder?: string;
|
placeholder?: string;
|
||||||
theme_mode?: 'light' | 'dark';
|
theme_mode?: 'light' | 'dark';
|
||||||
|
|||||||
@@ -230,6 +230,30 @@ export interface ae_JournalEntry extends ae_BaseObj {
|
|||||||
file_count?: number;
|
file_count?: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export type ae_JournalEntryDraft = Omit<
|
||||||
|
Partial<ae_JournalEntry>,
|
||||||
|
| '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
|
* Person - A human entity
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ import { journals_loc } from '$lib/ae_journals/ae_journals_stores';
|
|||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
content: string;
|
content: string;
|
||||||
summary: string; // Bindable
|
summary: string | null | undefined; // Bindable
|
||||||
on_save: () => void;
|
on_save: () => void;
|
||||||
log_lvl?: number;
|
log_lvl?: number;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,17 +8,16 @@ import { LockKeyhole, RefreshCcw, Save } from '@lucide/svelte';
|
|||||||
import { ae_loc } from '$lib/stores/ae_stores';
|
import { ae_loc } from '$lib/stores/ae_stores';
|
||||||
import { journals_loc } from '$lib/ae_journals/ae_journals_stores';
|
import { journals_loc } 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,
|
||||||
type JournalEntryDraft = Partial<ae_JournalEntry> & {
|
ae_Journal,
|
||||||
content?: string | false;
|
ae_JournalEntryDraft
|
||||||
[key: string]: unknown;
|
} from '$lib/types/ae_types';
|
||||||
};
|
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
entry: ae_JournalEntry;
|
entry: ae_JournalEntry;
|
||||||
journal: ae_Journal;
|
journal: ae_Journal;
|
||||||
tmp_entry_obj: JournalEntryDraft; // Bindable
|
tmp_entry_obj: ae_JournalEntryDraft; // Bindable
|
||||||
editor_view?: unknown; // Bindable
|
editor_view?: unknown; // Bindable
|
||||||
has_changed: boolean;
|
has_changed: boolean;
|
||||||
updated_idb: boolean;
|
updated_idb: boolean;
|
||||||
|
|||||||
@@ -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_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';
|
||||||
|
import type { ae_JournalEntryDraft } from '$lib/types/ae_types';
|
||||||
|
|
||||||
// Icons
|
// Icons
|
||||||
import { CircleAlert, CircleX, LoaderCircle } from '@lucide/svelte';
|
import { CircleAlert, CircleX, LoaderCircle } from '@lucide/svelte';
|
||||||
@@ -53,8 +54,8 @@ let {
|
|||||||
|
|
||||||
// *** 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: ae_JournalEntryDraft | null = $state(null);
|
||||||
let tmp_entry_obj: key_val = $state({});
|
let tmp_entry_obj: ae_JournalEntryDraft = $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>;
|
||||||
@@ -65,7 +66,7 @@ let show_config_modal = $state(false);
|
|||||||
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: ae_JournalEntryDraft = {};
|
||||||
for (const key in obj) {
|
for (const key in obj) {
|
||||||
const val = obj[key];
|
const val = obj[key];
|
||||||
if (val instanceof Date) {
|
if (val instanceof Date) {
|
||||||
@@ -240,9 +241,12 @@ async function run_decryption_workflow() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// SUCCESS
|
// 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 = 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.content_encrypted = null;
|
||||||
tmp_entry_obj.history_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 (!fields_kv) {
|
||||||
if (tmp_entry_obj.private) {
|
if (tmp_entry_obj.private) {
|
||||||
if (tmp_entry_obj.content) {
|
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(
|
data_kv.content_encrypted = await ae_util.encrypt_wrapper(
|
||||||
tmp_entry_obj.content,
|
content_to_encrypt,
|
||||||
decrypt_key
|
decrypt_key
|
||||||
);
|
);
|
||||||
data_kv.content = null;
|
data_kv.content = null;
|
||||||
@@ -316,9 +324,15 @@ async function update_journal_entry(fields_kv?: key_val) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
try {
|
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({
|
await journals_func.update_ae_obj__journal_entry({
|
||||||
api_cfg: $ae_api,
|
api_cfg: $ae_api,
|
||||||
journal_entry_id: $lq__journal_entry_obj?.journal_entry_id,
|
journal_entry_id,
|
||||||
data_kv: data_kv,
|
data_kv: data_kv,
|
||||||
log_lvl: 1
|
log_lvl: 1
|
||||||
});
|
});
|
||||||
@@ -468,7 +482,11 @@ let modal_mode: 'append' | 'prepend' | 'auto' = $state('auto');
|
|||||||
? 'ring-primary-500/40 ring-2 ring-inset'
|
? 'ring-primary-500/40 ring-2 ring-inset'
|
||||||
: ''}">
|
: ''}">
|
||||||
<AE_Comp_Journal_Entry_AiTools
|
<AE_Comp_Journal_Entry_AiTools
|
||||||
content={tmp_entry_obj.content}
|
content={
|
||||||
|
typeof tmp_entry_obj.content === 'string'
|
||||||
|
? 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} />
|
||||||
@@ -509,7 +527,6 @@ let modal_mode: 'append' | 'prepend' | 'auto' = $state('auto');
|
|||||||
|
|
||||||
<AE_Comp_Modal_Journal_Entry_Config
|
<AE_Comp_Modal_Journal_Entry_Config
|
||||||
bind:show={show_config_modal}
|
bind:show={show_config_modal}
|
||||||
entry={$lq__journal_entry_obj}
|
|
||||||
journal={$lq__journal_obj}
|
journal={$lq__journal_obj}
|
||||||
bind:tmp_entry_obj
|
bind:tmp_entry_obj
|
||||||
on_save={() => update_journal_entry()}
|
on_save={() => update_journal_entry()}
|
||||||
|
|||||||
@@ -52,8 +52,9 @@ import {
|
|||||||
} 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';
|
||||||
|
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
|
// 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
|
||||||
|
|||||||
@@ -5,10 +5,11 @@ 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';
|
||||||
|
import type { ae_JournalEntryDraft } from '$lib/types/ae_types';
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
open: boolean;
|
open: boolean;
|
||||||
journal_entry: key_val;
|
journal_entry: ae_JournalEntryDraft;
|
||||||
journal_config: key_val; // The cfg_json from the journal object
|
journal_config: key_val; // The cfg_json from the journal object
|
||||||
mode?: 'append' | 'prepend' | 'auto';
|
mode?: 'append' | 'prepend' | 'auto';
|
||||||
on_close: () => void;
|
on_close: () => void;
|
||||||
@@ -26,7 +27,7 @@ let {
|
|||||||
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: ae_JournalEntryDraft = $state({});
|
||||||
|
|
||||||
// Header Options
|
// Header Options
|
||||||
let add_timestamp_header: boolean = $state(true);
|
let add_timestamp_header: boolean = $state(true);
|
||||||
@@ -50,7 +51,8 @@ $effect(() => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
async function handle_save() {
|
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 add_content = '';
|
||||||
let new_content = current_entry_content;
|
let new_content = current_entry_content;
|
||||||
|
|
||||||
@@ -109,11 +111,16 @@ async function handle_save() {
|
|||||||
new_content = new_content.trim() + '\n';
|
new_content = new_content.trim() + '\n';
|
||||||
|
|
||||||
let data_kv = { content: new_content };
|
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 {
|
try {
|
||||||
let update_result = await journals_func.update_ae_obj__journal_entry({
|
let update_result = 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,
|
||||||
data_kv: data_kv,
|
data_kv: data_kv,
|
||||||
log_lvl: log_lvl
|
log_lvl: log_lvl
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -7,13 +7,11 @@ import {
|
|||||||
ArrowDownToLine,
|
ArrowDownToLine,
|
||||||
ArrowUpToLine,
|
ArrowUpToLine,
|
||||||
Check,
|
Check,
|
||||||
CircleAlert,
|
|
||||||
CodeXml,
|
CodeXml,
|
||||||
Copy,
|
Copy,
|
||||||
FileDown,
|
FileDown,
|
||||||
Fingerprint,
|
FingerprintPattern,
|
||||||
Minus,
|
Minus,
|
||||||
Plus,
|
|
||||||
RefreshCcw,
|
RefreshCcw,
|
||||||
Settings,
|
Settings,
|
||||||
Shapes,
|
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 { 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';
|
||||||
import type { ae_Journal, ae_JournalEntry } from '$lib/types/ae_types';
|
import type {
|
||||||
|
ae_Journal,
|
||||||
|
ae_JournalEntryDraft
|
||||||
|
} from '$lib/types/ae_types';
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
log_lvl?: number;
|
log_lvl?: number;
|
||||||
show?: boolean;
|
show?: boolean;
|
||||||
journal: ae_Journal;
|
journal: ae_Journal;
|
||||||
tmp_entry_obj: ae_JournalEntry; // Bindable
|
tmp_entry_obj: ae_JournalEntryDraft; // Bindable
|
||||||
on_save: () => void;
|
on_save: () => void;
|
||||||
on_force_reset?: () => void;
|
on_force_reset?: () => void;
|
||||||
on_show_export?: () => void;
|
on_show_export?: () => void;
|
||||||
@@ -77,6 +78,12 @@ function tab_button_class(is_active: boolean): string {
|
|||||||
async function handle_update_entry() {
|
async function handle_update_entry() {
|
||||||
try {
|
try {
|
||||||
// WHITELISTED BASE TABLE COLUMNS ONLY
|
// 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 = {
|
const data_kv = {
|
||||||
name: tmp_entry_obj.name,
|
name: tmp_entry_obj.name,
|
||||||
short_name: tmp_entry_obj.short_name,
|
short_name: tmp_entry_obj.short_name,
|
||||||
@@ -86,6 +93,7 @@ async function handle_update_entry() {
|
|||||||
type_code: tmp_entry_obj.type_code,
|
type_code: tmp_entry_obj.type_code,
|
||||||
topic_code: tmp_entry_obj.topic_code,
|
topic_code: tmp_entry_obj.topic_code,
|
||||||
tags: tmp_entry_obj.tags,
|
tags: tmp_entry_obj.tags,
|
||||||
|
passcode_hash: tmp_entry_obj.passcode_hash,
|
||||||
private: tmp_entry_obj.private,
|
private: tmp_entry_obj.private,
|
||||||
public: tmp_entry_obj.public,
|
public: tmp_entry_obj.public,
|
||||||
personal: tmp_entry_obj.personal,
|
personal: tmp_entry_obj.personal,
|
||||||
@@ -105,7 +113,7 @@ async function handle_update_entry() {
|
|||||||
|
|
||||||
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,
|
||||||
data_kv: data_kv,
|
data_kv: data_kv,
|
||||||
log_lvl: log_lvl
|
log_lvl: log_lvl
|
||||||
});
|
});
|
||||||
@@ -119,6 +127,12 @@ async function handle_admin_delete_action() {
|
|||||||
const delete_method = can_delete ? 'delete' : 'disable';
|
const delete_method = can_delete ? 'delete' : 'disable';
|
||||||
const action_label = can_delete ? 'delete' : 'remove';
|
const action_label = can_delete ? 'delete' : 'remove';
|
||||||
const confirm_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?`)) {
|
if (!confirm(`Are you sure you want to ${confirm_label} this journal entry?`)) {
|
||||||
return;
|
return;
|
||||||
@@ -127,7 +141,7 @@ async function handle_admin_delete_action() {
|
|||||||
try {
|
try {
|
||||||
await journals_func.delete_ae_obj_id__journal_entry({
|
await journals_func.delete_ae_obj_id__journal_entry({
|
||||||
api_cfg: $ae_api,
|
api_cfg: $ae_api,
|
||||||
journal_entry_id: tmp_entry_obj.journal_entry_id,
|
journal_entry_id,
|
||||||
method: delete_method,
|
method: delete_method,
|
||||||
log_lvl
|
log_lvl
|
||||||
});
|
});
|
||||||
@@ -149,7 +163,7 @@ async function handle_admin_delete_action() {
|
|||||||
size="lg"
|
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"
|
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"
|
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()}
|
{#snippet header()}
|
||||||
<h3 class="flex flex-1 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" />
|
||||||
@@ -338,6 +352,25 @@ async function handle_admin_delete_action() {
|
|||||||
}}
|
}}
|
||||||
class="input" />
|
class="input" />
|
||||||
</label>
|
</label>
|
||||||
|
|
||||||
|
{#if !journal?.cfg_json?.hide_btn_template}
|
||||||
|
<label class={field_card_class}>
|
||||||
|
<input
|
||||||
|
type="checkbox"
|
||||||
|
bind:checked={tmp_entry_obj.template}
|
||||||
|
onchange={() => {
|
||||||
|
handle_update_entry();
|
||||||
|
on_save();
|
||||||
|
}}
|
||||||
|
class="checkbox checkbox-primary" />
|
||||||
|
<div class="flex flex-col">
|
||||||
|
<span class="font-bold">Template</span>
|
||||||
|
<span class="text-xs opacity-60">
|
||||||
|
Mark this entry as a reusable template
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</label>
|
||||||
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
</div>
|
</div>
|
||||||
@@ -346,118 +379,27 @@ async function handle_admin_delete_action() {
|
|||||||
<section class={panel_class}>
|
<section class={panel_class}>
|
||||||
<h2 class={panel_title_class}>
|
<h2 class={panel_title_class}>
|
||||||
<ShieldCheck size="1.2em" class="text-primary-500" />
|
<ShieldCheck size="1.2em" class="text-primary-500" />
|
||||||
Status & Security
|
Status
|
||||||
</h2>
|
</h2>
|
||||||
<div class="grid grid-cols-1 gap-4 md:grid-cols-2">
|
<div class="grid grid-cols-1 gap-4 md:grid-cols-2">
|
||||||
<label class={field_card_class}>
|
{#if tmp_entry_obj.alert}
|
||||||
<input
|
<label class="label md:col-span-2 flex flex-col items-start gap-1">
|
||||||
type="checkbox"
|
<span class="text-sm font-bold opacity-70"
|
||||||
bind:checked={tmp_entry_obj.enable}
|
>Alert Message</span>
|
||||||
onchange={() => {
|
<textarea
|
||||||
handle_update_entry();
|
bind:value={tmp_entry_obj.alert_msg}
|
||||||
on_save();
|
class="textarea min-h-24 w-full"
|
||||||
}}
|
placeholder="Optional alert or notice shown with the entry"
|
||||||
class="checkbox checkbox-primary" />
|
onchange={() => {
|
||||||
<div class="flex flex-col">
|
|
||||||
<span class="font-bold">Enabled</span>
|
|
||||||
<span class="text-xs opacity-60"
|
|
||||||
>Allow access to this entry</span>
|
|
||||||
</div>
|
|
||||||
</label>
|
|
||||||
<label class={field_card_class}>
|
|
||||||
<input
|
|
||||||
type="checkbox"
|
|
||||||
bind:checked={tmp_entry_obj.hide}
|
|
||||||
onchange={() => {
|
|
||||||
handle_update_entry();
|
|
||||||
on_save();
|
|
||||||
}}
|
|
||||||
class="checkbox checkbox-primary" />
|
|
||||||
<div class="flex flex-col">
|
|
||||||
<span class="font-bold">Hidden</span>
|
|
||||||
<span class="text-xs opacity-60"
|
|
||||||
>Hide from standard lists</span>
|
|
||||||
</div>
|
|
||||||
</label>
|
|
||||||
<label class={field_card_class}>
|
|
||||||
<input
|
|
||||||
type="checkbox"
|
|
||||||
bind:checked={tmp_entry_obj.priority}
|
|
||||||
onchange={() => {
|
|
||||||
handle_update_entry();
|
|
||||||
on_save();
|
|
||||||
}}
|
|
||||||
class="checkbox checkbox-primary" />
|
|
||||||
<div class="flex flex-col">
|
|
||||||
<span class="font-bold">Priority Entry</span>
|
|
||||||
<span class="text-xs opacity-60"
|
|
||||||
>Star or pin to top</span>
|
|
||||||
</div>
|
|
||||||
</label>
|
|
||||||
<div class="bg-surface-500/5 border-surface-500/10 flex items-center justify-between rounded-lg border p-3">
|
|
||||||
<div class="flex flex-col">
|
|
||||||
<span class="text-sm font-bold"
|
|
||||||
>Sort Order</span>
|
|
||||||
<span class="text-xs opacity-60"
|
|
||||||
>Manual list position</span>
|
|
||||||
</div>
|
|
||||||
<div class="flex items-center gap-2">
|
|
||||||
<button
|
|
||||||
type="button"
|
|
||||||
class="btn-icon btn-icon-sm preset-tonal-surface"
|
|
||||||
onclick={() => {
|
|
||||||
tmp_entry_obj.sort =
|
|
||||||
(tmp_entry_obj.sort ?? 0) - 1;
|
|
||||||
handle_update_entry();
|
handle_update_entry();
|
||||||
on_save();
|
on_save();
|
||||||
}}><Minus size="1em" /></button>
|
}}></textarea>
|
||||||
<span
|
<span class="text-xs opacity-60">
|
||||||
class="w-8 text-center font-mono text-lg font-bold"
|
Shown when Alert is enabled.
|
||||||
>{tmp_entry_obj.sort ?? 0}</span>
|
</span>
|
||||||
<button
|
</label>
|
||||||
type="button"
|
{/if}
|
||||||
class="btn-icon btn-icon-sm preset-tonal-surface"
|
|
||||||
onclick={() => {
|
|
||||||
tmp_entry_obj.sort =
|
|
||||||
(tmp_entry_obj.sort ?? 0) + 1;
|
|
||||||
handle_update_entry();
|
|
||||||
on_save();
|
|
||||||
}}><Plus size="1em" /></button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
|
|
||||||
<section class={panel_class}>
|
|
||||||
<h2 class={panel_title_class}>
|
|
||||||
<Fingerprint size="1.2em" class="text-primary-500" />
|
|
||||||
Privacy Flags
|
|
||||||
</h2>
|
|
||||||
<p class="text-xs opacity-60">
|
|
||||||
Visibility and audience controls for the entry itself.
|
|
||||||
</p>
|
|
||||||
<AE_Object_Flags
|
|
||||||
bind:obj={tmp_entry_obj}
|
|
||||||
show_labels={false}
|
|
||||||
on_toggle={() => {
|
|
||||||
handle_update_entry();
|
|
||||||
on_save();
|
|
||||||
}}
|
|
||||||
hide_alert={journal?.cfg_json?.hide_btn_alert}
|
|
||||||
hide_private={journal?.cfg_json?.hide_btn_private}
|
|
||||||
hide_public={journal?.cfg_json?.hide_btn_public}
|
|
||||||
hide_personal={journal?.cfg_json?.hide_btn_personal}
|
|
||||||
hide_professional={journal?.cfg_json
|
|
||||||
?.hide_btn_professional}
|
|
||||||
hide_template={journal?.cfg_json?.hide_btn_template} />
|
|
||||||
</section>
|
|
||||||
|
|
||||||
<section class={panel_class}>
|
|
||||||
<h2 class={panel_title_class}>
|
|
||||||
<CircleAlert size="1.2em" class="text-primary-500" />
|
|
||||||
Alerts & Messaging
|
|
||||||
</h2>
|
|
||||||
<div class="grid grid-cols-1 gap-4 md:grid-cols-2">
|
|
||||||
<label class={field_card_class}>
|
<label class={field_card_class}>
|
||||||
<input
|
<input
|
||||||
type="checkbox"
|
type="checkbox"
|
||||||
@@ -469,22 +411,103 @@ async function handle_admin_delete_action() {
|
|||||||
class="checkbox checkbox-primary" />
|
class="checkbox checkbox-primary" />
|
||||||
<div class="flex flex-col">
|
<div class="flex flex-col">
|
||||||
<span class="font-bold">Alert</span>
|
<span class="font-bold">Alert</span>
|
||||||
<span class="text-xs opacity-60"
|
<span class="text-xs opacity-60">
|
||||||
>Flag this entry for emphasis</span>
|
Action required or reminder.
|
||||||
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</label>
|
</label>
|
||||||
<label class="label md:col-span-2">
|
|
||||||
<span class="text-sm font-bold opacity-70"
|
<label class={field_card_class}>
|
||||||
>Alert Message</span>
|
<input
|
||||||
<textarea
|
type="checkbox"
|
||||||
bind:value={tmp_entry_obj.alert_msg}
|
bind:checked={tmp_entry_obj.priority}
|
||||||
class="textarea min-h-24"
|
|
||||||
placeholder="Optional note shown with the alert"
|
|
||||||
onchange={() => {
|
onchange={() => {
|
||||||
handle_update_entry();
|
handle_update_entry();
|
||||||
on_save();
|
on_save();
|
||||||
}}></textarea>
|
}}
|
||||||
|
class="checkbox checkbox-primary" />
|
||||||
|
<div class="flex flex-col">
|
||||||
|
<span class="font-bold">Priority Entry</span>
|
||||||
|
<span class="text-xs opacity-60">
|
||||||
|
Pin this entry ahead of the standard order.
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
</label>
|
</label>
|
||||||
|
|
||||||
|
<label class="bg-surface-500/5 border-surface-500/10 flex items-center justify-between rounded-lg border p-3">
|
||||||
|
<div class="flex flex-col">
|
||||||
|
<span class="text-sm font-bold">Sort Order</span>
|
||||||
|
<span class="text-xs opacity-60">
|
||||||
|
Lower numbers appear earlier in lists.
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<input
|
||||||
|
type="number"
|
||||||
|
bind:value={tmp_entry_obj.sort}
|
||||||
|
class="input input-sm w-24 text-right" />
|
||||||
|
</label>
|
||||||
|
|
||||||
|
<label class={field_card_class}>
|
||||||
|
<input
|
||||||
|
type="checkbox"
|
||||||
|
bind:checked={tmp_entry_obj.hide}
|
||||||
|
onchange={() => {
|
||||||
|
handle_update_entry();
|
||||||
|
on_save();
|
||||||
|
}}
|
||||||
|
class="checkbox checkbox-primary" />
|
||||||
|
<div class="flex flex-col">
|
||||||
|
<span class="font-bold">Hidden</span>
|
||||||
|
<span class="text-xs opacity-60">
|
||||||
|
Hide from standard lists.
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section class={panel_class}>
|
||||||
|
<h2 class={panel_title_class}>
|
||||||
|
<FingerprintPattern size="1.2em" class="text-primary-500" />
|
||||||
|
Security and Privacy
|
||||||
|
</h2>
|
||||||
|
<div class="space-y-4 rounded-xl border border-surface-500/10 bg-surface-500/5 p-4">
|
||||||
|
<label class="label flex flex-col items-start gap-1">
|
||||||
|
<span class="text-sm font-bold opacity-70"
|
||||||
|
>Passcode</span>
|
||||||
|
<input
|
||||||
|
type="password"
|
||||||
|
bind:value={tmp_entry_obj.passcode_hash}
|
||||||
|
class="input w-full"
|
||||||
|
placeholder="journal_entry.passcode_hash"
|
||||||
|
onchange={() => {
|
||||||
|
handle_update_entry();
|
||||||
|
on_save();
|
||||||
|
}} />
|
||||||
|
<span class="text-xs opacity-60">
|
||||||
|
Stored on the entry as passcode_hash.
|
||||||
|
</span>
|
||||||
|
</label>
|
||||||
|
|
||||||
|
<div class="space-y-2">
|
||||||
|
<p class="text-xs font-semibold uppercase tracking-wider opacity-60">
|
||||||
|
Visibility and audience
|
||||||
|
</p>
|
||||||
|
<AE_Object_Flags
|
||||||
|
bind:obj={tmp_entry_obj}
|
||||||
|
show_labels={false}
|
||||||
|
on_toggle={() => {
|
||||||
|
handle_update_entry();
|
||||||
|
on_save();
|
||||||
|
}}
|
||||||
|
hide_alert={true}
|
||||||
|
hide_private={journal?.cfg_json?.hide_btn_private}
|
||||||
|
hide_public={journal?.cfg_json?.hide_btn_public}
|
||||||
|
hide_personal={journal?.cfg_json?.hide_btn_personal}
|
||||||
|
hide_professional={journal?.cfg_json
|
||||||
|
?.hide_btn_professional}
|
||||||
|
hide_template={true} />
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
@@ -513,26 +536,23 @@ async function handle_admin_delete_action() {
|
|||||||
</section>
|
</section>
|
||||||
{/if}
|
{/if}
|
||||||
|
|
||||||
{#if $ae_loc.trusted_access || $ae_loc.manager_access || $ae_loc.administrator_access}
|
<details class="rounded-xl border border-surface-500/20 bg-surface-500/5 shadow-sm">
|
||||||
<section class={panel_class}>
|
<summary class="flex cursor-pointer items-center gap-2 border-b border-surface-500/20 px-4 py-3 text-lg font-bold">
|
||||||
<h2 class={panel_title_class}>
|
<Settings size="1.2em" class="text-primary-500" />
|
||||||
<Settings size="1.2em" class="text-primary-500" />
|
Admin
|
||||||
Admin
|
</summary>
|
||||||
</h2>
|
<div class="space-y-4 p-4">
|
||||||
<p class="text-xs opacity-60">
|
<p class="text-xs opacity-60">
|
||||||
Trusted access and above only. Notes are for staff
|
Trusted access and above only. Notes are for staff use; managers and admins see Delete while trusted access sees Remove.
|
||||||
use; managers and admins see Delete while trusted
|
|
||||||
access sees Remove.
|
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<div class="grid grid-cols-1 gap-4">
|
<div class="grid grid-cols-1 gap-4">
|
||||||
<label class="label">
|
<label class="label">
|
||||||
<span class="text-sm font-bold opacity-70"
|
<span class="text-sm font-bold opacity-70">Notes</span>
|
||||||
>Notes</span>
|
|
||||||
<textarea
|
<textarea
|
||||||
bind:value={tmp_entry_obj.notes}
|
bind:value={tmp_entry_obj.notes}
|
||||||
class="textarea min-h-24"
|
class="textarea min-h-24"
|
||||||
placeholder="Rarely used staff/admin note"
|
placeholder="Rarely used special case admin/staff notes"
|
||||||
onchange={() => {
|
onchange={() => {
|
||||||
handle_update_entry();
|
handle_update_entry();
|
||||||
on_save();
|
on_save();
|
||||||
@@ -551,8 +571,9 @@ async function handle_admin_delete_action() {
|
|||||||
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">
|
||||||
>Default access state for this entry</span>
|
Allow default access for AE object type; essentially marked for deletion.
|
||||||
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</label>
|
</label>
|
||||||
|
|
||||||
@@ -573,8 +594,8 @@ async function handle_admin_delete_action() {
|
|||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</div>
|
||||||
{/if}
|
</details>
|
||||||
</div>
|
</div>
|
||||||
{:else if tab === 'json'}
|
{:else if tab === 'json'}
|
||||||
<div class="h-full min-h-100">
|
<div class="h-full min-h-100">
|
||||||
|
|||||||
Reference in New Issue
Block a user