Files
sam-react-prod/src/components/production/bending/BendingSearchModal.tsx

186 lines
7.4 KiB
TypeScript

'use client';
/**
* 기초관리 부품 검색 모달
*
* SearchableSelectionModal 활용 — 다중선택 + 필터 드롭다운 + 테이블
* 모델 폼에서 [+ 부품 추가] 시 사용
*/
import { useCallback, useState, useEffect, useRef } from 'react';
import { SearchableSelectionModal } from '@/components/organisms/SearchableSelectionModal';
import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from '@/components/ui/table';
import { Checkbox } from '@/components/ui/checkbox';
import { Badge } from '@/components/ui/badge';
import {
Select,
SelectContent,
SelectItem,
SelectTrigger,
SelectValue,
} from '@/components/ui/select';
import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from '@/components/ui/tooltip';
import { getBendingItems, getBendingItemFilters } from './actions';
import type { BendingItem, BendingItemFilters } from './types';
interface BendingSearchModalProps {
open: boolean;
onOpenChange: (open: boolean) => void;
onSelect: (items: BendingItem[]) => void;
}
export function BendingSearchModal({ open, onOpenChange, onSelect }: BendingSearchModalProps) {
const [filters, setFilters] = useState<BendingItemFilters | null>(null);
const [filterSep, setFilterSep] = useState('all');
const [filterBending, setFilterBending] = useState('all');
const [filterMaterial, setFilterMaterial] = useState('all');
// 필터값을 ref로 유지 (fetchData 클로저에서 최신값 접근)
const filterRef = useRef({ sep: filterSep, bending: filterBending, material: filterMaterial });
filterRef.current = { sep: filterSep, bending: filterBending, material: filterMaterial };
// 필터 옵션 로드
useEffect(() => {
if (open && !filters) {
getBendingItemFilters().then((r) => {
if (r.success && r.data) setFilters(r.data as BendingItemFilters);
});
}
}, [open, filters]);
const fetchData = useCallback(async (query: string) => {
const f = filterRef.current;
const result = await getBendingItems({
search: query,
perPage: 500,
item_sep: f.sep !== 'all' ? f.sep : undefined,
item_bending: f.bending !== 'all' ? f.bending : undefined,
material: f.material !== 'all' ? f.material : undefined,
});
if (result.success && result.data) {
return result.data as unknown as BendingItem[];
}
return [];
}, []);
// 필터 변경 시 key를 바꿔서 재검색
const filterKey = `${filterSep}-${filterBending}-${filterMaterial}`;
return (
<SearchableSelectionModal<BendingItem>
key={filterKey}
open={open}
onOpenChange={onOpenChange}
title="절곡 부품 검색"
searchPlaceholder="품명/코드 검색..."
fetchData={fetchData}
keyExtractor={(item) => String(item.id)}
loadOnOpen
dialogClassName="sm:max-w-4xl"
listContainerClassName="max-h-[400px] overflow-y-auto border rounded-md"
mode="multiple"
onSelect={onSelect}
confirmLabel="선택 적용"
allowSelectAll
renderHeader={() => (
<div className="flex gap-2 mb-3">
<Select value={filterSep} onValueChange={setFilterSep}>
<SelectTrigger className="h-8 w-[130px] text-xs">
<SelectValue placeholder="전체(대분류)" />
</SelectTrigger>
<SelectContent>
<SelectItem value="all">()</SelectItem>
{filters?.item_sep?.map((v) => (
<SelectItem key={v} value={v}>{v}</SelectItem>
))}
</SelectContent>
</Select>
<Select value={filterBending} onValueChange={setFilterBending}>
<SelectTrigger className="h-8 w-[130px] text-xs">
<SelectValue placeholder="전체(분류)" />
</SelectTrigger>
<SelectContent>
<SelectItem value="all">()</SelectItem>
{filters?.item_bending?.map((v) => (
<SelectItem key={v} value={v}>{v}</SelectItem>
))}
</SelectContent>
</Select>
<Select value={filterMaterial} onValueChange={setFilterMaterial}>
<SelectTrigger className="h-8 w-[130px] text-xs">
<SelectValue placeholder="전체(재질)" />
</SelectTrigger>
<SelectContent>
<SelectItem value="all">()</SelectItem>
{filters?.material?.map((v) => (
<SelectItem key={v} value={v}>{v}</SelectItem>
))}
</SelectContent>
</Select>
</div>
)}
listWrapper={(children, selectState) => (
<Table>
<TableHeader>
<TableRow>
<TableHead className="w-10">
{selectState && (
<Checkbox
checked={selectState.isAllSelected}
onCheckedChange={selectState.onToggleAll}
/>
)}
</TableHead>
<TableHead className="w-[100px]"></TableHead>
<TableHead className="w-[70px] text-center"></TableHead>
<TableHead className="w-[80px]"></TableHead>
<TableHead></TableHead>
<TableHead className="w-[80px]"></TableHead>
<TableHead className="w-[60px] text-center"></TableHead>
<TableHead className="w-[60px] text-right"></TableHead>
<TableHead className="w-[60px] text-center"></TableHead>
</TableRow>
</TableHeader>
<TableBody>{children}</TableBody>
</Table>
)}
renderItem={(item, isSelected) => (
<TableRow key={item.id} className="cursor-pointer hover:bg-muted/50">
<TableCell onClick={(e) => e.stopPropagation()}>
<Checkbox checked={isSelected} />
</TableCell>
<TableCell className="font-mono text-xs">{item.code}</TableCell>
<TableCell className="text-center">
<Badge variant="outline" className={`text-xs ${item.item_sep === '철재' ? 'bg-orange-50 text-orange-700 border-orange-200' : 'bg-blue-50 text-blue-700 border-blue-200'}`}>
{item.item_sep}
</Badge>
</TableCell>
<TableCell>
<Badge variant="outline" className="text-xs">{item.item_bending}</Badge>
</TableCell>
<TableCell>{item.item_name || item.name}</TableCell>
<TableCell className="text-xs">{item.material}</TableCell>
<TableCell className="text-center">
{item.image_url ? (
<TooltipProvider delayDuration={200}>
<Tooltip>
<TooltipTrigger asChild>
<img src={item.image_url} alt="" className="w-8 h-8 object-contain mx-auto rounded cursor-pointer" />
</TooltipTrigger>
<TooltipContent side="left" className="p-1 max-w-none">
<img src={item.image_url} alt="" className="w-52 h-52 object-contain" />
</TooltipContent>
</Tooltip>
</TooltipProvider>
) : (
<span className="text-muted-foreground">-</span>
)}
</TableCell>
<TableCell className="text-right font-mono">{item.width_sum || '-'}</TableCell>
<TableCell className="text-center">{item.bend_count || '-'}</TableCell>
</TableRow>
)}
/>
);
}