From 2f807b4c7353a674c179568edd2c1e1c5249125c Mon Sep 17 00:00:00 2001 From: Pierre HUBERT Date: Mon, 12 May 2025 20:36:42 +0200 Subject: [PATCH] Can delete a single inbox entry --- moneymgr_web/src/api/InboxApi.ts | 10 ++ moneymgr_web/src/routes/AccountRoute.tsx | 8 +- moneymgr_web/src/routes/InboxRoute.tsx | 100 +++++++++++++++++++- moneymgr_web/src/widgets/MovementWidget.tsx | 1 + 4 files changed, 115 insertions(+), 4 deletions(-) diff --git a/moneymgr_web/src/api/InboxApi.ts b/moneymgr_web/src/api/InboxApi.ts index 9394755..0336b53 100644 --- a/moneymgr_web/src/api/InboxApi.ts +++ b/moneymgr_web/src/api/InboxApi.ts @@ -69,4 +69,14 @@ export class InboxApi { }) ).data; } + + /** + * Delete an inbox entry + */ + static async Delete(movement: InboxEntry): Promise { + await APIClient.exec({ + uri: `/inbox/${movement.id}`, + method: "DELETE", + }); + } } diff --git a/moneymgr_web/src/routes/AccountRoute.tsx b/moneymgr_web/src/routes/AccountRoute.tsx index 08c4ff8..9fc4431 100644 --- a/moneymgr_web/src/routes/AccountRoute.tsx +++ b/moneymgr_web/src/routes/AccountRoute.tsx @@ -167,7 +167,7 @@ function MovementsTable(p: { try { if ( !(await confirm( - `Do you really want to detach the file attached to the movement ${movement.label} (${movement.amount}€)? The associated file will be automatically deleted within the day if it is not referenced anywhere else!`, + `Do you really want to detach the file attached to the movement ${movement.label} (${movement.amount} €)? The associated file will be automatically deleted within the day if it is not referenced anywhere else!`, `Detach file from movement`, `Detach file` )) @@ -186,7 +186,7 @@ function MovementsTable(p: { try { if ( !(await confirm( - `Do you really want to delete the movement ${movement.label} (${movement.amount}€)?` + `Do you really want to delete the movement ${movement.label} (${movement.amount} €)?` )) ) return; @@ -377,7 +377,9 @@ function MovementsTable(p: { variant="standard" size="small" value={labelFilter} - onChange={(e) => { setLabelFilter(e.target.value); }} + onChange={(e) => { + setLabelFilter(e.target.value); + }} style={{ padding: "0px", flex: 1 }} /> diff --git a/moneymgr_web/src/routes/InboxRoute.tsx b/moneymgr_web/src/routes/InboxRoute.tsx index 76e144d..243eced 100644 --- a/moneymgr_web/src/routes/InboxRoute.tsx +++ b/moneymgr_web/src/routes/InboxRoute.tsx @@ -1,10 +1,23 @@ +import DeleteIcon from "@mui/icons-material/DeleteOutlined"; +import MoreVertIcon from "@mui/icons-material/MoreVert"; import RefreshIcon from "@mui/icons-material/Refresh"; -import { Checkbox, FormControlLabel, IconButton, Tooltip } from "@mui/material"; +import { + Checkbox, + FormControlLabel, + IconButton, + ListItemIcon, + ListItemText, + Menu, + MenuItem, + Tooltip, +} from "@mui/material"; import { DataGrid, GridColDef, GridRowSelectionModel } from "@mui/x-data-grid"; import React from "react"; import { InboxApi, InboxEntry } from "../api/InboxApi"; import { useAlert } from "../hooks/context_providers/AlertDialogProvider"; +import { useConfirm } from "../hooks/context_providers/ConfirmDialogProvider"; import { useLoadingMessage } from "../hooks/context_providers/LoadingMessageProvider"; +import { useSnackbar } from "../hooks/context_providers/SnackbarProvider"; import { useUnmatchedInboxEntriesCount } from "../hooks/UnmatchedInboxEntriesCountProvider"; import { AmountWidget } from "../widgets/AmountWidget"; import { AsyncWidget } from "../widgets/AsyncWidget"; @@ -92,9 +105,38 @@ function InboxTable(p: { onReload: (skipEntries: boolean) => void; showMovements?: boolean; }): React.ReactElement { + const alert = useAlert(); + const confirm = useConfirm(); + const snackbar = useSnackbar(); + const loadingMessage = useLoadingMessage(); + const [rowSelectionModel, setRowSelectionModel] = React.useState([]); + // Delete inbox entry + const handleDeleteClick = async (entry: InboxEntry) => { + try { + if ( + !(await confirm( + `Do you really want to delete this inbox entry ${ + entry.label ?? "" + } (${entry.amount ?? 0} €)?` + )) + ) + return; + + await InboxApi.Delete(entry); + + const id = p.entries.findIndex((m) => entry.id === m.id); + p.entries.slice(id, id); + + p.onReload(false); + } catch (e) { + console.error("Failed to delete movement!", e); + alert(`Failed to delete movement! ${e}`); + } + }; + const columns: GridColDef<(typeof p.entries)[number]>[] = [ { field: "time", @@ -153,6 +195,23 @@ function InboxTable(p: { return ; }, }, + { + field: "actions", + type: "actions", + headerName: "", + width: 55, + cellClassName: "actions", + editable: false, + getActions: (params) => { + return [ + , + ]; + }, + }, ]; return ( @@ -191,3 +250,42 @@ function InboxTable(p: { ); } + +function InboxEntryActionMenu(p: { + movement: InboxEntry; + onDelete: (entry: InboxEntry) => void; +}): React.ReactElement { + const [anchorEl, setAnchorEl] = React.useState(null); + const open = Boolean(anchorEl); + const handleClick = (event: React.MouseEvent) => { + setAnchorEl(event.currentTarget); + }; + const handleClose = () => { + setAnchorEl(null); + }; + return ( + <> + + + + + {/* Delete */} + { + handleClose(); + p.onDelete(p.movement); + }} + > + + + + Delete + + + + ); +} diff --git a/moneymgr_web/src/widgets/MovementWidget.tsx b/moneymgr_web/src/widgets/MovementWidget.tsx index be54046..e5276f7 100644 --- a/moneymgr_web/src/widgets/MovementWidget.tsx +++ b/moneymgr_web/src/widgets/MovementWidget.tsx @@ -36,6 +36,7 @@ export function MovementWidget(p: { id: number }): React.ReactElement { )}