117 lines
3.4 KiB
TypeScript
117 lines
3.4 KiB
TypeScript
|
|
"use client";
|
||
|
|
|
||
|
|
import React, { createContext, useContext, useEffect, useState } from "react";
|
||
|
|
import { getCookiesDecrypt } from "@/lib/utils";
|
||
|
|
import { getUserLevelModuleAccessesByUserLevelId } from "@/service/user-level-module-accesses";
|
||
|
|
import { getUserLevelMenuActionAccesses } from "@/service/user-level-menu-action-accesses";
|
||
|
|
import { getUserInfo } from "@/service/user";
|
||
|
|
|
||
|
|
type ModuleAccessMap = Record<string, boolean>;
|
||
|
|
type ActionAccessMap = Record<string, string[]>;
|
||
|
|
|
||
|
|
interface PermissionContextType {
|
||
|
|
canModule: (moduleCode: string) => boolean;
|
||
|
|
canAction: (actionCode: string) => boolean;
|
||
|
|
can: (moduleCode: string, actionCode: string) => boolean;
|
||
|
|
loading: boolean;
|
||
|
|
}
|
||
|
|
|
||
|
|
const PermissionContext = createContext<PermissionContextType | null>(null);
|
||
|
|
|
||
|
|
export const PermissionProvider = ({
|
||
|
|
children,
|
||
|
|
}: {
|
||
|
|
children: React.ReactNode;
|
||
|
|
}) => {
|
||
|
|
const [moduleAccess, setModuleAccess] = useState<ModuleAccessMap>({});
|
||
|
|
const [actionCodes, setActionCodes] = useState<string[]>([]);
|
||
|
|
const [loading, setLoading] = useState(true);
|
||
|
|
|
||
|
|
useEffect(() => {
|
||
|
|
const loadPermissions = async () => {
|
||
|
|
try {
|
||
|
|
// ✅ SUMBER KEBENARAN: API USER INFO
|
||
|
|
const userRes = await getUserInfo();
|
||
|
|
const userLevelId = userRes?.data?.data?.userLevelId;
|
||
|
|
|
||
|
|
console.log("USER LEVEL ID FROM API:", userLevelId);
|
||
|
|
|
||
|
|
if (!userLevelId) {
|
||
|
|
console.error("userLevelId not found from users/info");
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
|
||
|
|
const [moduleRes, actionRes] = await Promise.all([
|
||
|
|
getUserLevelModuleAccessesByUserLevelId(userLevelId),
|
||
|
|
getUserLevelMenuActionAccesses({
|
||
|
|
userLevelId,
|
||
|
|
canAccess: true,
|
||
|
|
limit: 10000,
|
||
|
|
}),
|
||
|
|
]);
|
||
|
|
|
||
|
|
console.log("ACTION ACCESS RAW:", actionRes?.data?.data);
|
||
|
|
|
||
|
|
// 🔹 MODULE ACCESS
|
||
|
|
// MODULE ACCESS
|
||
|
|
const moduleMap: Record<string, boolean> = {};
|
||
|
|
moduleRes?.data?.data?.forEach((item: any) => {
|
||
|
|
if (
|
||
|
|
item.module?.code &&
|
||
|
|
item.canAccess === true &&
|
||
|
|
item.isActive !== false
|
||
|
|
) {
|
||
|
|
moduleMap[item.module.code] = true;
|
||
|
|
}
|
||
|
|
});
|
||
|
|
|
||
|
|
// ACTION ACCESS
|
||
|
|
const actions =
|
||
|
|
actionRes?.data?.data
|
||
|
|
?.filter(
|
||
|
|
(item: any) =>
|
||
|
|
Number(item.userLevelId) === Number(userLevelId) &&
|
||
|
|
item.canAccess === true &&
|
||
|
|
item.isActive === true,
|
||
|
|
)
|
||
|
|
.map((item: any) => item.actionCode) ?? [];
|
||
|
|
|
||
|
|
setModuleAccess(moduleMap);
|
||
|
|
setActionCodes(actions);
|
||
|
|
} catch (error) {
|
||
|
|
console.error("Failed to load permissions", error);
|
||
|
|
} finally {
|
||
|
|
setLoading(false);
|
||
|
|
}
|
||
|
|
};
|
||
|
|
|
||
|
|
loadPermissions();
|
||
|
|
}, []);
|
||
|
|
|
||
|
|
const canModule = (moduleCode: string) => moduleAccess[moduleCode] === true;
|
||
|
|
|
||
|
|
const canAction = (actionCode: string) => actionCodes.includes(actionCode);
|
||
|
|
|
||
|
|
/**
|
||
|
|
* FINAL GUARD
|
||
|
|
* - harus punya module
|
||
|
|
* - harus punya action
|
||
|
|
*/
|
||
|
|
const can = (moduleCode: any, actionCode: any) =>
|
||
|
|
canModule(moduleCode) && canAction(actionCode);
|
||
|
|
|
||
|
|
return (
|
||
|
|
<PermissionContext.Provider value={{ canModule, canAction, can, loading }}>
|
||
|
|
{children}
|
||
|
|
</PermissionContext.Provider>
|
||
|
|
);
|
||
|
|
};
|
||
|
|
|
||
|
|
export const usePermission = () => {
|
||
|
|
const ctx = useContext(PermissionContext);
|
||
|
|
if (!ctx) {
|
||
|
|
throw new Error("usePermission must be used inside PermissionProvider");
|
||
|
|
}
|
||
|
|
return ctx;
|
||
|
|
};
|