"use client"; import React, { useState, useRef, useCallback, useEffect } from "react"; import { useRouter } from "next/navigation"; import { apiClient } from "@/lib/api/client"; /* ------------------------------------------------------------------ */ /* Types */ /* ------------------------------------------------------------------ */ interface ProductionMenuItem { id: string; title: string; gradient: string; shadowColor: string; icon: React.ReactNode; href: string; } interface RecentActivityItem { id: string; time: string; processName: string; itemName: string; qty: string; statusColor: string; statusLabel: string; } interface KpiData { todayCompleted: number; inProgressCount: number; completedCount: number; } /* ------------------------------------------------------------------ */ /* Data */ /* ------------------------------------------------------------------ */ const MENU_ITEMS: ProductionMenuItem[] = [ { id: "process", title: "공정실행", gradient: "linear-gradient(135deg,#f59e0b,#d97706)", shadowColor: "rgba(245,158,11,.3)", icon: ( ), href: "/pop/production/process", }, { id: "work-order", title: "작업지시", gradient: "linear-gradient(135deg,#f59e0b,#d97706)", shadowColor: "rgba(245,158,11,.3)", icon: ( ), href: "#", }, { id: "status", title: "생산현황", gradient: "linear-gradient(135deg,#f59e0b,#d97706)", shadowColor: "rgba(245,158,11,.3)", icon: ( ), href: "#", }, { id: "defect", title: "불량관리", gradient: "linear-gradient(135deg,#f59e0b,#d97706)", shadowColor: "rgba(245,158,11,.3)", icon: ( ), href: "#", }, { id: "result", title: "실적조회", gradient: "linear-gradient(135deg,#f59e0b,#d97706)", shadowColor: "rgba(245,158,11,.3)", icon: ( ), href: "#", }, ]; /* ------------------------------------------------------------------ */ /* Component */ /* ------------------------------------------------------------------ */ export function ProductionMain() { const router = useRouter(); /* KPI carousel */ const [kpiIdx, setKpiIdx] = useState(0); const kpiTimerRef = useRef | null>(null); /* Data state */ const [kpi, setKpi] = useState({ todayCompleted: 0, inProgressCount: 0, completedCount: 0, }); const [recentItems, setRecentItems] = useState([]); const [loading, setLoading] = useState(true); const startKpiAuto = useCallback(() => { if (kpiTimerRef.current) clearInterval(kpiTimerRef.current); kpiTimerRef.current = setInterval(() => setKpiIdx((p) => (p + 1) % 3), 4000); }, []); useEffect(() => { startKpiAuto(); return () => { if (kpiTimerRef.current) clearInterval(kpiTimerRef.current); }; }, [startKpiAuto]); /* Fetch real data */ useEffect(() => { const fetchData = async () => { try { setLoading(true); const wiRes = await apiClient.get("/work-instruction/list"); let wiData: any[] = []; if (wiRes.data?.data) { wiData = Array.isArray(wiRes.data.data) ? wiRes.data.data : wiRes.data.data.rows || []; } else if (Array.isArray(wiRes.data)) { wiData = wiRes.data; } // KPI 계산 let todayCompleted = 0; let inProgressCount = 0; let completedCount = 0; for (const wi of wiData) { const status = wi.progress_status || wi.status || ""; if (status === "completed" || status === "완료") { completedCount++; todayCompleted += Number(wi.completed_qty) || 0; } else if (status === "in_progress" || status === "진행중") { inProgressCount++; } } setKpi({ todayCompleted, inProgressCount, completedCount }); // 최근 활동 5건 (최신순) const sorted = [...wiData].sort((a, b) => { const da = new Date(a.start_date || a.created_date || "").getTime() || 0; const db = new Date(b.start_date || b.created_date || "").getTime() || 0; return db - da; }); const recent: RecentActivityItem[] = sorted.slice(0, 5).map((wi, idx) => { const dateObj = wi.start_date ? new Date(wi.start_date) : null; const time = dateObj ? `${String(dateObj.getMonth() + 1).padStart(2, "0")}/${String(dateObj.getDate()).padStart(2, "0")}` : "--/--"; const status = wi.progress_status || wi.status || ""; let statusColor = "text-gray-600 bg-gray-50"; let statusLabel = "대기"; if (status === "completed" || status === "완료") { statusColor = "text-green-600 bg-green-50"; statusLabel = "완료"; } else if (status === "in_progress" || status === "진행중") { statusColor = "text-blue-600 bg-blue-50"; statusLabel = "진행중"; } else if (status === "acceptable" || status === "접수가능") { statusColor = "text-amber-600 bg-amber-50"; statusLabel = "접수가능"; } return { id: String(wi.id || idx), time, processName: wi.routing ? "공정 있음" : "공정 미설정", itemName: wi.item_name || wi.item_code || wi.work_instruction_no || "-", qty: `${(Number(wi.qty) || 0).toLocaleString()} EA`, statusColor, statusLabel, }; }); setRecentItems(recent); } catch { // 실패 시 0/빈 배열 유지 } finally { setLoading(false); } }; fetchData(); }, []); const handleMenuClick = (item: ProductionMenuItem) => { if (item.href === "#") { alert(`${item.title} 화면은 준비 중입니다.`); } else { router.push(item.href); } }; return ( {/* ===== Back + Title ===== */} router.push("/pop/home")} className="w-10 h-10 rounded-xl bg-white border border-gray-200 flex items-center justify-center text-gray-500 hover:bg-gray-50 active:scale-95 transition-all" > 생산관리 메뉴를 선택하세요 {/* ===== KPI Carousel ===== */} {/* Slide 1 */} {/* Single dot (only 1 slide) */} {[0].map((idx) => ( { setKpiIdx(idx); startKpiAuto(); }} className="border-none p-0 transition-all duration-300 cursor-pointer" style={{ width: 24, height: 8, borderRadius: 4, background: "#f59e0b", }} aria-label={`KPI 슬라이드 ${idx + 1}`} /> ))} {/* ===== Menu Icons ===== */} 생산 메뉴 {MENU_ITEMS.map((item) => ( ))} {/* ===== Recent Activity ===== */} 최근 생산활동 최근 5건 {loading ? ( {[1, 2, 3].map((i) => ( ))} ) : recentItems.length === 0 ? ( 최근 생산활동 내역이 없습니다 ) : ( recentItems.map((item) => ( {item.time} {item.itemName} {item.statusLabel} {item.processName} | {item.qty} )) )} ); } /* ------------------------------------------------------------------ */ /* Sub-components */ /* ------------------------------------------------------------------ */ function KpiCell({ value, label, color, unit }: { value: string; label: string; color: string; unit?: string }) { return ( {value} {unit && {unit}} {label} ); } function MenuIcon({ item, onClick }: { item: ProductionMenuItem; onClick: (item: ProductionMenuItem) => void }) { return ( onClick(item)} > {item.icon} {item.title} ); }
메뉴를 선택하세요