Can delete multiple movements

This commit is contained in:
Pierre HUBERT 2025-04-23 20:55:52 +02:00
parent 53494210af
commit a24eb02dc4
2 changed files with 115 additions and 36 deletions

View File

@ -9,7 +9,7 @@ import {
import React, { PropsWithChildren } from "react"; import React, { PropsWithChildren } from "react";
type ConfirmContext = ( type ConfirmContext = (
message: string, message: string | React.ReactElement,
title?: string, title?: string,
confirmButton?: string confirmButton?: string
) => Promise<boolean>; ) => Promise<boolean>;
@ -22,7 +22,7 @@ export function ConfirmDialogProvider(
const [open, setOpen] = React.useState(false); const [open, setOpen] = React.useState(false);
const [title, setTitle] = React.useState<string | undefined>(undefined); const [title, setTitle] = React.useState<string | undefined>(undefined);
const [message, setMessage] = React.useState(""); const [message, setMessage] = React.useState<string | React.ReactElement>("");
const [confirmButton, setConfirmButton] = React.useState<string | undefined>( const [confirmButton, setConfirmButton] = React.useState<string | undefined>(
undefined undefined
); );
@ -53,13 +53,13 @@ export function ConfirmDialogProvider(
return ( return (
<> <>
<ConfirmContextK value={hook}> <ConfirmContextK value={hook}>{p.children}</ConfirmContextK>
{p.children}
</ConfirmContextK>
<Dialog <Dialog
open={open} open={open}
onClose={() => { handleClose(false); }} onClose={() => {
handleClose(false);
}}
aria-labelledby="alert-dialog-title" aria-labelledby="alert-dialog-title"
aria-describedby="alert-dialog-description" aria-describedby="alert-dialog-description"
onKeyUp={keyUp} onKeyUp={keyUp}
@ -71,10 +71,20 @@ export function ConfirmDialogProvider(
</DialogContentText> </DialogContentText>
</DialogContent> </DialogContent>
<DialogActions> <DialogActions>
<Button onClick={() => { handleClose(false); }} autoFocus> <Button
onClick={() => {
handleClose(false);
}}
autoFocus
>
Cancel Cancel
</Button> </Button>
<Button onClick={() => { handleClose(true); }} color="error"> <Button
onClick={() => {
handleClose(true);
}}
color="error"
>
{confirmButton ?? "Confirm"} {confirmButton ?? "Confirm"}
</Button> </Button>
</DialogActions> </DialogActions>

View File

@ -1,7 +1,12 @@
import DeleteIcon from "@mui/icons-material/DeleteOutlined"; import DeleteIcon from "@mui/icons-material/DeleteOutlined";
import DriveFileMoveOutlineIcon from "@mui/icons-material/DriveFileMoveOutline"; import DriveFileMoveOutlineIcon from "@mui/icons-material/DriveFileMoveOutline";
import { Tooltip, Typography } from "@mui/material"; import { IconButton, Tooltip, Typography } from "@mui/material";
import { DataGrid, GridActionsCellItem, GridColDef } from "@mui/x-data-grid"; import {
DataGrid,
GridActionsCellItem,
GridColDef,
GridRowSelectionModel,
} from "@mui/x-data-grid";
import React from "react"; import React from "react";
import { useParams } from "react-router-dom"; import { useParams } from "react-router-dom";
import { Movement, MovementApi } from "../api/MovementsApi"; import { Movement, MovementApi } from "../api/MovementsApi";
@ -93,9 +98,13 @@ function MovementsTable(p: {
const alert = useAlert(); const alert = useAlert();
const confirm = useConfirm(); const confirm = useConfirm();
const snackbar = useSnackbar(); const snackbar = useSnackbar();
const loadingMessage = useLoadingMessage();
const chooseAccount = useSelectAccount(); const chooseAccount = useSelectAccount();
const [rowSelectionModel, setRowSelectionModel] =
React.useState<GridRowSelectionModel>([]);
// Change account of movement // Change account of movement
const handleMoveClick = async (movement: Movement) => { const handleMoveClick = async (movement: Movement) => {
const target = await chooseAccount( 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:
<ul>
{movements.map((m) => (
<li>
{m.label} ({m.amount} )
</li>
))}
</ul>
</>
))
)
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]>[] = [ const columns: GridColDef<(typeof p.movements)[number]>[] = [
{ {
field: "checked", field: "checked",
@ -221,33 +270,53 @@ function MovementsTable(p: {
}, },
}, },
]; ];
return ( return (
<DataGrid<Movement> <>
columns={columns} <div>
rows={p.movements} <Tooltip title="Delete all the selected entries">
autoPageSize <IconButton
checkboxSelection disabled={
initialState={{ rowSelectionModel.length === 0 ||
sorting: { rowSelectionModel.length === p.movements.length
sortModel: [{ field: "time", sort: "desc" }], }
}, onClick={deleteMultiple}
columns: { >
columnVisibilityModel: { <DeleteIcon />
checked: false, </IconButton>
</Tooltip>
</div>
<DataGrid<Movement>
columns={columns}
rows={p.movements}
autoPageSize
checkboxSelection
initialState={{
sorting: {
sortModel: [{ field: "time", sort: "desc" }],
}, },
}, columns: {
}} columnVisibilityModel: {
processRowUpdate={async (n) => { checked: false,
try { },
return await MovementApi.Update(n); },
} catch (e) { }}
console.error("Failed to update movement information!", e); onRowSelectionModelChange={(newRowSelectionModel) => {
alert(`Failed to update row! ${e}`); setRowSelectionModel(newRowSelectionModel);
throw e; }}
} finally { rowSelectionModel={rowSelectionModel}
p.needReload(true); 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);
}
}}
/>
</>
); );
} }