feat(WEB): 공정관리 드래그 순서변경, 수주서/출고증 리디자인, 체크리스트 관리 추가

- 공정관리: 드래그&드롭 순서 변경 기능 추가 (reorderProcesses API)
- 수주서(SalesOrderDocument): 기획서 D1.8 기준 리디자인, 출고증과 동일 자재 섹션 구조
- 출고증(ShipmentOrderDocument): 레이아웃 개선
- 체크리스트 관리 페이지 신규 추가 (master-data/checklist-management)
- QMS 품질감사: 타입 및 목데이터 수정
- menuRefresh 유틸 정리

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
유병철
2026-02-09 17:52:43 +09:00
parent ce36101929
commit 3ea6a57a10
26 changed files with 3398 additions and 829 deletions

View File

@@ -11,7 +11,7 @@
import { useState, useEffect, useCallback, useRef } from 'react';
import { useRouter } from 'next/navigation';
import { ArrowLeft, Edit, GripVertical, Plus, Package } from 'lucide-react';
import { ArrowLeft, Edit, GripVertical, Plus, Package, Trash2 } from 'lucide-react';
import { Button } from '@/components/ui/button';
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
import { Badge } from '@/components/ui/badge';
@@ -19,7 +19,9 @@ import { PageLayout } from '@/components/organisms/PageLayout';
import { PageHeader } from '@/components/organisms/PageHeader';
import { useMenuStore } from '@/store/menuStore';
import { usePermission } from '@/hooks/usePermission';
import { getProcessSteps, reorderProcessSteps } from './actions';
import { toast } from 'sonner';
import { DeleteConfirmDialog } from '@/components/ui/confirm-dialog';
import { getProcessSteps, reorderProcessSteps, deleteProcess } from './actions';
import type { Process, ProcessStep } from '@/types/process';
interface ProcessDetailProps {
@@ -35,6 +37,10 @@ export function ProcessDetail({ process }: ProcessDetailProps) {
const [steps, setSteps] = useState<ProcessStep[]>([]);
const [isStepsLoading, setIsStepsLoading] = useState(true);
// 삭제 다이얼로그 상태
const [deleteDialogOpen, setDeleteDialogOpen] = useState(false);
const [isDeleting, setIsDeleting] = useState(false);
// 드래그 상태
const [dragIndex, setDragIndex] = useState<number | null>(null);
const [dragOverIndex, setDragOverIndex] = useState<number | null>(null);
@@ -75,6 +81,24 @@ export function ProcessDetail({ process }: ProcessDetailProps) {
router.push(`/ko/master-data/process-management/${process.id}/steps/${stepId}`);
};
const handleDelete = async () => {
setIsDeleting(true);
try {
const result = await deleteProcess(process.id);
if (result.success) {
toast.success('공정이 삭제되었습니다.');
router.push('/ko/master-data/process-management');
} else {
toast.error(result.error || '삭제에 실패했습니다.');
}
} catch {
toast.error('삭제 중 오류가 발생했습니다.');
} finally {
setIsDeleting(false);
setDeleteDialogOpen(false);
}
};
// ===== 드래그&드롭 (HTML5 네이티브) =====
const handleDragStart = useCallback((e: React.DragEvent<HTMLTableRowElement>, index: number) => {
setDragIndex(index);
@@ -343,12 +367,27 @@ export function ProcessDetail({ process }: ProcessDetailProps) {
<span className="hidden md:inline"></span>
</Button>
{canUpdate && (
<Button onClick={handleEdit} size="sm" className="md:size-default">
<Edit className="h-4 w-4 md:mr-2" />
<span className="hidden md:inline"></span>
</Button>
<div className="flex items-center gap-2">
<Button variant="destructive" onClick={() => setDeleteDialogOpen(true)} size="sm" className="md:size-default">
<Trash2 className="h-4 w-4 md:mr-2" />
<span className="hidden md:inline"></span>
</Button>
<Button onClick={handleEdit} size="sm" className="md:size-default">
<Edit className="h-4 w-4 md:mr-2" />
<span className="hidden md:inline"></span>
</Button>
</div>
)}
</div>
{/* 삭제 확인 다이얼로그 */}
<DeleteConfirmDialog
open={deleteDialogOpen}
onOpenChange={setDeleteDialogOpen}
description="이 공정을 삭제하시겠습니까? 이 작업은 되돌릴 수 없습니다."
loading={isDeleting}
onConfirm={handleDelete}
/>
</PageLayout>
);
}