Added role actions
This commit is contained in:
parent
85daf8cfe7
commit
77bb120a00
|
|
@ -1,20 +0,0 @@
|
||||||
"use client"
|
|
||||||
import { Button } from '@mantine/core'
|
|
||||||
import Link from 'next/link'
|
|
||||||
import { usePathname, useRouter } from 'next/navigation'
|
|
||||||
import React, { useState } from 'react'
|
|
||||||
import { TbPlus } from 'react-icons/tb'
|
|
||||||
import { CreateModal } from '../../_modals'
|
|
||||||
|
|
||||||
export default function CreateButton() {
|
|
||||||
|
|
||||||
const [isModalOpened, setModalOpened] = useState(false)
|
|
||||||
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<Button leftSection={<TbPlus />} onClick={() => setModalOpened(true)}>New Role</Button>
|
|
||||||
|
|
||||||
<CreateModal opened={isModalOpened} onClose={() => setModalOpened(false)} />
|
|
||||||
</>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
@ -1,25 +0,0 @@
|
||||||
import React from "react";
|
|
||||||
import FormModal from "../FormModal";
|
|
||||||
|
|
||||||
interface Props {
|
|
||||||
opened: boolean
|
|
||||||
onClose?: () => void
|
|
||||||
}
|
|
||||||
|
|
||||||
export default function CreateModal(props: Props) {
|
|
||||||
return (
|
|
||||||
<FormModal
|
|
||||||
title="Create new role"
|
|
||||||
data={{
|
|
||||||
code: "",
|
|
||||||
description: "",
|
|
||||||
id: "",
|
|
||||||
isActive: false,
|
|
||||||
name: "",
|
|
||||||
}}
|
|
||||||
readonly={false}
|
|
||||||
opened={props.opened}
|
|
||||||
onClose={props.onClose}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
@ -1 +0,0 @@
|
||||||
export { default } from "./CreateModal"
|
|
||||||
|
|
@ -0,0 +1,93 @@
|
||||||
|
"use client";
|
||||||
|
import { useRouter } from "next/navigation";
|
||||||
|
import React, { useState } from "react";
|
||||||
|
import {
|
||||||
|
Avatar,
|
||||||
|
Button,
|
||||||
|
Center,
|
||||||
|
Flex,
|
||||||
|
Modal,
|
||||||
|
ScrollArea,
|
||||||
|
Text,
|
||||||
|
Stack,
|
||||||
|
TextInput,
|
||||||
|
Title,
|
||||||
|
} from "@mantine/core";
|
||||||
|
import { showNotification } from "@/utils/notifications";
|
||||||
|
import deleteRole from "@/features/dashboard/roles/actions/deleteRole";
|
||||||
|
|
||||||
|
export interface DeleteModalProps {
|
||||||
|
data?: {
|
||||||
|
id: string,
|
||||||
|
name: string,
|
||||||
|
};
|
||||||
|
onClose: () => void
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function DeleteModal(props: DeleteModalProps) {
|
||||||
|
const router = useRouter();
|
||||||
|
|
||||||
|
const [isSubmitting, setSubmitting] = useState(false);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Closes the modal. It won't close if a submission is in progress.
|
||||||
|
*/
|
||||||
|
const closeModal = () => {
|
||||||
|
if (isSubmitting) return;
|
||||||
|
props.onClose()
|
||||||
|
};
|
||||||
|
|
||||||
|
const confirmAction = () => {
|
||||||
|
if (!props.data?.id) return;
|
||||||
|
setSubmitting(true)
|
||||||
|
deleteRole(props.data.id)
|
||||||
|
.then((response) => {
|
||||||
|
if (response.success){
|
||||||
|
showNotification(response.message);
|
||||||
|
setSubmitting(false)
|
||||||
|
props.onClose()
|
||||||
|
return;
|
||||||
|
} else {
|
||||||
|
showNotification(response.message, "error")
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch(() => {
|
||||||
|
//TODO: Handle Error
|
||||||
|
})
|
||||||
|
.finally(() => {
|
||||||
|
setSubmitting(false)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Modal opened={!!props.data} onClose={closeModal} title={`Delete confirmation`}>
|
||||||
|
<Text size="sm">
|
||||||
|
Are you sure you want to delete role{" "}
|
||||||
|
<Text span fw={700}>
|
||||||
|
{props.data?.name}
|
||||||
|
</Text>
|
||||||
|
? This action is irreversible.
|
||||||
|
</Text>
|
||||||
|
{/* Buttons */}
|
||||||
|
<Flex justify="flex-end" align="center" gap="lg" mt="lg">
|
||||||
|
<Button
|
||||||
|
variant="outline"
|
||||||
|
onClick={closeModal}
|
||||||
|
disabled={isSubmitting}
|
||||||
|
>
|
||||||
|
Cancel
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
|
variant="subtle"
|
||||||
|
// leftSection={<TbDeviceFloppy size={20} />}
|
||||||
|
type="submit"
|
||||||
|
color="red"
|
||||||
|
loading={isSubmitting}
|
||||||
|
onClick={confirmAction}
|
||||||
|
>
|
||||||
|
Delete Role
|
||||||
|
</Button>
|
||||||
|
</Flex>
|
||||||
|
</Modal>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1 @@
|
||||||
|
export { default } from "./DeleteModal";
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
"use client";
|
/* eslint-disable react-hooks/exhaustive-deps */
|
||||||
|
import getRoleById from "@/features/dashboard/roles/actions/getRoleById";
|
||||||
import upsertRole from "@/features/dashboard/roles/actions/upsertRole";
|
import upsertRole from "@/features/dashboard/roles/actions/upsertRole";
|
||||||
import roleFormDataSchema, {
|
import roleFormDataSchema, {
|
||||||
RoleFormData,
|
RoleFormData,
|
||||||
|
|
@ -14,59 +15,101 @@ import {
|
||||||
Button,
|
Button,
|
||||||
ScrollArea,
|
ScrollArea,
|
||||||
Checkbox,
|
Checkbox,
|
||||||
|
Skeleton,
|
||||||
|
Fieldset,
|
||||||
} from "@mantine/core";
|
} from "@mantine/core";
|
||||||
import { useForm, zodResolver } from "@mantine/form";
|
import { useForm, zodResolver } from "@mantine/form";
|
||||||
import { notifications } from "@mantine/notifications";
|
|
||||||
import { useRouter } from "next/navigation";
|
import { useRouter } from "next/navigation";
|
||||||
import React, { useState } from "react";
|
import React, { useCallback, useEffect, useState } from "react";
|
||||||
import { TbDeviceFloppy } from "react-icons/tb";
|
import { TbDeviceFloppy } from "react-icons/tb";
|
||||||
|
|
||||||
interface Props {
|
export interface ModalProps {
|
||||||
title: string;
|
title: string;
|
||||||
readonly?: boolean;
|
readonly?: boolean;
|
||||||
data: RoleFormData;
|
id?: string;
|
||||||
opened: boolean;
|
opened: boolean;
|
||||||
onClose?: () => void;
|
onClose?: () => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function FormModal(props: Props) {
|
/**
|
||||||
|
* A component for rendering a modal with a form to create or edit a role.
|
||||||
|
*
|
||||||
|
* @param props - The props for the component.
|
||||||
|
* @returns The rendered element.
|
||||||
|
*/
|
||||||
|
export default function FormModal(props: ModalProps) {
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
|
const [isSubmitting, setSubmitting] = useState(false);
|
||||||
|
const [isFetching, setFetching] = useState(false);
|
||||||
|
|
||||||
const [isSubmitting, setSubmitting] = useState(false);
|
const form = useForm<RoleFormData>({
|
||||||
const [value, setValue] = useState("");
|
initialValues: {
|
||||||
|
code: "",
|
||||||
|
description: "",
|
||||||
|
id: "",
|
||||||
|
isActive: false,
|
||||||
|
name: "",
|
||||||
|
},
|
||||||
|
validate: zodResolver(roleFormDataSchema),
|
||||||
|
validateInputOnChange: false,
|
||||||
|
onValuesChange: (values) => {
|
||||||
|
console.log(values);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
const form = useForm<RoleFormData>({
|
/**
|
||||||
initialValues: props.data,
|
* Fetches role data by ID and populates the form if the modal is opened and an ID is provided.
|
||||||
validate: zodResolver(roleFormDataSchema),
|
*/
|
||||||
validateInputOnChange: false,
|
useEffect(() => {
|
||||||
onValuesChange: (values) => {
|
if (!props.opened || !props.id) {
|
||||||
console.log(values);
|
return;
|
||||||
},
|
}
|
||||||
});
|
|
||||||
|
setFetching(true);
|
||||||
|
getRoleById(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 = () => {
|
const closeModal = () => {
|
||||||
form.reset();
|
|
||||||
props.onClose ? props.onClose() : router.replace("?");
|
props.onClose ? props.onClose() : router.replace("?");
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleSubmit = (values: RoleFormData) => {
|
const handleSubmit = (values: RoleFormData) => {
|
||||||
upsertRole(values)
|
upsertRole(values)
|
||||||
.then((response) => {
|
.then((response) => {
|
||||||
if (response.success){
|
if (response.success) {
|
||||||
showNotification(response.message,"success");
|
showNotification(response.message, "success");
|
||||||
return closeModal()
|
return closeModal();
|
||||||
} else {
|
} else {
|
||||||
form.setErrors(response.errors ?? {});
|
form.setErrors(response.errors ?? {});
|
||||||
if (!response.errors){
|
if (!response.errors) {
|
||||||
showNotification(response.message, "error")
|
showNotification(response.message, "error");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.catch(e =>{
|
.catch((e) => {
|
||||||
//TODO: Handle Error
|
//TODO: Handle Error
|
||||||
console.log(e)
|
console.log(e);
|
||||||
})
|
});
|
||||||
}
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Modal
|
<Modal
|
||||||
|
|
@ -74,11 +117,12 @@ export default function FormModal(props: Props) {
|
||||||
onClose={closeModal}
|
onClose={closeModal}
|
||||||
title={props.title}
|
title={props.title}
|
||||||
scrollAreaComponent={ScrollArea.Autosize}
|
scrollAreaComponent={ScrollArea.Autosize}
|
||||||
|
size="xl"
|
||||||
>
|
>
|
||||||
<form onSubmit={form.onSubmit(handleSubmit)}>
|
<form onSubmit={form.onSubmit(handleSubmit)}>
|
||||||
<Stack mt="sm" gap="lg" px="lg">
|
<Stack mt="sm" gap="lg" px="lg">
|
||||||
{/* ID */}
|
{/* ID */}
|
||||||
{props.data.id ? (
|
{form.values.id ? (
|
||||||
<TextInput
|
<TextInput
|
||||||
label="ID"
|
label="ID"
|
||||||
readOnly
|
readOnly
|
||||||
|
|
@ -90,37 +134,45 @@ export default function FormModal(props: Props) {
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{/* Code */}
|
{/* Code */}
|
||||||
<TextInput
|
<Skeleton visible={isFetching}>
|
||||||
data-autofocus
|
<TextInput
|
||||||
label="Code"
|
data-autofocus
|
||||||
readOnly={props.readonly}
|
label="Code"
|
||||||
disabled={isSubmitting}
|
readOnly={props.readonly}
|
||||||
{...form.getInputProps("code")}
|
disabled={isSubmitting}
|
||||||
/>
|
{...form.getInputProps("code")}
|
||||||
|
/>
|
||||||
|
</Skeleton>
|
||||||
|
|
||||||
{/* Name */}
|
{/* Name */}
|
||||||
<TextInput
|
<Skeleton visible={isFetching}>
|
||||||
label="Name"
|
<TextInput
|
||||||
readOnly={props.readonly}
|
label="Name"
|
||||||
disabled={isSubmitting}
|
readOnly={props.readonly}
|
||||||
{...form.getInputProps("name")}
|
disabled={isSubmitting}
|
||||||
/>
|
{...form.getInputProps("name")}
|
||||||
|
/>
|
||||||
|
</Skeleton>
|
||||||
|
|
||||||
{/* Description */}
|
{/* Description */}
|
||||||
<Textarea
|
<Skeleton visible={isFetching}>
|
||||||
label="Description"
|
<Textarea
|
||||||
readOnly={props.readonly}
|
label="Description"
|
||||||
disabled={isSubmitting}
|
readOnly={props.readonly}
|
||||||
{...form.getInputProps("description")}
|
disabled={isSubmitting}
|
||||||
/>
|
{...form.getInputProps("description")}
|
||||||
|
/>
|
||||||
|
</Skeleton>
|
||||||
|
|
||||||
<Checkbox
|
<Skeleton visible={isFetching}>
|
||||||
label="Active"
|
<Checkbox
|
||||||
labelPosition="right"
|
label="Active"
|
||||||
{...form.getInputProps("isActive", {
|
labelPosition="right"
|
||||||
type: "checkbox",
|
{...form.getInputProps("isActive", {
|
||||||
})}
|
type: "checkbox",
|
||||||
/>
|
})}
|
||||||
|
/>
|
||||||
|
</Skeleton>
|
||||||
|
|
||||||
{/* Buttons */}
|
{/* Buttons */}
|
||||||
<Flex justify="flex-end" align="center" gap="lg" mt="lg">
|
<Flex justify="flex-end" align="center" gap="lg" mt="lg">
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,4 @@
|
||||||
import CreateModal from "./CreateModal";
|
import FormModal from "./FormModal";
|
||||||
|
import DeleteModal from "./DeleteModal";
|
||||||
|
|
||||||
export {
|
export { DeleteModal, FormModal };
|
||||||
CreateModal
|
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -1,25 +1,48 @@
|
||||||
"use client";
|
"use client";
|
||||||
import { Table, Text } from "@mantine/core";
|
import { Table, Text, Flex, Button, Center } from "@mantine/core";
|
||||||
import {
|
import {
|
||||||
flexRender,
|
flexRender,
|
||||||
getCoreRowModel,
|
getCoreRowModel,
|
||||||
useReactTable,
|
useReactTable,
|
||||||
} from "@tanstack/react-table";
|
} from "@tanstack/react-table";
|
||||||
import React from "react";
|
import React, { useState } from "react";
|
||||||
import CrudPermissions from "@/features/auth/types/CrudPermissions";
|
import CrudPermissions from "@/features/auth/types/CrudPermissions";
|
||||||
import getRoles from "@/features/dashboard/roles/data/getRoles";
|
import getRoles from "@/features/dashboard/roles/data/getRoles";
|
||||||
import createColumns from "./columns";
|
import createColumns from "./columns";
|
||||||
|
import FormModal from "../../_modals/FormModal";
|
||||||
|
import { ModalProps } from "../../_modals/FormModal/FormModal";
|
||||||
|
import { TbPlus } from "react-icons/tb";
|
||||||
|
import { RoleFormData } from "@/features/dashboard/roles/formSchemas/RoleFormData";
|
||||||
|
import { string } from "zod";
|
||||||
|
import { DeleteModal } from "../../_modals";
|
||||||
|
import { DeleteModalProps } from "../../_modals/DeleteModal/DeleteModal";
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
permissions: Partial<CrudPermissions>,
|
permissions: Partial<CrudPermissions>;
|
||||||
roles: Awaited<ReturnType<typeof getRoles>>
|
roles: Awaited<ReturnType<typeof getRoles>>;
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function RolesTable(props: Props) {
|
export default function RolesTable(props: Props) {
|
||||||
|
const [modalProps, setModalProps] = useState<ModalProps>({
|
||||||
|
opened: false,
|
||||||
|
title: "",
|
||||||
|
});
|
||||||
|
|
||||||
|
const [deleteModalProps, setDeleteModalProps] = useState<
|
||||||
|
Omit<DeleteModalProps, "onClose">
|
||||||
|
>({
|
||||||
|
data: undefined,
|
||||||
|
});
|
||||||
|
|
||||||
const table = useReactTable({
|
const table = useReactTable({
|
||||||
data: props.roles,
|
data: props.roles,
|
||||||
columns: createColumns({
|
columns: createColumns({
|
||||||
permissions: props.permissions
|
permissions: props.permissions,
|
||||||
|
actions: {
|
||||||
|
detail: (id: string) => openFormModal("detail", id),
|
||||||
|
edit: (id: string) => openFormModal("edit", id),
|
||||||
|
delete: (id: string, name: string) => openDeleteModal(id, name),
|
||||||
|
},
|
||||||
}),
|
}),
|
||||||
getCoreRowModel: getCoreRowModel(),
|
getCoreRowModel: getCoreRowModel(),
|
||||||
defaultColumn: {
|
defaultColumn: {
|
||||||
|
|
@ -27,54 +50,130 @@ export default function RolesTable(props: Props) {
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
// TODO: Add view when data is empty
|
const openFormModal = (type: "create" | "edit" | "detail", id?: string) => {
|
||||||
|
const openCreateModal = () => {
|
||||||
|
setModalProps({
|
||||||
|
id,
|
||||||
|
opened: true,
|
||||||
|
title: "Create new role",
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const openDetailModal = () => {
|
||||||
|
setModalProps({
|
||||||
|
id,
|
||||||
|
opened: true,
|
||||||
|
title: "Role detail",
|
||||||
|
readonly: true,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const openEditModal = () => {
|
||||||
|
setModalProps({
|
||||||
|
id,
|
||||||
|
opened: true,
|
||||||
|
title: "Edit role",
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
type === "create"
|
||||||
|
? openCreateModal()
|
||||||
|
: type === "detail"
|
||||||
|
? openDetailModal()
|
||||||
|
: openEditModal();
|
||||||
|
};
|
||||||
|
|
||||||
|
const openDeleteModal = (id: string, name: string) => {
|
||||||
|
setDeleteModalProps({
|
||||||
|
data: {
|
||||||
|
id,
|
||||||
|
name,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const closeModal = () => {
|
||||||
|
setModalProps({
|
||||||
|
id: "",
|
||||||
|
opened: false,
|
||||||
|
title: "",
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
// TODO: Add view when data is empty
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Table verticalSpacing="xs" horizontalSpacing="xs">
|
<>
|
||||||
{/* Thead */}
|
<Flex justify="flex-end">
|
||||||
<Table.Thead>
|
{props.permissions.create && (
|
||||||
{table.getHeaderGroups().map((headerGroup) => (
|
<Button
|
||||||
<Table.Tr key={headerGroup.id}>
|
leftSection={<TbPlus />}
|
||||||
{headerGroup.headers.map((header) => (
|
onClick={() => openFormModal("create")}
|
||||||
<Table.Th
|
>
|
||||||
key={header.id}
|
New Role
|
||||||
style={{
|
</Button>
|
||||||
maxWidth: `${header.column.columnDef.maxSize}px`,
|
)}
|
||||||
width: `${header.getSize()}`,
|
</Flex>
|
||||||
}}
|
<Table verticalSpacing="xs" horizontalSpacing="xs">
|
||||||
>
|
{/* Thead */}
|
||||||
{header.isPlaceholder
|
<Table.Thead>
|
||||||
? null
|
{table.getHeaderGroups().map((headerGroup) => (
|
||||||
: flexRender(
|
<Table.Tr key={headerGroup.id}>
|
||||||
header.column.columnDef.header,
|
{headerGroup.headers.map((header) => (
|
||||||
header.getContext()
|
<Table.Th
|
||||||
)}
|
key={header.id}
|
||||||
</Table.Th>
|
style={{
|
||||||
))}
|
maxWidth: `${header.column.columnDef.maxSize}px`,
|
||||||
</Table.Tr>
|
width: `${header.getSize()}`,
|
||||||
))}
|
}}
|
||||||
</Table.Thead>
|
>
|
||||||
|
{header.isPlaceholder
|
||||||
|
? null
|
||||||
|
: flexRender(
|
||||||
|
header.column.columnDef.header,
|
||||||
|
header.getContext()
|
||||||
|
)}
|
||||||
|
</Table.Th>
|
||||||
|
))}
|
||||||
|
</Table.Tr>
|
||||||
|
))}
|
||||||
|
</Table.Thead>
|
||||||
|
|
||||||
{/* Tbody */}
|
{/* Tbody */}
|
||||||
<Table.Tbody>
|
<Table.Tbody>
|
||||||
{table.getRowModel().rows.map((row) => (
|
{table.getRowModel().rows.length > 0 ? (
|
||||||
<Table.Tr key={row.id}>
|
table.getRowModel().rows.map((row) => (
|
||||||
{row.getVisibleCells().map((cell) => (
|
<Table.Tr key={row.id}>
|
||||||
<Table.Td
|
{row.getVisibleCells().map((cell) => (
|
||||||
key={cell.id}
|
<Table.Td
|
||||||
style={{
|
key={cell.id}
|
||||||
maxWidth: `${cell.column.columnDef.maxSize}px`,
|
style={{
|
||||||
}}
|
maxWidth: `${cell.column.columnDef.maxSize}px`,
|
||||||
>
|
}}
|
||||||
{flexRender(
|
>
|
||||||
cell.column.columnDef.cell,
|
{flexRender(
|
||||||
cell.getContext()
|
cell.column.columnDef.cell,
|
||||||
)}
|
cell.getContext()
|
||||||
|
)}
|
||||||
|
</Table.Td>
|
||||||
|
))}
|
||||||
|
</Table.Tr>
|
||||||
|
))
|
||||||
|
) : (
|
||||||
|
<Table.Tr>
|
||||||
|
<Table.Td colSpan={table.getAllColumns().length}>
|
||||||
|
<Center>- No Data -</Center>
|
||||||
</Table.Td>
|
</Table.Td>
|
||||||
))}
|
</Table.Tr>
|
||||||
</Table.Tr>
|
)}
|
||||||
))}
|
</Table.Tbody>
|
||||||
</Table.Tbody>
|
</Table>
|
||||||
</Table>
|
|
||||||
|
<FormModal {...modalProps} onClose={closeModal} />
|
||||||
|
<DeleteModal
|
||||||
|
{...deleteModalProps}
|
||||||
|
onClose={() => setDeleteModalProps({})}
|
||||||
|
/>
|
||||||
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
import CrudPermissions from "@/features/auth/types/CrudPermissions";
|
import CrudPermissions from "@/features/auth/types/CrudPermissions";
|
||||||
|
import { RoleFormData } from "@/features/dashboard/roles/formSchemas/RoleFormData";
|
||||||
import createActionButtons from "@/features/dashboard/utils/createActionButtons";
|
import createActionButtons from "@/features/dashboard/utils/createActionButtons";
|
||||||
import { Badge, Flex, Tooltip, ActionIcon } from "@mantine/core";
|
import { Badge, Flex, Tooltip, ActionIcon } from "@mantine/core";
|
||||||
import { createColumnHelper } from "@tanstack/react-table";
|
import { createColumnHelper } from "@tanstack/react-table";
|
||||||
|
|
@ -17,10 +18,10 @@ export interface RoleRow {
|
||||||
|
|
||||||
interface ColumnOptions {
|
interface ColumnOptions {
|
||||||
permissions: Partial<CrudPermissions>;
|
permissions: Partial<CrudPermissions>;
|
||||||
actions?: {
|
actions: {
|
||||||
detail?: () => void;
|
detail: (id: string) => void;
|
||||||
edit?: () => void;
|
edit: (id: string) => void;
|
||||||
delete?: () => void;
|
delete: (id: string, name: string) => void;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -67,28 +68,28 @@ const createColumns = (options: ColumnOptions) => {
|
||||||
meta: {
|
meta: {
|
||||||
className: "w-fit",
|
className: "w-fit",
|
||||||
},
|
},
|
||||||
cell: () => (
|
cell: (props) => (
|
||||||
<Flex gap="xs">
|
<Flex gap="xs">
|
||||||
{
|
{
|
||||||
createActionButtons([
|
createActionButtons([
|
||||||
{
|
{
|
||||||
label: "Detail",
|
label: "Detail",
|
||||||
permission: options.permissions.read,
|
permission: options.permissions.read,
|
||||||
action: options.actions?.detail,
|
action: () => options.actions.detail(props.row.original.id),
|
||||||
color: "green",
|
color: "green",
|
||||||
icon: <TbEye />
|
icon: <TbEye />
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: "Edit",
|
label: "Edit",
|
||||||
permission: options.permissions.update,
|
permission: options.permissions.update,
|
||||||
action: options.actions?.edit,
|
action: () => options.actions.edit(props.row.original.id),
|
||||||
color: "yellow",
|
color: "yellow",
|
||||||
icon: <TbPencil />
|
icon: <TbPencil />
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: "Delete",
|
label: "Delete",
|
||||||
permission: options.permissions.delete,
|
permission: options.permissions.delete,
|
||||||
action: options.actions?.delete,
|
action: () => options.actions.delete(props.row.original.id, props.row.original.name),
|
||||||
color: "red",
|
color: "red",
|
||||||
icon: <TbTrash />
|
icon: <TbTrash />
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,45 +1,38 @@
|
||||||
import { Button, Card, Flex, Stack, Title } from "@mantine/core";
|
import { Card, Stack, Title } from "@mantine/core";
|
||||||
import { Metadata } from "next";
|
import { Metadata } from "next";
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import RolesTable from "./_tables/RolesTable/RolesTable";
|
import RolesTable from "./_tables/RolesTable/RolesTable";
|
||||||
import { TbPlus } from "react-icons/tb";
|
|
||||||
import checkPermission from "@/features/auth/tools/checkPermission";
|
|
||||||
import { unauthorized } from "@/BaseError";
|
|
||||||
import Link from "next/link";
|
|
||||||
import { CreateModal } from "./_modals";
|
|
||||||
import FormModal from "./_modals/FormModal";
|
|
||||||
import CreateButton from "./_components/CreateButton/CreateButton";
|
|
||||||
import getRoles from "@/features/dashboard/roles/data/getRoles";
|
import getRoles from "@/features/dashboard/roles/data/getRoles";
|
||||||
import checkMultiplePermissions from "@/features/auth/tools/checkMultiplePermissions";
|
import checkMultiplePermissions from "@/features/auth/tools/checkMultiplePermissions";
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
searchParams: {
|
searchParams: {
|
||||||
detail?: string,
|
detail?: string;
|
||||||
edit?: string,
|
edit?: string;
|
||||||
delete?: string,
|
delete?: string;
|
||||||
create?: string,
|
create?: string;
|
||||||
}
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export default async function RolesPage({searchParams}: Props) {
|
export const metadata: Metadata = {
|
||||||
|
title: "Roles - Dashboard",
|
||||||
|
};
|
||||||
|
|
||||||
|
export default async function RolesPage({ searchParams }: Props) {
|
||||||
const permissions = await checkMultiplePermissions({
|
const permissions = await checkMultiplePermissions({
|
||||||
create: "role.create",
|
create: "role.create",
|
||||||
readAll: "role.readAll",
|
readAll: "role.readAll",
|
||||||
read: 'role.read',
|
read: "role.read",
|
||||||
update: 'role.update',
|
update: "role.update",
|
||||||
delete: 'role.delete'
|
delete: "role.delete",
|
||||||
})
|
});
|
||||||
|
|
||||||
const roles = await getRoles()
|
const roles = await getRoles();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Stack>
|
<Stack>
|
||||||
<Title order={1}>Roles</Title>
|
<Title order={1}>Roles</Title>
|
||||||
<Card>
|
<Card>
|
||||||
<Flex justify="flex-end">
|
|
||||||
{ permissions.create && <CreateButton />}
|
|
||||||
</Flex>
|
|
||||||
<RolesTable permissions={permissions} roles={roles} />
|
<RolesTable permissions={permissions} roles={roles} />
|
||||||
</Card>
|
</Card>
|
||||||
</Stack>
|
</Stack>
|
||||||
|
|
|
||||||
26
src/features/dashboard/roles/actions/deleteRole.ts
Normal file
26
src/features/dashboard/roles/actions/deleteRole.ts
Normal file
|
|
@ -0,0 +1,26 @@
|
||||||
|
"use server";
|
||||||
|
|
||||||
|
import { unauthorized } from "@/BaseError";
|
||||||
|
import prisma from "@/db";
|
||||||
|
import checkPermission from "@/features/auth/tools/checkPermission";
|
||||||
|
|
||||||
|
export default async function deleteRole(id: string) {
|
||||||
|
if (!(await checkPermission("role.delete"))) return unauthorized();
|
||||||
|
|
||||||
|
try {
|
||||||
|
const role = await prisma.role.delete({
|
||||||
|
where: { id },
|
||||||
|
});
|
||||||
|
|
||||||
|
return {
|
||||||
|
success: true,
|
||||||
|
message: "The role has been deleted successfully",
|
||||||
|
} as const;
|
||||||
|
} catch (e) {
|
||||||
|
//TODO: Handle error
|
||||||
|
return {
|
||||||
|
success: false,
|
||||||
|
message: "Unable to delete the role",
|
||||||
|
} as const;
|
||||||
|
}
|
||||||
|
}
|
||||||
40
src/features/dashboard/roles/actions/getRoleById.ts
Normal file
40
src/features/dashboard/roles/actions/getRoleById.ts
Normal file
|
|
@ -0,0 +1,40 @@
|
||||||
|
"use server";
|
||||||
|
|
||||||
|
import { unauthorized } from "@/BaseError";
|
||||||
|
import prisma from "@/db";
|
||||||
|
import checkPermission from "@/features/auth/tools/checkPermission";
|
||||||
|
|
||||||
|
export default async function getRoleById(id: string) {
|
||||||
|
if (!(await checkPermission("role.read"))) unauthorized();
|
||||||
|
|
||||||
|
const role = await prisma.role.findFirst({
|
||||||
|
where: { id },
|
||||||
|
select: {
|
||||||
|
code: true,
|
||||||
|
description: true,
|
||||||
|
id: true,
|
||||||
|
isActive: true,
|
||||||
|
name: true,
|
||||||
|
permissions: {
|
||||||
|
select: {
|
||||||
|
id: true,
|
||||||
|
code: true,
|
||||||
|
name: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!role) {
|
||||||
|
return {
|
||||||
|
success: false,
|
||||||
|
message: "Role not found",
|
||||||
|
} as const;
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
success: true,
|
||||||
|
message: "Role fetched successfully",
|
||||||
|
data: role,
|
||||||
|
} as const;
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue
Block a user