import permissionConfig from "@/lib/listPermission.json"; import { ActionIcon, Checkbox, Collapse, Group, Stack, Text, } from "@mantine/core"; import { IconChevronDown, IconChevronRight } from "@tabler/icons-react"; import { useState } from "react"; interface Node { label: string; key: string; children?: Node[]; } export default function PermissionTree({ selected, onChange, }: { selected: string[]; onChange: (val: string[]) => void; }) { // Ambil semua child dari node const [openNodes, setOpenNodes] = useState>({}); function toggleNode(label: string) { setOpenNodes((prev) => ({ ...prev, [label]: !prev[label] })); } function getAllChildKeys(node: Node): string[] { let result: string[] = []; if (node.children) { node.children.forEach((c) => { result.push(c.key); result = [...result, ...getAllChildKeys(c)]; }); } return result; } // Dapatkan parentKey, jika ada function getParentKey(key: string) { const split = key.split("."); if (split.length <= 1) return null; split.pop(); return split.join("."); } // Update parent ke atas secara rekursif function updateParent(next: string[], parentKey: string | null): string[] { if (!parentKey) return next; const allChildKeys = findAllChildKeysFromKey(parentKey); const selectedChild = allChildKeys.filter((c) => next.includes(c)); if (selectedChild.length === 0) { // Semua child uncheck → parent uncheck next = next.filter((x) => x !== parentKey); } else if (selectedChild.length === allChildKeys.length) { // Semua child check → parent check if (!next.includes(parentKey)) { next.push(parentKey); } } else { // Sebagian child check → parent intermediate (checked = true, rendered sebagai indeterminate) if (!next.includes(parentKey)) { next.push(parentKey); } } // Rekursif naik ke atas return updateParent(next, getParentKey(parentKey)); } // dapatkan child dari string key function findAllChildKeysFromKey(parentKey: string) { const list: string[] = []; function traverse(nodes: Node[]) { nodes.forEach((n) => { if (n.key.startsWith(parentKey + ".") && n.key !== parentKey) { list.push(n.key); } if (n.children) traverse(n.children); }); } traverse(permissionConfig.menus); return list; } const RenderMenu = ({ menu }: { menu: Node }) => { const hasChild = menu.children && menu.children.length > 0; const open = openNodes[menu.label] ?? false; const childKeys = getAllChildKeys(menu); const isChecked = selected.includes(menu.key); const isIndeterminate = !isChecked && selected.some( (x) => typeof x === "string" && x.startsWith(menu.key + "."), ); function handleCheck() { let next = [...selected]; if (childKeys.length > 0) { // klik parent if (!isChecked) { next = [...new Set([...next, menu.key, ...childKeys])]; } else { next = next.filter((x) => x !== menu.key && !childKeys.includes(x)); } next = updateParent(next, getParentKey(menu.key)); onChange(next); return; } // klik child if (isChecked) { next = next.filter((x) => x !== menu.key); } else { next.push(menu.key); } next = updateParent(next, getParentKey(menu.key)); onChange(next); } return ( {menu.children && menu.children.length > 0 ? ( toggleNode(menu.label)}> {openNodes[menu.label] ? ( ) : ( )} ) : (
)} {menu.children && ( {menu.children.map((child) => ( ))} )} ); }; return ( Hak Akses {permissionConfig.menus .filter( (menu: Node) => !menu.key.startsWith("api") && !menu.key.startsWith("credential"), ) .map((menu: Node) => ( ))} ); }