feat: immutable entries + full task logging
Entries now immutable once journal is generated: - Edit/delete returns ENTRY_IMMUTABLE error if journal exists - Frontend shows lock message and hides delete button - Delete Journal button to unlock entries Task logging now stores full JSON: - request: full JSON request sent to AI provider - response: full JSON response from AI provider - prompt: formatted human-readable prompt Prompt structure: 1. System prompt 2. Previous diary entries (journals) 3. Today's entries
This commit is contained in:
@@ -3,9 +3,10 @@ import type { Entry } from '../lib/api';
|
||||
interface Props {
|
||||
entries: Entry[];
|
||||
onDelete: (id: string) => void;
|
||||
readOnly?: boolean;
|
||||
}
|
||||
|
||||
export default function EntryList({ entries, onDelete }: Props) {
|
||||
export default function EntryList({ entries, onDelete, readOnly }: Props) {
|
||||
const formatTime = (dateStr: string) => {
|
||||
return new Date(dateStr).toLocaleTimeString('en-US', { hour: 'numeric', minute: '2-digit' });
|
||||
};
|
||||
@@ -34,6 +35,11 @@ export default function EntryList({ entries, onDelete }: Props) {
|
||||
|
||||
return (
|
||||
<div className="space-y-3">
|
||||
{readOnly && (
|
||||
<div className="bg-amber-500/10 border border-amber-500/30 rounded-lg p-3 text-sm text-amber-400">
|
||||
🔒 Entries are locked because a journal has been generated. Delete the journal to edit entries.
|
||||
</div>
|
||||
)}
|
||||
{entries.map((entry) => (
|
||||
<div
|
||||
key={entry.id}
|
||||
@@ -63,12 +69,14 @@ export default function EntryList({ entries, onDelete }: Props) {
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
<button
|
||||
onClick={() => onDelete(entry.id)}
|
||||
className="text-slate-500 hover:text-red-400 text-sm transition"
|
||||
>
|
||||
×
|
||||
</button>
|
||||
{!readOnly && (
|
||||
<button
|
||||
onClick={() => onDelete(entry.id)}
|
||||
className="text-slate-500 hover:text-red-400 text-sm transition"
|
||||
>
|
||||
×
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
|
||||
@@ -9,6 +9,7 @@ export default function Day() {
|
||||
const [entries, setEntries] = useState<Entry[]>([]);
|
||||
const [loading, setLoading] = useState(true);
|
||||
const [hasJournal, setHasJournal] = useState(false);
|
||||
const [deleteError, setDeleteError] = useState<string | null>(null);
|
||||
|
||||
useEffect(() => {
|
||||
if (date) loadEntries();
|
||||
@@ -35,9 +36,21 @@ export default function Day() {
|
||||
};
|
||||
|
||||
const handleDeleteEntry = async (id: string) => {
|
||||
setDeleteError(null);
|
||||
const res = await api.deleteEntry(id);
|
||||
if (res.data) {
|
||||
setEntries((prev) => prev.filter((e) => e.id !== id));
|
||||
} else if (res.error?.code === 'ENTRY_IMMUTABLE') {
|
||||
setDeleteError(res.error.message);
|
||||
}
|
||||
};
|
||||
|
||||
const handleDeleteJournal = async () => {
|
||||
if (!date) return;
|
||||
if (!confirm('Delete journal? This will unlock entries for editing.')) return;
|
||||
const res = await api.deleteDay(date);
|
||||
if (res.data) {
|
||||
setHasJournal(false);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -56,12 +69,26 @@ export default function Day() {
|
||||
<h1 className="text-2xl font-bold">{formatDate(date)}</h1>
|
||||
</div>
|
||||
{hasJournal && (
|
||||
<a href={`/journal/${date}`} className="px-4 py-2 bg-purple-600 hover:bg-purple-700 rounded-lg font-medium transition">
|
||||
View Journal
|
||||
</a>
|
||||
<div className="flex gap-2">
|
||||
<button
|
||||
onClick={handleDeleteJournal}
|
||||
className="px-3 py-2 bg-red-600/20 hover:bg-red-600/30 text-red-400 rounded-lg text-sm transition"
|
||||
>
|
||||
Delete Journal
|
||||
</button>
|
||||
<a href={`/journal/${date}`} className="px-4 py-2 bg-purple-600 hover:bg-purple-700 rounded-lg font-medium transition">
|
||||
View Journal
|
||||
</a>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{deleteError && (
|
||||
<div className="bg-red-500/20 border border-red-500/30 rounded-lg p-3 mb-4 text-sm text-red-400">
|
||||
{deleteError}
|
||||
</div>
|
||||
)}
|
||||
|
||||
<EntryInput onSubmit={handleAddEntry} />
|
||||
|
||||
{loading ? (
|
||||
@@ -71,7 +98,7 @@ export default function Day() {
|
||||
<p className="text-slate-400">No entries for this day</p>
|
||||
</div>
|
||||
) : (
|
||||
<EntryList entries={entries} onDelete={handleDeleteEntry} />
|
||||
<EntryList entries={entries} onDelete={handleDeleteEntry} readOnly={hasJournal} />
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user