From 27fdced0c990d78277cb7b2fc05e49e316f918f1 Mon Sep 17 00:00:00 2001 From: sianida26 Date: Sun, 18 Feb 2024 05:01:45 +0700 Subject: [PATCH] Added update lnk --- .../resellerOffice365/actions/inputLinks.ts | 81 +++++++++++++++++++ .../errors/ResellerOffice365Error.ts | 25 ++++++ .../resellerOffice365/modals/RequestModal.tsx | 66 ++++++++++++--- .../types/RequestLinkForm.d.ts | 2 + 4 files changed, 163 insertions(+), 11 deletions(-) create mode 100644 src/modules/resellerOffice365/actions/inputLinks.ts create mode 100644 src/modules/resellerOffice365/errors/ResellerOffice365Error.ts diff --git a/src/modules/resellerOffice365/actions/inputLinks.ts b/src/modules/resellerOffice365/actions/inputLinks.ts new file mode 100644 index 0000000..d0beadf --- /dev/null +++ b/src/modules/resellerOffice365/actions/inputLinks.ts @@ -0,0 +1,81 @@ +"use server"; +import db from "@/core/db"; +import checkPermission from "@/modules/dashboard/services/checkPermission"; +import ServerResponseAction from "@/modules/dashboard/types/ServerResponseAction"; +import handleCatch from "@/modules/dashboard/utils/handleCatch"; +import notFound from "@/modules/dashboard/utils/notFound"; +import unauthorized from "@/modules/dashboard/utils/unauthorized"; +import "server-only"; +import ResellerOffice365Error from "../errors/ResellerOffice365Error"; +import { revalidatePath } from "next/cache"; + +interface Args { + id: string; + data: { + linkId: string; + link: string; + }[]; +} + +export default async function inputLink( + args: Args +): Promise { + try { + //TODO: Implement permission + if (!(await checkPermission("authenticated-only"))) + return unauthorized(); + + const data = await db.office365LinkRequest.findFirst({ + where: { + id: args.id, + }, + include: { + links: true, + }, + }); + + if (!data) + return notFound({ + message: + "The requested link is not found. It might seems has been deleted. Please contact administrator", + }); + + if (data.status !== "WAITING") + throw new ResellerOffice365Error({ + errorCode: "REQUEST_IS_NOT_IN_WAITING_STATE", + message: `This request has been ${ + data.acceptedAt + ? "completed" + : data.rejectedAt + ? "rejected" + : "cancelled" + } thus cannot be updated. Please contact administrator`, + }); + + await db.office365LinkRequest.update({ + where: { + id: args.id, + }, + data: { + acceptedAt: new Date(), + status: "ACCEPTED", + links: { + updateMany: args.data.map((linkItem) => ({ + where: { id: linkItem.linkId }, + data: { + link: linkItem.link, + }, + })), + }, + }, + }); + + revalidatePath(".") + + return { + success: true, + }; + } catch (e) { + return handleCatch(e); + } +} diff --git a/src/modules/resellerOffice365/errors/ResellerOffice365Error.ts b/src/modules/resellerOffice365/errors/ResellerOffice365Error.ts new file mode 100644 index 0000000..409f8f8 --- /dev/null +++ b/src/modules/resellerOffice365/errors/ResellerOffice365Error.ts @@ -0,0 +1,25 @@ +import DashboardError from "@/modules/dashboard/errors/DashboardError"; + +export const ResellerOffice365ErrorCodes = [ + "REQUEST_IS_NOT_IN_WAITING_STATE" +] as const; + +interface ResellerOffice365ErrorOptions { + message?: string; + errorCode: (typeof ResellerOffice365ErrorCodes)[number] | (string & {}); + formErrors?: Record +} + +export default class ResellerOffice365Error extends DashboardError { + public readonly errorCode: ResellerOffice365ErrorOptions['errorCode']; + public readonly formErrors?: ResellerOffice365ErrorOptions['formErrors'] + + constructor(options: ResellerOffice365ErrorOptions) { + super({ + errorCode: options.errorCode, + message: options.message, + }); + + this.errorCode = options.errorCode; + } +} diff --git a/src/modules/resellerOffice365/modals/RequestModal.tsx b/src/modules/resellerOffice365/modals/RequestModal.tsx index d10a445..e96cbb9 100644 --- a/src/modules/resellerOffice365/modals/RequestModal.tsx +++ b/src/modules/resellerOffice365/modals/RequestModal.tsx @@ -34,6 +34,7 @@ import { notifications } from "@mantine/notifications"; 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"; export interface ModalProps { title: string; @@ -89,6 +90,8 @@ export default function RequestModal(props: ModalProps) { activePeriod: item.activePeriod, email: item.email, endUserQty: item.numberOfUsers, + id: item.id, + link: item.link ?? "", })), }); }) @@ -116,6 +119,7 @@ export default function RequestModal(props: ModalProps) { email: "", activePeriod: "2 Years", endUserQty: 1, + link: "", }, ], }, @@ -132,6 +136,7 @@ export default function RequestModal(props: ModalProps) { email: "", activePeriod: "2 Years", endUserQty: 1, + link: "", }); } @@ -154,10 +159,9 @@ export default function RequestModal(props: ModalProps) { if (!submitableState.includes(formState)) return; //prevent submit when not in subitable state - setFormState("submitting"); - switch (props.type) { case "create": { + setFormState("submitting"); withServerAction(createLinkRequest, values) .then((response) => { notifications.show({ @@ -192,16 +196,43 @@ export default function RequestModal(props: ModalProps) { break; } case "input link": { - //TODO: Handle add link + if (!props.detailId) return; + setFormState("submitting"); + withServerAction(inputLink, { + id: props.detailId, + data: form.values.details.map((item) => ({ + link: item.link, + linkId: item.id ?? "", + })), + }) + .then(() => { + setFormState("idle"); + notifications.show({ + message: "Data has been updated", + color: "green", + }); + closeModal(); + }) + .catch((e) => { + if (e instanceof Error) { + setErrorMessage(e.message); + } else { + setErrorMessage("An unkown error occured"); + } + }) + .finally(() => { + setFormState("idle"); + }); } } }; const disableChange = formState !== "idle"; - const readonly = ["input link", "detail"].includes(props.type) + 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 showActivationLink = ["input link", "detail"].includes(props.type); + const enableInputActivationLink = props.type === "input link"; + const showSubmitButton = ["create", "input link"].includes(props.type); return ( )} @@ -315,7 +357,7 @@ export default function RequestModal(props: ModalProps) { > Close - {formState === "waiting" && ( + {showSubmitButton && ( )} diff --git a/src/modules/resellerOffice365/types/RequestLinkForm.d.ts b/src/modules/resellerOffice365/types/RequestLinkForm.d.ts index 3745dc3..2269f8d 100644 --- a/src/modules/resellerOffice365/types/RequestLinkForm.d.ts +++ b/src/modules/resellerOffice365/types/RequestLinkForm.d.ts @@ -2,8 +2,10 @@ interface RequestLinkForm { id: string | undefined; numberOfLinks: number; details: { + id?: string; email: string; activePeriod: (typeof resellerOffice365Config.activePeriods)[number]; endUserQty: number; + link: string }[]; }