diff options
Diffstat (limited to 'lib/material/vendor-material/simple-vendor-materials.tsx')
| -rw-r--r-- | lib/material/vendor-material/simple-vendor-materials.tsx | 372 |
1 files changed, 372 insertions, 0 deletions
diff --git a/lib/material/vendor-material/simple-vendor-materials.tsx b/lib/material/vendor-material/simple-vendor-materials.tsx new file mode 100644 index 00000000..73fdfd18 --- /dev/null +++ b/lib/material/vendor-material/simple-vendor-materials.tsx @@ -0,0 +1,372 @@ +"use client"; + +import * as React from "react"; +import { useState, useMemo } from "react"; +import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card"; +import { Input } from "@/components/ui/input"; +import { Button } from "@/components/ui/button"; +import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from "@/components/ui/table"; +import { ArrowUpDown, Search } from "lucide-react"; +import { VendorPossibleMaterial } from "../vendor-possible-material-service"; +import { AddConfirmedMaterial } from "./add-confirmed-material"; +// formatDate 함수 제거 - YYYY-MM-DD 형식으로 직접 포맷 + +interface SimpleVendorMaterialsProps { + vendorId: number; + confirmedMaterials: VendorPossibleMaterial[]; + vendorInputMaterials: VendorPossibleMaterial[]; + onDataRefresh?: () => void; +} + +type SortField = "itemCode" | "itemName" | "createdAt" | "recentPoDate" | "recentDeliveryDate" | "vendorTypeNameEn" | "recentPoNo" | "registerUserName"; +type SortDirection = "asc" | "desc"; + +interface SortState { + field: SortField | null; + direction: SortDirection; +} + +export function SimpleVendorMaterials({ + vendorId, + confirmedMaterials, + vendorInputMaterials, + onDataRefresh, +}: SimpleVendorMaterialsProps) { + // 날짜를 YYYY-MM-DD 형식으로 포맷하는 함수 + const formatDateToYMD = (date: Date | null | undefined): string => { + if (!date) return "-"; + return date.toISOString().split('T')[0]; + }; + const [confirmedSearch, setConfirmedSearch] = useState(""); + const [vendorInputSearch, setVendorInputSearch] = useState(""); + const [confirmedSort, setConfirmedSort] = useState<SortState>({ field: null, direction: "asc" }); + const [vendorInputSort, setVendorInputSort] = useState<SortState>({ field: null, direction: "asc" }); + + // 검색 필터링 함수 + const filterMaterials = (materials: VendorPossibleMaterial[], searchTerm: string) => { + if (!searchTerm.trim()) return materials; + + const lowercaseSearch = searchTerm.toLowerCase(); + return materials.filter(material => + (material.itemCode?.toLowerCase().includes(lowercaseSearch)) || + (material.itemName?.toLowerCase().includes(lowercaseSearch)) + ); + }; + + // 정렬 함수 + const sortMaterials = (materials: VendorPossibleMaterial[], sortState: SortState) => { + if (!sortState.field) return materials; + + return [...materials].sort((a, b) => { + let aValue: string | Date; + let bValue: string | Date; + + switch (sortState.field) { + case "itemCode": + aValue = a.itemCode || ""; + bValue = b.itemCode || ""; + break; + case "itemName": + aValue = a.itemName || ""; + bValue = b.itemName || ""; + break; + case "createdAt": + aValue = a.createdAt; + bValue = b.createdAt; + break; + case "recentPoDate": + aValue = a.recentPoDate || new Date(0); + bValue = b.recentPoDate || new Date(0); + break; + case "recentDeliveryDate": + aValue = a.recentDeliveryDate || new Date(0); + bValue = b.recentDeliveryDate || new Date(0); + break; + case "vendorTypeNameEn": + aValue = a.vendorTypeNameEn || ""; + bValue = b.vendorTypeNameEn || ""; + break; + case "recentPoNo": + aValue = a.recentPoNo || ""; + bValue = b.recentPoNo || ""; + break; + case "registerUserName": + aValue = a.registerUserName || ""; + bValue = b.registerUserName || ""; + break; + default: + return 0; + } + + if (aValue < bValue) return sortState.direction === "asc" ? -1 : 1; + if (aValue > bValue) return sortState.direction === "asc" ? 1 : -1; + return 0; + }); + }; + + // 필터링 및 정렬된 데이터 + const filteredAndSortedConfirmed = useMemo(() => { + const filtered = filterMaterials(confirmedMaterials, confirmedSearch); + return sortMaterials(filtered, confirmedSort); + }, [confirmedMaterials, confirmedSearch, confirmedSort]); + + const filteredAndSortedVendorInput = useMemo(() => { + const filtered = filterMaterials(vendorInputMaterials, vendorInputSearch); + return sortMaterials(filtered, vendorInputSort); + }, [vendorInputMaterials, vendorInputSearch, vendorInputSort]); + + // 정렬 핸들러 + const handleSort = ( + field: SortField, + currentSort: SortState, + setSort: React.Dispatch<React.SetStateAction<SortState>> + ) => { + setSort(prev => ({ + field, + direction: prev.field === field && prev.direction === "asc" ? "desc" : "asc" + })); + }; + + // 정렬 버튼 컴포넌트 + const SortButton = ({ + field, + children, + onSort + }: { + field: SortField; + children: React.ReactNode; + onSort: (field: SortField) => void; + }) => ( + <Button + variant="ghost" + size="sm" + className="h-8 px-2" + onClick={() => onSort(field)} + > + {children} + <ArrowUpDown className="ml-2 h-4 w-4" /> + </Button> + ); + + return ( + <div className="space-y-8"> + {/* 확정정보 테이블 */} + <Card> + <CardHeader> + <div className="flex items-center justify-between"> + <div> + <CardTitle>확정정보</CardTitle> + <CardDescription> + 구매담당자가 확정한 공급 품목 정보입니다. + </CardDescription> + </div> + <AddConfirmedMaterial + vendorId={vendorId} + existingConfirmedMaterials={confirmedMaterials} + onMaterialAdded={onDataRefresh} + /> + </div> + <div className="flex items-center space-x-2"> + <Search className="h-4 w-4 text-muted-foreground" /> + <Input + placeholder="자재그룹코드 또는 자재그룹명으로 검색..." + value={confirmedSearch} + onChange={(e) => setConfirmedSearch(e.target.value)} + className="max-w-sm" + /> + </div> + </CardHeader> + <CardContent> + <div className="rounded-md border max-h-96 overflow-auto"> + <Table> + <TableHeader className="sticky top-0 bg-background z-10"> + <TableRow> + <TableHead> + <SortButton + field="vendorTypeNameEn" + onSort={(field) => handleSort(field, confirmedSort, setConfirmedSort)} + > + 업체유형 + </SortButton> + </TableHead> + <TableHead> + <SortButton + field="itemCode" + onSort={(field) => handleSort(field, confirmedSort, setConfirmedSort)} + > + 자재그룹코드 + </SortButton> + </TableHead> + <TableHead> + <SortButton + field="itemName" + onSort={(field) => handleSort(field, confirmedSort, setConfirmedSort)} + > + 자재그룹명 + </SortButton> + </TableHead> + <TableHead> + <SortButton + field="recentPoNo" + onSort={(field) => handleSort(field, confirmedSort, setConfirmedSort)} + > + 최근 Po No. + </SortButton> + </TableHead> + <TableHead> + <SortButton + field="recentPoDate" + onSort={(field) => handleSort(field, confirmedSort, setConfirmedSort)} + > + 최근 Po일 + </SortButton> + </TableHead> + <TableHead> + <SortButton + field="recentDeliveryDate" + onSort={(field) => handleSort(field, confirmedSort, setConfirmedSort)} + > + 최근 납품일 + </SortButton> + </TableHead> + <TableHead> + <SortButton + field="createdAt" + onSort={(field) => handleSort(field, confirmedSort, setConfirmedSort)} + > + 등록일 + </SortButton> + </TableHead> + <TableHead> + <SortButton + field="registerUserName" + onSort={(field) => handleSort(field, confirmedSort, setConfirmedSort)} + > + 등록자명 + </SortButton> + </TableHead> + </TableRow> + </TableHeader> + <TableBody> + {filteredAndSortedConfirmed.length === 0 ? ( + <TableRow> + <TableCell colSpan={8} className="text-center text-muted-foreground"> + {confirmedSearch ? "검색 결과가 없습니다." : "확정된 공급품목이 없습니다."} + </TableCell> + </TableRow> + ) : ( + filteredAndSortedConfirmed.map((material) => ( + <TableRow key={material.id}> + <TableCell>{material.vendorTypeNameEn || "-"}</TableCell> + <TableCell className="font-mono">{material.itemCode || "-"}</TableCell> + <TableCell>{material.itemName || "-"}</TableCell> + <TableCell className="font-mono">{material.recentPoNo || "-"}</TableCell> + <TableCell>{formatDateToYMD(material.recentPoDate)}</TableCell> + <TableCell>{formatDateToYMD(material.recentDeliveryDate)}</TableCell> + <TableCell>{formatDateToYMD(material.createdAt)}</TableCell> + <TableCell>{material.registerUserName || "-"}</TableCell> + </TableRow> + )) + )} + </TableBody> + </Table> + </div> + <div className="mt-2 text-sm text-muted-foreground"> + 총 {filteredAndSortedConfirmed.length}건 + </div> + </CardContent> + </Card> + + {/* 업체입력정보 테이블 */} + <Card> + <CardHeader> + <CardTitle>업체입력정보</CardTitle> + <CardDescription> + 업체에서 입력한 공급 가능 품목 정보입니다. + </CardDescription> + <div className="flex items-center space-x-2"> + <Search className="h-4 w-4 text-muted-foreground" /> + <Input + placeholder="자재그룹코드 또는 자재그룹명으로 검색..." + value={vendorInputSearch} + onChange={(e) => setVendorInputSearch(e.target.value)} + className="max-w-sm" + /> + </div> + </CardHeader> + <CardContent> + <div className="rounded-md border max-h-96 overflow-auto"> + <Table> + <TableHeader className="sticky top-0 bg-background z-10"> + <TableRow> + <TableHead> + <SortButton + field="vendorTypeNameEn" + onSort={(field) => handleSort(field, vendorInputSort, setVendorInputSort)} + > + 업체유형 + </SortButton> + </TableHead> + <TableHead> + <SortButton + field="itemCode" + onSort={(field) => handleSort(field, vendorInputSort, setVendorInputSort)} + > + 자재그룹코드 + </SortButton> + </TableHead> + <TableHead> + <SortButton + field="itemName" + onSort={(field) => handleSort(field, vendorInputSort, setVendorInputSort)} + > + 자재그룹명 + </SortButton> + </TableHead> + <TableHead> + <SortButton + field="createdAt" + onSort={(field) => handleSort(field, vendorInputSort, setVendorInputSort)} + > + 등록일 + </SortButton> + </TableHead> + <TableHead> + <SortButton + field="registerUserName" + onSort={(field) => handleSort(field, vendorInputSort, setVendorInputSort)} + > + 등록자명 + </SortButton> + </TableHead> + </TableRow> + </TableHeader> + <TableBody> + {filteredAndSortedVendorInput.length === 0 ? ( + <TableRow> + <TableCell colSpan={5} className="text-center text-muted-foreground"> + {vendorInputSearch ? "검색 결과가 없습니다." : "업체입력 정보가 없습니다."} + </TableCell> + </TableRow> + ) : ( + filteredAndSortedVendorInput.map((material) => ( + <TableRow key={material.id}> + <TableCell>{material.vendorTypeNameEn || "-"}</TableCell> + <TableCell className="font-mono">{material.itemCode || "-"}</TableCell> + <TableCell>{material.itemName || "-"}</TableCell> + <TableCell>{formatDateToYMD(material.createdAt)}</TableCell> + <TableCell>{material.registerUserName || "-"}</TableCell> + </TableRow> + )) + )} + </TableBody> + </Table> + </div> + <div className="mt-2 text-sm text-muted-foreground"> + 총 {filteredAndSortedVendorInput.length}건 + </div> + </CardContent> + </Card> + </div> + ); +} |
