186 lines
7.4 KiB
TypeScript
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>
|
|
)}
|
|
/>
|
|
);
|
|
}
|