From c0be27cc41379d073a0eb9f31fb0534b0641d64b Mon Sep 17 00:00:00 2001 From: Jonas Honecker Date: Fri, 5 Jul 2024 13:05:43 +0200 Subject: [PATCH] Add loading states to save, update and confirm delete button --- .../buttons/ConfirmDeleteButton.tsx | 40 +++++++++++++++++ .../src/components/buttons/SaveButton.tsx | 39 ++++++++++++----- .../src/components/buttons/UpdateButton.tsx | 43 +++++++++++++------ .../dialogues/ConfirmDeletionDialogue.tsx | 14 +++++- frontend/src/components/forms/TicketForm.tsx | 14 +++++- frontend/src/components/pages/MainPage.tsx | 5 +++ 6 files changed, 129 insertions(+), 26 deletions(-) create mode 100644 frontend/src/components/buttons/ConfirmDeleteButton.tsx diff --git a/frontend/src/components/buttons/ConfirmDeleteButton.tsx b/frontend/src/components/buttons/ConfirmDeleteButton.tsx new file mode 100644 index 0000000..8ee8816 --- /dev/null +++ b/frontend/src/components/buttons/ConfirmDeleteButton.tsx @@ -0,0 +1,40 @@ +import { Box, Button, CircularProgress } from "@mui/material"; + +type DeleteButtonProps = { + onClick: () => void; + pendingRequest: boolean; +}; + +export default function ConfirmDeleteButton({ + onClick, + pendingRequest, +}: Readonly) { + return ( + + + {pendingRequest && ( + + )} + + ); +} diff --git a/frontend/src/components/buttons/SaveButton.tsx b/frontend/src/components/buttons/SaveButton.tsx index 5a759a6..302b391 100644 --- a/frontend/src/components/buttons/SaveButton.tsx +++ b/frontend/src/components/buttons/SaveButton.tsx @@ -1,18 +1,37 @@ -import { Button } from "@mui/material"; +import { Box, Button, CircularProgress } from "@mui/material"; type SaveButtonProps = { onClick: () => void; + pendingRequest: boolean; }; -export default function SaveButton({ onClick }: Readonly) { +export default function SaveButton({ + onClick, + pendingRequest, +}: Readonly) { return ( - + + + {pendingRequest && ( + + )} + ); } diff --git a/frontend/src/components/buttons/UpdateButton.tsx b/frontend/src/components/buttons/UpdateButton.tsx index 44e600e..dbae8dc 100644 --- a/frontend/src/components/buttons/UpdateButton.tsx +++ b/frontend/src/components/buttons/UpdateButton.tsx @@ -1,20 +1,39 @@ -import { Button } from "@mui/material"; +import { Box, Button, CircularProgress } from "@mui/material"; type UpdateButtonProps = { onClick: () => void; + pendingRequest: boolean; }; -export default function UpdateButton({ onClick }: Readonly) { +export default function UpdateButton({ + onClick, + pendingRequest, +}: Readonly) { return ( - + + + {pendingRequest && ( + + )} + ); } diff --git a/frontend/src/components/dialogues/ConfirmDeletionDialogue.tsx b/frontend/src/components/dialogues/ConfirmDeletionDialogue.tsx index 925593b..0985a90 100644 --- a/frontend/src/components/dialogues/ConfirmDeletionDialogue.tsx +++ b/frontend/src/components/dialogues/ConfirmDeletionDialogue.tsx @@ -7,10 +7,10 @@ import { } from "@mui/material"; import CancelButton from "../buttons/CancelButton.tsx"; import { Dispatch, SetStateAction } from "react"; -import DeleteButton from "../buttons/DeleteButton.tsx"; import { SnackbarConfig, SidepanelConfig } from "../../types/Config.ts"; import { deleteTicket } from "../utils/ApiRequests.tsx"; import { Ticket } from "../../types/Ticket.ts"; +import ConfirmDeleteButton from "../buttons/ConfirmDeleteButton.tsx"; type ConfirmDeletionDialogueProps = { confirmDeletion: boolean; @@ -20,6 +20,8 @@ type ConfirmDeletionDialogueProps = { searchResults: Ticket[] | undefined; setSearchResults: Dispatch>; setSnackbarConfig: Dispatch>; + pendingRequest: boolean; + setPendingRequest: Dispatch>; }; export default function ConfirmDeletionDialogue({ @@ -30,14 +32,18 @@ export default function ConfirmDeletionDialogue({ searchResults, setSearchResults, setSnackbarConfig, + pendingRequest, + setPendingRequest, }: Readonly) { if (sidePanelConfig.formType !== "UpdateTicket") { return; } const handleDelete = () => { + setPendingRequest(true); deleteTicket(sidePanelConfig.ticket.id) .then(() => { + setPendingRequest(false); setSearchResults( searchResults?.filter((ticket) => { return ticket.id !== sidePanelConfig.ticket.id; @@ -52,6 +58,7 @@ export default function ConfirmDeletionDialogue({ setSidepanelConfig({ ...sidePanelConfig, open: false }); }) .catch((error) => { + setPendingRequest(false); setSnackbarConfig({ open: true, severity: "error", @@ -79,7 +86,10 @@ export default function ConfirmDeletionDialogue({ - + ); diff --git a/frontend/src/components/forms/TicketForm.tsx b/frontend/src/components/forms/TicketForm.tsx index 24f859d..b1b134b 100644 --- a/frontend/src/components/forms/TicketForm.tsx +++ b/frontend/src/components/forms/TicketForm.tsx @@ -21,6 +21,8 @@ type TicketFormProps = { searchResults: Ticket[] | undefined; setSearchResults: Dispatch>; setConfirmDeletion: Dispatch>; + pendingRequest: boolean; + setPendingRequest: Dispatch>; }; export default function TicketForm({ @@ -31,6 +33,8 @@ export default function TicketForm({ searchResults, setSearchResults, setConfirmDeletion, + pendingRequest, + setPendingRequest, }: Readonly) { const [title, setTitle] = useState(""); const [titleError, setTitleError] = useState(false); @@ -58,8 +62,10 @@ export default function TicketForm({ const [isTitleError, isDescriptionError] = validateTitleAndDescription(); if (!isTitleError && !isDescriptionError) { + setPendingRequest(true); createNewTicket({ title: title, description: description }) .then(() => { + setPendingRequest(false); setSnackbarConfig({ open: true, severity: "success", @@ -68,6 +74,7 @@ export default function TicketForm({ setSidepanelConfig({ ...sidePanelConfig, open: false }); }) .catch((error) => { + setPendingRequest(false); setSnackbarConfig({ open: true, severity: "error", @@ -85,11 +92,13 @@ export default function TicketForm({ } if (!isTitleError && !isDescriptionError) { + setPendingRequest(true); updateTicket(sidePanelConfig.ticket.id, { title: title, description: description, }) .then((response) => { + setPendingRequest(false); setSearchResults( searchResults?.map((ticket) => { if (ticket.id === response.data.id) { @@ -107,6 +116,7 @@ export default function TicketForm({ setSidepanelConfig({ ...sidePanelConfig, open: false }); }) .catch((error) => { + setPendingRequest(false); setSnackbarConfig({ open: true, severity: "error", @@ -160,10 +170,10 @@ export default function TicketForm({ )} {sidePanelConfig.formType == "NewTicket" && ( - + )} {sidePanelConfig.formType == "UpdateTicket" && ( - + )} diff --git a/frontend/src/components/pages/MainPage.tsx b/frontend/src/components/pages/MainPage.tsx index 1cc8735..fd6279c 100644 --- a/frontend/src/components/pages/MainPage.tsx +++ b/frontend/src/components/pages/MainPage.tsx @@ -33,6 +33,7 @@ export default function MainPage({ }); const [confirmDeletion, setConfirmDeletion] = useState(false); const [loadingTickets, setLoadingTickets] = useState(false); + const [pendingRequest, setPendingRequest] = useState(false); return ( <> @@ -67,6 +68,8 @@ export default function MainPage({ searchResults={searchResults} setSearchResults={setSearchResults} setConfirmDeletion={setConfirmDeletion} + pendingRequest={pendingRequest} + setPendingRequest={setPendingRequest} /> @@ -82,6 +85,8 @@ export default function MainPage({ searchResults={searchResults} setSearchResults={setSearchResults} setSnackbarConfig={setSnackbarConfig} + pendingRequest={pendingRequest} + setPendingRequest={setPendingRequest} /> );