Change permission into action-service pattern

This commit is contained in:
sianida26 2024-03-29 23:32:12 +07:00
parent d40ec6b5aa
commit 854b1f9b34
15 changed files with 243 additions and 213 deletions

View File

@ -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 (
<Stack>
<Title order={1}>Permissions</Title>
<Card>
<PermissionsTable permissions={permissions} permissionData={res.data} />
<PermissionsTable permissions={permissions} data={permissionsData} />
</Card>
</Stack>
);

View File

@ -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<ServerResponseAction> {
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)
}
}

View File

@ -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<ServerResponseAction> {
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);
}
}

View File

@ -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<ServerResponseAction<Permission[]>> {
// 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)
}
}

View File

@ -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<Permission[]>
> {
// 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);
}
}

View File

@ -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<ServerResponseAction<Permission>> {
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) {

View File

@ -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<ServerResponseAction> {
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)
}
}

View File

@ -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<ServerResponseAction> {
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);
}
}

View File

@ -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"

View File

@ -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();

View File

@ -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;
}

View File

@ -0,0 +1,32 @@
import db from "@/core/db";
import "server-only";
import Permission from "../types/Permission";
export default async function getAllPermissions(): Promise<Permission[]> {
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;
}

View File

@ -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;
}

View File

@ -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,
});
}
}

View File

@ -15,7 +15,7 @@ import DashboardTable from "@/modules/dashboard/components/DashboardTable";
interface Props {
permissions: Partial<CrudPermissions>;
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: {