diff --git a/moneymgr_web/src/hooks/UnmatchedInboxEntriesCountProvider.tsx b/moneymgr_web/src/hooks/UnmatchedInboxEntriesCountProvider.tsx index 255094f..9dbae3d 100644 --- a/moneymgr_web/src/hooks/UnmatchedInboxEntriesCountProvider.tsx +++ b/moneymgr_web/src/hooks/UnmatchedInboxEntriesCountProvider.tsx @@ -24,8 +24,6 @@ export function UnmatchedInboxEntriesCountProvider( }; const onReload = async () => { - loadKey.current += 1; - load(); return new Promise((res) => { diff --git a/moneymgr_web/src/routes/InboxRoute.tsx b/moneymgr_web/src/routes/InboxRoute.tsx index 243eced..b34abea 100644 --- a/moneymgr_web/src/routes/InboxRoute.tsx +++ b/moneymgr_web/src/routes/InboxRoute.tsx @@ -1,4 +1,5 @@ import DeleteIcon from "@mui/icons-material/DeleteOutlined"; +import LinkOffIcon from "@mui/icons-material/LinkOff"; import MoreVertIcon from "@mui/icons-material/MoreVert"; import RefreshIcon from "@mui/icons-material/Refresh"; import { @@ -88,6 +89,11 @@ export function InboxRoute(): React.ReactElement { build={() => ( { + setEntries((entries) => { + return entries?.filter((m) => m.id !== del.id); + }); + }} onReload={reload} showMovements={includeAttached} /> @@ -102,6 +108,7 @@ export function InboxRoute(): React.ReactElement { function InboxTable(p: { entries: InboxEntry[]; + onDeleteEntry: (entry: InboxEntry) => void; onReload: (skipEntries: boolean) => void; showMovements?: boolean; }): React.ReactElement { @@ -127,13 +134,37 @@ function InboxTable(p: { await InboxApi.Delete(entry); - const id = p.entries.findIndex((m) => entry.id === m.id); - p.entries.slice(id, id); + p.onDeleteEntry(entry); - p.onReload(false); + p.onReload(true); } catch (e) { - console.error("Failed to delete movement!", e); - alert(`Failed to delete movement! ${e}`); + console.error("Failed to delete entry!", e); + alert(`Failed to delete entry! ${e}`); + } + }; + + // Detach inbox entry from movement + const handleDetachClick = async (entry: InboxEntry) => { + try { + if ( + !(await confirm( + `Do you really want to detach this inbox entry ${ + entry.label ?? "" + } (${entry.amount ?? 0} €) from its movement?`, + "Detach inbox entry from movement", + "Detach" + )) + ) + return; + + entry.movement_id = undefined; + + await InboxApi.Update(entry); + + p.onReload(true); + } catch (e) { + console.error("Failed to detach movement from entry!", e); + alert(`Failed to detach movement from entry! ${e}`); } }; @@ -206,8 +237,9 @@ function InboxTable(p: { return [ , ]; }, @@ -252,8 +284,9 @@ function InboxTable(p: { } function InboxEntryActionMenu(p: { - movement: InboxEntry; + entry: InboxEntry; onDelete: (entry: InboxEntry) => void; + onDetach: (entry: InboxEntry) => void; }): React.ReactElement { const [anchorEl, setAnchorEl] = React.useState(null); const open = Boolean(anchorEl); @@ -273,11 +306,28 @@ function InboxEntryActionMenu(p: { + {/* Unlink entry */} + {p.entry.movement_id && ( + { + handleClose(); + p.onDetach(p.entry); + }} + > + + + + + Detach from movement + + + )} + {/* Delete */} { handleClose(); - p.onDelete(p.movement); + p.onDelete(p.entry); }} >