diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 0000000..2ba986f --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,15 @@ +{ + // Use IntelliSense to learn about possible attributes. + // Hover to view descriptions of existing attributes. + // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 + "version": "0.2.0", + "configurations": [ + { + "type": "chrome", + "request": "launch", + "name": "Launch Chrome against localhost", + "url": "http://localhost:8080", + "webRoot": "${workspaceFolder}" + } + ] +} \ No newline at end of file diff --git a/bun.lockb b/bun.lockb index 5122c5d..6a58ffd 100755 Binary files a/bun.lockb and b/bun.lockb differ diff --git a/prisma/seeds/roleSeed.ts b/prisma/seeds/roleSeed.ts index 878523a..54638d0 100644 --- a/prisma/seeds/roleSeed.ts +++ b/prisma/seeds/roleSeed.ts @@ -11,6 +11,12 @@ export default async function roleSeed(prisma: PrismaClient) { description: "Has full access to the system and can manage all features and settings", isActive: true, name: "Super Admin" + }, + { + code: "reseller-office-365", + description: "Has ability to make request Office 365 links", + isActive: true, + name: "Reseller Office 365" } ]; diff --git a/src/app/dashboard/reseller-office-365/request/page.tsx b/src/app/dashboard/reseller-office-365/request/page.tsx new file mode 100644 index 0000000..2db2afb --- /dev/null +++ b/src/app/dashboard/reseller-office-365/request/page.tsx @@ -0,0 +1,28 @@ +import getUserRoles from "@/modules/auth/utils/getUserRoles"; +import checkMultiplePermissions from "@/modules/dashboard/services/checkMultiplePermissions"; +import checkPermission from "@/modules/dashboard/services/checkPermission"; +import RequestTable from "@/modules/resellerOffice365/tables/RequestTable/RequestTable"; +import { Card, Stack, Title } from "@mantine/core"; +import { notFound } from "next/navigation"; +import React from "react"; + +export default async function RequestLinkPage() { + const permissions = await checkMultiplePermissions({ + create: "office-365-link.create", + readAll: "office-365-link.readAll", + read: "office-365-link.read", + update: "office-365-link.update", + delete: "office-365-link.delete", + }); + + if (!permissions.readAll) notFound(); + + return ( + + Permohonan Link Office 365 + + + + + ); +} diff --git a/src/modules/resellerOffice365/config.ts b/src/modules/resellerOffice365/config.ts new file mode 100644 index 0000000..a3ef800 --- /dev/null +++ b/src/modules/resellerOffice365/config.ts @@ -0,0 +1,5 @@ +const resellerOffice365Config = { + activePeriods: ["1 Month", "1 Year", "2 Years"], +} as const; + +export default resellerOffice365Config; diff --git a/src/modules/resellerOffice365/modals/RequestModal.tsx b/src/modules/resellerOffice365/modals/RequestModal.tsx new file mode 100644 index 0000000..6f83dce --- /dev/null +++ b/src/modules/resellerOffice365/modals/RequestModal.tsx @@ -0,0 +1,194 @@ +import { + Button, + Divider, + Fieldset, + Flex, + Group, + Modal, + NumberInput, + ScrollArea, + Select, + Stack, + TextInput, +} from "@mantine/core"; +import { useForm } from "@mantine/form"; +import React, { useState } from "react"; +import { + TbAt, + TbCalendarTime, + TbDeviceFloppy, + TbLink, + TbUser, + TbUsers, +} from "react-icons/tb"; +import resellerOffice365Config from "../config"; + +export interface ModalProps { + title: string; + opened: boolean; + readonly: boolean; + onClose?: () => void; +} + +interface FormType { + numberOfLinks: number; + details: { + email: string; + activePeriod: (typeof resellerOffice365Config.activePeriods)[number]; + endUserQty: number; + }[]; +} + +export default function RequestModal(props: ModalProps) { + const [isSubmitting, setSubmitting] = useState(false); + const [formState, setFormState] = useState< + "idle" | "submitting" | "waiting" + >("idle"); + + const closeModal = () => { + props.onClose ? props.onClose() : ""; + }; + + const form = useForm({ + initialValues: { + numberOfLinks: 1, + details: [ + { + email: "", + activePeriod: "2 Years", + endUserQty: 1, + }, + ], + }, + onValuesChange: (values, prev) => { + // Check if numberOfLinks has changed + if (values.numberOfLinks !== prev.numberOfLinks) { + const currentDetails = values.details; + const targetLength = values.numberOfLinks; + + // Add new detail objects if numberOfLinks has increased + while (currentDetails.length < targetLength) { + currentDetails.push({ + email: "", + activePeriod: "2 Years", + endUserQty: 1, + }); + } + + // Remove extra detail objects if numberOfLinks has decreased + if (currentDetails.length > targetLength) { + currentDetails.length = targetLength; // Adjusts the array length + } + + // Update the form values with the adjusted details array + form.setValues({ + ...values, + details: currentDetails, + }); + } + }, + }); + + const disableChange = formState !== "idle"; + + const handleSubmit = (values: FormType) => { + const submitableState = ["idle"]; + + if (!submitableState.includes(formState)) return; //prevent submit + + setFormState("submitting"); + }; + + return ( + +
+ + } + disabled={disableChange} + {...form.getInputProps("numberOfLinks")} + /> + + + + + {form.values.details.map((item, i) => ( +
+ } + label="Email" + disabled={disableChange} + {...form.getInputProps( + `details.${i}.email` + )} + /> + +