remove reseller
This commit is contained in:
parent
fa8422849a
commit
7bbf2b8a5e
101
server/socket.ts
101
server/socket.ts
|
|
@ -1,101 +0,0 @@
|
|||
import getUserFromToken from "@/modules/auth/utils/getUserFromToken";
|
||||
import { User } from "@prisma/client";
|
||||
import prisma from "@/core/db";
|
||||
import Bun from "bun";
|
||||
|
||||
const intents = {
|
||||
listenMyWaitingLinkRequest: "listenMyWaitingLinkRequest",
|
||||
} as const;
|
||||
|
||||
const waitingLinkRequestConnections: Map<
|
||||
string,
|
||||
Bun.ServerWebSocket<{
|
||||
channel: string;
|
||||
user: User;
|
||||
intent: string;
|
||||
}>
|
||||
> = new Map();
|
||||
|
||||
export const server = Bun.serve<{
|
||||
channel: string;
|
||||
user: User;
|
||||
intent: string;
|
||||
}>({
|
||||
async fetch(req, server) {
|
||||
const url = new URL(req.url);
|
||||
req.headers.getSetCookie();
|
||||
const pathname = url.pathname;
|
||||
|
||||
const cookies = req.headers.get("Cookie");
|
||||
|
||||
// Extract the Authorization header
|
||||
const authHeader = req.headers.get("Authorization");
|
||||
const token = authHeader?.startsWith("Bearer ")
|
||||
? authHeader.substring(7, authHeader.length)
|
||||
: null;
|
||||
|
||||
const user = token ? await getUserFromToken(token) : null;
|
||||
|
||||
const intent = pathname.substring(1);
|
||||
|
||||
switch (intent) {
|
||||
case `/${intents.listenMyWaitingLinkRequest}`: {
|
||||
if (!user) {
|
||||
return new Response("Unauthorized", { status: 401 });
|
||||
}
|
||||
const channel = `mwrl-${user.id}`;
|
||||
const success = server.upgrade(req, {
|
||||
data: { user, channel, intent },
|
||||
});
|
||||
if (success) return undefined;
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
return new Response("");
|
||||
}
|
||||
}
|
||||
},
|
||||
websocket: {
|
||||
async open(ws) {
|
||||
switch (ws.data.intent) {
|
||||
case intents.listenMyWaitingLinkRequest: {
|
||||
ws.subscribe(ws.data.channel);
|
||||
|
||||
//retrieve user's link requests with status of waiting
|
||||
const result = await prisma.office365LinkRequest.findMany({
|
||||
where: {
|
||||
createdBy: ws.data.user.id,
|
||||
status: "WAITING",
|
||||
},
|
||||
select: {
|
||||
id: true,
|
||||
},
|
||||
});
|
||||
|
||||
server.publish(ws.data.channel, JSON.stringify(result));
|
||||
|
||||
waitingLinkRequestConnections.set(ws.data.channel, ws);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
message(ws, message) {
|
||||
// the server re-broadcasts incoming messages to everyone
|
||||
// server.publish("the-group-chat", `: ${message}`);
|
||||
},
|
||||
close(ws) {
|
||||
// const msg = ` has left the chat`;
|
||||
// server.publish("the-group-chat", msg);
|
||||
// ws.unsubscribe("the-group-chat");
|
||||
switch (ws.data.intent) {
|
||||
case intents.listenMyWaitingLinkRequest: {
|
||||
ws.unsubscribe(ws.data.channel);
|
||||
waitingLinkRequestConnections.delete(ws.data.channel);
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
port: 3001,
|
||||
});
|
||||
|
||||
console.log(`Listening on ${server.hostname}:${server.port}`);
|
||||
|
|
@ -1,39 +0,0 @@
|
|||
import getUserRoles from "@/modules/auth/utils/getUserRoles";
|
||||
import checkMultiplePermissions from "@/modules/dashboard/services/checkMultiplePermissions";
|
||||
import checkPermission from "@/modules/dashboard/services/checkPermission";
|
||||
import getAllLinkRequests from "@/modules/resellerOffice365/actions/getAllLinkRequests";
|
||||
import getLinkRequests from "@/modules/resellerOffice365/actions/getLinkRequests";
|
||||
import ListOfRequestTable from "@/modules/resellerOffice365/tables/ListOfRequestTable/ListOfRequestTable";
|
||||
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();
|
||||
|
||||
const data = await getAllLinkRequests();
|
||||
if (!data.success) {
|
||||
//todo: handle error
|
||||
console.error(data.error);
|
||||
throw new Error("Error while fetch data");
|
||||
}
|
||||
const tableData = data.data;
|
||||
|
||||
return (
|
||||
<Stack>
|
||||
<Title order={1}>List Link Office 365</Title>
|
||||
<Card>
|
||||
<ListOfRequestTable permissions={permissions} tableData={tableData} />
|
||||
</Card>
|
||||
</Stack>
|
||||
);
|
||||
}
|
||||
|
|
@ -1,37 +0,0 @@
|
|||
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";
|
||||
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();
|
||||
|
||||
const data = await getLinkRequests()
|
||||
if (!data.success){
|
||||
//todo: handle error
|
||||
console.error(data.error)
|
||||
throw new Error("Error while fetch data")
|
||||
}
|
||||
const tableData = data.data
|
||||
|
||||
return (
|
||||
<Stack>
|
||||
<Title order={1}>Permohonan Link Office 365</Title>
|
||||
<Card>
|
||||
<RequestTable permissions={permissions} tableData={tableData} />
|
||||
</Card>
|
||||
</Stack>
|
||||
);
|
||||
}
|
||||
|
|
@ -1,48 +0,0 @@
|
|||
"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<ServerResponseAction> {
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,65 +0,0 @@
|
|||
"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);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,55 +0,0 @@
|
|||
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 db from "@/core/db";
|
||||
import RequestLinkWithIssuerData from "../types/RequestLinkWithIssuerData";
|
||||
|
||||
export default async function getAllLinkRequests(): Promise<
|
||||
ServerResponseAction<RequestLinkWithIssuerData[]>
|
||||
> {
|
||||
try {
|
||||
//TODO: Fix permission check
|
||||
if (!(await checkPermission("authenticated-only")))
|
||||
return unauthorized();
|
||||
|
||||
const requestLinks = await db.office365LinkRequest.findMany({
|
||||
orderBy: [
|
||||
{
|
||||
status: "asc"
|
||||
},
|
||||
{
|
||||
requestedAt: "desc"
|
||||
}
|
||||
],
|
||||
select: {
|
||||
id: true,
|
||||
creator: {
|
||||
select: {
|
||||
id: true,
|
||||
name: true,
|
||||
photoProfile: true,
|
||||
email: true,
|
||||
},
|
||||
},
|
||||
status: true,
|
||||
requestedAt: true,
|
||||
_count: {
|
||||
select: {
|
||||
links: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
return {
|
||||
success: true,
|
||||
data: requestLinks.map((item) => ({
|
||||
...item,
|
||||
userCount: item._count.links,
|
||||
})),
|
||||
};
|
||||
} catch (e) {
|
||||
return handleCatch(e);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,70 +0,0 @@
|
|||
"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 RequestLinkWithIssuerData from "../types/RequestLinkWithIssuerData";
|
||||
import db from "@/core/db";
|
||||
import { string } from "zod";
|
||||
import notFound from "@/modules/dashboard/utils/notFound";
|
||||
|
||||
async function getOffice365LinkRequestData(id: string) {
|
||||
const data = await db.office365LinkRequest.findFirst({
|
||||
where: { id },
|
||||
select: {
|
||||
acceptedAt: true,
|
||||
cancelledAt: true,
|
||||
creator: {
|
||||
select: {
|
||||
name: true,
|
||||
id: true,
|
||||
email: true,
|
||||
},
|
||||
},
|
||||
id: true,
|
||||
links: {
|
||||
select: {
|
||||
activePeriod: true,
|
||||
email: true,
|
||||
id: true,
|
||||
link: true,
|
||||
numberOfUsers: true,
|
||||
},
|
||||
},
|
||||
rejectedAt: true,
|
||||
requestedAt: true,
|
||||
status: true,
|
||||
},
|
||||
});
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
export default async function getLinkRequestDataById(
|
||||
id: string
|
||||
): Promise<
|
||||
ServerResponseAction<
|
||||
NonNullable<Awaited<ReturnType<typeof getOffice365LinkRequestData>>>
|
||||
>
|
||||
> {
|
||||
try {
|
||||
//TODO: Adjust permission
|
||||
if (!(await checkPermission("authenticated-only")))
|
||||
return unauthorized();
|
||||
|
||||
const data = await getOffice365LinkRequestData(id);
|
||||
|
||||
if (!data) {
|
||||
return notFound({
|
||||
message: "The requested link request item is not found",
|
||||
});
|
||||
}
|
||||
|
||||
return {
|
||||
success: true,
|
||||
data,
|
||||
};
|
||||
} catch (e) {
|
||||
return handleCatch(e);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,57 +0,0 @@
|
|||
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({
|
||||
orderBy: [
|
||||
{
|
||||
status: "asc"
|
||||
},
|
||||
{
|
||||
requestedAt: "desc"
|
||||
}
|
||||
],
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,81 +0,0 @@
|
|||
"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<ServerResponseAction> {
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,5 +0,0 @@
|
|||
const resellerOffice365Config = {
|
||||
activePeriods: ["1 Month", "1 Year", "2 Years"],
|
||||
} as const;
|
||||
|
||||
export default resellerOffice365Config;
|
||||
|
|
@ -1,25 +0,0 @@
|
|||
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<string, string>
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,15 +0,0 @@
|
|||
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,449 +0,0 @@
|
|||
/* eslint-disable react-hooks/exhaustive-deps */
|
||||
import {
|
||||
Alert,
|
||||
Button,
|
||||
Divider,
|
||||
Fieldset,
|
||||
Flex,
|
||||
Group,
|
||||
Modal,
|
||||
NumberInput,
|
||||
ScrollArea,
|
||||
Select,
|
||||
Stack,
|
||||
TextInput,
|
||||
Loader,
|
||||
Text,
|
||||
Skeleton,
|
||||
} from "@mantine/core";
|
||||
import { useForm, zodResolver } from "@mantine/form";
|
||||
import React, { useEffect, useState } from "react";
|
||||
import {
|
||||
TbAt,
|
||||
TbCalendarTime,
|
||||
TbDeviceFloppy,
|
||||
TbLink,
|
||||
TbUser,
|
||||
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";
|
||||
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;
|
||||
opened: boolean;
|
||||
onClose?: () => void;
|
||||
type: "create" | "detail" | "waiting" | "input link";
|
||||
detailId: string | null;
|
||||
}
|
||||
|
||||
export default function RequestModal(props: ModalProps) {
|
||||
const [formState, setFormState] = useState<
|
||||
| "idle"
|
||||
| "submitting"
|
||||
| "waiting"
|
||||
| "fetching"
|
||||
| "error"
|
||||
| "confirming cancel"
|
||||
| "cancelling"
|
||||
>("idle");
|
||||
|
||||
const [errorMessage, setErrorMessage] = useState("");
|
||||
const [requestStatus, setRequestStatus] = useState<
|
||||
Office365LinkRequestStatus | "CREATING"
|
||||
>("CREATING");
|
||||
|
||||
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() : "";
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
const fetchDataById = async (id: string) => {
|
||||
const { data } = await withServerAction(getLinkRequestDataById, id);
|
||||
if (!props.opened) return;
|
||||
return data;
|
||||
};
|
||||
|
||||
switch (props.type) {
|
||||
case "detail":
|
||||
case "input link": {
|
||||
if (!props.detailId || !props.opened) return;
|
||||
setFormState("fetching");
|
||||
fetchDataById(props.detailId)
|
||||
.then((data) => {
|
||||
if (!data) {
|
||||
closeModal();
|
||||
notifications.show({
|
||||
message:
|
||||
"The returned data from server is empty. Please try again",
|
||||
color: "red",
|
||||
});
|
||||
return;
|
||||
}
|
||||
form.setValues({
|
||||
numberOfLinks: data.links.length,
|
||||
id: data.id,
|
||||
details: data.links.map((item) => ({
|
||||
activePeriod: item.activePeriod,
|
||||
email: item.email,
|
||||
endUserQty: item.numberOfUsers,
|
||||
id: item.id,
|
||||
link: item.link ?? "",
|
||||
})),
|
||||
});
|
||||
setRequestStatus(data.status);
|
||||
if (
|
||||
data.status === "WAITING" &&
|
||||
props.type === "detail"
|
||||
) {
|
||||
setFormState("waiting");
|
||||
} else {
|
||||
setFormState("idle");
|
||||
}
|
||||
})
|
||||
.catch((e) => {
|
||||
if (e instanceof Error) {
|
||||
setErrorMessage(e.message);
|
||||
} else {
|
||||
setErrorMessage("Unkown error occured");
|
||||
}
|
||||
setFormState("idle");
|
||||
});
|
||||
break;
|
||||
}
|
||||
}
|
||||
}, [props]);
|
||||
|
||||
const form = useForm<RequestLinkForm>({
|
||||
initialValues: {
|
||||
id: undefined,
|
||||
numberOfLinks: 1,
|
||||
details: [
|
||||
{
|
||||
email: "",
|
||||
activePeriod: "2 Years",
|
||||
endUserQty: 1,
|
||||
link: "",
|
||||
},
|
||||
],
|
||||
},
|
||||
validate: zodResolver(requestLinkFormSchema),
|
||||
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,
|
||||
link: "",
|
||||
});
|
||||
}
|
||||
|
||||
// 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 handleSubmit = (values: RequestLinkForm) => {
|
||||
const submitableState: (typeof formState)[] = ["idle"];
|
||||
|
||||
if (!submitableState.includes(formState)) return; //prevent submit when not in subitable state
|
||||
|
||||
switch (props.type) {
|
||||
case "create": {
|
||||
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");
|
||||
});
|
||||
break;
|
||||
}
|
||||
case "input 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 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 = () => (
|
||||
<Flex justify="flex-end" align="center" gap="lg" mt="lg">
|
||||
<Button
|
||||
variant="outline"
|
||||
onClick={() => closeModal()}
|
||||
disabled={["submitting"].includes(formState)}
|
||||
>
|
||||
Close
|
||||
</Button>
|
||||
{showCancelButton && (
|
||||
<Button variant="outline" color="red" type="button" onClick={() => setFormState("confirming cancel")}>
|
||||
Cancel Request
|
||||
</Button>
|
||||
)}
|
||||
{showSubmitButton && (
|
||||
<Button
|
||||
variant="filled"
|
||||
leftSection={<TbDeviceFloppy size={20} />}
|
||||
type="submit"
|
||||
disabled={["submitting", "waiting"].includes(formState)}
|
||||
loading={["submitting"].includes(formState)}
|
||||
>
|
||||
{props.type === "create" ? "Make Request" : "Save"}
|
||||
</Button>
|
||||
)}
|
||||
</Flex>
|
||||
);
|
||||
|
||||
const renderCancelConfirmation = () => (
|
||||
<Stack mt="lg" gap={0}>
|
||||
<Text>Are you sure to cancel this link request?</Text>
|
||||
<Flex justify="flex-end" align="center" gap="lg" mt="lg">
|
||||
<Button type="button" variant="outline" onClick={() => setFormState("waiting")}>
|
||||
Cancel
|
||||
</Button>
|
||||
<Button type="button" variant="transparent" color="red" onClick={handleCancel}>
|
||||
Yes, I am sure
|
||||
</Button>
|
||||
</Flex>
|
||||
</Stack>
|
||||
);
|
||||
|
||||
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 (
|
||||
<Modal
|
||||
size="sm"
|
||||
opened={props.opened}
|
||||
title={
|
||||
formState === "waiting"
|
||||
? "Link Request Detail"
|
||||
: "Create New Request"
|
||||
}
|
||||
onClose={closeModal}
|
||||
scrollAreaComponent={ScrollArea.Autosize}
|
||||
>
|
||||
<form onSubmit={form.onSubmit(handleSubmit)}>
|
||||
<Stack gap="md">
|
||||
{showWaitingAlert && (
|
||||
<Alert color="orange">
|
||||
Your request is being processed by administrator
|
||||
</Alert>
|
||||
)}
|
||||
|
||||
{errorMessage && <Alert color="red">{errorMessage}</Alert>}
|
||||
|
||||
<Skeleton visible={showSkeleton}>
|
||||
<NumberInput
|
||||
label="Please input the number of links you request"
|
||||
min={1}
|
||||
max={3}
|
||||
allowDecimal={false}
|
||||
clampBehavior="strict"
|
||||
leftSection={<TbLink />}
|
||||
disabled={disableChange}
|
||||
readOnly={readonly}
|
||||
{...form.getInputProps("numberOfLinks")}
|
||||
/>
|
||||
</Skeleton>
|
||||
|
||||
<Divider
|
||||
label="End User Information"
|
||||
labelPosition="center"
|
||||
/>
|
||||
|
||||
<Stack>
|
||||
{form.values.details.map((item, i) => (
|
||||
<Fieldset key={i} legend={`Information ${i + 1}`}>
|
||||
<Stack gap="xs">
|
||||
<Skeleton visible={showSkeleton}>
|
||||
<TextInput
|
||||
leftSection={<TbAt />}
|
||||
label="Email"
|
||||
readOnly={readonly}
|
||||
disabled={disableChange}
|
||||
{...form.getInputProps(
|
||||
`details.${i}.email`
|
||||
)}
|
||||
/>
|
||||
</Skeleton>
|
||||
<Flex gap="md">
|
||||
<Skeleton visible={showSkeleton}>
|
||||
<Select
|
||||
data={
|
||||
resellerOffice365Config.activePeriods
|
||||
}
|
||||
label="Active Period"
|
||||
disabled={disableChange}
|
||||
readOnly={readonly}
|
||||
leftSection={<TbCalendarTime />}
|
||||
{...form.getInputProps(
|
||||
`details.${i}.activePeriod`
|
||||
)}
|
||||
/>
|
||||
</Skeleton>
|
||||
<Skeleton visible={showSkeleton}>
|
||||
<NumberInput
|
||||
label="End User Quantity"
|
||||
leftSection={<TbUsers />}
|
||||
min={1}
|
||||
max={5}
|
||||
disabled={disableChange}
|
||||
allowDecimal={false}
|
||||
readOnly={readonly}
|
||||
clampBehavior="strict"
|
||||
{...form.getInputProps(
|
||||
`details.${i}.endUserQty`
|
||||
)}
|
||||
/>
|
||||
</Skeleton>
|
||||
</Flex>
|
||||
{showActivationLink && (
|
||||
<Skeleton visible={showSkeleton}>
|
||||
<TextInput
|
||||
label="Activation Link"
|
||||
required={
|
||||
enableInputActivationLink
|
||||
}
|
||||
disabled={disableChange}
|
||||
readOnly={
|
||||
!enableInputActivationLink
|
||||
}
|
||||
{...form.getInputProps(
|
||||
`details.${i}.link`
|
||||
)}
|
||||
placeholder={
|
||||
enableInputActivationLink
|
||||
? "Enter link here"
|
||||
: "No link provided"
|
||||
}
|
||||
/>
|
||||
</Skeleton>
|
||||
)}
|
||||
</Stack>
|
||||
</Fieldset>
|
||||
))}
|
||||
</Stack>
|
||||
|
||||
{/* Buttons */}
|
||||
{showCancelRequestConfirmation
|
||||
? renderCancelConfirmation()
|
||||
: renderActionButtons()}
|
||||
</Stack>
|
||||
</form>
|
||||
</Modal>
|
||||
);
|
||||
}
|
||||
|
|
@ -1,66 +0,0 @@
|
|||
"use client";
|
||||
import DashboardTable from "@/modules/dashboard/components/DashboardTable";
|
||||
import { Button, Flex, Text } from "@mantine/core";
|
||||
import { getCoreRowModel, useReactTable } from "@tanstack/react-table";
|
||||
import React, { ReactNode, useState } from "react";
|
||||
import { TbPlus } from "react-icons/tb";
|
||||
import createColumns from "./columns";
|
||||
import CrudPermissions from "@/modules/dashboard/types/CrudPermissions";
|
||||
import RequestLink from "../../types/RequestLink";
|
||||
import RequestModal, { ModalProps } from "../../modals/RequestModal";
|
||||
import RequestLinkWithIssuerData from "../../types/RequestLinkWithIssuerData";
|
||||
|
||||
interface Props {
|
||||
permissions: Partial<CrudPermissions>;
|
||||
tableData: RequestLinkWithIssuerData[];
|
||||
}
|
||||
|
||||
const defaultModalProps: ModalProps = {
|
||||
opened: false,
|
||||
title: "Create new Link",
|
||||
type: "create",
|
||||
detailId: null,
|
||||
};
|
||||
|
||||
export default function ListOfRequestTable(props: Props) {
|
||||
const [modalProps, setModalProps] = useState<ModalProps>(defaultModalProps);
|
||||
// const [openModal, setOpenModal] = useState(false);
|
||||
|
||||
const table = useReactTable({
|
||||
data: props.tableData,
|
||||
columns: createColumns({
|
||||
permissions: props.permissions,
|
||||
actions: {
|
||||
detail: (id) => {
|
||||
openFormModal(id);
|
||||
},
|
||||
},
|
||||
}),
|
||||
|
||||
getCoreRowModel: getCoreRowModel(),
|
||||
defaultColumn: {
|
||||
cell: (props) => <Text>{props.getValue() as ReactNode}</Text>,
|
||||
},
|
||||
});
|
||||
|
||||
const openFormModal = (id: string) => {
|
||||
setModalProps({
|
||||
opened: true,
|
||||
title: "Request Detail",
|
||||
type: "input link",
|
||||
detailId: id,
|
||||
});
|
||||
};
|
||||
|
||||
const closeModal = () => {
|
||||
setModalProps(defaultModalProps);
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<DashboardTable table={table} />
|
||||
|
||||
<RequestModal {...modalProps} onClose={closeModal} />
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
|
@ -1,95 +0,0 @@
|
|||
import { createColumnHelper } from "@tanstack/react-table";
|
||||
import { Avatar, Badge, Flex, Stack, Text } from "@mantine/core";
|
||||
import { TbEye, TbPencil, TbTrash } from "react-icons/tb";
|
||||
import CrudPermissions from "@/modules/dashboard/types/CrudPermissions";
|
||||
import createActionButtons from "@/modules/dashboard/utils/createActionButton";
|
||||
import RequestLinkWithIssuerData from "../../types/RequestLinkWithIssuerData";
|
||||
|
||||
interface ColumnOptions {
|
||||
permissions: Partial<CrudPermissions>;
|
||||
actions: {
|
||||
detail: (id: string) => void;
|
||||
// edit: (id: string) => void;
|
||||
// delete: (id: string) => void;
|
||||
};
|
||||
}
|
||||
|
||||
const createColumns = (options: ColumnOptions) => {
|
||||
const columnHelper = createColumnHelper<RequestLinkWithIssuerData>();
|
||||
|
||||
const columns = [
|
||||
columnHelper.accessor("id", {
|
||||
id: "sequence",
|
||||
header: "#",
|
||||
cell: (props) => props.row.index + 1,
|
||||
}),
|
||||
|
||||
columnHelper.accessor("requestedAt", {
|
||||
header: "Request Date",
|
||||
cell: (props) => {
|
||||
const date = new Date(props.row.original.requestedAt);
|
||||
return `${date.toDateString()}; ${date.toLocaleTimeString()}`;
|
||||
},
|
||||
}),
|
||||
|
||||
columnHelper.accessor("creator", {
|
||||
header: "Issuer",
|
||||
cell: (props) => {
|
||||
return (<Flex gap="sm" align="center">
|
||||
<Avatar src={props.cell.getValue().photoProfile} />
|
||||
<div>
|
||||
<Text>{props.cell.getValue().name}</Text>
|
||||
<Text size="xs" c="gray">{props.cell.getValue().email}</Text>
|
||||
</div>
|
||||
</Flex>)
|
||||
}
|
||||
}),
|
||||
|
||||
columnHelper.accessor("userCount", {
|
||||
header: "User Count",
|
||||
}),
|
||||
|
||||
columnHelper.accessor("status", {
|
||||
header: "Status",
|
||||
cell: (props) => {
|
||||
switch (props.row.original.status) {
|
||||
case "WAITING":
|
||||
return <Badge color="cyan">WAITING</Badge>;
|
||||
break;
|
||||
case "ACCEPTED":
|
||||
return <Badge color="green">ACCEPTED</Badge>;
|
||||
break;
|
||||
case "CANCELLED":
|
||||
return <Badge color="gray">CANCELLED</Badge>;
|
||||
break;
|
||||
case "REJECTED":
|
||||
return <Badge color="red">REJECTED</Badge>;
|
||||
break;
|
||||
}
|
||||
},
|
||||
}),
|
||||
|
||||
columnHelper.display({
|
||||
id: "Actions",
|
||||
header: "Actions",
|
||||
cell: (props) => (
|
||||
<Flex gap="xs">
|
||||
{createActionButtons([
|
||||
{
|
||||
label: "Detail",
|
||||
permission: options.permissions.read,
|
||||
action: () =>
|
||||
options.actions.detail(props.row.original.id),
|
||||
color: "green",
|
||||
icon: <TbEye />,
|
||||
},
|
||||
])}
|
||||
</Flex>
|
||||
),
|
||||
}),
|
||||
];
|
||||
|
||||
return columns;
|
||||
};
|
||||
|
||||
export default createColumns;
|
||||
|
|
@ -1,80 +0,0 @@
|
|||
"use client";
|
||||
import DashboardTable from "@/modules/dashboard/components/DashboardTable";
|
||||
import { Button, Flex, Text } from "@mantine/core";
|
||||
import { getCoreRowModel, useReactTable } from "@tanstack/react-table";
|
||||
import React, { ReactNode, useState } from "react";
|
||||
import { TbPlus } from "react-icons/tb";
|
||||
import createColumns from "./columns";
|
||||
import CrudPermissions from "@/modules/dashboard/types/CrudPermissions";
|
||||
import RequestLink from "../../types/RequestLink";
|
||||
import RequestModal, { ModalProps } from "../../modals/RequestModal";
|
||||
|
||||
interface Props {
|
||||
permissions: Partial<CrudPermissions>;
|
||||
tableData: RequestLink[];
|
||||
}
|
||||
|
||||
const defaultModalProps: ModalProps = {
|
||||
opened: false,
|
||||
title: "",
|
||||
type: "create",
|
||||
detailId: null,
|
||||
};
|
||||
|
||||
export default function RequestTable(props: Props) {
|
||||
const [modalProps, setModalProps] = useState<ModalProps>(defaultModalProps);
|
||||
|
||||
const table = useReactTable({
|
||||
data: props.tableData,
|
||||
columns: createColumns({
|
||||
permissions: props.permissions,
|
||||
actions: {
|
||||
detail: (id) => {
|
||||
openDetailModal(id);
|
||||
},
|
||||
},
|
||||
}),
|
||||
getCoreRowModel: getCoreRowModel(),
|
||||
defaultColumn: {
|
||||
cell: (props) => <Text>{props.getValue() as ReactNode}</Text>,
|
||||
},
|
||||
});
|
||||
|
||||
const openCreateModal = () => {
|
||||
setModalProps({
|
||||
opened: true,
|
||||
title: "Create New Office 365 Link Request",
|
||||
detailId: null,
|
||||
type: "create",
|
||||
});
|
||||
};
|
||||
|
||||
const openDetailModal = (id: string) => {
|
||||
setModalProps({
|
||||
opened: true,
|
||||
title: "Office 365 Link Request Detail",
|
||||
detailId: id,
|
||||
type: "detail",
|
||||
});
|
||||
};
|
||||
|
||||
const closeModal = () => {
|
||||
setModalProps(defaultModalProps);
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<Flex justify="flex-end">
|
||||
{
|
||||
<Button leftSection={<TbPlus />} onClick={openCreateModal}>
|
||||
New Link Request
|
||||
</Button>
|
||||
}
|
||||
</Flex>
|
||||
|
||||
<DashboardTable table={table} />
|
||||
|
||||
<RequestModal {...modalProps} onClose={closeModal} />
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
|
@ -1,96 +0,0 @@
|
|||
import { createColumnHelper } from "@tanstack/react-table";
|
||||
import { Badge, Flex } from "@mantine/core";
|
||||
import { TbEye, TbPencil, TbTrash } from "react-icons/tb";
|
||||
import CrudPermissions from "@/modules/dashboard/types/CrudPermissions";
|
||||
import createActionButtons from "@/modules/dashboard/utils/createActionButton";
|
||||
|
||||
export interface RequestLinkRow {
|
||||
id: string;
|
||||
requestDate: string;
|
||||
userCount: number;
|
||||
status: string;
|
||||
}
|
||||
|
||||
interface ColumnOptions {
|
||||
permissions: Partial<CrudPermissions>;
|
||||
actions: {
|
||||
detail: (id: string) => void;
|
||||
// edit: (id: string) => void;
|
||||
// delete: (id: string) => void;
|
||||
};
|
||||
}
|
||||
|
||||
const createColumns = (options: ColumnOptions) => {
|
||||
const columnHelper = createColumnHelper<RequestLinkRow>();
|
||||
|
||||
const columns = [
|
||||
columnHelper.accessor("id", {
|
||||
id: "sequence",
|
||||
header: "#",
|
||||
cell: (props) => props.row.index + 1,
|
||||
}),
|
||||
|
||||
columnHelper.accessor("requestDate", {
|
||||
header: "Request Date",
|
||||
cell: (props) => {
|
||||
const date = new Date(props.row.original.requestDate);
|
||||
return `${date.toDateString()}; ${date.toLocaleTimeString()}`;
|
||||
},
|
||||
}),
|
||||
|
||||
columnHelper.accessor("userCount", {
|
||||
header: "User Count",
|
||||
}),
|
||||
|
||||
columnHelper.accessor("status", {
|
||||
header: "Status",
|
||||
cell: (props) => {
|
||||
switch (props.row.original.status) {
|
||||
case "WAITING":
|
||||
return <Badge color="cyan">WAITING</Badge>;
|
||||
break;
|
||||
case "ACCEPTED":
|
||||
return <Badge color="green">ACCEPTED</Badge>;
|
||||
break;
|
||||
case "CANCELLED":
|
||||
return <Badge color="gray">CANCELLED</Badge>;
|
||||
break;
|
||||
case "REJECTED":
|
||||
return <Badge color="red">REJECTED</Badge>;
|
||||
break;
|
||||
}
|
||||
},
|
||||
}),
|
||||
|
||||
columnHelper.display({
|
||||
id: "Actions",
|
||||
header: "Actions",
|
||||
cell: (props) => (
|
||||
<Flex gap="xs">
|
||||
{createActionButtons([
|
||||
{
|
||||
label: "Detail",
|
||||
permission: options.permissions.read,
|
||||
action: () =>
|
||||
options.actions.detail(props.row.original.id),
|
||||
color: "green",
|
||||
icon: <TbEye />,
|
||||
},
|
||||
// {
|
||||
// label: "Edit",
|
||||
// permission: options.permissions.update,
|
||||
// // action: () =>
|
||||
// // options.actions.edit(props.row.original.id),
|
||||
// color: "yellow",
|
||||
// icon: <TbPencil />,
|
||||
// },
|
||||
])}
|
||||
</Flex>
|
||||
),
|
||||
}),
|
||||
];
|
||||
|
||||
return columns;
|
||||
};
|
||||
|
||||
export default createColumns;
|
||||
|
|
@ -1,6 +0,0 @@
|
|||
export default interface RequestLink {
|
||||
id: string;
|
||||
requestDate: string,
|
||||
userCount: number,
|
||||
status: string
|
||||
}
|
||||
|
|
@ -1,11 +0,0 @@
|
|||
interface RequestLinkForm {
|
||||
id: string | undefined;
|
||||
numberOfLinks: number;
|
||||
details: {
|
||||
id?: string;
|
||||
email: string;
|
||||
activePeriod: (typeof resellerOffice365Config.activePeriods)[number];
|
||||
endUserQty: number;
|
||||
link: string
|
||||
}[];
|
||||
}
|
||||
|
|
@ -1,14 +0,0 @@
|
|||
import { Office365LinkRequestStatus, Prisma } from "@prisma/client";
|
||||
|
||||
export default interface RequestLinkWithIssuerData {
|
||||
id: string;
|
||||
status: Office365LinkRequestStatus;
|
||||
requestedAt: Date;
|
||||
creator: {
|
||||
id: string;
|
||||
name: string | null;
|
||||
email: string | null;
|
||||
photoProfile: string | null;
|
||||
};
|
||||
userCount: number
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user