summaryrefslogtreecommitdiff
path: root/components/system/permissionsTree.tsx
diff options
context:
space:
mode:
authordujinkim <dujin.kim@dtsolution.co.kr>2025-03-26 00:37:41 +0000
committerdujinkim <dujin.kim@dtsolution.co.kr>2025-03-26 00:37:41 +0000
commite0dfb55c5457aec489fc084c4567e791b4c65eb1 (patch)
tree68543a65d88f5afb3a0202925804103daa91bc6f /components/system/permissionsTree.tsx
3/25 까지의 대표님 작업사항
Diffstat (limited to 'components/system/permissionsTree.tsx')
-rw-r--r--components/system/permissionsTree.tsx167
1 files changed, 167 insertions, 0 deletions
diff --git a/components/system/permissionsTree.tsx b/components/system/permissionsTree.tsx
new file mode 100644
index 00000000..8f6adfb0
--- /dev/null
+++ b/components/system/permissionsTree.tsx
@@ -0,0 +1,167 @@
+"use client"
+
+import * as React from 'react';
+import Box from '@mui/material/Box';
+import Stack from '@mui/material/Stack';
+import SvgIcon, { SvgIconProps } from '@mui/material/SvgIcon';
+import { styled } from '@mui/material/styles';
+import { SimpleTreeView } from '@mui/x-tree-view/SimpleTreeView';
+import { TreeItem, treeItemClasses } from '@mui/x-tree-view/TreeItem';
+import { Minus, MinusSquare, Plus, SquarePlus } from 'lucide-react';
+import { Button } from "@/components/ui/button";
+import { mainNav, additionalNav, MenuSection } from "@/config/menuConfig";
+import { PermissionDialog } from './permissionDialog';
+
+// ------------------- Custom TreeItem Style -------------------
+const CustomTreeItem = styled(TreeItem)({
+ [`& .${treeItemClasses.iconContainer}`]: {
+ '& .close': {
+ opacity: 0.3,
+ },
+ },
+});
+
+function CloseSquare(props: SvgIconProps) {
+ return (
+ <SvgIcon
+ className="close"
+ fontSize="inherit"
+ style={{ width: 14, height: 14 }}
+ {...props}
+ >
+ {/* tslint:disable-next-line: max-line-length */}
+ <path d="M17.485 17.512q-.281.281-.682.281t-.696-.268l-4.12-4.147-4.12 4.147q-.294.268-.696.268t-.682-.281-.281-.682.294-.669l4.12-4.147-4.12-4.147q-.294-.268-.294-.669t.281-.682.682-.281.696.268l4.12 4.147 4.12-4.147q.294-.268.696-.268t.682.281 .281.669-.294.682l-4.12 4.147 4.12 4.147q.294.268 .294.669t-.281.682zM22.047 22.074v0 0-20.147 0h-20.12v0 20.147 0h20.12zM22.047 24h-20.12q-.803 0-1.365-.562t-.562-1.365v-20.147q0-.776.562-1.351t1.365-.575h20.147q.776 0 1.351.575t.575 1.351v20.147q0 .803-.575 1.365t-1.378.562v0z" />
+ </SvgIcon>
+ );
+}
+
+
+interface SelectedKey {
+ key: string;
+ title: string;
+}
+
+export default function PermissionsTree() {
+ const [expandedItems, setExpandedItems] = React.useState<string[]>([]);
+ const [dialogOpen, setDialogOpen] = React.useState(false);
+ const [selectedKey, setSelectedKey] = React.useState<SelectedKey | null>(null);
+
+ const handleExpandedItemsChange = (
+ event: React.SyntheticEvent,
+ itemIds: string[],
+ ) => {
+ setExpandedItems(itemIds);
+ };
+
+ const handleExpandClick = () => {
+ if (expandedItems.length === 0) {
+ // 모든 노드를 펼치기
+ // 실제로는 mainNav와 additionalNav를 순회해 itemId를 전부 수집하는 방식
+ setExpandedItems([...collectAllIds()]);
+ } else {
+ setExpandedItems([]);
+ }
+ };
+
+ // (4) 수동으로 "모든 TreeItem의 itemId"를 수집하는 함수
+ const collectAllIds = React.useCallback(() => {
+ const ids: string[] = [];
+
+ // mainNav: 상위 = section.title, 하위 = item.title
+ mainNav.forEach((section) => {
+ ids.push(section.title); // 상위
+ section.items.forEach((itm) => ids.push(itm.title));
+ });
+
+ // additionalNav를 "기타메뉴" 아래에 넣을 경우, "기타메뉴" 라는 itemId + each item
+ additionalNav.forEach((itm) => ids.push(itm.title));
+ return ids;
+ }, []);
+
+
+ function handleItemClick(key: SelectedKey) {
+ // 1) Dialog 열기
+ setSelectedKey(key); // 이 값은 Dialog에서 어떤 메뉴인지 식별에 사용
+ setDialogOpen(true);
+ }
+
+ // (5) 실제 렌더
+ return (
+ <div className='lg:max-w-2xl'>
+ <Stack spacing={2}>
+ <div>
+ <Button onClick={handleExpandClick} type='button'>
+ {expandedItems.length === 0 ? (
+ <>
+ <Plus />
+ Expand All
+ </>
+ ) : (
+ <>
+ <Minus />
+ Collapse All
+ </>
+ )}
+ </Button>
+ </div>
+
+ <Box sx={{ minHeight: 352, minWidth: 250 }}>
+ <SimpleTreeView
+ // 아래 props로 아이콘 지정
+ slots={{
+ expandIcon: SquarePlus,
+ collapseIcon: MinusSquare,
+ endIcon: CloseSquare,
+ }}
+ expansionTrigger="iconContainer"
+ onExpandedItemsChange={handleExpandedItemsChange}
+ expandedItems={expandedItems}
+ >
+ {/* (A) mainNav를 트리로 렌더 */}
+ {mainNav.map((section) => (
+ <CustomTreeItem
+ key={section.title}
+ itemId={section.title}
+ label={section.title}
+ >
+ {section.items.map((itm) => {
+ const lastSegment = itm.href.split("/").pop() || itm.title;
+ const key = { key: lastSegment, title: itm.title }
+ return (
+ <CustomTreeItem
+ key={lastSegment}
+ itemId={lastSegment}
+ label={itm.title}
+ onClick={() => handleItemClick(key)}
+ />
+ );
+ })}
+ </CustomTreeItem>
+ ))}
+
+
+ {additionalNav.map((itm) => {
+ const lastSegment = itm.href.split("/").pop() || itm.title;
+ const key = { key: lastSegment, title: itm.title }
+ return (
+ <CustomTreeItem
+ key={lastSegment}
+ itemId={lastSegment}
+ label={itm.title}
+ onClick={() => handleItemClick(key)}
+ />
+ );
+ })}
+ </SimpleTreeView>
+ </Box>
+ </Stack>
+
+ <PermissionDialog
+ open={dialogOpen}
+ onOpenChange={setDialogOpen}
+ itemKey={selectedKey?.key}
+ itemTitle={selectedKey?.title}
+ />
+ </div>
+ );
+} \ No newline at end of file