"use client"; import React, { useState, useMemo, useEffect, useCallback } from "react"; import { Button } from "@/components/ui/button"; import { Input } from "@/components/ui/input"; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue, } from "@/components/ui/select"; import { Card, CardContent } from "@/components/ui/card"; import { Badge } from "@/components/ui/badge"; import { Checkbox } from "@/components/ui/checkbox"; import { Label } from "@/components/ui/label"; import { ResizableHandle, ResizablePanel, ResizablePanelGroup, } from "@/components/ui/resizable"; import { Search, RotateCcw, Package, ClipboardList, Factory, MapPin, AlertTriangle, CheckCircle2, } from "lucide-react"; import { cn } from "@/lib/utils"; // --- Types --- type WorkOrderStatus = "pending" | "in_progress"; interface WorkOrder { id: string; itemCode: string; itemName: string; quantity: number; date: string; status: WorkOrderStatus; } interface MaterialLocation { location: string; qty: number; } interface Material { code: string; name: string; required: number; current: number; unit: string; locations: MaterialLocation[]; } interface Warehouse { code: string; name: string; } // --- Sample Data --- const sampleWarehouses: Warehouse[] = [ { code: "WH001", name: "제1창고 (위치관리)" }, { code: "WH002", name: "제2창고 (위치관리)" }, { code: "WH003", name: "제3창고 (위치관리)" }, ]; const sampleWorkOrders: WorkOrder[] = [ { id: "WO2024001", itemCode: "PROD-A001", itemName: "상품 A", quantity: 1000, date: "2024-11-06", status: "pending", }, { id: "WO2024002", itemCode: "PROD-A002", itemName: "상품 B", quantity: 500, date: "2024-11-07", status: "pending", }, { id: "WO2024003", itemCode: "PROD-A003", itemName: "상품 C", quantity: 800, date: "2024-11-08", status: "pending", }, { id: "WO2024004", itemCode: "PROD-A004", itemName: "상품 D", quantity: 1200, date: "2024-11-09", status: "in_progress", }, ]; const sampleMaterials: Material[] = [ { code: "MAT-R001", name: "원자재 A", required: 5000, current: 4200, unit: "kg", locations: [ { location: "A-01-01", qty: 2000 }, { location: "A-01-02", qty: 1500 }, { location: "A-01-03", qty: 700 }, ], }, { code: "MAT-R002", name: "원자재 B", required: 3000, current: 3500, unit: "kg", locations: [ { location: "A-02-01", qty: 2000 }, { location: "A-02-02", qty: 1500 }, ], }, { code: "MAT-R003", name: "원자재 C", required: 2000, current: 800, unit: "EA", locations: [ { location: "B-01-01", qty: 500 }, { location: "B-01-02", qty: 300 }, ], }, { code: "MAT-R004", name: "원자재 D", required: 1500, current: 1500, unit: "L", locations: [{ location: "C-01-01", qty: 1500 }], }, { code: "MAT-R005", name: "원자재 E", required: 4000, current: 2500, unit: "kg", locations: [ { location: "A-03-01", qty: 1000 }, { location: "A-03-02", qty: 1000 }, { location: "A-03-03", qty: 500 }, ], }, ]; const formatDate = (date: Date) => { const y = date.getFullYear(); const m = String(date.getMonth() + 1).padStart(2, "0"); const d = String(date.getDate()).padStart(2, "0"); return `${y}-${m}-${d}`; }; const getStatusLabel = (status: WorkOrderStatus) => status === "pending" ? "대기" : "진행중"; const getStatusStyle = (status: WorkOrderStatus) => status === "pending" ? "bg-amber-100 text-amber-700 border-amber-200" : "bg-blue-100 text-blue-700 border-blue-200"; export default function MaterialStatusPage() { const today = new Date(); const weekAgo = new Date(today); weekAgo.setDate(today.getDate() - 7); const [searchDateFrom, setSearchDateFrom] = useState(formatDate(weekAgo)); const [searchDateTo, setSearchDateTo] = useState(formatDate(today)); const [searchItemCode, setSearchItemCode] = useState(""); const [searchItemName, setSearchItemName] = useState(""); const [workOrders] = useState(sampleWorkOrders); const [checkedWoIds, setCheckedWoIds] = useState([]); const [selectedWoId, setSelectedWoId] = useState(null); const [warehouse, setWarehouse] = useState(sampleWarehouses[0]?.code || ""); const [materialSearch, setMaterialSearch] = useState(""); const [showShortageOnly, setShowShortageOnly] = useState(false); const [materials] = useState(sampleMaterials); const isAllChecked = workOrders.length > 0 && checkedWoIds.length === workOrders.length; const handleCheckAll = useCallback( (checked: boolean) => { setCheckedWoIds(checked ? workOrders.map((wo) => wo.id) : []); }, [workOrders] ); const handleCheckWo = useCallback((id: string, checked: boolean) => { setCheckedWoIds((prev) => checked ? [...prev, id] : prev.filter((i) => i !== id) ); }, []); const handleSelectWo = useCallback((id: string) => { setSelectedWoId((prev) => (prev === id ? null : id)); }, []); const handleLoadSelectedMaterials = useCallback(() => { if (checkedWoIds.length === 0) { alert("자재를 조회할 작업지시를 선택해주세요."); return; } console.log("선택된 작업지시:", checkedWoIds); }, [checkedWoIds]); const handleResetSearch = useCallback(() => { const t = new Date(); const w = new Date(t); w.setDate(t.getDate() - 7); setSearchDateFrom(formatDate(w)); setSearchDateTo(formatDate(t)); setSearchItemCode(""); setSearchItemName(""); setMaterialSearch(""); setShowShortageOnly(false); }, []); const filteredMaterials = useMemo(() => { if (!warehouse) return []; return materials.filter((m) => { const searchLower = materialSearch.toLowerCase(); const matchesSearch = !materialSearch || m.code.toLowerCase().includes(searchLower) || m.name.toLowerCase().includes(searchLower); const matchesShortage = !showShortageOnly || m.current < m.required; return matchesSearch && matchesShortage; }); }, [materials, warehouse, materialSearch, showShortageOnly]); return (
{/* 헤더 */}

자재현황

작업지시 대비 원자재 재고 현황

{/* 검색 영역 */}
setSearchDateFrom(e.target.value)} /> ~ setSearchDateTo(e.target.value)} />
setSearchItemCode(e.target.value)} />
setSearchItemName(e.target.value)} />
{/* 메인 콘텐츠 (좌우 분할) */}
{/* 왼쪽: 작업지시 리스트 */}
{/* 패널 헤더 */}
작업지시 리스트
{workOrders.length}
{/* 작업지시 목록 */}
{workOrders.length === 0 ? (

작업지시가 없습니다

) : ( workOrders.map((wo) => (
handleSelectWo(wo.id)} >
e.stopPropagation()} > handleCheckWo(wo.id, c as boolean) } />
{wo.id} {getStatusLabel(wo.status)}
{wo.itemName} ({wo.itemCode})
수량: {wo.quantity.toLocaleString()}개 | 일자: {wo.date}
)) )}
{/* 오른쪽: 원자재 현황 */}
{/* 패널 헤더 */}
원자재 재고 현황
{/* 필터 */}
setMaterialSearch(e.target.value)} /> {filteredMaterials.length}개 품목
{/* 원자재 목록 */}
{!warehouse ? (

창고를 선택해주세요

) : filteredMaterials.length === 0 ? (

조회된 원자재가 없습니다

) : ( filteredMaterials.map((material) => { const shortage = material.required - material.current; const isShortage = shortage > 0; const percentage = Math.min( (material.current / material.required) * 100, 100 ); return (
{/* 메인 정보 라인 */}
{material.name} ({material.code}) | 필요: {material.required.toLocaleString()} {material.unit} | 현재: {material.current.toLocaleString()} {material.unit} | {isShortage ? "부족:" : "여유:"} {Math.abs(shortage).toLocaleString()} {material.unit} ({percentage.toFixed(0)}%) {isShortage ? ( 부족 ) : ( 충분 )}
{/* 위치별 재고 */}
{material.locations.map((loc, idx) => ( {loc.location} {loc.qty.toLocaleString()} {material.unit} ))}
); }) )}
); }