From 31cb674565583c4d0046f88862df722b9884f58a Mon Sep 17 00:00:00 2001 From: sianida26 Date: Sun, 18 Feb 2024 22:56:02 +0700 Subject: [PATCH] Add cancellation --- .../actions/cancelRequest.ts | 48 +++++++ .../resellerOffice365/modals/RequestModal.tsx | 129 ++++++++++++++---- 2 files changed, 147 insertions(+), 30 deletions(-) create mode 100644 src/modules/resellerOffice365/actions/cancelRequest.ts diff --git a/src/modules/resellerOffice365/actions/cancelRequest.ts b/src/modules/resellerOffice365/actions/cancelRequest.ts new file mode 100644 index 0000000..fd75589 --- /dev/null +++ b/src/modules/resellerOffice365/actions/cancelRequest.ts @@ -0,0 +1,48 @@ +"use server"; + +import db from "@/core/db"; +import checkPermission from "@/modules/dashboard/services/checkPermission"; +import handleCatch from "@/modules/dashboard/utils/handleCatch"; +import notFound from "@/modules/dashboard/utils/notFound"; +import unauthorized from "@/modules/dashboard/utils/unauthorized"; +import ResellerOffice365Error from "../errors/ResellerOffice365Error"; +import getCurrentUser from "@/modules/auth/utils/getCurrentUser"; +import ServerResponseAction from "@/modules/dashboard/types/ServerResponseAction"; +import { revalidatePath } from "next/cache"; +import "server-only" + +export default async function cancelRequest(id: string): Promise { + try { + //TODO: Fix permission + if (!(await checkPermission("authenticated-only"))) return unauthorized(); + + const data = await db.office365LinkRequest.findFirst({ + where: { id }, + }); + + if (!data) return notFound({message: "The Provided ID does not match any"}) + + if (data.status !== "WAITING") throw new ResellerOffice365Error({ + errorCode: "REQUEST_IS_NOT_IN_WAITING_STATE", + message: "This item is not in \"waiting\" state to perform cancellation. This might be due to the request has been accepted" + }); + + if (data.createdBy !== (await getCurrentUser())?.id) return unauthorized(); + + await db.office365LinkRequest.update({ + where: {id}, + data: { + status: "CANCELLED", + cancelledAt: new Date() + } + }) + + revalidatePath(".") + + return { + success: true + } + } catch (e) { + return handleCatch(e); + } +} diff --git a/src/modules/resellerOffice365/modals/RequestModal.tsx b/src/modules/resellerOffice365/modals/RequestModal.tsx index e96cbb9..581c8ce 100644 --- a/src/modules/resellerOffice365/modals/RequestModal.tsx +++ b/src/modules/resellerOffice365/modals/RequestModal.tsx @@ -35,6 +35,8 @@ import DashboardError from "@/modules/dashboard/errors/DashboardError"; import getLinkRequestDataById from "../actions/getLinkRequestDataById"; import { isPagesAPIRouteMatch } from "next/dist/server/future/route-matches/pages-api-route-match"; import inputLink from "../actions/inputLinks"; +import { Office365LinkRequestStatus } from "@prisma/client"; +import cancelRequest from "../actions/cancelRequest"; export interface ModalProps { title: string; @@ -46,15 +48,27 @@ export interface ModalProps { export default function RequestModal(props: ModalProps) { const [formState, setFormState] = useState< - "idle" | "submitting" | "waiting" | "fetching" | "error" + | "idle" + | "submitting" + | "waiting" + | "fetching" + | "error" + | "confirming cancel" + | "cancelling" >("idle"); const [errorMessage, setErrorMessage] = useState(""); + const [requestStatus, setRequestStatus] = useState< + Office365LinkRequestStatus | "CREATING" + >("CREATING"); - const closeModal = () => { - if (formState === "submitting") return; //prevents closing + const closeModal = (force?: boolean) => { + if (!force){ + if (["submitting, cancelling"].includes(formState)) return; //prevents closing + } //reset state setErrorMessage(""); + setRequestStatus("CREATING"); setFormState("idle"); form.reset(); props.onClose ? props.onClose() : ""; @@ -94,6 +108,15 @@ export default function RequestModal(props: ModalProps) { link: item.link ?? "", })), }); + setRequestStatus(data.status); + if ( + data.status === "WAITING" && + props.type === "detail" + ) { + setFormState("waiting"); + } else { + setFormState("idle"); + } }) .catch((e) => { if (e instanceof Error) { @@ -101,8 +124,6 @@ export default function RequestModal(props: ModalProps) { } else { setErrorMessage("Unkown error occured"); } - }) - .finally(() => { setFormState("idle"); }); break; @@ -227,12 +248,81 @@ export default function RequestModal(props: ModalProps) { } }; + const handleCancel = () => { + if (!props.detailId) return setErrorMessage("Cannot request cancellation. Cause: the ID is empty. Please contact your administrator") + setFormState("cancelling") + withServerAction(cancelRequest, props.detailId) + .then(() => { + notifications.show({ + message: "The request has been cancelled", + color: "green" + }) + setFormState("idle") + closeModal(true) + }) + .catch(e => { + if (e instanceof Error){ + setErrorMessage(e.message) + } + setFormState("idle") + }) + } + + const renderActionButtons = () => ( + + + {showCancelButton && ( + + )} + {showSubmitButton && ( + + )} + + ); + + const renderCancelConfirmation = () => ( + + Are you sure to cancel this link request? + + + + + + ); + const disableChange = formState !== "idle"; const readonly = ["input link", "detail"].includes(props.type); const showSkeleton = formState === "fetching"; const showActivationLink = ["input link", "detail"].includes(props.type); const enableInputActivationLink = props.type === "input link"; const showSubmitButton = ["create", "input link"].includes(props.type); + const showCancelButton = + ["detail"].includes(props.type) && formState === "waiting"; + const showCancelRequestConfirmation = [ + "confirming cancel", + "cancelling", + ].includes(formState); + const showWaitingAlert = (["waiting", "confirming cancel"] as typeof formState[]).includes(formState) return (
- {formState === "waiting" && ( + {showWaitingAlert && ( Your request is being processed by administrator @@ -349,30 +439,9 @@ export default function RequestModal(props: ModalProps) { {/* Buttons */} - - - {showSubmitButton && ( - - )} - + {showCancelRequestConfirmation + ? renderCancelConfirmation() + : renderActionButtons()}