diff --git a/moneymgr_web/src/hooks/context_providers/ConfirmDialogProvider.tsx b/moneymgr_web/src/hooks/context_providers/ConfirmDialogProvider.tsx index 55e0682..a0df110 100644 --- a/moneymgr_web/src/hooks/context_providers/ConfirmDialogProvider.tsx +++ b/moneymgr_web/src/hooks/context_providers/ConfirmDialogProvider.tsx @@ -9,7 +9,7 @@ import { import React, { PropsWithChildren } from "react"; type ConfirmContext = ( - message: string, + message: string | React.ReactElement, title?: string, confirmButton?: string ) => Promise; @@ -22,7 +22,7 @@ export function ConfirmDialogProvider( const [open, setOpen] = React.useState(false); const [title, setTitle] = React.useState(undefined); - const [message, setMessage] = React.useState(""); + const [message, setMessage] = React.useState(""); const [confirmButton, setConfirmButton] = React.useState( undefined ); @@ -53,13 +53,13 @@ export function ConfirmDialogProvider( return ( <> - - {p.children} - + {p.children} { handleClose(false); }} + onClose={() => { + handleClose(false); + }} aria-labelledby="alert-dialog-title" aria-describedby="alert-dialog-description" onKeyUp={keyUp} @@ -71,10 +71,20 @@ export function ConfirmDialogProvider( - - diff --git a/moneymgr_web/src/routes/AccountRoute.tsx b/moneymgr_web/src/routes/AccountRoute.tsx index af8e415..39756a6 100644 --- a/moneymgr_web/src/routes/AccountRoute.tsx +++ b/moneymgr_web/src/routes/AccountRoute.tsx @@ -1,7 +1,12 @@ import DeleteIcon from "@mui/icons-material/DeleteOutlined"; import DriveFileMoveOutlineIcon from "@mui/icons-material/DriveFileMoveOutline"; -import { Tooltip, Typography } from "@mui/material"; -import { DataGrid, GridActionsCellItem, GridColDef } from "@mui/x-data-grid"; +import { IconButton, Tooltip, Typography } from "@mui/material"; +import { + DataGrid, + GridActionsCellItem, + GridColDef, + GridRowSelectionModel, +} from "@mui/x-data-grid"; import React from "react"; import { useParams } from "react-router-dom"; import { Movement, MovementApi } from "../api/MovementsApi"; @@ -93,9 +98,13 @@ function MovementsTable(p: { const alert = useAlert(); const confirm = useConfirm(); const snackbar = useSnackbar(); + const loadingMessage = useLoadingMessage(); const chooseAccount = useSelectAccount(); + const [rowSelectionModel, setRowSelectionModel] = + React.useState([]); + // Change account of movement const handleMoveClick = async (movement: Movement) => { const target = await chooseAccount( @@ -143,6 +152,46 @@ function MovementsTable(p: { } }; + // Delete multiple movements + const deleteMultiple = async () => { + try { + const movements = p.movements.filter((m) => + rowSelectionModel.includes(m.id) + ); + + if ( + !(await confirm( + <> + Do you really want to delete the following movements: +
    + {movements.map((m) => ( +
  • + {m.label} ({m.amount} €) +
  • + ))} +
+ + )) + ) + return; + + for (const [num, m] of movements.entries()) { + loadingMessage.show(`Deleting movement ${num}/${movements.length}`); + + await MovementApi.Delete(m); + } + + snackbar("The movements have been successfully deleted!"); + + p.needReload(false); + } catch (e) { + console.error("Failed to delete multiple movements!", e); + alert(`Failed to delete multiple movements! ${e}`); + } finally { + loadingMessage.hide(); + } + }; + const columns: GridColDef<(typeof p.movements)[number]>[] = [ { field: "checked", @@ -221,33 +270,53 @@ function MovementsTable(p: { }, }, ]; + return ( - - columns={columns} - rows={p.movements} - autoPageSize - checkboxSelection - initialState={{ - sorting: { - sortModel: [{ field: "time", sort: "desc" }], - }, - columns: { - columnVisibilityModel: { - checked: false, + <> +
+ + + + + +
+ + columns={columns} + rows={p.movements} + autoPageSize + checkboxSelection + initialState={{ + sorting: { + sortModel: [{ field: "time", sort: "desc" }], }, - }, - }} - processRowUpdate={async (n) => { - try { - return await MovementApi.Update(n); - } catch (e) { - console.error("Failed to update movement information!", e); - alert(`Failed to update row! ${e}`); - throw e; - } finally { - p.needReload(true); - } - }} - /> + columns: { + columnVisibilityModel: { + checked: false, + }, + }, + }} + onRowSelectionModelChange={(newRowSelectionModel) => { + setRowSelectionModel(newRowSelectionModel); + }} + rowSelectionModel={rowSelectionModel} + processRowUpdate={async (n) => { + try { + return await MovementApi.Update(n); + } catch (e) { + console.error("Failed to update movement information!", e); + alert(`Failed to update row! ${e}`); + throw e; + } finally { + p.needReload(true); + } + }} + /> + ); }