Added actions
This commit is contained in:
parent
f4c384665c
commit
da678fd487
|
|
@ -0,0 +1,30 @@
|
|||
-- CreateTable
|
||||
CREATE TABLE `Office365LinkRequest` (
|
||||
`id` VARCHAR(191) NOT NULL,
|
||||
`status` ENUM('WAITING', 'ACCEPTED', 'CANCELLED', 'REJECTED') NOT NULL DEFAULT 'WAITING',
|
||||
`requestedAt` DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
|
||||
`acceptedAt` DATETIME(3) NULL,
|
||||
`cancelledAt` DATETIME(3) NULL,
|
||||
`rejectedAt` DATETIME(3) NULL,
|
||||
`createdBy` VARCHAR(191) NOT NULL,
|
||||
|
||||
PRIMARY KEY (`id`)
|
||||
) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE `Office365ReferralLink` (
|
||||
`id` VARCHAR(191) NOT NULL,
|
||||
`email` VARCHAR(191) NOT NULL,
|
||||
`activePeriod` VARCHAR(191) NOT NULL,
|
||||
`numberOfUsers` INTEGER NOT NULL,
|
||||
`link` VARCHAR(191) NULL,
|
||||
`requestId` VARCHAR(191) NOT NULL,
|
||||
|
||||
PRIMARY KEY (`id`)
|
||||
) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE `Office365LinkRequest` ADD CONSTRAINT `Office365LinkRequest_createdBy_fkey` FOREIGN KEY (`createdBy`) REFERENCES `User`(`id`) ON DELETE RESTRICT ON UPDATE CASCADE;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE `Office365ReferralLink` ADD CONSTRAINT `Office365ReferralLink_requestId_fkey` FOREIGN KEY (`requestId`) REFERENCES `Office365LinkRequest`(`id`) ON DELETE RESTRICT ON UPDATE CASCADE;
|
||||
|
|
@ -16,6 +16,7 @@ model User {
|
|||
photoProfile String?
|
||||
directPermissions Permission[] @relation("PermissionToUser")
|
||||
roles Role[] @relation("RoleToUser")
|
||||
linkRequests Office365LinkRequest[]
|
||||
}
|
||||
|
||||
model Role {
|
||||
|
|
@ -37,3 +38,32 @@ model Permission {
|
|||
roles Role[] @relation("PermissionToRole")
|
||||
directUsers User[] @relation("PermissionToUser")
|
||||
}
|
||||
|
||||
enum Office365LinkRequestStatus {
|
||||
WAITING
|
||||
ACCEPTED
|
||||
CANCELLED
|
||||
REJECTED
|
||||
}
|
||||
|
||||
model Office365LinkRequest {
|
||||
id String @id @default(cuid())
|
||||
creator User @relation(fields: [createdBy], references: [id])
|
||||
status Office365LinkRequestStatus @default(WAITING)
|
||||
requestedAt DateTime @default(now())
|
||||
acceptedAt DateTime?
|
||||
cancelledAt DateTime?
|
||||
rejectedAt DateTime?
|
||||
createdBy String
|
||||
links Office365ReferralLink[]
|
||||
}
|
||||
|
||||
model Office365ReferralLink {
|
||||
id String @id @default(cuid())
|
||||
request Office365LinkRequest @relation(fields: [requestId], references: [id])
|
||||
email String
|
||||
activePeriod String
|
||||
numberOfUsers Int
|
||||
link String?
|
||||
requestId String
|
||||
}
|
||||
|
|
@ -81,6 +81,18 @@ export default async function permissionSeed(prisma: PrismaClient) {
|
|||
description: "Allows deleting a user",
|
||||
isActive: true,
|
||||
},
|
||||
{
|
||||
code: "office-365-request.create",
|
||||
name: "Create Office 365 Request",
|
||||
description: "Allows create an Office 365 Reseller Request",
|
||||
isActive: true
|
||||
},
|
||||
{
|
||||
code: "office-365-request.getMine",
|
||||
name: "Get my Office 365 Requests",
|
||||
description: "Allows retrieve user's Office 365 Link Requests",
|
||||
isActive: true
|
||||
}
|
||||
];
|
||||
|
||||
await Promise.all(
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
import getUserRoles from "@/modules/auth/utils/getUserRoles";
|
||||
import checkMultiplePermissions from "@/modules/dashboard/services/checkMultiplePermissions";
|
||||
import checkPermission from "@/modules/dashboard/services/checkPermission";
|
||||
import getLinkRequests from "@/modules/resellerOffice365/actions/getLinkRequests";
|
||||
import RequestTable from "@/modules/resellerOffice365/tables/RequestTable/RequestTable";
|
||||
import { Card, Stack, Title } from "@mantine/core";
|
||||
import { notFound } from "next/navigation";
|
||||
|
|
@ -17,11 +18,19 @@ export default async function RequestLinkPage() {
|
|||
|
||||
if (!permissions.readAll) notFound();
|
||||
|
||||
const data = await getLinkRequests()
|
||||
if (!data.success){
|
||||
//todo: handle error
|
||||
console.error(data.error)
|
||||
throw new Error("Error while fetch permission")
|
||||
}
|
||||
const tableData = data.data
|
||||
|
||||
return (
|
||||
<Stack>
|
||||
<Title order={1}>Permohonan Link Office 365</Title>
|
||||
<Card>
|
||||
<RequestTable permissions={permissions} tableData={[]} />
|
||||
<RequestTable permissions={permissions} tableData={tableData} />
|
||||
</Card>
|
||||
</Stack>
|
||||
);
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ export const DashboardErrorCodes = [
|
|||
"INVALID_JWT_TOKEN",
|
||||
"JWT_SECRET_EMPTY",
|
||||
"USER_ALREADY_EXISTS",
|
||||
"INVALID_FORM_DATA"
|
||||
] as const;
|
||||
|
||||
interface DashboardErrorOptions {
|
||||
|
|
|
|||
65
src/modules/resellerOffice365/actions/createLinkRequest.ts
Normal file
65
src/modules/resellerOffice365/actions/createLinkRequest.ts
Normal file
|
|
@ -0,0 +1,65 @@
|
|||
"use server";
|
||||
import checkPermission from "@/modules/dashboard/services/checkPermission";
|
||||
import ServerResponseAction from "@/modules/dashboard/types/ServerResponseAction";
|
||||
import handleCatch from "@/modules/dashboard/utils/handleCatch";
|
||||
import unauthorized from "@/modules/dashboard/utils/unauthorized";
|
||||
import "server-only";
|
||||
import requestLinkFormSchema from "../formSchemas/requestLinkFormSchema";
|
||||
import DashboardError from "@/modules/dashboard/errors/DashboardError";
|
||||
import mapObjectToFirstValue from "@/utils/mapObjectToFirstValue";
|
||||
import db from "@/core/db";
|
||||
import getCurrentUser from "@/modules/auth/utils/getCurrentUser";
|
||||
import { revalidatePath } from "next/cache";
|
||||
|
||||
export default async function createLinkRequest(
|
||||
formData: RequestLinkForm
|
||||
): Promise<ServerResponseAction> {
|
||||
try {
|
||||
if (!(await checkPermission("office-365-request.create")))
|
||||
unauthorized();
|
||||
|
||||
const currentUser = await getCurrentUser();
|
||||
if (!currentUser) return unauthorized();
|
||||
|
||||
const validatedFields = requestLinkFormSchema.safeParse(formData);
|
||||
if (!validatedFields.success) {
|
||||
throw new DashboardError({
|
||||
errorCode: "INVALID_FORM_DATA",
|
||||
formErrors: mapObjectToFirstValue(
|
||||
validatedFields.error.flatten().fieldErrors
|
||||
),
|
||||
});
|
||||
}
|
||||
|
||||
//database operations
|
||||
await db.office365LinkRequest.create({
|
||||
data: {
|
||||
creator: {
|
||||
connect: {
|
||||
id: currentUser.id,
|
||||
},
|
||||
},
|
||||
status: "WAITING",
|
||||
links: {
|
||||
createMany: {
|
||||
data: validatedFields.data.details.map((detail) => ({
|
||||
numberOfUsers: detail.endUserQty,
|
||||
activePeriod: detail.activePeriod,
|
||||
email: detail.email,
|
||||
})),
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
revalidatePath(".")
|
||||
|
||||
return {
|
||||
success: true,
|
||||
message:
|
||||
"Your request has been made. Please wait while our admin processing your request",
|
||||
};
|
||||
} catch (e) {
|
||||
return handleCatch(e);
|
||||
}
|
||||
}
|
||||
49
src/modules/resellerOffice365/actions/getLinkRequests.ts
Normal file
49
src/modules/resellerOffice365/actions/getLinkRequests.ts
Normal file
|
|
@ -0,0 +1,49 @@
|
|||
import ServerResponseAction from "@/modules/dashboard/types/ServerResponseAction";
|
||||
import RequestLink from "../types/RequestLink";
|
||||
import handleCatch from "@/modules/dashboard/utils/handleCatch";
|
||||
import checkPermission from "@/modules/dashboard/services/checkPermission";
|
||||
import unauthorized from "@/modules/dashboard/utils/unauthorized";
|
||||
import getCurrentUser from "@/modules/auth/utils/getCurrentUser";
|
||||
import db from "@/core/db";
|
||||
|
||||
export default async function getLinkRequests(): Promise<
|
||||
ServerResponseAction<RequestLink[]>
|
||||
> {
|
||||
try {
|
||||
if (!(await checkPermission("office-365-request.getMine")))
|
||||
unauthorized();
|
||||
|
||||
const user = await getCurrentUser();
|
||||
|
||||
if (!user) return unauthorized();
|
||||
|
||||
const requests = await db.office365LinkRequest.findMany({
|
||||
where: {
|
||||
creator: { id: user.id },
|
||||
},
|
||||
select: {
|
||||
id: true,
|
||||
requestedAt: true,
|
||||
status: true,
|
||||
links: true,
|
||||
},
|
||||
});
|
||||
|
||||
const result: RequestLink[] = requests.map((request) => ({
|
||||
id: request.id,
|
||||
requestDate: request.requestedAt.toISOString(),
|
||||
status: request.status,
|
||||
userCount: request.links.reduce(
|
||||
(prev, curr) => prev + curr.numberOfUsers,
|
||||
0
|
||||
),
|
||||
}));
|
||||
|
||||
return {
|
||||
success: true,
|
||||
data: result,
|
||||
};
|
||||
} catch (e) {
|
||||
return handleCatch(e);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,15 @@
|
|||
import { z } from "zod";
|
||||
import resellerOffice365Config from "../config";
|
||||
|
||||
const requestLinkFormSchema = z.object({
|
||||
numberOfLinks: z.number().min(1), // Assuming you need at least one link
|
||||
details: z.array(
|
||||
z.object({
|
||||
email: z.string().email(), // Validate string as an email
|
||||
activePeriod: z.enum(resellerOffice365Config.activePeriods), // Validate against the specific allowed values
|
||||
endUserQty: z.number().min(1), // Assuming you need at least one end user
|
||||
})
|
||||
),
|
||||
});
|
||||
|
||||
export default requestLinkFormSchema;
|
||||
|
|
@ -1,4 +1,5 @@
|
|||
import {
|
||||
Alert,
|
||||
Button,
|
||||
Divider,
|
||||
Fieldset,
|
||||
|
|
@ -10,8 +11,10 @@ import {
|
|||
Select,
|
||||
Stack,
|
||||
TextInput,
|
||||
Loader,
|
||||
Text,
|
||||
} from "@mantine/core";
|
||||
import { useForm } from "@mantine/form";
|
||||
import { useForm, zodResolver } from "@mantine/form";
|
||||
import React, { useState } from "react";
|
||||
import {
|
||||
TbAt,
|
||||
|
|
@ -22,6 +25,11 @@ import {
|
|||
TbUsers,
|
||||
} from "react-icons/tb";
|
||||
import resellerOffice365Config from "../config";
|
||||
import requestLinkFormSchema from "../formSchemas/requestLinkFormSchema";
|
||||
import withServerAction from "@/modules/dashboard/utils/withServerAction";
|
||||
import createLinkRequest from "../actions/createLinkRequest";
|
||||
import { notifications } from "@mantine/notifications";
|
||||
import DashboardError from "@/modules/dashboard/errors/DashboardError";
|
||||
|
||||
export interface ModalProps {
|
||||
title: string;
|
||||
|
|
@ -30,25 +38,18 @@ export interface ModalProps {
|
|||
onClose?: () => void;
|
||||
}
|
||||
|
||||
interface FormType {
|
||||
numberOfLinks: number;
|
||||
details: {
|
||||
email: string;
|
||||
activePeriod: (typeof resellerOffice365Config.activePeriods)[number];
|
||||
endUserQty: number;
|
||||
}[];
|
||||
}
|
||||
|
||||
export default function RequestModal(props: ModalProps) {
|
||||
const [formState, setFormState] = useState<
|
||||
"idle" | "submitting" | "waiting"
|
||||
>("idle");
|
||||
|
||||
const [errorMessage, setErrorMessage] = useState("");
|
||||
|
||||
const closeModal = () => {
|
||||
props.onClose ? props.onClose() : "";
|
||||
};
|
||||
|
||||
const form = useForm<FormType>({
|
||||
const form = useForm<RequestLinkForm>({
|
||||
initialValues: {
|
||||
numberOfLinks: 1,
|
||||
details: [
|
||||
|
|
@ -59,6 +60,7 @@ export default function RequestModal(props: ModalProps) {
|
|||
},
|
||||
],
|
||||
},
|
||||
validate: zodResolver(requestLinkFormSchema),
|
||||
onValuesChange: (values, prev) => {
|
||||
// Check if numberOfLinks has changed
|
||||
if (values.numberOfLinks !== prev.numberOfLinks) {
|
||||
|
|
@ -90,24 +92,60 @@ export default function RequestModal(props: ModalProps) {
|
|||
|
||||
const disableChange = formState !== "idle";
|
||||
|
||||
const handleSubmit = (values: FormType) => {
|
||||
const handleSubmit = (values: RequestLinkForm) => {
|
||||
const submitableState = ["idle"];
|
||||
|
||||
if (!submitableState.includes(formState)) return; //prevent submit
|
||||
|
||||
setFormState("submitting");
|
||||
|
||||
withServerAction(createLinkRequest, values)
|
||||
.then((response) => {
|
||||
notifications.show({
|
||||
message: response.message,
|
||||
color: "green",
|
||||
});
|
||||
setFormState("waiting");
|
||||
})
|
||||
.catch((e) => {
|
||||
if (e instanceof DashboardError) {
|
||||
if (e.errorCode === "INVALID_FORM_DATA") {
|
||||
if (e.formErrors) {
|
||||
form.setErrors(e.formErrors);
|
||||
} else {
|
||||
setErrorMessage(e.message);
|
||||
}
|
||||
} else {
|
||||
setErrorMessage(`ERROR: ${e.message} (${e.errorCode})`);
|
||||
}
|
||||
} else if (e instanceof Error) {
|
||||
setErrorMessage(`ERROR: ${e.message}`);
|
||||
} else {
|
||||
setErrorMessage(
|
||||
`Unkown error is occured. Please contact administrator`
|
||||
);
|
||||
}
|
||||
|
||||
setFormState("idle");
|
||||
});
|
||||
};
|
||||
|
||||
return (
|
||||
<Modal
|
||||
size="sm"
|
||||
opened={props.opened}
|
||||
title={props.title}
|
||||
title={formState === "waiting" ? "Link Request Detail" : "Create New Request"}
|
||||
onClose={closeModal}
|
||||
scrollAreaComponent={ScrollArea.Autosize}
|
||||
>
|
||||
<form onSubmit={form.onSubmit(handleSubmit)}>
|
||||
<Stack gap="md">
|
||||
{formState === "waiting" && (
|
||||
<Alert color="orange">
|
||||
Your request is being processed by administrator
|
||||
</Alert>
|
||||
)}
|
||||
|
||||
<NumberInput
|
||||
label="Please input the number of links you request"
|
||||
min={1}
|
||||
|
|
@ -178,9 +216,10 @@ export default function RequestModal(props: ModalProps) {
|
|||
variant="filled"
|
||||
leftSection={<TbDeviceFloppy size={20} />}
|
||||
type="submit"
|
||||
loading={["submitting", "waiting"].includes(
|
||||
disabled={["submitting", "waiting"].includes(
|
||||
formState
|
||||
)}
|
||||
loading={["submitting"].includes(formState)}
|
||||
>
|
||||
Make Request
|
||||
</Button>
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ import createActionButtons from "@/modules/dashboard/utils/createActionButton";
|
|||
|
||||
export interface RequestLinkRow {
|
||||
id: string;
|
||||
requestDate: Date,
|
||||
requestDate: string,
|
||||
userCount: number,
|
||||
status: string
|
||||
}
|
||||
|
|
@ -32,6 +32,10 @@ const createColumns = (options: ColumnOptions) => {
|
|||
|
||||
columnHelper.accessor("requestDate", {
|
||||
header: "Request Date",
|
||||
cell: (props) => {
|
||||
const date = new Date(props.row.original.requestDate);
|
||||
return `${date.toDateString()}; ${date.toLocaleTimeString()}`
|
||||
}
|
||||
}),
|
||||
|
||||
columnHelper.accessor("userCount", {
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
export default interface RequestLink {
|
||||
id: string;
|
||||
requestDate: Date,
|
||||
requestDate: string,
|
||||
userCount: number,
|
||||
status: string
|
||||
}
|
||||
8
src/modules/resellerOffice365/types/RequestLinkForm.d.ts
vendored
Normal file
8
src/modules/resellerOffice365/types/RequestLinkForm.d.ts
vendored
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
interface RequestLinkForm {
|
||||
numberOfLinks: number;
|
||||
details: {
|
||||
email: string;
|
||||
activePeriod: (typeof resellerOffice365Config.activePeriods)[number];
|
||||
endUserQty: number;
|
||||
}[];
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user