diff --git a/moneymgr_web/src/routes/AccountRoute.tsx b/moneymgr_web/src/routes/AccountRoute.tsx index c8605e4..402f4b1 100644 --- a/moneymgr_web/src/routes/AccountRoute.tsx +++ b/moneymgr_web/src/routes/AccountRoute.tsx @@ -1,14 +1,20 @@ import DeleteIcon from "@mui/icons-material/DeleteOutlined"; import DriveFileMoveOutlineIcon from "@mui/icons-material/DriveFileMoveOutline"; -import { IconButton, Tooltip, Typography } from "@mui/material"; +import LinkOffIcon from "@mui/icons-material/LinkOff"; +import MoreVertIcon from "@mui/icons-material/MoreVert"; import { - DataGrid, - GridActionsCellItem, - GridColDef, - GridRowSelectionModel, -} from "@mui/x-data-grid"; + IconButton, + ListItemIcon, + ListItemText, + Tooltip, + Typography, +} from "@mui/material"; +import Menu from "@mui/material/Menu"; +import MenuItem from "@mui/material/MenuItem"; +import { DataGrid, GridColDef, GridRowSelectionModel } from "@mui/x-data-grid"; import React from "react"; import { useParams } from "react-router-dom"; +import { UploadedFile } from "../api/FileApi"; import { Movement, MovementApi } from "../api/MovementsApi"; import { useAccounts } from "../hooks/AccountsListProvider"; import { useAlert } from "../hooks/context_providers/AlertDialogProvider"; @@ -20,12 +26,11 @@ import { AccountWidget } from "../widgets/AccountWidget"; import { AmountWidget } from "../widgets/AmountWidget"; import { AsyncWidget } from "../widgets/AsyncWidget"; import { DateWidget } from "../widgets/DateWidget"; +import { UploadFileButton } from "../widgets/forms/UploadFileButton"; import { MoneyMgrWebRouteContainer } from "../widgets/MoneyMgrWebRouteContainer"; import { NewMovementWidget } from "../widgets/NewMovementWidget"; -import { NotFoundRoute } from "./NotFound"; -import { UploadFileButton } from "../widgets/forms/UploadFileButton"; -import { UploadedFile } from "../api/FileApi"; import { UploadedFileWidget } from "../widgets/UploadedFileWidget"; +import { NotFoundRoute } from "./NotFound"; export function AccountRoute(): React.ReactElement { const loadingMessage = useLoadingMessage(); @@ -80,7 +85,7 @@ export function AccountRoute(): React.ReactElement { ( @@ -148,6 +153,25 @@ function MovementsTable(p: { } }; + // Detach movement from account + const handleDetachFile = async (movement: Movement) => { + 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!`, + `Detach file from movement`, + `Detach file` + )) + ) + return; + + await setUploadedFile(movement, undefined); + } catch (e) { + console.error("Failed to detach file from movement!", e); + alert(`Failed to detach file from movement! ${e}`); + } + }; + // Delete movement const handleDeleteClick = async (movement: Movement) => { try { @@ -318,28 +342,19 @@ function MovementsTable(p: { { field: "actions", type: "actions", - headerName: "Actions", - width: 80, + headerName: "", + width: 55, cellClassName: "actions", editable: false, getActions: (params) => { return [ - - } - label="Move to another account" - onClick={() => handleMoveClick(params.row)} - color="inherit" - /> - , - - } - label="Delete" - onClick={() => handleDeleteClick(params.row)} - color="inherit" - /> - , + , ]; }, }, @@ -407,3 +422,74 @@ function MovementsTable(p: { ); } + +function MovementActionMenu(p: { + movement: Movement; + onDetachFile: (m: Movement) => void; + onMove: (m: Movement) => void; + onDelete: (m: Movement) => 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 ( + <> + + + + + {/* Detach file */} + {p.movement.file_id && ( + { + handleClose(); + p.onDetachFile(p.movement); + }} + > + + + + + Detach file + + + )} + {/* Move to another account */} + { + handleClose(); + p.onMove(p.movement); + }} + > + + + + + Move + + + {/* Delete */} + { + handleClose(); + p.onDelete(p.movement); + }} + > + + + + Delete + + + + ); +}