214 lines
5.0 KiB
TypeScript
214 lines
5.0 KiB
TypeScript
/* eslint-disable react-hooks/exhaustive-deps */
|
|
import { showNotification } from "@/utils/notifications";
|
|
import {
|
|
Flex,
|
|
Modal,
|
|
Stack,
|
|
Switch,
|
|
TextInput,
|
|
Textarea,
|
|
Button,
|
|
ScrollArea,
|
|
Checkbox,
|
|
Skeleton,
|
|
Fieldset,
|
|
Alert,
|
|
} from "@mantine/core";
|
|
import { useForm, zodResolver } from "@mantine/form";
|
|
import { useRouter } from "next/navigation";
|
|
import React, { useCallback, useEffect, useState } from "react";
|
|
import { TbDeviceFloppy } from "react-icons/tb";
|
|
import permissionFormDataSchema, { PermissionFormData } from "../formSchemas/PermissionFormData";
|
|
import getPermissionById from "../actions/getPermissionById";
|
|
import withServerAction from "@/modules/dashboard/utils/withServerAction";
|
|
import upsertPermission from "../actions/upsertPermission";
|
|
import DashboardError from "@/modules/dashboard/errors/DashboardError";
|
|
|
|
export interface ModalProps {
|
|
title: string;
|
|
readonly?: boolean;
|
|
id?: string;
|
|
opened: boolean;
|
|
onClose?: () => void;
|
|
}
|
|
|
|
/**
|
|
* A component for rendering a modal with a form to create or edit a permission.
|
|
*
|
|
* @param props - The props for the component.
|
|
* @returns The rendered element.
|
|
*/
|
|
export default function FormModal(props: ModalProps) {
|
|
const router = useRouter();
|
|
const [isSubmitting, setSubmitting] = useState(false);
|
|
const [isFetching, setFetching] = useState(false);
|
|
const [errorMessage, setErrorMessage] = useState("");
|
|
|
|
const form = useForm<PermissionFormData>({
|
|
initialValues: {
|
|
code: "",
|
|
description: "",
|
|
id: "",
|
|
isActive: false,
|
|
name: "",
|
|
},
|
|
validate: zodResolver(permissionFormDataSchema),
|
|
validateInputOnChange: false,
|
|
onValuesChange: (values) => {
|
|
console.log(values);
|
|
},
|
|
});
|
|
|
|
/**
|
|
* Fetches permission data by ID and populates the form if the modal is opened and an ID is provided.
|
|
*/
|
|
useEffect(() => {
|
|
if (!props.opened || !props.id) {
|
|
return;
|
|
}
|
|
|
|
setFetching(true);
|
|
getPermissionById(props.id)
|
|
.then((response) => {
|
|
if (response.success) {
|
|
const data = response.data;
|
|
form.setValues({
|
|
code: data.code,
|
|
description: data.description,
|
|
id: data.id,
|
|
isActive: data.isActive,
|
|
name: data.name,
|
|
});
|
|
}
|
|
})
|
|
.catch((e) => {
|
|
//TODO: Handle error
|
|
console.log(e);
|
|
})
|
|
.finally(() => {
|
|
setFetching(false);
|
|
});
|
|
}, [props.opened, props.id]);
|
|
|
|
const closeModal = () => {
|
|
form.reset()
|
|
props.onClose ? props.onClose() : router.replace("?");
|
|
};
|
|
|
|
const handleSubmit = (values: PermissionFormData) => {
|
|
setSubmitting(true);
|
|
withServerAction(upsertPermission, values)
|
|
.then((response) => {
|
|
showNotification(response.message!, "success");
|
|
closeModal();
|
|
})
|
|
.catch((e) => {
|
|
if (e instanceof DashboardError) {
|
|
if (e.errorCode === "INVALID_FORM_DATA") {
|
|
form.setErrors(e.formErrors ?? {});
|
|
} 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`
|
|
);
|
|
}
|
|
})
|
|
.finally(() => {
|
|
setSubmitting(false);
|
|
});
|
|
};
|
|
|
|
return (
|
|
<Modal
|
|
opened={props.opened}
|
|
onClose={closeModal}
|
|
title={props.title}
|
|
scrollAreaComponent={ScrollArea.Autosize}
|
|
size="xl"
|
|
>
|
|
<form onSubmit={form.onSubmit(handleSubmit)}>
|
|
<Stack mt="sm" gap="lg" px="lg">
|
|
{errorMessage && <Alert color="red">{errorMessage}</Alert>}
|
|
{/* ID */}
|
|
{form.values.id ? (
|
|
<TextInput
|
|
label="ID"
|
|
readOnly
|
|
variant="filled"
|
|
{...form.getInputProps("id")}
|
|
/>
|
|
) : (
|
|
<div></div>
|
|
)}
|
|
|
|
{/* Code */}
|
|
<Skeleton visible={isFetching}>
|
|
<TextInput
|
|
data-autofocus
|
|
label="Code"
|
|
readOnly={props.readonly}
|
|
disabled={isSubmitting}
|
|
{...form.getInputProps("code")}
|
|
/>
|
|
</Skeleton>
|
|
|
|
{/* Name */}
|
|
<Skeleton visible={isFetching}>
|
|
<TextInput
|
|
label="Name"
|
|
readOnly={props.readonly}
|
|
disabled={isSubmitting}
|
|
{...form.getInputProps("name")}
|
|
/>
|
|
</Skeleton>
|
|
|
|
{/* Description */}
|
|
<Skeleton visible={isFetching}>
|
|
<Textarea
|
|
label="Description"
|
|
readOnly={props.readonly}
|
|
disabled={isSubmitting}
|
|
{...form.getInputProps("description")}
|
|
/>
|
|
</Skeleton>
|
|
|
|
<Skeleton visible={isFetching}>
|
|
<Checkbox
|
|
label="Active"
|
|
labelPosition="right"
|
|
{...form.getInputProps("isActive", {
|
|
type: "checkbox",
|
|
})}
|
|
/>
|
|
</Skeleton>
|
|
|
|
{/* Buttons */}
|
|
<Flex justify="flex-end" align="center" gap="lg" mt="lg">
|
|
<Button
|
|
variant="outline"
|
|
onClick={closeModal}
|
|
disabled={isSubmitting}
|
|
>
|
|
Close
|
|
</Button>
|
|
{!props.readonly && (
|
|
<Button
|
|
variant="filled"
|
|
leftSection={<TbDeviceFloppy size={20} />}
|
|
type="submit"
|
|
loading={isSubmitting}
|
|
>
|
|
Save
|
|
</Button>
|
|
)}
|
|
</Flex>
|
|
</Stack>
|
|
</form>
|
|
</Modal>
|
|
);
|
|
}
|