feat(WEB): QMS 검사 모달 개선, 전자결재/생산대시보드/템플릿 기능 수정

- QMS: InspectionModal/InspectionModalV2 개선, mockData 정리
- 전자결재: DocumentCreate 기능 수정
- 생산대시보드: ProductionDashboard 개선
- 템플릿: IntegratedDetailTemplate/UniversalListPage 기능 수정
- 문서: i18n 가이드 업데이트, 문서뷰어 아키텍처 계획 추가

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
유병철
2026-02-03 09:09:05 +09:00
parent ca6247286a
commit f0987127eb
10 changed files with 578 additions and 230 deletions

View File

@@ -11,6 +11,7 @@
import { useState, useEffect, useCallback, useMemo, forwardRef, useImperativeHandle } from 'react';
import { useRouter, useParams } from 'next/navigation';
import { usePermission } from '@/hooks/usePermission';
import { DeleteConfirmDialog } from '@/components/ui/confirm-dialog';
import { PageLayout } from '@/components/organisms/PageLayout';
import { PageHeader } from '@/components/organisms/PageHeader';
@@ -53,6 +54,7 @@ function IntegratedDetailTemplateInner<T extends Record<string, unknown>>(
const router = useRouter();
const params = useParams();
const locale = (params.locale as string) || 'ko';
const { canCreate: permCanCreate, canUpdate: permCanUpdate, canDelete: permCanDelete } = usePermission();
// ===== 상태 =====
const [mode, setMode] = useState<DetailMode>(initialMode);
@@ -104,14 +106,15 @@ function IntegratedDetailTemplateInner<T extends Record<string, unknown>>(
}), [formData, config, initialData]);
// ===== 권한 계산 =====
// config.permissions가 명시적으로 설정되면 우선, 아니면 usePermission() fallback
const permissions = useMemo(() => {
const p = config.permissions || {};
return {
canEdit: typeof p.canEdit === 'function' ? p.canEdit() : p.canEdit ?? true,
canDelete: typeof p.canDelete === 'function' ? p.canDelete() : p.canDelete ?? true,
canCreate: typeof p.canCreate === 'function' ? p.canCreate() : p.canCreate ?? true,
canEdit: typeof p.canEdit === 'function' ? p.canEdit() : (p.canEdit !== undefined ? p.canEdit : permCanUpdate),
canDelete: typeof p.canDelete === 'function' ? p.canDelete() : (p.canDelete !== undefined ? p.canDelete : permCanDelete),
canCreate: typeof p.canCreate === 'function' ? p.canCreate() : (p.canCreate !== undefined ? p.canCreate : permCanCreate),
};
}, [config.permissions]);
}, [config.permissions, permCanUpdate, permCanDelete, permCanCreate]);
// ===== 모드 헬퍼 =====
const isViewMode = mode === 'view';

View File

@@ -13,6 +13,7 @@
import { useState, useEffect, useCallback, useMemo, useRef } from 'react';
import { useRouter, useParams } from 'next/navigation';
import { usePermission } from '@/hooks/usePermission';
import { toast } from 'sonner';
import { Download, Loader2 } from 'lucide-react';
import { Button } from '@/components/ui/button';
@@ -43,6 +44,7 @@ export function UniversalListPage<T>({
const router = useRouter();
const params = useParams();
const locale = (params.locale as string) || 'ko';
const { canCreate: permCanCreate, canDelete: permCanDelete } = usePermission();
// ===== 상태 관리 =====
// 원본 데이터 (클라이언트 사이드 필터링용)
@@ -825,7 +827,7 @@ export function UniversalListPage<T>({
onToggle: () => toggleSelection(id),
onRowClick: () => handleRowClick(item),
onEdit: () => handleEdit(item),
onDelete: () => handleDeleteClick(item),
onDelete: permCanDelete ? () => handleDeleteClick(item) : undefined,
});
},
[config, effectiveGetItemId, handleDeleteClick, handleEdit, handleRowClick, effectiveSelectedItems, toggleSelection]
@@ -838,7 +840,7 @@ export function UniversalListPage<T>({
onToggle,
onRowClick: () => handleRowClick(item),
onEdit: () => handleEdit(item),
onDelete: () => handleDeleteClick(item),
onDelete: permCanDelete ? () => handleDeleteClick(item) : undefined,
});
},
[config, handleDeleteClick, handleEdit, handleRowClick]
@@ -874,7 +876,7 @@ export function UniversalListPage<T>({
}
// 공통 헤더 옵션 (달력/등록버튼)
dateRangeSelector={config.dateRangeSelector}
createButton={config.createButton}
createButton={permCanCreate ? config.createButton : undefined}
// 탭 콘텐츠
tabsContent={config.tabsContent}
// 통계 카드