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.tsx138
1 files changed, 118 insertions, 20 deletions
diff --git a/lib/material/vendor-material/simple-vendor-materials.tsx b/lib/material/vendor-material/simple-vendor-materials.tsx
index 73fdfd18..5b5128c4 100644
--- a/lib/material/vendor-material/simple-vendor-materials.tsx
+++ b/lib/material/vendor-material/simple-vendor-materials.tsx
@@ -6,9 +6,12 @@ import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/com
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 { Checkbox } from "@/components/ui/checkbox";
+import { ArrowUpDown, Search, Trash2 } from "lucide-react";
+import { VendorPossibleMaterial, removeConfirmedMaterials } from "../vendor-possible-material-service";
import { AddConfirmedMaterial } from "./add-confirmed-material";
+import { toast } from "sonner";
+import { useTransition } from "react";
// formatDate 함수 제거 - YYYY-MM-DD 형식으로 직접 포맷
interface SimpleVendorMaterialsProps {
@@ -37,22 +40,16 @@ export function SimpleVendorMaterials({
if (!date) return "-";
return date.toISOString().split('T')[0];
};
+
+ // 선택 상태 관리
+ const [selectedConfirmedIds, setSelectedConfirmedIds] = useState<number[]>([]);
+ const [isDeletePending, startDeleteTransition] = useTransition();
+
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;
@@ -104,7 +101,18 @@ export function SimpleVendorMaterials({
});
};
- // 필터링 및 정렬된 데이터
+
+ // 검색 필터링 함수
+ 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 filteredAndSortedConfirmed = useMemo(() => {
const filtered = filterMaterials(confirmedMaterials, confirmedSearch);
return sortMaterials(filtered, confirmedSort);
@@ -115,6 +123,63 @@ export function SimpleVendorMaterials({
return sortMaterials(filtered, vendorInputSort);
}, [vendorInputMaterials, vendorInputSearch, vendorInputSort]);
+ // 선택 상태 관리 함수들 (필터링된 데이터 이후에 선언)
+ const handleSelectAllConfirmed = (checked: boolean) => {
+ if (checked) {
+ const allIds = filteredAndSortedConfirmed.map(material => material.id);
+ setSelectedConfirmedIds(allIds);
+ } else {
+ setSelectedConfirmedIds([]);
+ }
+ };
+
+ // 데이터 변경 시 선택 상태 정리 (유효하지 않은 ID들 제거)
+ React.useEffect(() => {
+ const validIds = confirmedMaterials.map(material => material.id);
+ setSelectedConfirmedIds(prev => prev.filter(id => validIds.includes(id)));
+ }, [confirmedMaterials]);
+
+ // 전체 선택 체크박스의 상태 계산
+ const isAllSelected = filteredAndSortedConfirmed.length > 0 &&
+ selectedConfirmedIds.length === filteredAndSortedConfirmed.length &&
+ filteredAndSortedConfirmed.every(material => selectedConfirmedIds.includes(material.id));
+
+ const isIndeterminate = selectedConfirmedIds.length > 0 &&
+ selectedConfirmedIds.length < filteredAndSortedConfirmed.length;
+
+ const handleSelectConfirmed = (id: number, checked: boolean) => {
+ if (checked) {
+ setSelectedConfirmedIds(prev => [...prev, id]);
+ } else {
+ setSelectedConfirmedIds(prev => prev.filter(selectedId => selectedId !== id));
+ }
+ };
+
+ // 삭제 함수
+ const handleDeleteSelected = () => {
+ if (selectedConfirmedIds.length === 0) {
+ toast.error("삭제할 항목을 선택해주세요.");
+ return;
+ }
+
+ startDeleteTransition(async () => {
+ const result = await removeConfirmedMaterials(vendorId, selectedConfirmedIds);
+
+ if (result.success) {
+ toast.success(result.message);
+ setSelectedConfirmedIds([]); // 선택 상태 초기화
+ onDataRefresh?.(); // 데이터 새로고침
+ } else {
+ toast.error(result.message);
+ }
+ });
+ };
+
+
+
+
+
+
// 정렬 핸들러
const handleSort = (
field: SortField,
@@ -160,11 +225,25 @@ export function SimpleVendorMaterials({
구매담당자가 확정한 공급 품목 정보입니다.
</CardDescription>
</div>
- <AddConfirmedMaterial
- vendorId={vendorId}
- existingConfirmedMaterials={confirmedMaterials}
- onMaterialAdded={onDataRefresh}
- />
+ <div className="flex items-center gap-2">
+ {selectedConfirmedIds.length > 0 && (
+ <Button
+ variant="destructive"
+ size="sm"
+ onClick={handleDeleteSelected}
+ disabled={isDeletePending}
+ className="flex items-center gap-2"
+ >
+ <Trash2 className="h-4 w-4" />
+ {isDeletePending ? "삭제 중..." : `삭제 (${selectedConfirmedIds.length})`}
+ </Button>
+ )}
+ <AddConfirmedMaterial
+ vendorId={vendorId}
+ existingConfirmedMaterials={confirmedMaterials}
+ onMaterialAdded={onDataRefresh}
+ />
+ </div>
</div>
<div className="flex items-center space-x-2">
<Search className="h-4 w-4 text-muted-foreground" />
@@ -181,6 +260,18 @@ export function SimpleVendorMaterials({
<Table>
<TableHeader className="sticky top-0 bg-background z-10">
<TableRow>
+ <TableHead className="w-12">
+ <Checkbox
+ checked={isAllSelected}
+ ref={(el) => {
+ if (el && 'indeterminate' in el) {
+ el.indeterminate = isIndeterminate;
+ }
+ }}
+ onCheckedChange={handleSelectAllConfirmed}
+ aria-label="전체 선택"
+ />
+ </TableHead>
<TableHead>
<SortButton
field="vendorTypeNameEn"
@@ -250,13 +341,20 @@ export function SimpleVendorMaterials({
<TableBody>
{filteredAndSortedConfirmed.length === 0 ? (
<TableRow>
- <TableCell colSpan={8} className="text-center text-muted-foreground">
+ <TableCell colSpan={9} className="text-center text-muted-foreground">
{confirmedSearch ? "검색 결과가 없습니다." : "확정된 공급품목이 없습니다."}
</TableCell>
</TableRow>
) : (
filteredAndSortedConfirmed.map((material) => (
<TableRow key={material.id}>
+ <TableCell>
+ <Checkbox
+ checked={selectedConfirmedIds.includes(material.id)}
+ onCheckedChange={(checked) => handleSelectConfirmed(material.id, checked as boolean)}
+ aria-label={`자재 ${material.itemCode} 선택`}
+ />
+ </TableCell>
<TableCell>{material.vendorTypeNameEn || "-"}</TableCell>
<TableCell className="font-mono">{material.itemCode || "-"}</TableCell>
<TableCell>{material.itemName || "-"}</TableCell>