summaryrefslogtreecommitdiff
path: root/lib/material/vendor-material/simple-vendor-materials.tsx
diff options
context:
space:
mode:
Diffstat (limited to 'lib/material/vendor-material/simple-vendor-materials.tsx')
-rw-r--r--lib/material/vendor-material/simple-vendor-materials.tsx372
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>
+ );
+}