Can delete a single inbox entry
This commit is contained in:
		@@ -69,4 +69,14 @@ export class InboxApi {
 | 
				
			|||||||
      })
 | 
					      })
 | 
				
			||||||
    ).data;
 | 
					    ).data;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  /**
 | 
				
			||||||
 | 
					   * Delete an inbox entry
 | 
				
			||||||
 | 
					   */
 | 
				
			||||||
 | 
					  static async Delete(movement: InboxEntry): Promise<void> {
 | 
				
			||||||
 | 
					    await APIClient.exec({
 | 
				
			||||||
 | 
					      uri: `/inbox/${movement.id}`,
 | 
				
			||||||
 | 
					      method: "DELETE",
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -167,7 +167,7 @@ function MovementsTable(p: {
 | 
				
			|||||||
    try {
 | 
					    try {
 | 
				
			||||||
      if (
 | 
					      if (
 | 
				
			||||||
        !(await confirm(
 | 
					        !(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 from movement`,
 | 
				
			||||||
          `Detach file`
 | 
					          `Detach file`
 | 
				
			||||||
        ))
 | 
					        ))
 | 
				
			||||||
@@ -186,7 +186,7 @@ function MovementsTable(p: {
 | 
				
			|||||||
    try {
 | 
					    try {
 | 
				
			||||||
      if (
 | 
					      if (
 | 
				
			||||||
        !(await confirm(
 | 
					        !(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;
 | 
					        return;
 | 
				
			||||||
@@ -377,7 +377,9 @@ function MovementsTable(p: {
 | 
				
			|||||||
          variant="standard"
 | 
					          variant="standard"
 | 
				
			||||||
          size="small"
 | 
					          size="small"
 | 
				
			||||||
          value={labelFilter}
 | 
					          value={labelFilter}
 | 
				
			||||||
          onChange={(e) => { setLabelFilter(e.target.value); }}
 | 
					          onChange={(e) => {
 | 
				
			||||||
 | 
					            setLabelFilter(e.target.value);
 | 
				
			||||||
 | 
					          }}
 | 
				
			||||||
          style={{ padding: "0px", flex: 1 }}
 | 
					          style={{ padding: "0px", flex: 1 }}
 | 
				
			||||||
        />
 | 
					        />
 | 
				
			||||||
        <span style={{ flex: 1 }}></span>
 | 
					        <span style={{ flex: 1 }}></span>
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -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 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 { DataGrid, GridColDef, GridRowSelectionModel } from "@mui/x-data-grid";
 | 
				
			||||||
import React from "react";
 | 
					import React from "react";
 | 
				
			||||||
import { InboxApi, InboxEntry } from "../api/InboxApi";
 | 
					import { InboxApi, InboxEntry } from "../api/InboxApi";
 | 
				
			||||||
import { useAlert } from "../hooks/context_providers/AlertDialogProvider";
 | 
					import { useAlert } from "../hooks/context_providers/AlertDialogProvider";
 | 
				
			||||||
 | 
					import { useConfirm } from "../hooks/context_providers/ConfirmDialogProvider";
 | 
				
			||||||
import { useLoadingMessage } from "../hooks/context_providers/LoadingMessageProvider";
 | 
					import { useLoadingMessage } from "../hooks/context_providers/LoadingMessageProvider";
 | 
				
			||||||
 | 
					import { useSnackbar } from "../hooks/context_providers/SnackbarProvider";
 | 
				
			||||||
import { useUnmatchedInboxEntriesCount } from "../hooks/UnmatchedInboxEntriesCountProvider";
 | 
					import { useUnmatchedInboxEntriesCount } from "../hooks/UnmatchedInboxEntriesCountProvider";
 | 
				
			||||||
import { AmountWidget } from "../widgets/AmountWidget";
 | 
					import { AmountWidget } from "../widgets/AmountWidget";
 | 
				
			||||||
import { AsyncWidget } from "../widgets/AsyncWidget";
 | 
					import { AsyncWidget } from "../widgets/AsyncWidget";
 | 
				
			||||||
@@ -92,9 +105,38 @@ function InboxTable(p: {
 | 
				
			|||||||
  onReload: (skipEntries: boolean) => void;
 | 
					  onReload: (skipEntries: boolean) => void;
 | 
				
			||||||
  showMovements?: boolean;
 | 
					  showMovements?: boolean;
 | 
				
			||||||
}): React.ReactElement {
 | 
					}): React.ReactElement {
 | 
				
			||||||
 | 
					  const alert = useAlert();
 | 
				
			||||||
 | 
					  const confirm = useConfirm();
 | 
				
			||||||
 | 
					  const snackbar = useSnackbar();
 | 
				
			||||||
 | 
					  const loadingMessage = useLoadingMessage();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  const [rowSelectionModel, setRowSelectionModel] =
 | 
					  const [rowSelectionModel, setRowSelectionModel] =
 | 
				
			||||||
    React.useState<GridRowSelectionModel>([]);
 | 
					    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]>[] = [
 | 
					  const columns: GridColDef<(typeof p.entries)[number]>[] = [
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
      field: "time",
 | 
					      field: "time",
 | 
				
			||||||
@@ -153,6 +195,23 @@ function InboxTable(p: {
 | 
				
			|||||||
          return <MovementWidget id={params.row.movement_id} />;
 | 
					          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 (
 | 
					  return (
 | 
				
			||||||
@@ -191,3 +250,42 @@ function InboxTable(p: {
 | 
				
			|||||||
    </div>
 | 
					    </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>
 | 
				
			||||||
 | 
					    </>
 | 
				
			||||||
 | 
					  );
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -36,6 +36,7 @@ export function MovementWidget(p: { id: number }): React.ReactElement {
 | 
				
			|||||||
          )}
 | 
					          )}
 | 
				
			||||||
          <span
 | 
					          <span
 | 
				
			||||||
            style={{
 | 
					            style={{
 | 
				
			||||||
 | 
					              marginLeft: "5px",
 | 
				
			||||||
              display: "inline-flex",
 | 
					              display: "inline-flex",
 | 
				
			||||||
              flexDirection: "column",
 | 
					              flexDirection: "column",
 | 
				
			||||||
              justifyContent: "center",
 | 
					              justifyContent: "center",
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user