Files
OSIT-AE-App-Svelte/src/routes/journals/ae_comp__modal_journal_entry_append.svelte
Scott Idem 8fd11d7224 feat(journals): implement Quick Add and unified Append/Prepend shared component
- Created AeCompJournalEntryQuickAdd for high-velocity note creation
- Extracted robust append/prepend logic from List View into AeCompModalJournalEntryAppend
- Unified List and Detail views to use the shared modal for content manipulation
- Added explicit Append/Prepend actions to Journal Entry settings menu
- Updated TODO.md and Journals module documentation
2026-01-13 22:59:08 -05:00

197 lines
7.4 KiB
Svelte

<script lang="ts">
import { Modal } from 'flowbite-svelte';
import { Check, X } from '@lucide/svelte';
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';
interface Props {
open: boolean;
journal_entry: key_val;
journal_config: key_val; // The cfg_json from the journal object
mode?: 'append' | 'prepend' | 'auto';
onClose: () => void;
onUpdate: () => void;
log_lvl?: number;
}
let {
open = $bindable(false),
journal_entry,
journal_config,
mode = 'auto',
onClose,
onUpdate,
log_lvl = 0
}: Props = $props();
// Local State
let tmp_entry_obj: key_val = $state({});
// Header Options
let add_timestamp_header: boolean = $state(true);
let add_timestamp_header_w_day_of_week: boolean = $state(true);
let add_text_header: string = $state('');
let add_text: string = $state('');
// Change detection
let has_changes: boolean = $derived(add_text_header.length > 0 || add_text.length > 0);
// Initialize tmp object when entry changes or modal opens
$effect(() => {
if (open && journal_entry) {
tmp_entry_obj = JSON.parse(JSON.stringify(journal_entry));
// Reset fields
add_text_header = '';
add_text = '';
}
});
async function handle_save() {
let current_entry_content = tmp_entry_obj?.content || '';
let add_content = '';
let new_content = current_entry_content;
// Construct the header/content to add (Following original logic)
let timestamp_str = ae_util.iso_datetime_formatter(
new Date(),
'datetime_iso_12_no_seconds'
);
let day_of_week_str = add_timestamp_header_w_day_of_week
? ' (' + ae_util.iso_datetime_formatter(new Date(), 'week_long') + ')'
: '';
if (add_timestamp_header && add_text_header) {
add_content =
'## ' +
timestamp_str +
day_of_week_str +
' - ' +
add_text_header.trim() +
'\n' +
add_text.trim() +
'\n\n';
} else if (add_timestamp_header) {
add_content = '## ' + timestamp_str + day_of_week_str + '\n' + add_text.trim() + '\n\n';
} else if (add_text_header) {
add_content =
'## ' + add_text_header.trim() + day_of_week_str + '\n' + add_text.trim() + '\n\n';
} else {
add_content = add_text.trim() + '\n\n';
}
// Determine Append or Prepend
let effective_mode = mode;
if (effective_mode === 'auto') {
effective_mode = journal_config?.entry_add_text || 'append';
}
if (effective_mode == 'prepend') {
new_content = add_content + new_content;
} else {
// Append
new_content = new_content.trim() + '\n\n' + add_content;
}
new_content = new_content.trim() + '\n';
let data_kv = { content: new_content };
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,
data_kv: data_kv,
log_lvl: log_lvl
});
if (update_result) {
// Success
onUpdate();
open = false;
} else {
alert('Failed to update journal entry.');
}
} catch (error) {
console.error('Error updating journal entry:', error);
alert('Failed to update journal entry.');
}
}
</script>
<Modal
title="{(mode === 'auto' ? journal_config?.entry_add_text : mode) == 'append' ? 'Append to' : 'Prepend to'} Journal Entry: {journal_entry?.name ?? journal_entry?.created_on}"
bind:open={open}
autoclose={false}
placement="top-center"
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"
>
<div class="modal">
<div class="modal-box">
<div class="flex flex-col gap-1">
<!-- Checkbox Options -->
<div>
<input
type="checkbox"
id="append_timestamp_header"
bind:checked={add_timestamp_header}
class="p-2 bg-slate-100 text-gray-900 dark:bg-slate-900 dark:text-gray-100 shadow-lg rounded-lg border border-gray-200 dark:border-gray-700 hover:border-gray-500 dark:hover:border-gray-500 inline-block"
/>
<label for="append_timestamp_header" class="p-2 inline-block">
Use timestamp as Markdown header
</label>
<input
type="checkbox"
id="append_timestamp_header_w_day_of_week"
bind:checked={add_timestamp_header_w_day_of_week}
class="p-2 bg-slate-100 text-gray-900 dark:bg-slate-900 dark:text-gray-100 shadow-lg rounded-lg border border-gray-200 dark:border-gray-700 hover:border-gray-500 dark:hover:border-gray-500 inline-block"
/>
<label for="append_timestamp_header_w_day_of_week" class="p-2 inline-block">
Include day of week
</label>
</div>
<!-- Text Header Input -->
<input
type="text"
placeholder="Markdown header for content (Optional)"
bind:value={add_text_header}
class="grow min-h-12 h-full w-full p-2 bg-slate-100 text-gray-900 dark:bg-slate-900 dark:text-gray-100 shadow-lg rounded-lg border border-gray-200 dark:border-gray-700 hover:border-gray-500 dark:hover:border-gray-500"
/>
<!-- Main Content Area -->
<textarea
bind:value={add_text}
class="grow min-h-48 h-full w-full p-2 bg-slate-100 text-gray-900 dark:bg-slate-900 dark:text-gray-100 shadow-lg rounded-lg border border-gray-200 dark:border-gray-700 hover:border-gray-500 dark:hover:border-gray-500"
placeholder="Content to {mode === 'auto'
? journal_config?.entry_add_text
: mode}...">
</textarea>
</div>
<div class="modal-action flex justify-end gap-2 mt-4">
<button
type="button"
disabled={!has_changes}
onclick={handle_save}
class="btn btn-sm md:btn-md lg:btn-lg min-w-32 hover:variant-outline-success hover:preset-filled-success-500"
class:preset-filled-primary-500={has_changes}
class:preset-filled-surface-500={!has_changes}
>
<Check class="mr-1" />
Update
</button>
<button
type="button"
onclick={onClose}
class="btn preset-tonal-surface border border-surface-500 hover:preset-filled-surface-500 transition"
>
<X class="mr-1" />
Cancel
</button>
</div>
</div>
</div>
</Modal>