Can delete a single inbox entry

This commit is contained in:
Pierre HUBERT 2025-05-12 20:36:42 +02:00
parent 07ee499742
commit 2f807b4c73
4 changed files with 115 additions and 4 deletions

View File

@ -69,4 +69,14 @@ export class InboxApi {
})
).data;
}
/**
* Delete an inbox entry
*/
static async Delete(movement: InboxEntry): Promise<void> {
await APIClient.exec({
uri: `/inbox/${movement.id}`,
method: "DELETE",
});
}
}

View File

@ -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 }}
/>
<span style={{ flex: 1 }}></span>

View File

@ -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<GridRowSelectionModel>([]);
// 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 <MovementWidget id={params.row.movement_id} />;
},
},
{
field: "actions",
type: "actions",
headerName: "",
width: 55,
cellClassName: "actions",
editable: false,
getActions: (params) => {
return [
<InboxEntryActionMenu
key="menu"
movement={params.row}
onDelete={handleDeleteClick}
/>,
];
},
},
];
return (
@ -191,3 +250,42 @@ function InboxTable(p: {
</div>
);
}
function InboxEntryActionMenu(p: {
movement: InboxEntry;
onDelete: (entry: InboxEntry) => void;
}): React.ReactElement {
const [anchorEl, setAnchorEl] = React.useState<null | HTMLElement>(null);
const open = Boolean(anchorEl);
const handleClick = (event: React.MouseEvent<HTMLElement>) => {
setAnchorEl(event.currentTarget);
};
const handleClose = () => {
setAnchorEl(null);
};
return (
<>
<IconButton
aria-label="Actions"
aria-haspopup="true"
onClick={handleClick}
>
<MoreVertIcon />
</IconButton>
<Menu anchorEl={anchorEl} open={open} onClose={handleClose}>
{/* Delete */}
<MenuItem
onClick={() => {
handleClose();
p.onDelete(p.movement);
}}
>
<ListItemIcon>
<DeleteIcon color="error" />
</ListItemIcon>
<ListItemText secondary={"Delete the entry"}>Delete</ListItemText>
</MenuItem>
</Menu>
</>
);
}

View File

@ -36,6 +36,7 @@ export function MovementWidget(p: { id: number }): React.ReactElement {
)}
<span
style={{
marginLeft: "5px",
display: "inline-flex",
flexDirection: "column",
justifyContent: "center",