188 lines
6.4 KiB
TypeScript
188 lines
6.4 KiB
TypeScript
"use client";
|
|
|
|
import React, { useState, useEffect, useCallback } from "react";
|
|
import { Search, X } from "lucide-react";
|
|
import { Button } from "@/components/ui/button";
|
|
import { Input } from "@/components/ui/input";
|
|
import {
|
|
Dialog,
|
|
DialogContent,
|
|
DialogDescription,
|
|
DialogHeader,
|
|
DialogTitle,
|
|
DialogFooter,
|
|
} from "@/components/ui/dialog";
|
|
import { apiClient } from "@/lib/api/client";
|
|
import { InspectionStandard } from "../types";
|
|
|
|
interface InspectionStandardLookupProps {
|
|
open: boolean;
|
|
onClose: () => void;
|
|
onSelect: (item: InspectionStandard) => void;
|
|
}
|
|
|
|
export function InspectionStandardLookup({
|
|
open,
|
|
onClose,
|
|
onSelect,
|
|
}: InspectionStandardLookupProps) {
|
|
const [data, setData] = useState<any[]>([]);
|
|
const [searchText, setSearchText] = useState("");
|
|
const [loading, setLoading] = useState(false);
|
|
|
|
const fetchData = useCallback(async () => {
|
|
setLoading(true);
|
|
try {
|
|
const search: Record<string, any> = {};
|
|
if (searchText.trim()) {
|
|
search.inspection_item = searchText.trim();
|
|
search.inspection_code = searchText.trim();
|
|
}
|
|
const params = new URLSearchParams({
|
|
page: "1",
|
|
size: "100",
|
|
enableEntityJoin: "true",
|
|
...(searchText.trim() ? { search: JSON.stringify(search) } : {}),
|
|
});
|
|
const res = await apiClient.get(
|
|
`/table-management/tables/inspection_standard/data-with-joins?${params}`
|
|
);
|
|
if (res.data?.success) {
|
|
const result = res.data.data;
|
|
setData(Array.isArray(result) ? result : result?.data || []);
|
|
}
|
|
} catch (err) {
|
|
console.error("검사기준 조회 실패", err);
|
|
} finally {
|
|
setLoading(false);
|
|
}
|
|
}, [searchText]);
|
|
|
|
useEffect(() => {
|
|
if (open) {
|
|
fetchData();
|
|
}
|
|
}, [open, fetchData]);
|
|
|
|
const handleSelect = (item: any) => {
|
|
onSelect({
|
|
id: item.id,
|
|
inspection_code: item.inspection_code || "",
|
|
inspection_item: item.inspection_item || item.inspection_criteria || "",
|
|
inspection_method: item.inspection_method || "",
|
|
unit: item.unit || "",
|
|
lower_limit: item.lower_limit || "",
|
|
upper_limit: item.upper_limit || "",
|
|
});
|
|
onClose();
|
|
};
|
|
|
|
return (
|
|
<Dialog open={open} onOpenChange={(v) => !v && onClose()}>
|
|
<DialogContent className="max-w-[95vw] sm:max-w-[700px]">
|
|
<DialogHeader>
|
|
<DialogTitle className="flex items-center gap-2 text-base sm:text-lg">
|
|
<Search className="h-5 w-5" />
|
|
검사기준 조회
|
|
</DialogTitle>
|
|
<DialogDescription className="text-xs sm:text-sm">
|
|
검사기준을 검색하여 선택하세요
|
|
</DialogDescription>
|
|
</DialogHeader>
|
|
|
|
<div className="space-y-3">
|
|
<div className="flex gap-2">
|
|
<Input
|
|
placeholder="검사항목명 또는 검사코드로 검색..."
|
|
value={searchText}
|
|
onChange={(e) => setSearchText(e.target.value)}
|
|
onKeyDown={(e) => e.key === "Enter" && fetchData()}
|
|
className="h-9 text-sm"
|
|
/>
|
|
</div>
|
|
|
|
<div className="max-h-[400px] overflow-auto rounded border">
|
|
<table className="w-full text-sm">
|
|
<thead className="sticky top-0 bg-muted">
|
|
<tr className="border-b">
|
|
<th className="px-3 py-2 text-left font-medium text-muted-foreground">
|
|
검사코드
|
|
</th>
|
|
<th className="px-3 py-2 text-left font-medium text-muted-foreground">
|
|
검사항목
|
|
</th>
|
|
<th className="px-3 py-2 text-left font-medium text-muted-foreground">
|
|
검사방법
|
|
</th>
|
|
<th className="px-3 py-2 text-center font-medium text-muted-foreground">
|
|
하한
|
|
</th>
|
|
<th className="px-3 py-2 text-center font-medium text-muted-foreground">
|
|
상한
|
|
</th>
|
|
<th className="px-3 py-2 text-center font-medium text-muted-foreground">
|
|
단위
|
|
</th>
|
|
<th className="w-16 px-3 py-2 text-center font-medium text-muted-foreground">
|
|
선택
|
|
</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
{loading ? (
|
|
<tr>
|
|
<td colSpan={7} className="py-8 text-center text-muted-foreground">
|
|
조회 중...
|
|
</td>
|
|
</tr>
|
|
) : data.length === 0 ? (
|
|
<tr>
|
|
<td colSpan={7} className="py-8 text-center text-muted-foreground">
|
|
검사기준이 없습니다
|
|
</td>
|
|
</tr>
|
|
) : (
|
|
data.map((item, idx) => (
|
|
<tr
|
|
key={item.id || idx}
|
|
className="border-b transition-colors hover:bg-muted/30"
|
|
>
|
|
<td className="px-3 py-2">{item.inspection_code || "-"}</td>
|
|
<td className="px-3 py-2">
|
|
{item.inspection_item || item.inspection_criteria || "-"}
|
|
</td>
|
|
<td className="px-3 py-2">{item.inspection_method || "-"}</td>
|
|
<td className="px-3 py-2 text-center">
|
|
{item.lower_limit || "-"}
|
|
</td>
|
|
<td className="px-3 py-2 text-center">
|
|
{item.upper_limit || "-"}
|
|
</td>
|
|
<td className="px-3 py-2 text-center">{item.unit || "-"}</td>
|
|
<td className="px-3 py-2 text-center">
|
|
<Button
|
|
size="sm"
|
|
className="h-7 px-3 text-xs"
|
|
onClick={() => handleSelect(item)}
|
|
>
|
|
선택
|
|
</Button>
|
|
</td>
|
|
</tr>
|
|
))
|
|
)}
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
</div>
|
|
|
|
<DialogFooter>
|
|
<Button variant="outline" onClick={onClose} className="h-8 text-xs sm:h-10 sm:text-sm">
|
|
닫기
|
|
</Button>
|
|
</DialogFooter>
|
|
</DialogContent>
|
|
</Dialog>
|
|
);
|
|
}
|