Added UI for request
This commit is contained in:
parent
b86dd2a821
commit
5530b07769
15
.vscode/launch.json
vendored
Normal file
15
.vscode/launch.json
vendored
Normal file
|
|
@ -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}"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
@ -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",
|
description: "Has full access to the system and can manage all features and settings",
|
||||||
isActive: true,
|
isActive: true,
|
||||||
name: "Super Admin"
|
name: "Super Admin"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
code: "reseller-office-365",
|
||||||
|
description: "Has ability to make request Office 365 links",
|
||||||
|
isActive: true,
|
||||||
|
name: "Reseller Office 365"
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
|
|
||||||
|
|
|
||||||
28
src/app/dashboard/reseller-office-365/request/page.tsx
Normal file
28
src/app/dashboard/reseller-office-365/request/page.tsx
Normal file
|
|
@ -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 (
|
||||||
|
<Stack>
|
||||||
|
<Title order={1}>Permohonan Link Office 365</Title>
|
||||||
|
<Card>
|
||||||
|
<RequestTable permissions={permissions} tableData={[]} />
|
||||||
|
</Card>
|
||||||
|
</Stack>
|
||||||
|
);
|
||||||
|
}
|
||||||
5
src/modules/resellerOffice365/config.ts
Normal file
5
src/modules/resellerOffice365/config.ts
Normal file
|
|
@ -0,0 +1,5 @@
|
||||||
|
const resellerOffice365Config = {
|
||||||
|
activePeriods: ["1 Month", "1 Year", "2 Years"],
|
||||||
|
} as const;
|
||||||
|
|
||||||
|
export default resellerOffice365Config;
|
||||||
194
src/modules/resellerOffice365/modals/RequestModal.tsx
Normal file
194
src/modules/resellerOffice365/modals/RequestModal.tsx
Normal file
|
|
@ -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<FormType>({
|
||||||
|
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 (
|
||||||
|
<Modal
|
||||||
|
size="sm"
|
||||||
|
opened={props.opened}
|
||||||
|
title={props.title}
|
||||||
|
onClose={closeModal}
|
||||||
|
scrollAreaComponent={ScrollArea.Autosize}
|
||||||
|
>
|
||||||
|
<form onSubmit={form.onSubmit(handleSubmit)}>
|
||||||
|
<Stack gap="md">
|
||||||
|
<NumberInput
|
||||||
|
label="Please input the number of links you request"
|
||||||
|
min={1}
|
||||||
|
max={3}
|
||||||
|
allowDecimal={false}
|
||||||
|
clampBehavior="strict"
|
||||||
|
leftSection={<TbLink />}
|
||||||
|
disabled={disableChange}
|
||||||
|
{...form.getInputProps("numberOfLinks")}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<Divider
|
||||||
|
label="End User Information"
|
||||||
|
labelPosition="center"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<Stack>
|
||||||
|
{form.values.details.map((item, i) => (
|
||||||
|
<Fieldset key={i} legend={`Information ${i + 1}`}>
|
||||||
|
<TextInput
|
||||||
|
leftSection={<TbAt />}
|
||||||
|
label="Email"
|
||||||
|
disabled={disableChange}
|
||||||
|
{...form.getInputProps(
|
||||||
|
`details.${i}.email`
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
<Flex gap="md">
|
||||||
|
<Select
|
||||||
|
data={
|
||||||
|
resellerOffice365Config.activePeriods
|
||||||
|
}
|
||||||
|
label="Active Period"
|
||||||
|
disabled={disableChange}
|
||||||
|
leftSection={<TbCalendarTime />}
|
||||||
|
{...form.getInputProps(
|
||||||
|
`details.${i}.activePeriod`
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
<NumberInput
|
||||||
|
label="End User Quantity"
|
||||||
|
leftSection={<TbUsers />}
|
||||||
|
min={1}
|
||||||
|
max={5}
|
||||||
|
disabled={disableChange}
|
||||||
|
allowDecimal={false}
|
||||||
|
clampBehavior="strict"
|
||||||
|
{...form.getInputProps(
|
||||||
|
`details.${i}.endUserQty`
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
</Flex>
|
||||||
|
</Fieldset>
|
||||||
|
))}
|
||||||
|
</Stack>
|
||||||
|
|
||||||
|
{/* Buttons */}
|
||||||
|
<Flex justify="flex-end" align="center" gap="lg" mt="lg">
|
||||||
|
<Button
|
||||||
|
variant="outline"
|
||||||
|
onClick={closeModal}
|
||||||
|
disabled={["submitting"].includes(formState)}
|
||||||
|
>
|
||||||
|
Close
|
||||||
|
</Button>
|
||||||
|
{(!props.readonly || formState === "waiting") && (
|
||||||
|
<Button
|
||||||
|
variant="filled"
|
||||||
|
leftSection={<TbDeviceFloppy size={20} />}
|
||||||
|
type="submit"
|
||||||
|
loading={["submitting", "waiting"].includes(
|
||||||
|
formState
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
Make Request
|
||||||
|
</Button>
|
||||||
|
)}
|
||||||
|
</Flex>
|
||||||
|
</Stack>
|
||||||
|
</form>
|
||||||
|
</Modal>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,76 @@
|
||||||
|
"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[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function RequestTable(props: Props) {
|
||||||
|
// const [modalProps, setModalProps] = useState<ModalProps>({
|
||||||
|
// opened: false,
|
||||||
|
// title: "",
|
||||||
|
// readonly: false,
|
||||||
|
// });
|
||||||
|
const [openModal, setOpenModal] = useState(false);
|
||||||
|
|
||||||
|
const table = useReactTable({
|
||||||
|
data: props.tableData,
|
||||||
|
columns: createColumns({
|
||||||
|
permissions: props.permissions,
|
||||||
|
actions: {
|
||||||
|
detail: (id) => {console.log(id)}
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
getCoreRowModel: getCoreRowModel(),
|
||||||
|
defaultColumn: {
|
||||||
|
cell: (props) => <Text>{props.getValue() as ReactNode}</Text>,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const openFormModall = () => {
|
||||||
|
// setModalProps({
|
||||||
|
// opened: true,
|
||||||
|
// title: "Request new link",
|
||||||
|
// readonly: false,
|
||||||
|
// });
|
||||||
|
// console.log('hai')
|
||||||
|
setOpenModal(true);
|
||||||
|
};
|
||||||
|
|
||||||
|
const closeModal = () => {
|
||||||
|
// setModalProps({
|
||||||
|
// opened: false,
|
||||||
|
// title: "",
|
||||||
|
// readonly: false,
|
||||||
|
// });
|
||||||
|
setOpenModal(false)
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<Flex justify="flex-end">
|
||||||
|
{
|
||||||
|
<Button
|
||||||
|
leftSection={<TbPlus />}
|
||||||
|
onClick={() => openFormModall()}
|
||||||
|
>
|
||||||
|
New Link Request
|
||||||
|
</Button>
|
||||||
|
}
|
||||||
|
</Flex>
|
||||||
|
|
||||||
|
<DashboardTable table={table} />
|
||||||
|
|
||||||
|
<RequestModal opened={openModal} readonly={false} title="Create new Link Request" onClose={closeModal} />
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,86 @@
|
||||||
|
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: Date,
|
||||||
|
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",
|
||||||
|
}),
|
||||||
|
|
||||||
|
columnHelper.accessor("userCount", {
|
||||||
|
header: "User Count",
|
||||||
|
}),
|
||||||
|
|
||||||
|
columnHelper.accessor("status", {
|
||||||
|
header: "Status",
|
||||||
|
}),
|
||||||
|
|
||||||
|
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 />,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "Delete",
|
||||||
|
permission: options.permissions.delete,
|
||||||
|
// action: () =>
|
||||||
|
// options.actions.delete(
|
||||||
|
// props.row.original.id
|
||||||
|
// ),
|
||||||
|
color: "red",
|
||||||
|
icon: <TbTrash />,
|
||||||
|
},
|
||||||
|
])}
|
||||||
|
</Flex>
|
||||||
|
),
|
||||||
|
}),
|
||||||
|
];
|
||||||
|
|
||||||
|
return columns;
|
||||||
|
};
|
||||||
|
|
||||||
|
export default createColumns;
|
||||||
6
src/modules/resellerOffice365/types/RequestLink.d.ts
vendored
Normal file
6
src/modules/resellerOffice365/types/RequestLink.d.ts
vendored
Normal file
|
|
@ -0,0 +1,6 @@
|
||||||
|
export default interface RequestLink {
|
||||||
|
id: string;
|
||||||
|
requestDate: Date,
|
||||||
|
userCount: number,
|
||||||
|
status: string
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue
Block a user