'use client'; /** * 작업자 화면 메인 컴포넌트 * API 연동 완료 (2025-12-26) * * 기능: * - 상단 통계 카드 4개 (할당/작업중/완료/긴급) * - 내 작업 목록 카드 리스트 * - 각 작업 카드별 버튼 (전량완료/공정상세/자재투입/작업일지/이슈보고) */ import { useState, useMemo, useCallback, useEffect } from 'react'; import { ClipboardList, PlayCircle, CheckCircle2, AlertTriangle } from 'lucide-react'; import { ContentLoadingSpinner } from '@/components/ui/loading-spinner'; import { Card, CardContent } from '@/components/ui/card'; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue, } from '@/components/ui/select'; import { PageLayout } from '@/components/organisms/PageLayout'; import { toast } from 'sonner'; import { getMyWorkOrders, completeWorkOrder } from './actions'; import type { WorkOrder } from '../ProductionDashboard/types'; import { isNextRedirectError } from '@/lib/utils/redirect-error'; import type { WorkerStats, CompletionToastInfo, MaterialInput } from './types'; import { WorkCard } from './WorkCard'; import { CompletionConfirmDialog } from './CompletionConfirmDialog'; import { CompletionToast } from './CompletionToast'; import { MaterialInputModal } from './MaterialInputModal'; import { WorkLogModal } from './WorkLogModal'; import { IssueReportModal } from './IssueReportModal'; import { WorkCompletionResultDialog } from './WorkCompletionResultDialog'; export default function WorkerScreen() { // ===== 상태 관리 ===== const [workOrders, setWorkOrders] = useState([]); const [isLoading, setIsLoading] = useState(true); // 데이터 로드 const loadData = useCallback(async () => { setIsLoading(true); try { const result = await getMyWorkOrders(); if (result.success) { setWorkOrders(result.data); } else { toast.error(result.error || '작업 목록 조회에 실패했습니다.'); } } catch (error) { if (isNextRedirectError(error)) throw error; console.error('[WorkerScreen] loadData error:', error); toast.error('데이터 로드 중 오류가 발생했습니다.'); } finally { setIsLoading(false); } }, []); useEffect(() => { loadData(); }, [loadData]); // 모달/다이얼로그 상태 const [selectedOrder, setSelectedOrder] = useState(null); const [isCompletionDialogOpen, setIsCompletionDialogOpen] = useState(false); const [isMaterialModalOpen, setIsMaterialModalOpen] = useState(false); const [isWorkLogModalOpen, setIsWorkLogModalOpen] = useState(false); const [isIssueReportModalOpen, setIsIssueReportModalOpen] = useState(false); // 전량완료 흐름 상태 const [isCompletionFlow, setIsCompletionFlow] = useState(false); const [isCompletionResultOpen, setIsCompletionResultOpen] = useState(false); const [completionLotNo, setCompletionLotNo] = useState(''); // 투입된 자재 관리 (orderId -> MaterialInput[]) const [inputMaterialsMap, setInputMaterialsMap] = useState>( new Map() ); // 완료 토스트 상태 const [toastInfo, setToastInfo] = useState(null); // 정렬 상태 const [sortBy, setSortBy] = useState<'dueDate' | 'latest'>('dueDate'); // ===== 통계 계산 ===== const stats: WorkerStats = useMemo(() => { return { assigned: workOrders.length, inProgress: workOrders.filter((o) => o.status === 'inProgress').length, completed: 0, // 완료된 것은 목록에서 제외되므로 0 urgent: workOrders.filter((o) => o.isUrgent).length, }; }, [workOrders]); // ===== 정렬된 작업 목록 ===== const sortedWorkOrders = useMemo(() => { return [...workOrders].sort((a, b) => { if (sortBy === 'dueDate') { // 납기일순 (가까운 날짜 먼저) return new Date(a.dueDate).getTime() - new Date(b.dueDate).getTime(); } else { // 최신등록순 (최근 ID가 더 큼 = 최근 등록) return b.id.localeCompare(a.id); } }); }, [workOrders, sortBy]); // ===== 핸들러 ===== // 전량완료 버튼 클릭 const handleComplete = useCallback( (order: WorkOrder) => { setSelectedOrder(order); // 이미 투입된 자재가 있으면 바로 완료 결과 팝업 const savedMaterials = inputMaterialsMap.get(order.id); if (savedMaterials && savedMaterials.length > 0) { // LOT 번호 생성 const lotNo = `KD-SA-${new Date().toISOString().slice(2, 10).replace(/-/g, '')}-01`; setCompletionLotNo(lotNo); setIsCompletionResultOpen(true); } else { // 자재 투입이 필요합니다 팝업 setIsCompletionDialogOpen(true); } }, [inputMaterialsMap] ); // "자재 투입이 필요합니다" 팝업에서 확인 클릭 → MaterialInputModal 열기 const handleCompletionConfirm = useCallback(() => { setIsCompletionFlow(true); setIsMaterialModalOpen(true); }, []); // MaterialInputModal에서 투입 등록/건너뛰기 후 → 작업 완료 결과 팝업 표시 const handleWorkCompletion = useCallback(() => { if (!selectedOrder) return; // LOT 번호 생성 const lotNo = `KD-SA-${new Date().toISOString().slice(2, 10).replace(/-/g, '')}-01`; setCompletionLotNo(lotNo); // 완료 결과 팝업 표시 setIsCompletionResultOpen(true); setIsCompletionFlow(false); }, [selectedOrder]); // 자재 저장 핸들러 const handleSaveMaterials = useCallback((orderId: string, materials: MaterialInput[]) => { setInputMaterialsMap((prev) => { const next = new Map(prev); next.set(orderId, materials); return next; }); }, []); // 완료 결과 팝업에서 확인 → API 완료 처리 후 목록에서 제거 const handleCompletionResultConfirm = useCallback(async () => { if (!selectedOrder) return; try { // API로 완료 처리 const materials = inputMaterialsMap.get(selectedOrder.id); const result = await completeWorkOrder( selectedOrder.id, materials?.map((m) => ({ materialId: parseInt(m.id), quantity: m.inputQuantity, lotNo: m.lotNo, })) ); if (result.success) { toast.success('작업이 완료되었습니다.'); // 투입된 자재 맵에서도 제거 setInputMaterialsMap((prev) => { const next = new Map(prev); next.delete(selectedOrder.id); return next; }); // 목록에서 제거 setWorkOrders((prev) => prev.filter((o) => o.id !== selectedOrder.id)); } else { toast.error(result.error || '작업 완료 처리에 실패했습니다.'); } } catch (error) { if (isNextRedirectError(error)) throw error; console.error('[WorkerScreen] handleCompletionResultConfirm error:', error); toast.error('작업 완료 중 오류가 발생했습니다.'); } finally { setSelectedOrder(null); setCompletionLotNo(''); } }, [selectedOrder, inputMaterialsMap]); const handleProcessDetail = useCallback((order: WorkOrder) => { setSelectedOrder(order); // 공정상세는 카드 내 토글로 처리 (Phase 4에서 구현) console.log('[공정상세] 토글:', order.orderNo); }, []); const handleMaterialInput = useCallback((order: WorkOrder) => { setSelectedOrder(order); setIsMaterialModalOpen(true); }, []); const handleWorkLog = useCallback((order: WorkOrder) => { setSelectedOrder(order); setIsWorkLogModalOpen(true); }, []); const handleIssueReport = useCallback((order: WorkOrder) => { setSelectedOrder(order); setIsIssueReportModalOpen(true); }, []); return (
{/* 완료 토스트 */} {toastInfo && } {/* 헤더 */}

작업자 화면

내 작업 목록을 확인하고 관리합니다.

{/* 통계 카드 */}
} variant="default" /> } variant="blue" /> } variant="green" /> } variant="red" />
{/* 작업 목록 */}

내 작업 목록

{isLoading ? ( ) : sortedWorkOrders.length === 0 ? ( 배정된 작업이 없습니다. ) : (
{sortedWorkOrders.map((order) => ( ))}
)}
{/* 모달/다이얼로그 */}
); } // ===== 하위 컴포넌트 ===== interface StatCardProps { title: string; value: number; icon: React.ReactNode; variant: 'default' | 'blue' | 'green' | 'red'; } function StatCard({ title, value, icon, variant }: StatCardProps) { const variantClasses = { default: 'bg-gray-50 text-gray-700', blue: 'bg-blue-50 text-blue-700', green: 'bg-green-50 text-green-700', red: 'bg-red-50 text-red-700', }; return (
{title} {icon}

{value}

); }