ERP-node/frontend/components/pop/PopProductionPanel.tsx

347 lines
13 KiB
TypeScript

"use client";
import React from "react";
import { X, Play, Square, ChevronRight } from "lucide-react";
import { WorkOrder, WorkStep } from "./types";
interface PopProductionPanelProps {
isOpen: boolean;
workOrder: WorkOrder | null;
workSteps: WorkStep[];
currentStepIndex: number;
currentDateTime: Date;
onStepChange: (index: number) => void;
onStepsUpdate: (steps: WorkStep[]) => void;
onClose: () => void;
}
export function PopProductionPanel({
isOpen,
workOrder,
workSteps,
currentStepIndex,
currentDateTime,
onStepChange,
onStepsUpdate,
onClose,
}: PopProductionPanelProps) {
if (!isOpen || !workOrder) return null;
const currentStep = workSteps[currentStepIndex];
const formatDate = (date: Date) => {
const year = date.getFullYear();
const month = String(date.getMonth() + 1).padStart(2, "0");
const day = String(date.getDate()).padStart(2, "0");
return `${year}-${month}-${day}`;
};
const formatTime = (date: Date | null) => {
if (!date) return "--:--";
const d = new Date(date);
return `${String(d.getHours()).padStart(2, "0")}:${String(d.getMinutes()).padStart(2, "0")}`;
};
const handleStartStep = () => {
const newSteps = [...workSteps];
newSteps[currentStepIndex] = {
...newSteps[currentStepIndex],
status: "in-progress",
startTime: new Date(),
};
onStepsUpdate(newSteps);
};
const handleEndStep = () => {
const newSteps = [...workSteps];
newSteps[currentStepIndex] = {
...newSteps[currentStepIndex],
endTime: new Date(),
};
onStepsUpdate(newSteps);
};
const handleSaveAndNext = () => {
const newSteps = [...workSteps];
const step = newSteps[currentStepIndex];
// 시간 자동 설정
if (!step.startTime) step.startTime = new Date();
if (!step.endTime) step.endTime = new Date();
step.status = "completed";
onStepsUpdate(newSteps);
// 다음 단계로 이동
if (currentStepIndex < workSteps.length - 1) {
onStepChange(currentStepIndex + 1);
}
};
const renderStepForm = () => {
if (!currentStep) return null;
const isCompleted = currentStep.status === "completed";
if (currentStep.type === "work" || currentStep.type === "record") {
return (
<div className="pop-step-form-section">
<h4 className="pop-step-form-title"> </h4>
<div className="pop-form-row">
<div className="pop-form-group">
<label className="pop-form-label"></label>
<input
type="number"
className="pop-input"
placeholder="0"
disabled={isCompleted}
/>
</div>
<div className="pop-form-group">
<label className="pop-form-label"></label>
<input
type="number"
className="pop-input"
placeholder="0"
disabled={isCompleted}
/>
</div>
</div>
<div className="pop-form-group">
<label className="pop-form-label"></label>
<textarea
className="pop-input"
rows={2}
placeholder="특이사항을 입력하세요"
disabled={isCompleted}
/>
</div>
</div>
);
}
if (currentStep.type === "equipment-check" || currentStep.type === "inspection") {
return (
<div className="pop-step-form-section">
<h4 className="pop-step-form-title"> </h4>
<div style={{ display: "flex", flexDirection: "column", gap: "var(--spacing-sm)" }}>
<label style={{ display: "flex", alignItems: "center", gap: "var(--spacing-sm)" }}>
<input type="checkbox" disabled={isCompleted} />
<span> </span>
</label>
<label style={{ display: "flex", alignItems: "center", gap: "var(--spacing-sm)" }}>
<input type="checkbox" disabled={isCompleted} />
<span> </span>
</label>
<label style={{ display: "flex", alignItems: "center", gap: "var(--spacing-sm)" }}>
<input type="checkbox" disabled={isCompleted} />
<span> </span>
</label>
</div>
<div className="pop-form-group" style={{ marginTop: "var(--spacing-md)" }}>
<label className="pop-form-label"></label>
<textarea
className="pop-input"
rows={2}
placeholder="점검 결과를 입력하세요"
disabled={isCompleted}
/>
</div>
</div>
);
}
return (
<div className="pop-step-form-section">
<h4 className="pop-step-form-title"> </h4>
<div className="pop-form-group">
<textarea
className="pop-input"
rows={3}
placeholder="메모를 입력하세요"
disabled={isCompleted}
/>
</div>
</div>
);
};
return (
<div className="pop-slide-panel active">
<div className="pop-slide-panel-overlay" onClick={onClose} />
<div className="pop-slide-panel-content">
{/* 헤더 */}
<div className="pop-slide-panel-header">
<div style={{ display: "flex", alignItems: "center", gap: "var(--spacing-md)" }}>
<h2 className="pop-slide-panel-title"></h2>
<span className="pop-badge pop-badge-primary">{workOrder.processName}</span>
</div>
<div style={{ display: "flex", alignItems: "center", gap: "var(--spacing-md)" }}>
<div style={{ fontSize: "var(--text-xs)", color: "rgb(var(--text-muted))" }}>
<span>{formatDate(currentDateTime)}</span>
<span style={{ marginLeft: "var(--spacing-sm)", color: "rgb(var(--neon-cyan))", fontWeight: 700 }}>
{formatTime(currentDateTime)}
</span>
</div>
<button className="pop-icon-btn" onClick={onClose}>
<X size={16} />
</button>
</div>
</div>
{/* 작업지시 정보 */}
<div className="pop-work-order-info-section">
<div className="pop-work-order-info-card">
<div className="pop-work-order-info-item">
<span className="label"></span>
<span className="value primary">{workOrder.id}</span>
</div>
<div className="pop-work-order-info-item">
<span className="label"></span>
<span className="value">{workOrder.itemName}</span>
</div>
<div className="pop-work-order-info-item">
<span className="label"></span>
<span className="value">{workOrder.spec}</span>
</div>
<div className="pop-work-order-info-item">
<span className="label"></span>
<span className="value">{workOrder.orderQuantity} EA</span>
</div>
<div className="pop-work-order-info-item">
<span className="label"></span>
<span className="value">{workOrder.producedQuantity} EA</span>
</div>
<div className="pop-work-order-info-item">
<span className="label"></span>
<span className="value">{workOrder.dueDate}</span>
</div>
</div>
</div>
{/* 바디 */}
<div className="pop-slide-panel-body">
<div className="pop-panel-body-content">
{/* 작업순서 사이드바 */}
<div className="pop-work-steps-sidebar">
<div className="pop-work-steps-header"></div>
<div className="pop-work-steps-list">
{workSteps.map((step, index) => (
<div
key={step.id}
className={`pop-work-step-item ${index === currentStepIndex ? "active" : ""} ${step.status}`}
onClick={() => onStepChange(index)}
>
<div className="pop-work-step-number">{index + 1}</div>
<div className="pop-work-step-info">
<div className="pop-work-step-name">{step.name}</div>
<div className="pop-work-step-time">
{formatTime(step.startTime)} ~ {formatTime(step.endTime)}
</div>
</div>
<span className={`pop-work-step-status ${step.status}`}>
{step.status === "completed" ? "완료" : step.status === "in-progress" ? "진행중" : "대기"}
</span>
</div>
))}
</div>
</div>
{/* 작업 콘텐츠 영역 */}
<div className="pop-work-content-area">
{currentStep && (
<>
{/* 스텝 헤더 */}
<div className="pop-step-header">
<h3 className="pop-step-title">{currentStep.name}</h3>
<p className="pop-step-description">{currentStep.description}</p>
</div>
{/* 시간 컨트롤 */}
{currentStep.status !== "completed" && (
<div className="pop-step-time-controls">
<button
className="pop-time-control-btn start"
onClick={handleStartStep}
disabled={!!currentStep.startTime}
>
<Play size={16} />
{currentStep.startTime ? formatTime(currentStep.startTime) : ""}
</button>
<button
className="pop-time-control-btn end"
onClick={handleEndStep}
disabled={!currentStep.startTime || !!currentStep.endTime}
>
<Square size={16} />
{currentStep.endTime ? formatTime(currentStep.endTime) : ""}
</button>
</div>
)}
{/* 폼 */}
{renderStepForm()}
{/* 액션 버튼 */}
{currentStep.status !== "completed" && (
<div style={{ marginTop: "auto", display: "flex", gap: "var(--spacing-md)" }}>
<button
className="pop-btn pop-btn-outline"
style={{ flex: 1 }}
onClick={() => onStepChange(Math.max(0, currentStepIndex - 1))}
disabled={currentStepIndex === 0}
>
</button>
<button
className="pop-btn pop-btn-primary"
style={{ flex: 1 }}
onClick={handleSaveAndNext}
>
{currentStepIndex === workSteps.length - 1 ? "완료" : "저장 후 다음"}
<ChevronRight size={16} />
</button>
</div>
)}
{/* 완료 메시지 */}
{currentStep.status === "completed" && (
<div
style={{
padding: "var(--spacing-md)",
background: "rgba(0, 255, 136, 0.1)",
border: "1px solid rgba(0, 255, 136, 0.3)",
borderRadius: "var(--radius-md)",
display: "flex",
alignItems: "center",
gap: "var(--spacing-sm)",
color: "rgb(var(--success))",
}}
>
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2">
<polyline points="20 6 9 17 4 12" />
</svg>
<span style={{ fontWeight: 600 }}> </span>
</div>
)}
</>
)}
</div>
</div>
</div>
{/* 푸터 */}
<div className="pop-slide-panel-footer">
<button className="pop-btn pop-btn-outline" style={{ flex: 1 }} onClick={onClose}>
</button>
<button className="pop-btn pop-btn-primary" style={{ flex: 1 }}>
</button>
</div>
</div>
</div>
);
}