From 854b1f9b34fec0323f383dc74c4d27f0c3c603ec Mon Sep 17 00:00:00 2001 From: sianida26 Date: Fri, 29 Mar 2024 23:32:12 +0700 Subject: [PATCH] Change permission into action-service pattern --- src/app/dashboard/permissions/page.tsx | 18 +--- .../permission/actions/deletePermission.ts | 28 ------- .../actions/deletePermissionAction.ts | 26 ++++++ .../permission/actions/getAllPermissions.ts | 57 ------------- .../actions/getAllPermissionsAction.ts | 34 ++++++++ ...sionById.ts => getPermissionByIdAction.ts} | 26 +----- .../permission/actions/upsertPermission.ts | 84 ------------------- .../actions/upsertPermissionAction.ts | 48 +++++++++++ .../modals/PermissionDeleteModal.tsx | 4 +- .../permission/modals/PermissionFormModal.tsx | 8 +- .../permission/services/deletePermission.ts | 15 ++++ .../permission/services/getAllPermissions.ts | 32 +++++++ .../permission/services/getPermissionById.ts | 22 +++++ .../permission/services/upsertPermission.ts | 50 +++++++++++ .../PermissionTable/PermissionTable.tsx | 4 +- 15 files changed, 243 insertions(+), 213 deletions(-) delete mode 100644 src/modules/permission/actions/deletePermission.ts create mode 100644 src/modules/permission/actions/deletePermissionAction.ts delete mode 100644 src/modules/permission/actions/getAllPermissions.ts create mode 100644 src/modules/permission/actions/getAllPermissionsAction.ts rename src/modules/permission/actions/{getPermissionById.ts => getPermissionByIdAction.ts} (52%) delete mode 100644 src/modules/permission/actions/upsertPermission.ts create mode 100644 src/modules/permission/actions/upsertPermissionAction.ts create mode 100644 src/modules/permission/services/deletePermission.ts create mode 100644 src/modules/permission/services/getAllPermissions.ts create mode 100644 src/modules/permission/services/getPermissionById.ts create mode 100644 src/modules/permission/services/upsertPermission.ts diff --git a/src/app/dashboard/permissions/page.tsx b/src/app/dashboard/permissions/page.tsx index 135bd44..b1ea645 100644 --- a/src/app/dashboard/permissions/page.tsx +++ b/src/app/dashboard/permissions/page.tsx @@ -1,24 +1,15 @@ import checkMultiplePermissions from "@/modules/auth/utils/checkMultiplePermissions"; -import getAllPermissions from "@/modules/permission/actions/getAllPermissions"; +import getAllPermissions from "@/modules/permission/services/getAllPermissions"; import PermissionsTable from "@/modules/permission/tables/PermissionTable/PermissionTable"; import { Card, Stack, Title } from "@mantine/core"; import { Metadata } from "next"; import React from "react"; -interface Props { - searchParams: { - detail?: string; - edit?: string; - delete?: string; - create?: string; - }; -} - export const metadata: Metadata = { title: "Permissions - Dashboard", }; -export default async function RolesPage({ searchParams }: Props) { +export default async function RolesPage() { const permissions = await checkMultiplePermissions({ create: "permissions.create", readAll: "permissions.readAll", @@ -27,14 +18,13 @@ export default async function RolesPage({ searchParams }: Props) { delete: "permissions.delete", }); - const res = await getAllPermissions(); - if (!res.success) throw new Error("Error while fetch permission"); + const permissionsData = await getAllPermissions(); return ( Permissions - + ); diff --git a/src/modules/permission/actions/deletePermission.ts b/src/modules/permission/actions/deletePermission.ts deleted file mode 100644 index 4860325..0000000 --- a/src/modules/permission/actions/deletePermission.ts +++ /dev/null @@ -1,28 +0,0 @@ -"use server"; - -import db from "@/core/db"; -import prisma from "@/db"; -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 { revalidatePath } from "next/cache"; - -export default async function deletePermission(id: string): Promise { - try { - if (!(await checkPermission("permission.delete"))) unauthorized(); - const permission = await db.permission.delete({ - where: { id }, - }); - - - revalidatePath(".") - - return { - success: true, - message: "The permission has been deleted successfully", - }; - } catch (e: unknown) { - return handleCatch(e) - } -} diff --git a/src/modules/permission/actions/deletePermissionAction.ts b/src/modules/permission/actions/deletePermissionAction.ts new file mode 100644 index 0000000..fe03a23 --- /dev/null +++ b/src/modules/permission/actions/deletePermissionAction.ts @@ -0,0 +1,26 @@ +"use server"; + +import ServerResponseAction from "@/modules/dashboard/types/ServerResponseAction"; +import handleCatch from "@/modules/dashboard/utils/handleCatch"; +import unauthorized from "@/modules/dashboard/utils/unauthorized"; +import { revalidatePath } from "next/cache"; +import deletePermission from "../services/deletePermission"; +import checkPermission from "@/modules/auth/utils/checkPermission"; + +export default async function deletePermissionAction( + id: string +): Promise { + try { + if (!(await checkPermission("permissions.delete"))) unauthorized(); + + await deletePermission(id); + revalidatePath("."); + + return { + success: true, + message: "The permission has been deleted successfully", + }; + } catch (e: unknown) { + return handleCatch(e); + } +} diff --git a/src/modules/permission/actions/getAllPermissions.ts b/src/modules/permission/actions/getAllPermissions.ts deleted file mode 100644 index 8be34a4..0000000 --- a/src/modules/permission/actions/getAllPermissions.ts +++ /dev/null @@ -1,57 +0,0 @@ -"use server" -import prisma from "@/db"; -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 Permission from "../types/Permission"; -import db from "@/core/db"; - -/** - * Retrieves all permissions along with the count of associated permissions and users. - * Authorization check is performed for the operation. - * - * @returns An array of permission objects each including details and counts of related permissions and users. - */ -export default async function getAllPermissions(): Promise> { - // Authorization check - if (!(await checkPermission("permissions.readAll"))) { - unauthorized(); - } - - try { - // Fetch permissions from the database - const permissions = await db.permission.findMany({ - include: { - _count: { - select: { - roles: true, - directUsers: true, - }, - }, - }, - }); - - // Transform the data into the desired format - const permissionsData = permissions.map( - ({ id, code, name, description, isActive, _count }) => ({ - id, - code, - name, - description, - isActive, - roleCount: _count.roles, - //User count counts only direct user - userCount: _count.directUsers, - }) - ); - - return { - success: true, - data: permissionsData - } - } catch (error) { - return handleCatch(error) - } -} diff --git a/src/modules/permission/actions/getAllPermissionsAction.ts b/src/modules/permission/actions/getAllPermissionsAction.ts new file mode 100644 index 0000000..7a26886 --- /dev/null +++ b/src/modules/permission/actions/getAllPermissionsAction.ts @@ -0,0 +1,34 @@ +"use server"; +import ServerResponseAction from "@/modules/dashboard/types/ServerResponseAction"; +import handleCatch from "@/modules/dashboard/utils/handleCatch"; +import unauthorized from "@/modules/dashboard/utils/unauthorized"; +import Permission from "../types/Permission"; +import getAllPermissions from "../services/getAllPermissions"; +import checkPermission from "@/modules/auth/utils/checkPermission"; + +/** + * Retrieves all permissions along with the count of associated permissions and users. + * Authorization check is performed for the operation. + * + * @returns An array of permission objects each including details and counts of related permissions and users. + */ +export default async function getAllPermissionsAction(): Promise< + ServerResponseAction +> { + // Authorization check + if (!(await checkPermission("permissions.readAll"))) { + unauthorized(); + } + + try { + // Fetch permissions from the database + const permissionsData = await getAllPermissions(); + + return { + success: true, + data: permissionsData, + }; + } catch (error) { + return handleCatch(error); + } +} diff --git a/src/modules/permission/actions/getPermissionById.ts b/src/modules/permission/actions/getPermissionByIdAction.ts similarity index 52% rename from src/modules/permission/actions/getPermissionById.ts rename to src/modules/permission/actions/getPermissionByIdAction.ts index 9cabbec..7e19dfa 100644 --- a/src/modules/permission/actions/getPermissionById.ts +++ b/src/modules/permission/actions/getPermissionByIdAction.ts @@ -1,11 +1,10 @@ "use server"; -import db from "@/core/db"; -import prisma from "@/db"; -import checkPermission from "@/modules/dashboard/services/checkPermission"; +import checkPermission from "@/modules/auth/utils/checkPermission"; import ServerResponseAction from "@/modules/dashboard/types/ServerResponseAction"; import handleCatch from "@/modules/dashboard/utils/handleCatch"; import unauthorized from "@/modules/dashboard/utils/unauthorized"; +import getPermissionById from "../services/getPermissionById"; interface Permission { id: string; @@ -15,33 +14,16 @@ interface Permission { isActive: boolean; } -export default async function getPermissionById( +export default async function getPermissionByIdAction( id: string ): Promise> { try { if (!(await checkPermission("permissions.read"))) unauthorized(); - const permission = await db.permission.findFirst({ - where: { id }, - select: { - code: true, - description: true, - id: true, - isActive: true, - name: true, - }, - }); - - if (!permission) { - return { - success: false, - message: "Permission not found", - } as const; - } + const permission = await getPermissionById(id); return { success: true, - message: "Permission fetched successfully", data: permission, } as const; } catch (e) { diff --git a/src/modules/permission/actions/upsertPermission.ts b/src/modules/permission/actions/upsertPermission.ts deleted file mode 100644 index 94d4c47..0000000 --- a/src/modules/permission/actions/upsertPermission.ts +++ /dev/null @@ -1,84 +0,0 @@ -"use server"; - -import permissionFormDataSchema, { PermissionFormData } from "../formSchemas/PermissionFormData"; -import mapObjectToFirstValue from "@/utils/mapObjectToFirstValue"; -import prisma from "@/db"; -import { revalidatePath } from "next/cache"; -import ServerResponseAction from "@/modules/dashboard/types/ServerResponseAction"; -import checkPermission from "@/modules/dashboard/services/checkPermission"; -import unauthorized from "@/modules/dashboard/utils/unauthorized"; -import DashboardError from "@/modules/dashboard/errors/DashboardError"; -import handleCatch from "@/modules/dashboard/utils/handleCatch"; -import db from "@/core/db"; - -/** - * Upserts a permission based on the provided PermissionFormData. - * If the permission already exists (determined by `id`), it updates the permission; otherwise, it creates a new permission. - * Authorization checks are performed based on whether it's a create or update operation. - * - * @param data - The data for creating or updating the permission. - * @returns An object containing the success status, message, and any errors. - */ -export default async function upsertPermission( - data: PermissionFormData -): Promise { - try { - const isInsert = !data.id; - - // Authorization check - const permissionType = isInsert ? "permission.create" : "permission.update"; - if (!(await checkPermission(permissionType))) { - unauthorized(); - } - - // Validate form data - const validatedFields = permissionFormDataSchema.safeParse(data); - if (!validatedFields.success) { - throw new DashboardError({ - errorCode: "INVALID_FORM_DATA", - formErrors: mapObjectToFirstValue(validatedFields.error.flatten().fieldErrors) - }) - } - const permissionData = { - code: validatedFields.data.code, - description: validatedFields.data.description, - name: validatedFields.data.name, - isActive: validatedFields.data.isActive, - }; - - // Database operation - if (isInsert) { - if (await db.permission.findFirst({ - where: { - code: permissionData.code - } - })){ - throw new DashboardError({ - errorCode: "INVALID_FORM_DATA", - formErrors: { - code: "The code is already exists" - } - }) - } - await db.permission.create({ data: permissionData }); - } else { - await db.permission.update({ - where: { id: validatedFields.data.id! }, - data: permissionData, - }); - } - - // Revalidate the cache - revalidatePath("."); - - // Return success message - return { - success: true, - message: `Permission ${validatedFields.data.name} has been successfully ${ - isInsert ? "created" : "updated" - }.`, - }; - } catch (error) { - return handleCatch(error) - } -} diff --git a/src/modules/permission/actions/upsertPermissionAction.ts b/src/modules/permission/actions/upsertPermissionAction.ts new file mode 100644 index 0000000..645e72a --- /dev/null +++ b/src/modules/permission/actions/upsertPermissionAction.ts @@ -0,0 +1,48 @@ +"use server"; + +import { PermissionFormData } from "../formSchemas/PermissionFormData"; +import { revalidatePath } from "next/cache"; +import ServerResponseAction from "@/modules/dashboard/types/ServerResponseAction"; +import unauthorized from "@/modules/dashboard/utils/unauthorized"; +import handleCatch from "@/modules/dashboard/utils/handleCatch"; +import checkPermission from "@/modules/auth/utils/checkPermission"; +import upsertPermission from "../services/upsertPermission"; + +/** + * Upserts a permission based on the provided PermissionFormData. + * If the permission already exists (determined by `id`), it updates the permission; otherwise, it creates a new permission. + * Authorization checks are performed based on whether it's a create or update operation. + * + * @param data - The data for creating or updating the permission. + * @returns An object containing the success status, message, and any errors. + */ +export default async function upsertPermissionAction( + data: PermissionFormData +): Promise { + try { + const isInsert = !data.id; + + // Authorization check + const permissionType = isInsert + ? "permissions.create" + : "permissions.update"; + if (!(await checkPermission(permissionType))) { + unauthorized(); + } + + const result = await upsertPermission(data); + + // Revalidate the cache + revalidatePath("."); + + // Return success message + return { + success: true, + message: `Permission ${result.name} has been successfully ${ + isInsert ? "created" : "updated" + }.`, + }; + } catch (error) { + return handleCatch(error); + } +} diff --git a/src/modules/permission/modals/PermissionDeleteModal.tsx b/src/modules/permission/modals/PermissionDeleteModal.tsx index 7d74a3a..1508751 100644 --- a/src/modules/permission/modals/PermissionDeleteModal.tsx +++ b/src/modules/permission/modals/PermissionDeleteModal.tsx @@ -9,7 +9,7 @@ import { } from "@mantine/core"; import { showNotification } from "@/utils/notifications"; import withServerAction from "@/modules/dashboard/utils/withServerAction"; -import deletePermission from "../actions/deletePermission"; +import deletePermissionAction from "../actions/deletePermissionAction"; import ClientError from "@/core/error/ClientError"; export interface DeleteModalProps { @@ -37,7 +37,7 @@ export default function DeleteModal(props: DeleteModalProps) { if (!props.data?.id) return; setSubmitting(true); - withServerAction(deletePermission, props.data!.id) + withServerAction(deletePermissionAction, props.data!.id) .then((response) => { showNotification( response.message ?? "Permission deleted successfully" diff --git a/src/modules/permission/modals/PermissionFormModal.tsx b/src/modules/permission/modals/PermissionFormModal.tsx index a4db843..b4ec01a 100644 --- a/src/modules/permission/modals/PermissionFormModal.tsx +++ b/src/modules/permission/modals/PermissionFormModal.tsx @@ -17,9 +17,9 @@ import { useRouter } from "next/navigation"; import React, { useEffect, useState } from "react"; import { TbDeviceFloppy } from "react-icons/tb"; import permissionFormDataSchema, { PermissionFormData } from "../formSchemas/PermissionFormData"; -import getPermissionById from "../actions/getPermissionById"; +import getPermissionByIdAction from "../actions/getPermissionByIdAction"; import withServerAction from "@/modules/dashboard/utils/withServerAction"; -import upsertPermission from "../actions/upsertPermission"; +import upsertPermissionAction from "../actions/upsertPermissionAction"; import ClientError from "@/core/error/ClientError"; export interface ModalProps { @@ -66,7 +66,7 @@ export default function FormModal(props: ModalProps) { } setFetching(true); - getPermissionById(props.id) + withServerAction(getPermissionByIdAction, props.id) .then((response) => { if (response.success) { const data = response.data; @@ -95,7 +95,7 @@ export default function FormModal(props: ModalProps) { const handleSubmit = (values: PermissionFormData) => { setSubmitting(true); - withServerAction(upsertPermission, values) + withServerAction(upsertPermissionAction, values) .then((response) => { showNotification(response.message!, "success"); closeModal(); diff --git a/src/modules/permission/services/deletePermission.ts b/src/modules/permission/services/deletePermission.ts new file mode 100644 index 0000000..bbd438f --- /dev/null +++ b/src/modules/permission/services/deletePermission.ts @@ -0,0 +1,15 @@ +import "server-only" +import db from "@/core/db"; +import notFound from "@/modules/dashboard/utils/notFound"; + +export default async function deletePermission(id: string) { + const permission = await db.permission.delete({ + where: { id }, + }); + + if (!permission) return notFound({ + message: "The permission does not exists" + }); + + return true as const; +} diff --git a/src/modules/permission/services/getAllPermissions.ts b/src/modules/permission/services/getAllPermissions.ts new file mode 100644 index 0000000..b1f77a4 --- /dev/null +++ b/src/modules/permission/services/getAllPermissions.ts @@ -0,0 +1,32 @@ +import db from "@/core/db"; +import "server-only"; +import Permission from "../types/Permission"; + +export default async function getAllPermissions(): Promise { + const permissions = await db.permission.findMany({ + include: { + _count: { + select: { + roles: true, + directUsers: true, + }, + }, + }, + }); + + // Transform the data into the desired format + const permissionsData = permissions.map( + ({ id, code, name, description, isActive, _count }) => ({ + id, + code, + name, + description, + isActive, + roleCount: _count.roles, + //User count counts only direct user + userCount: _count.directUsers, + }) + ); + + return permissionsData; +} diff --git a/src/modules/permission/services/getPermissionById.ts b/src/modules/permission/services/getPermissionById.ts new file mode 100644 index 0000000..59885bb --- /dev/null +++ b/src/modules/permission/services/getPermissionById.ts @@ -0,0 +1,22 @@ +import db from "@/core/db"; +import notFound from "@/modules/dashboard/utils/notFound"; +import "server-only" + +export default async function getPermissionById(id: string){ + const permission = await db.permission.findFirst({ + where: { id }, + select: { + code: true, + description: true, + id: true, + isActive: true, + name: true, + }, + }); + + if (!permission) return notFound({ + message: "The permission does not exists" + }) + + return permission; +} \ No newline at end of file diff --git a/src/modules/permission/services/upsertPermission.ts b/src/modules/permission/services/upsertPermission.ts new file mode 100644 index 0000000..5b1404c --- /dev/null +++ b/src/modules/permission/services/upsertPermission.ts @@ -0,0 +1,50 @@ +import "server-only"; +import permissionFormDataSchema, { PermissionFormData } from "../formSchemas/PermissionFormData"; +import DashboardError from "@/modules/dashboard/errors/DashboardError"; +import mapObjectToFirstValue from "@/utils/mapObjectToFirstValue"; +import db from "@/core/db"; + +export default async function upsertPermission(data: PermissionFormData) { + const isInsert = !data.id; + + // Validate form data + const validatedFields = permissionFormDataSchema.safeParse(data); + if (!validatedFields.success) { + throw new DashboardError({ + errorCode: "INVALID_FORM_DATA", + formErrors: mapObjectToFirstValue( + validatedFields.error.flatten().fieldErrors + ), + }); + } + const permissionData = { + code: validatedFields.data.code, + description: validatedFields.data.description, + name: validatedFields.data.name, + isActive: validatedFields.data.isActive, + }; + + // Database operation + if (isInsert) { + if ( + await db.permission.findFirst({ + where: { + code: permissionData.code, + }, + }) + ) { + throw new DashboardError({ + errorCode: "INVALID_FORM_DATA", + formErrors: { + code: "The code is already exists", + }, + }); + } + return await db.permission.create({ data: permissionData }); + } else { + return await db.permission.update({ + where: { id: validatedFields.data.id! }, + data: permissionData, + }); + } +} diff --git a/src/modules/permission/tables/PermissionTable/PermissionTable.tsx b/src/modules/permission/tables/PermissionTable/PermissionTable.tsx index 65e5df9..71b9056 100644 --- a/src/modules/permission/tables/PermissionTable/PermissionTable.tsx +++ b/src/modules/permission/tables/PermissionTable/PermissionTable.tsx @@ -15,7 +15,7 @@ import DashboardTable from "@/modules/dashboard/components/DashboardTable"; interface Props { permissions: Partial; - permissionData: Permission[]; + data: Permission[]; } export default function PermissionsTable(props: Props) { @@ -31,7 +31,7 @@ export default function PermissionsTable(props: Props) { }); const table = useReactTable({ - data: props.permissionData, + data: props.data, columns: createColumns({ permissions: props.permissions, actions: {