'use client'; import { useState, useMemo, useCallback, useEffect } from 'react'; import { useRouter } from 'next/navigation'; import { format } from 'date-fns'; import { Shield, Plus, Pencil, Trash2, Settings, Eye, EyeOff, } from 'lucide-react'; import { Button } from '@/components/ui/button'; import { Checkbox } from '@/components/ui/checkbox'; import { Badge } from '@/components/ui/badge'; import { TableRow, TableCell } from '@/components/ui/table'; import { IntegratedListTemplateV2, type TableColumn, type StatCard, type TabOption, } from '@/components/templates/IntegratedListTemplateV2'; import { ListMobileCard, InfoField } from '@/components/organisms/ListMobileCard'; import { AlertDialog, AlertDialogAction, AlertDialogCancel, AlertDialogContent, AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogTitle, } from '@/components/ui/alert-dialog'; import type { Permission } from './types'; // localStorage 키 const PERMISSIONS_STORAGE_KEY = 'buddy_permissions'; /** * 기본 권한 데이터 (PDF 54페이지 기준) */ const defaultPermissions: Permission[] = [ { id: 1, name: '관리자', status: 'active', menuPermissions: [], createdAt: '2025-01-01T00:00:00Z', }, { id: 2, name: '일반사용자', status: 'active', menuPermissions: [], createdAt: '2025-01-15T00:00:00Z', }, { id: 3, name: '인사담당자', status: 'active', menuPermissions: [], createdAt: '2025-02-01T00:00:00Z', }, { id: 4, name: '결재담당자', status: 'active', menuPermissions: [], createdAt: '2025-02-15T00:00:00Z', }, { id: 5, name: '게스트', status: 'hidden', menuPermissions: [], createdAt: '2025-03-01T00:00:00Z', }, ]; // localStorage에서 권한 데이터 로드 const loadPermissions = (): Permission[] => { if (typeof window === 'undefined') return defaultPermissions; try { const stored = localStorage.getItem(PERMISSIONS_STORAGE_KEY); if (stored) { return JSON.parse(stored); } } catch (error) { console.error('Failed to load permissions:', error); } return defaultPermissions; }; // localStorage에 권한 데이터 저장 const savePermissions = (permissions: Permission[]) => { if (typeof window === 'undefined') return; try { localStorage.setItem(PERMISSIONS_STORAGE_KEY, JSON.stringify(permissions)); } catch (error) { console.error('Failed to save permissions:', error); } }; export function PermissionManagement() { const router = useRouter(); // ===== 상태 관리 ===== const [searchQuery, setSearchQuery] = useState(''); const [selectedItems, setSelectedItems] = useState>(new Set()); const [currentPage, setCurrentPage] = useState(1); const itemsPerPage = 20; // 권한 데이터 const [permissions, setPermissions] = useState(defaultPermissions); // 삭제 확인 다이얼로그 const [deleteDialogOpen, setDeleteDialogOpen] = useState(false); const [permissionToDelete, setPermissionToDelete] = useState(null); const [isBulkDelete, setIsBulkDelete] = useState(false); // localStorage에서 초기 데이터 로드 useEffect(() => { setPermissions(loadPermissions()); }, []); // 권한 변경 감지 (상세 페이지에서 변경 시) useEffect(() => { const handlePermissionsUpdated = (event: CustomEvent) => { setPermissions(event.detail); }; window.addEventListener('permissionsUpdated', handlePermissionsUpdated as EventListener); return () => { window.removeEventListener('permissionsUpdated', handlePermissionsUpdated as EventListener); }; }, []); // ===== 탭 상태 ===== const [activeTab, setActiveTab] = useState('all'); // ===== 체크박스 핸들러 ===== const toggleSelection = useCallback((id: string) => { setSelectedItems(prev => { const newSet = new Set(prev); if (newSet.has(id)) newSet.delete(id); else newSet.add(id); return newSet; }); }, []); const toggleSelectAll = useCallback(() => { if (selectedItems.size === filteredData.length && filteredData.length > 0) { setSelectedItems(new Set()); } else { setSelectedItems(new Set(filteredData.map(item => item.id.toString()))); } }, [selectedItems.size]); // ===== 필터링된 데이터 ===== const filteredData = useMemo(() => { let result = permissions; // 탭 필터 if (activeTab === 'active') { result = result.filter(item => item.status === 'active'); } else if (activeTab === 'hidden') { result = result.filter(item => item.status === 'hidden'); } // 검색 필터 if (searchQuery) { result = result.filter(item => item.name.toLowerCase().includes(searchQuery.toLowerCase()) ); } return result; }, [permissions, searchQuery, activeTab]); const paginatedData = useMemo(() => { const startIndex = (currentPage - 1) * itemsPerPage; return filteredData.slice(startIndex, startIndex + itemsPerPage); }, [filteredData, currentPage, itemsPerPage]); const totalPages = Math.ceil(filteredData.length / itemsPerPage); // ===== 핸들러 ===== const handleAdd = () => { // 새 권한 등록 페이지로 이동 (저장 전까지 목록에 추가 안됨) router.push('/settings/permissions/new'); }; const handleEdit = (permission: Permission, e?: React.MouseEvent) => { e?.stopPropagation(); // 상세 페이지로 라우팅 router.push(`/settings/permissions/${permission.id}`); }; const handleViewDetail = (permission: Permission) => { // 상세 페이지로 라우팅 router.push(`/settings/permissions/${permission.id}`); }; const handleDelete = (permission: Permission, e?: React.MouseEvent) => { e?.stopPropagation(); setPermissionToDelete(permission); setIsBulkDelete(false); setDeleteDialogOpen(true); }; const handleBulkDelete = () => { if (selectedItems.size === 0) return; setIsBulkDelete(true); setDeleteDialogOpen(true); }; const confirmDelete = () => { let updatedPermissions: Permission[]; if (isBulkDelete) { updatedPermissions = permissions.filter(p => !selectedItems.has(p.id.toString())); setSelectedItems(new Set()); } else if (permissionToDelete) { updatedPermissions = permissions.filter(p => p.id !== permissionToDelete.id); } else { return; } setPermissions(updatedPermissions); savePermissions(updatedPermissions); setDeleteDialogOpen(false); setPermissionToDelete(null); }; // ===== 날짜 포맷 ===== const formatDate = (dateStr: string) => { return format(new Date(dateStr), 'yyyy-MM-dd'); }; // ===== 탭 설정 ===== const tabs: TabOption[] = useMemo(() => { const activeCount = permissions.filter(p => p.status === 'active').length; const hiddenCount = permissions.filter(p => p.status === 'hidden').length; return [ { value: 'all', label: '전체', count: permissions.length, color: 'blue' }, { value: 'active', label: '공개', count: activeCount, color: 'green' }, { value: 'hidden', label: '숨김', count: hiddenCount, color: 'gray' }, ]; }, [permissions]); // ===== 통계 카드 ===== const statCards: StatCard[] = useMemo(() => { const totalCount = permissions.length; const activeCount = permissions.filter(p => p.status === 'active').length; const hiddenCount = permissions.filter(p => p.status === 'hidden').length; return [ { label: '전체 권한', value: totalCount, icon: Shield, iconColor: 'text-blue-500', }, { label: '공개', value: activeCount, icon: Eye, iconColor: 'text-green-500', }, { label: '숨김', value: hiddenCount, icon: EyeOff, iconColor: 'text-gray-500', }, ]; }, [permissions]); // ===== 테이블 컬럼 ===== const tableColumns: TableColumn[] = useMemo(() => { const baseColumns: TableColumn[] = [ { key: 'index', label: '번호', className: 'text-center w-[80px]' }, { key: 'name', label: '권한', className: 'flex-1' }, { key: 'status', label: '상태', className: 'text-center flex-1' }, { key: 'createdAt', label: '등록일시', className: 'text-center flex-1' }, ]; // 체크박스 선택 시에만 작업 컬럼 표시 if (selectedItems.size > 0) { baseColumns.push({ key: 'action', label: '작업', className: 'text-center flex-1' }); } return baseColumns; }, [selectedItems.size]); // ===== 테이블 행 렌더링 ===== const renderTableRow = useCallback((item: Permission, index: number, globalIndex: number) => { const isSelected = selectedItems.has(item.id.toString()); const hasSelection = selectedItems.size > 0; return ( handleViewDetail(item)} > e.stopPropagation()}> toggleSelection(item.id.toString())} /> {globalIndex} {item.name} {item.status === 'active' ? '공개' : '숨김'} {formatDate(item.createdAt)} {hasSelection && ( e.stopPropagation()}>
)}
); }, [selectedItems, toggleSelection]); // ===== 모바일 카드 렌더링 ===== const renderMobileCard = useCallback(( item: Permission, index: number, globalIndex: number, isSelected: boolean, onToggle: () => void ) => { return ( {item.status === 'active' ? '공개' : '숨김'} } isSelected={isSelected} onToggleSelection={onToggle} infoGrid={
} actions={
} /> ); }, []); // ===== 헤더 액션 ===== const headerActions = (
{selectedItems.size > 0 && ( )}
); // ===== 목록 화면 ===== return ( <> item.id.toString()} onBulkDelete={handleBulkDelete} renderTableRow={renderTableRow} renderMobileCard={renderMobileCard} pagination={{ currentPage, totalPages, totalItems: filteredData.length, itemsPerPage, onPageChange: setCurrentPage, }} /> {/* 삭제 확인 다이얼로그 */} 권한 삭제 {isBulkDelete ? `선택한 ${selectedItems.size}개의 권한을 삭제하시겠습니까?` : `"${permissionToDelete?.name}" 권한을 삭제하시겠습니까?` }
이 권한을 사용 중인 사원이 있으면 해당 사원의 권한이 초기화됩니다.
취소 삭제
); }