"use client"; import React, { useState, useEffect, useCallback } from "react"; import "./styles.css"; import { AppState, ModalState, PanelState, StatusType, ProductionType, WorkOrder, WorkStep, Equipment, Process, } from "./types"; import { WORK_ORDERS, EQUIPMENTS, PROCESSES, WORK_STEP_TEMPLATES, STATUS_TEXT } from "./data"; import { PopHeader } from "./PopHeader"; import { PopStatusTabs } from "./PopStatusTabs"; import { PopWorkCard } from "./PopWorkCard"; import { PopBottomNav } from "./PopBottomNav"; import { PopEquipmentModal } from "./PopEquipmentModal"; import { PopProcessModal } from "./PopProcessModal"; import { PopAcceptModal } from "./PopAcceptModal"; import { PopSettingsModal } from "./PopSettingsModal"; import { PopProductionPanel } from "./PopProductionPanel"; export function PopApp() { // 앱 상태 const [appState, setAppState] = useState({ currentStatus: "waiting", selectedEquipment: null, selectedProcess: null, selectedWorkOrder: null, showMyWorkOnly: false, currentWorkSteps: [], currentStepIndex: 0, currentProductionType: "work-order", selectionMode: "single", completionAction: "close", acceptTargetWorkOrder: null, acceptQuantity: 0, theme: "dark", }); // 모달 상태 const [modalState, setModalState] = useState({ equipment: false, process: false, accept: false, settings: false, }); // 패널 상태 const [panelState, setPanelState] = useState({ production: false, }); // 현재 시간 (hydration 에러 방지를 위해 초기값 null) const [currentDateTime, setCurrentDateTime] = useState(null); const [isClient, setIsClient] = useState(false); // 작업지시 목록 (상태 변경을 위해 로컬 상태로 관리) const [workOrders, setWorkOrders] = useState(WORK_ORDERS); // 클라이언트 마운트 확인 및 시계 업데이트 useEffect(() => { setIsClient(true); setCurrentDateTime(new Date()); const timer = setInterval(() => { setCurrentDateTime(new Date()); }, 1000); return () => clearInterval(timer); }, []); // 로컬 스토리지에서 설정 로드 useEffect(() => { const savedSelectionMode = localStorage.getItem("selectionMode") as "single" | "multi" | null; const savedCompletionAction = localStorage.getItem("completionAction") as "close" | "stay" | null; const savedTheme = localStorage.getItem("popTheme") as "dark" | "light" | null; setAppState((prev) => ({ ...prev, selectionMode: savedSelectionMode || "single", completionAction: savedCompletionAction || "close", theme: savedTheme || "dark", })); }, []); // 상태별 카운트 계산 const getStatusCounts = useCallback(() => { const myProcessId = appState.selectedProcess?.id; let waitingCount = 0; let pendingAcceptCount = 0; let inProgressCount = 0; let completedCount = 0; workOrders.forEach((wo) => { if (!wo.processFlow) return; const myProcessIndex = myProcessId ? wo.processFlow.findIndex((step) => step.id === myProcessId) : -1; if (wo.status === "completed") { completedCount++; } else if (wo.status === "in-progress" && wo.accepted) { inProgressCount++; } else if (myProcessIndex >= 0) { const currentProcessIndex = wo.currentProcessIndex || 0; const myStep = wo.processFlow[myProcessIndex]; if (currentProcessIndex < myProcessIndex) { waitingCount++; } else if (currentProcessIndex === myProcessIndex && myStep.status !== "completed") { pendingAcceptCount++; } else if (myStep.status === "completed") { completedCount++; } } else { if (wo.status === "waiting") waitingCount++; else if (wo.status === "in-progress") inProgressCount++; } }); return { waitingCount, pendingAcceptCount, inProgressCount, completedCount }; }, [workOrders, appState.selectedProcess]); // 필터링된 작업 목록 const getFilteredWorkOrders = useCallback(() => { const myProcessId = appState.selectedProcess?.id; let filtered: WorkOrder[] = []; workOrders.forEach((wo) => { if (!wo.processFlow) return; const myProcessIndex = myProcessId ? wo.processFlow.findIndex((step) => step.id === myProcessId) : -1; const currentProcessIndex = wo.currentProcessIndex || 0; const myStep = myProcessIndex >= 0 ? wo.processFlow[myProcessIndex] : null; switch (appState.currentStatus) { case "waiting": if (myProcessIndex >= 0 && currentProcessIndex < myProcessIndex) { filtered.push(wo); } else if (!myProcessId && wo.status === "waiting") { filtered.push(wo); } break; case "pending-accept": if ( myProcessIndex >= 0 && currentProcessIndex === myProcessIndex && myStep && myStep.status !== "completed" && !wo.accepted ) { filtered.push(wo); } break; case "in-progress": if (wo.accepted && wo.status === "in-progress") { filtered.push(wo); } else if (!myProcessId && wo.status === "in-progress") { filtered.push(wo); } break; case "completed": if (wo.status === "completed") { filtered.push(wo); } else if (myStep && myStep.status === "completed") { filtered.push(wo); } break; } }); // 내 작업만 보기 필터 if (appState.showMyWorkOnly && myProcessId) { filtered = filtered.filter((wo) => { const mySteps = wo.processFlow.filter((step) => step.id === myProcessId); if (mySteps.length === 0) return false; return !mySteps.every((step) => step.status === "completed"); }); } return filtered; }, [workOrders, appState.currentStatus, appState.selectedProcess, appState.showMyWorkOnly]); // 상태 탭 변경 const handleStatusChange = (status: StatusType) => { setAppState((prev) => ({ ...prev, currentStatus: status })); }; // 생산 유형 변경 const handleProductionTypeChange = (type: ProductionType) => { setAppState((prev) => ({ ...prev, currentProductionType: type })); }; // 내 작업만 보기 토글 const handleMyWorkToggle = () => { setAppState((prev) => ({ ...prev, showMyWorkOnly: !prev.showMyWorkOnly })); }; // 테마 토글 const handleThemeToggle = () => { const newTheme = appState.theme === "dark" ? "light" : "dark"; setAppState((prev) => ({ ...prev, theme: newTheme })); localStorage.setItem("popTheme", newTheme); }; // 모달 열기/닫기 const openModal = (type: keyof ModalState) => { setModalState((prev) => ({ ...prev, [type]: true })); }; const closeModal = (type: keyof ModalState) => { setModalState((prev) => ({ ...prev, [type]: false })); }; // 설비 선택 const handleEquipmentSelect = (equipment: Equipment) => { setAppState((prev) => ({ ...prev, selectedEquipment: equipment, // 공정이 1개면 자동 선택 selectedProcess: equipment.processIds.length === 1 ? PROCESSES.find((p) => p.id === equipment.processIds[0]) || null : null, })); }; // 공정 선택 const handleProcessSelect = (process: Process) => { setAppState((prev) => ({ ...prev, selectedProcess: process })); }; // 작업 접수 모달 열기 const handleOpenAcceptModal = (workOrder: WorkOrder) => { const acceptedQty = workOrder.acceptedQuantity || 0; const remainingQty = workOrder.orderQuantity - acceptedQty; setAppState((prev) => ({ ...prev, acceptTargetWorkOrder: workOrder, acceptQuantity: remainingQty, })); openModal("accept"); }; // 접수 확인 const handleConfirmAccept = (quantity: number) => { if (!appState.acceptTargetWorkOrder) return; setWorkOrders((prev) => prev.map((wo) => { if (wo.id === appState.acceptTargetWorkOrder!.id) { const previousAccepted = wo.acceptedQuantity || 0; const newAccepted = previousAccepted + quantity; return { ...wo, acceptedQuantity: newAccepted, remainingQuantity: wo.orderQuantity - newAccepted, accepted: true, status: "in-progress" as const, isPartialAccept: newAccepted < wo.orderQuantity, }; } return wo; }) ); closeModal("accept"); setAppState((prev) => ({ ...prev, acceptTargetWorkOrder: null, acceptQuantity: 0, })); }; // 접수 취소 const handleCancelAccept = (workOrderId: string) => { setWorkOrders((prev) => prev.map((wo) => { if (wo.id === workOrderId) { return { ...wo, accepted: false, acceptedQuantity: 0, remainingQuantity: wo.orderQuantity, isPartialAccept: false, status: "waiting" as const, }; } return wo; }) ); }; // 생산진행 패널 열기 const handleOpenProductionPanel = (workOrder: WorkOrder) => { const template = WORK_STEP_TEMPLATES[workOrder.process] || WORK_STEP_TEMPLATES["default"]; const workSteps: WorkStep[] = template.map((step) => ({ ...step, status: "pending" as const, startTime: null, endTime: null, data: {}, })); setAppState((prev) => ({ ...prev, selectedWorkOrder: workOrder, currentWorkSteps: workSteps, currentStepIndex: 0, })); setPanelState((prev) => ({ ...prev, production: true })); }; // 생산진행 패널 닫기 const handleCloseProductionPanel = () => { setPanelState((prev) => ({ ...prev, production: false })); setAppState((prev) => ({ ...prev, selectedWorkOrder: null, currentWorkSteps: [], currentStepIndex: 0, })); }; // 설정 저장 const handleSaveSettings = (selectionMode: "single" | "multi", completionAction: "close" | "stay") => { setAppState((prev) => ({ ...prev, selectionMode, completionAction })); localStorage.setItem("selectionMode", selectionMode); localStorage.setItem("completionAction", completionAction); closeModal("settings"); }; const statusCounts = getStatusCounts(); const filteredWorkOrders = getFilteredWorkOrders(); return (
{/* 헤더 */} openModal("equipment")} onProcessClick={() => openModal("process")} onMyWorkToggle={handleMyWorkToggle} onSearchClick={() => { /* 조회 */ }} onSettingsClick={() => openModal("settings")} onThemeToggle={handleThemeToggle} /> {/* 상태 탭 */} {/* 메인 콘텐츠 */}
{filteredWorkOrders.length === 0 ? (
작업이 없습니다
{appState.currentStatus === "waiting" && "대기 중인 작업이 없습니다"} {appState.currentStatus === "pending-accept" && "접수 대기 작업이 없습니다"} {appState.currentStatus === "in-progress" && "진행 중인 작업이 없습니다"} {appState.currentStatus === "completed" && "완료된 작업이 없습니다"}
) : (
{filteredWorkOrders.map((workOrder) => ( handleOpenAcceptModal(workOrder)} onCancelAccept={() => handleCancelAccept(workOrder.id)} onStartProduction={() => handleOpenProductionPanel(workOrder)} onClick={() => handleOpenProductionPanel(workOrder)} /> ))}
)}
{/* 하단 네비게이션 */}
{/* 모달들 */} closeModal("equipment")} /> closeModal("process")} /> setAppState((prev) => ({ ...prev, acceptQuantity: qty }))} onConfirm={handleConfirmAccept} onClose={() => closeModal("accept")} /> closeModal("settings")} /> {/* 생산진행 패널 */} setAppState((prev) => ({ ...prev, currentStepIndex: index }))} onStepsUpdate={(steps) => setAppState((prev) => ({ ...prev, currentWorkSteps: steps }))} onClose={handleCloseProductionPanel} />
); }