'use client'; import { useState, useTransition } from 'react'; import { FoxButton } from '@fox/core/components/fox-button'; import { FoxButtonGroup } from '@fox/core/components/fox-button-group'; import { FoxIconButton } from '@fox/core/components/fox-icon-button'; import { FoxListContainer, type FoxListColumn, } from '@fox/core/components/fox-list-container'; import { FoxCaretDownIcon, FoxCaretRightIcon, FoxCheckIcon, FoxPencilSimpleIcon, FoxPlusIcon, FoxProhibitIcon, FoxTrashIcon, } from '@fox/core/icons'; import { useFeedback } from '@/app/_hooks/use-feedback'; import { flattenAdminMenuTree, formatActiveLabel, type AdminMenu, type AdminMenuNode, } from '@/lib/domain/admin-menu'; import { deleteAdminMenuAction } from '../_actions'; import { AdminMenuModal, type AdminMenuRole } from './admin-menu-modal'; import styles from './admin-menu-list.module.scss'; interface AdminMenuListProps { roots: AdminMenuNode[]; totalCount: number; roles: AdminMenuRole[]; } type ModalState = | { mode: 'create'; parent: AdminMenu; nextSortOrder: number } | { mode: 'edit'; menu: AdminMenuNode; parent: AdminMenu | null }; function findParent( roots: AdminMenuNode[], parentId: string | null ): AdminMenu | null { if (!parentId) return null; const stack = [...roots]; while (stack.length > 0) { const node = stack.pop(); if (!node) continue; if (node.id === parentId) return node; stack.push(...node.children); } return null; } export function AdminMenuList({ roots, totalCount, roles }: AdminMenuListProps) { const { showAlert, hideAlert, showToast } = useFeedback(); const [collapsedIds, setCollapsedIds] = useState>( new Set() ); const [modal, setModal] = useState(null); const [isDeleting, startDeleting] = useTransition(); const rows = flattenAdminMenuTree(roots, collapsedIds); function toggle(id: string) { setCollapsedIds((current) => { const next = new Set(current); if (next.has(id)) { next.delete(id); } else { next.add(id); } return next; }); } function confirmDelete(menu: AdminMenuNode) { showAlert({ variant: 'danger', title: '메뉴를 삭제하시겠습니까?', message: `${menu.name} 메뉴를 삭제합니다. 삭제 후에는 되돌릴 수 없습니다.`, actions: ( { hideAlert(); startDeleting(async () => { const result = await deleteAdminMenuAction(menu.id); showToast( result.status === 'error' ? { variant: 'danger', message: result.message ?? '삭제하지 못했습니다.', } : { variant: 'success', message: '메뉴를 삭제했습니다.' } ); }); }} /> ), }); } const columns: FoxListColumn[] = [ { key: 'id', header: '메뉴번호', width: 120 }, { key: 'isActive', header: '활성여부', width: 120, render: (row) => ( {row.isActive ? : } ), }, { key: 'name', header: '메뉴명', align: 'start', render: (row) => ( {row.children.length > 0 ? ( ) : ( ), }, { key: 'sortOrder', header: '메뉴순번', width: 120 }, { key: 'roleName', header: '권한', width: 160, render: (row) => row.roleName || row.roleId || '-', }, { key: 'actions', header: '관리', width: 160, render: (row) => ( } label="수정" onAction={() => setModal({ mode: 'edit', menu: row, parent: findParent(roots, row.parentId), }) } /> } label="하위 메뉴 추가" onAction={() => setModal({ mode: 'create', parent: row, nextSortOrder: row.children.length + 1, }) } /> } label={isDeleting ? '삭제 중' : '삭제'} disabled={isDeleting} onAction={() => confirmDelete(row)} /> ), }, ]; return ( <> title="메뉴관리" breadcrumb={[ { label: '홈', href: '/' }, { label: '관리자정보관리' }, { label: '메뉴관리' }, ]} caption="메뉴 목록" columns={columns} rows={rows} rowKey={(row) => row.id} totalCount={totalCount} emptyMessage="등록된 메뉴가 없습니다." searchHidden /> {modal && ( setModal(null)} /> )} ); }