1
Fork 0
mirror of https://github.com/thegeneralist01/archivr synced 2026-07-21 18:55:36 +02:00

feat: implement entry deletion

- database: cascade_cached_bytes_after_subtree_delete — one-pass set-aware
  SQL that excludes the entire subtree simultaneously, avoiding sibling-blob
  cross-counting bug
- database: delete_entry — collects subtree IDs, runs set-aware cascade,
  NULLs archive_run_items.produced_entry_id (FK blocker), deletes children
  then root; ON DELETE CASCADE handles artifacts/tags/collections
- server: DELETE /api/archives/:archive_id/entries/:entry_uid route,
  wrapped in a transaction for atomicity
- frontend: deleteEntry API call, handleEntryDeleted callback in App.jsx
  (optimistic list removal + selection clear), Delete entry button in
  ContextRail with confirm guard
- tests: 4 database tests (unknown uid, subtree removal, run_item nulling,
  cached_bytes recalculation) + 3 route tests (204+gone, 404, 401)
This commit is contained in:
TheGeneralist 2026-07-04 13:46:19 +02:00
parent cad3ae9885
commit ed1f883ff1
Signed by: thegeneralist01
SSH key fingerprint: SHA256:pp9qddbCNmVNoSjevdvQvM5z0DHN7LTa8qBMbcMq/R4
15 changed files with 416 additions and 128 deletions

View file

@ -212,6 +212,12 @@ export default function App() {
)
}, [])
const handleEntryDeleted = useCallback((entryUid) => {
setEntries(prev => prev.filter(e => e.entry_uid !== entryUid))
setSelectedEntry(prev => prev?.entry_uid === entryUid ? null : prev)
setSelectedEntryUid(prev => prev === entryUid ? null : prev)
}, [])
const handleCaptureClick = useCallback(() => {
setCaptureDialogOpen(true)
}, [])
@ -317,6 +323,7 @@ export default function App() {
tagNodes={tagNodes}
onTagsRefresh={handleTagsRefresh}
onEntryTitleChange={handleEntryTitleChange}
onEntryDeleted={handleEntryDeleted}
humanizeTags={humanizeTags}
/>
</main>