diff --git a/frontend/app/(main)/COMPANY_16/quality/inspection/page.tsx b/frontend/app/(main)/COMPANY_16/quality/inspection/page.tsx deleted file mode 100644 index cacd9d02..00000000 --- a/frontend/app/(main)/COMPANY_16/quality/inspection/page.tsx +++ /dev/null @@ -1,1703 +0,0 @@ -"use client"; - -import React, { useState, useEffect, useCallback, useMemo } from "react"; -import { Button } from "@/components/ui/button"; -import { Input } from "@/components/ui/input"; -import { Label } from "@/components/ui/label"; -import { Badge } from "@/components/ui/badge"; -import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select"; -import { Checkbox } from "@/components/ui/checkbox"; -import { Tabs, TabsList, TabsTrigger, TabsContent } from "@/components/ui/tabs"; -import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from "@/components/ui/table"; -import { - Dialog, - DialogContent, - DialogHeader, - DialogTitle, - DialogDescription, - DialogFooter, -} from "@/components/ui/dialog"; -import { - Plus, - Trash2, - Save, - Loader2, - Pencil, - ClipboardCheck, - AlertTriangle, - Wrench, - Search, - Inbox, - Settings2, -} from "lucide-react"; -import { DynamicSearchFilter, FilterValue } from "@/components/common/DynamicSearchFilter"; -import { cn } from "@/lib/utils"; -import { apiClient } from "@/lib/api/client"; -import { previewNumberingCode, allocateNumberingCode } from "@/lib/api/numberingRule"; -import { useAuth } from "@/hooks/useAuth"; -import { toast } from "sonner"; -import { useConfirmDialog } from "@/components/common/ConfirmDialog"; -import { useTableSettings } from "@/hooks/useTableSettings"; -import { TableSettingsModal } from "@/components/common/TableSettingsModal"; -import { EDataTable, EDataTableColumn } from "@/components/common/EDataTable"; - -/* ───── 테이블명 ───── */ -const INSPECTION_TABLE = "inspection_standard"; - -const INSPECTION_COLUMNS = [ - { key: "inspection_code", label: "검사코드" }, - { key: "inspection_type", label: "검사유형" }, - { key: "inspection_criteria", label: "검사기준" }, - { key: "inspection_item", label: "검사항목" }, - { key: "inspection_method", label: "검사방법" }, - { key: "judgment_criteria", label: "판단기준" }, - { key: "unit", label: "단위" }, - { key: "apply_type", label: "적용구분" }, - { key: "manager", label: "관리자" }, -]; -const DEFECT_TABLE = "defect_standard_mng"; -const EQUIPMENT_TABLE = "inspection_equipment_mng"; - -/* ───── 카테고리 flatten ───── */ -const flattenCategories = (vals: any[]): { code: string; label: string }[] => { - const result: { code: string; label: string }[] = []; - for (const v of vals) { - result.push({ code: v.valueCode, label: v.valueLabel }); - if (v.children?.length) result.push(...flattenCategories(v.children)); - } - return result; -}; - -export default function InspectionManagementPage() { - const { user } = useAuth(); - const { confirm, ConfirmDialogComponent } = useConfirmDialog(); - const ts = useTableSettings("c16-inspection", INSPECTION_TABLE, INSPECTION_COLUMNS); - - const [activeTab, setActiveTab] = useState("inspection"); - - /* ───── 검사기준 ───── */ - const [inspections, setInspections] = useState([]); - const [inspLoading, setInspLoading] = useState(false); - const [inspCount, setInspCount] = useState(0); - const [inspChecked, setInspChecked] = useState([]); - const [inspModalOpen, setInspModalOpen] = useState(false); - const [inspEditMode, setInspEditMode] = useState(false); - const [inspForm, setInspForm] = useState>({}); - const [inspSaving, setInspSaving] = useState(false); - const [searchFilters, setSearchFilters] = useState([]); - - /* ───── 불량관리 ───── */ - const [defects, setDefects] = useState([]); - const [defLoading, setDefLoading] = useState(false); - const [defCount, setDefCount] = useState(0); - const [defChecked, setDefChecked] = useState([]); - const [defModalOpen, setDefModalOpen] = useState(false); - const [defEditMode, setDefEditMode] = useState(false); - const [defForm, setDefForm] = useState>({}); - const [defSaving, setDefSaving] = useState(false); - const [defKeyword, setDefKeyword] = useState(""); - - /* ───── 검사장비 ───── */ - const [equipments, setEquipments] = useState([]); - const [eqLoading, setEqLoading] = useState(false); - const [eqCount, setEqCount] = useState(0); - const [eqChecked, setEqChecked] = useState([]); - const [eqModalOpen, setEqModalOpen] = useState(false); - const [eqEditMode, setEqEditMode] = useState(false); - const [eqForm, setEqForm] = useState>({}); - const [eqSaving, setEqSaving] = useState(false); - const [eqKeyword, setEqKeyword] = useState(""); - - /* ───── 채번 ───── */ - const [numberingRuleId, setNumberingRuleId] = useState(null); - const [previewCode, setPreviewCode] = useState(null); - - /* ───── 카테고리 옵션 ───── */ - const [catOptions, setCatOptions] = useState>({}); - const [userOptions, setUserOptions] = useState<{ code: string; label: string }[]>([]); - - /* ═══════════════════ 카테고리 로드 ═══════════════════ */ - useEffect(() => { - const load = async () => { - const optMap: Record = {}; - const catList = [ - { table: INSPECTION_TABLE, col: "inspection_type" }, - { table: INSPECTION_TABLE, col: "apply_type" }, - { table: INSPECTION_TABLE, col: "inspection_method" }, - { table: INSPECTION_TABLE, col: "judgment_criteria" }, - { table: INSPECTION_TABLE, col: "unit" }, - { table: DEFECT_TABLE, col: "defect_type" }, - { table: DEFECT_TABLE, col: "severity" }, - { table: DEFECT_TABLE, col: "inspection_type" }, - { table: DEFECT_TABLE, col: "is_active" }, - { table: EQUIPMENT_TABLE, col: "equipment_type" }, - { table: EQUIPMENT_TABLE, col: "equipment_status" }, - ]; - await Promise.all( - catList.map(async ({ table, col }) => { - try { - const res = await apiClient.get(`/table-categories/${table}/${col}/values`); - if (res.data?.data?.length > 0) { - optMap[`${table}.${col}`] = flattenCategories(res.data.data); - } - } catch { - /* skip */ - } - }), - ); - setCatOptions(optMap); - // 사용자 목록 로드 - try { - const userRes = await apiClient.post(`/table-management/tables/user_info/data`, { - page: 1, - size: 500, - autoFilter: true, - }); - const users = userRes.data?.data?.data || userRes.data?.data?.rows || []; - setUserOptions( - users.map((u: any) => ({ - code: u.user_id || u.id, - label: `${u.user_name || u.name || u.user_id}${u.dept_name ? ` (${u.dept_name})` : ""}`, - })), - ); - } catch { - /* skip */ - } - }; - load(); - }, []); - - const getCatLabel = (table: string, col: string, code: string) => { - if (!code) return ""; - const opts = catOptions[`${table}.${col}`]; - if (!opts) return code; - // 쉼표 구분 다중 코드 지원 - if (code.includes(",")) { - return code - .split(",") - .filter(Boolean) - .map((c) => opts.find((o) => o.code === c)?.label || c) - .join(", "); - } - return opts.find((o) => o.code === code)?.label || code; - }; - - const inspTableColumns = useMemo(() => { - return ts.visibleColumns.map((col) => { - const base: EDataTableColumn = { key: col.key, label: col.label }; - if (["inspection_type", "inspection_method", "judgment_criteria", "unit", "apply_type"].includes(col.key)) { - base.render = (v: any, row: any) => getCatLabel(INSPECTION_TABLE, col.key, row[col.key]); - } - return base; - }); - }, [ts.visibleColumns, catOptions]); // eslint-disable-line react-hooks/exhaustive-deps - - /* ═══════════════════ 데이터 조회 ═══════════════════ */ - // 다중값 컬럼 (쉼표 구분 저장) — 서버 equals 대신 contains 사용 - const MULTI_VALUE_COLUMNS = ["inspection_type"]; - - const fetchInspections = useCallback(async () => { - setInspLoading(true); - try { - const filters = searchFilters.map((f) => ({ - columnName: f.columnName, - operator: MULTI_VALUE_COLUMNS.includes(f.columnName) ? "contains" : f.operator, - value: f.value, - })); - const res = await apiClient.post(`/table-management/tables/${INSPECTION_TABLE}/data`, { - page: 1, - size: 500, - dataFilter: filters.length > 0 ? { enabled: true, filters } : undefined, - autoFilter: true, - }); - const rows = res.data?.data?.data || res.data?.data?.rows || []; - setInspections(rows); - setInspCount(rows.length); - } catch { - toast.error("검사기준 조회에 실패했어요"); - } finally { - setInspLoading(false); - } - }, [searchFilters]); - - const fetchDefects = useCallback(async () => { - setDefLoading(true); - try { - const res = await apiClient.post(`/table-management/tables/${DEFECT_TABLE}/data`, { - page: 1, - size: 500, - autoFilter: true, - }); - const rows = res.data?.data?.data || res.data?.data?.rows || []; - setDefects(rows); - setDefCount(rows.length); - } catch { - toast.error("불량관리 조회에 실패했어요"); - } finally { - setDefLoading(false); - } - }, []); - - const fetchEquipments = useCallback(async () => { - setEqLoading(true); - try { - const res = await apiClient.post(`/table-management/tables/${EQUIPMENT_TABLE}/data`, { - page: 1, - size: 500, - autoFilter: true, - }); - const rows = res.data?.data?.data || res.data?.data?.rows || []; - setEquipments(rows); - setEqCount(rows.length); - } catch { - toast.error("검사장비 조회에 실패했어요"); - } finally { - setEqLoading(false); - } - }, []); - - useEffect(() => { - fetchInspections(); - }, [fetchInspections]); - useEffect(() => { - fetchDefects(); - fetchEquipments(); - }, []); - - /* ───── 클라이언트 필터 ───── */ - const filteredDefects = defKeyword.trim() - ? defects.filter( - (r) => - (r.defect_name || "").toLowerCase().includes(defKeyword.toLowerCase()) || - (r.defect_type || "").toLowerCase().includes(defKeyword.toLowerCase()), - ) - : defects; - - const filteredEquipments = eqKeyword.trim() - ? equipments.filter( - (r) => - (r.equipment_name || "").toLowerCase().includes(eqKeyword.toLowerCase()) || - (r.model_name || "").toLowerCase().includes(eqKeyword.toLowerCase()), - ) - : equipments; - - /* ═══════════════════ 검사기준 CRUD ═══════════════════ */ - const openInspCreate = async () => { - setInspForm({}); - setInspEditMode(false); - setNumberingRuleId(null); - setPreviewCode(null); - setInspModalOpen(true); - try { - const ruleRes = await apiClient.get(`/numbering-rules/by-column/${INSPECTION_TABLE}/inspection_code`); - const ruleData = ruleRes.data; - if (ruleData?.success && ruleData?.data?.ruleId) { - const ruleId = ruleData.data.ruleId; - setNumberingRuleId(ruleId); - const prev = await previewNumberingCode(ruleId); - if (prev.success && prev.data?.generatedCode) { - setPreviewCode(prev.data.generatedCode); - } - } - } catch { /* 채번 규칙 없으면 무시 */ } - }; - const openInspEdit = (row: any) => { - setInspForm({ ...row }); - setInspEditMode(true); - setInspModalOpen(true); - }; - const saveInspection = async () => { - if (!numberingRuleId && !inspForm.inspection_code) { - toast.error("검사코드는 필수예요"); - return; - } - if (!inspForm.inspection_type) { - toast.error("유형을 1개 이상 선택해주세요"); - return; - } - if (!inspForm.inspection_criteria) { - toast.error("검사기준은 필수예요"); - return; - } - if (!inspForm.inspection_item) { - toast.error("검사항목은 필수예요"); - return; - } - if (!inspForm.judgment_criteria) { - toast.error("판단기준은 필수예요"); - return; - } - setInspSaving(true); - try { - let finalCode = inspForm.inspection_code || ""; - if (!inspEditMode && numberingRuleId) { - const allocRes = await allocateNumberingCode(numberingRuleId); - if (allocRes.success && allocRes.data?.generatedCode) { - finalCode = allocRes.data.generatedCode; - } else { - toast.error("채번 코드 할당에 실패했습니다."); - setInspSaving(false); - return; - } - } - if (inspEditMode) { - await apiClient.put(`/table-management/tables/${INSPECTION_TABLE}/edit`, { - originalData: { id: inspForm.id }, - updatedData: inspForm, - }); - toast.success("검사기준을 수정했어요"); - } else { - await apiClient.post(`/table-management/tables/${INSPECTION_TABLE}/add`, { - id: crypto.randomUUID(), - ...inspForm, - inspection_code: finalCode, - }); - toast.success("검사기준을 등록했어요"); - } - setInspModalOpen(false); - fetchInspections(); - } catch { - toast.error("저장에 실패했어요"); - } finally { - setInspSaving(false); - } - }; - const deleteInspections = async () => { - if (inspChecked.length === 0) { - toast.error("삭제할 항목을 선택해주세요"); - return; - } - const ok = await confirm("검사기준 삭제", { - description: `선택한 ${inspChecked.length}건을 삭제할까요? 이 작업은 되돌릴 수 없어요.`, - }); - if (!ok) return; - try { - await apiClient.delete(`/table-management/tables/${INSPECTION_TABLE}/delete`, { - data: inspChecked.map((id) => ({ id })), - }); - toast.success(`${inspChecked.length}건을 삭제했어요`); - setInspChecked([]); - fetchInspections(); - } catch { - toast.error("삭제에 실패했어요"); - } - }; - - /* ═══════════════════ 불량관리 CRUD ═══════════════════ */ - const openDefCreate = async () => { - setDefForm({}); - setDefEditMode(false); - setNumberingRuleId(null); - setPreviewCode(null); - setDefModalOpen(true); - try { - const ruleRes = await apiClient.get(`/numbering-rules/by-column/${DEFECT_TABLE}/defect_code`); - const ruleData = ruleRes.data; - if (ruleData?.success && ruleData?.data?.ruleId) { - const ruleId = ruleData.data.ruleId; - setNumberingRuleId(ruleId); - const prev = await previewNumberingCode(ruleId); - if (prev.success && prev.data?.generatedCode) { - setPreviewCode(prev.data.generatedCode); - } - } - } catch { /* 채번 규칙 없으면 무시 */ } - }; - const openDefEdit = (row: any) => { - setDefForm({ ...row }); - setDefEditMode(true); - setDefModalOpen(true); - }; - const saveDefect = async () => { - if (!numberingRuleId && !defForm.defect_code) { - toast.error("불량코드는 필수예요"); - return; - } - if (!defForm.defect_type) { - toast.error("불량유형은 필수예요"); - return; - } - if (!defForm.defect_name) { - toast.error("불량명은 필수예요"); - return; - } - if (!defForm.severity) { - toast.error("심각도는 필수예요"); - return; - } - if (!defForm.defect_content) { - toast.error("불량내용은 필수예요"); - return; - } - if (!defForm.inspection_type) { - toast.error("검사유형을 1개 이상 선택해주세요"); - return; - } - setDefSaving(true); - try { - let finalCode = defForm.defect_code || ""; - if (!defEditMode && numberingRuleId) { - const allocRes = await allocateNumberingCode(numberingRuleId); - if (allocRes.success && allocRes.data?.generatedCode) { - finalCode = allocRes.data.generatedCode; - } else { - toast.error("채번 코드 할당에 실패했습니다."); - setDefSaving(false); - return; - } - } - if (defEditMode) { - await apiClient.put(`/table-management/tables/${DEFECT_TABLE}/edit`, { - originalData: { id: defForm.id }, - updatedData: defForm, - }); - toast.success("불량유형을 수정했어요"); - } else { - await apiClient.post(`/table-management/tables/${DEFECT_TABLE}/add`, { id: crypto.randomUUID(), ...defForm, defect_code: finalCode }); - toast.success("불량유형을 등록했어요"); - } - setDefModalOpen(false); - fetchDefects(); - } catch { - toast.error("저장에 실패했어요"); - } finally { - setDefSaving(false); - } - }; - const deleteDefects = async () => { - if (defChecked.length === 0) { - toast.error("삭제할 항목을 선택해주세요"); - return; - } - const ok = await confirm("불량유형 삭제", { - description: `선택한 ${defChecked.length}건을 삭제할까요? 이 작업은 되돌릴 수 없어요.`, - }); - if (!ok) return; - try { - await apiClient.delete(`/table-management/tables/${DEFECT_TABLE}/delete`, { - data: defChecked.map((id) => ({ id })), - }); - toast.success(`${defChecked.length}건을 삭제했어요`); - setDefChecked([]); - fetchDefects(); - } catch { - toast.error("삭제에 실패했어요"); - } - }; - - /* ═══════════════════ 검사장비 CRUD ═══════════════════ */ - const openEqCreate = async () => { - setEqForm({ - calibration_period: "12", - equipment_status: "NORMAL", - }); - setEqEditMode(false); - setNumberingRuleId(null); - setPreviewCode(null); - setEqModalOpen(true); - try { - const ruleRes = await apiClient.get(`/numbering-rules/by-column/${EQUIPMENT_TABLE}/equipment_code`); - const ruleData = ruleRes.data; - if (ruleData?.success && ruleData?.data?.ruleId) { - const ruleId = ruleData.data.ruleId; - setNumberingRuleId(ruleId); - const prev = await previewNumberingCode(ruleId); - if (prev.success && prev.data?.generatedCode) { - setPreviewCode(prev.data.generatedCode); - } - } else { - // 채번 규칙 없으면 기존 수동 채번 fallback - const maxNum = - equipments - .map((e: any) => e.equipment_code || "") - .filter((c: string) => /^EQP-\d+$/.test(c)) - .map((c: string) => parseInt(c.replace("EQP-", ""), 10)) - .sort((a: number, b: number) => b - a)[0] || 0; - setEqForm((p) => ({ ...p, equipment_code: `EQP-${String(maxNum + 1).padStart(3, "0")}` })); - } - } catch { - // 채번 규칙 조회 실패 시 기존 수동 채번 fallback - const maxNum = - equipments - .map((e: any) => e.equipment_code || "") - .filter((c: string) => /^EQP-\d+$/.test(c)) - .map((c: string) => parseInt(c.replace("EQP-", ""), 10)) - .sort((a: number, b: number) => b - a)[0] || 0; - setEqForm((p) => ({ ...p, equipment_code: `EQP-${String(maxNum + 1).padStart(3, "0")}` })); - } - }; - const openEqEdit = (row: any) => { - setEqForm({ ...row }); - setEqEditMode(true); - setEqModalOpen(true); - }; - const saveEquipment = async () => { - if (!numberingRuleId && !eqForm.equipment_code) { - toast.error("장비코드는 필수예요"); - return; - } - if (!eqForm.equipment_name) { - toast.error("장비명은 필수예요"); - return; - } - if (!eqForm.equipment_type) { - toast.error("장비유형은 필수예요"); - return; - } - setEqSaving(true); - try { - let finalCode = eqForm.equipment_code || ""; - if (!eqEditMode && numberingRuleId) { - const allocRes = await allocateNumberingCode(numberingRuleId); - if (allocRes.success && allocRes.data?.generatedCode) { - finalCode = allocRes.data.generatedCode; - } else { - toast.error("채번 코드 할당에 실패했습니다."); - setEqSaving(false); - return; - } - } - if (eqEditMode) { - await apiClient.put(`/table-management/tables/${EQUIPMENT_TABLE}/edit`, { - originalData: { id: eqForm.id }, - updatedData: eqForm, - }); - toast.success("검사장비를 수정했어요"); - } else { - await apiClient.post(`/table-management/tables/${EQUIPMENT_TABLE}/add`, { id: crypto.randomUUID(), ...eqForm, equipment_code: finalCode }); - toast.success("검사장비를 등록했어요"); - } - setEqModalOpen(false); - fetchEquipments(); - } catch { - toast.error("저장에 실패했어요"); - } finally { - setEqSaving(false); - } - }; - const deleteEquipments = async () => { - if (eqChecked.length === 0) { - toast.error("삭제할 항목을 선택해주세요"); - return; - } - const ok = await confirm("검사장비 삭제", { - description: `선택한 ${eqChecked.length}건을 삭제할까요? 이 작업은 되돌릴 수 없어요.`, - }); - if (!ok) return; - try { - await apiClient.delete(`/table-management/tables/${EQUIPMENT_TABLE}/delete`, { - data: eqChecked.map((id) => ({ id })), - }); - toast.success(`${eqChecked.length}건을 삭제했어요`); - setEqChecked([]); - fetchEquipments(); - } catch { - toast.error("삭제에 실패했어요"); - } - }; - - /* ═══════════════════ JSX ═══════════════════ */ - return ( -
- {ConfirmDialogComponent} - -
- -
- - - - 검사기준 - - {inspCount} - - - - - 불량관리 - - {defCount} - - - - - 검사장비 - - {eqCount} - - - -
- - {/* ──── 검사기준 탭 ──── */} - -
- - - - - -
- } - /> -
-
- openInspEdit(row)} - showPagination={true} - draggableColumns={false} - columnOrderKey="c16-inspection-main" - /> -
- - - {/* ──── 불량관리 탭 ──── */} - -
-
-
- - setDefKeyword(e.target.value)} - /> -
- - {filteredDefects.length}건 - -
-
- - - -
-
-
- - - - - 0 && defChecked.length === filteredDefects.length} - onCheckedChange={(v) => setDefChecked(v ? filteredDefects.map((r) => r.id) : [])} - /> - - - 불량코드 - - - 불량유형 - - - 불량명 - - - 불량내용 - - - 심각도 - - - 검사유형 - - - 적용대상 - - - 사용여부 - - - 등록일 - - - 관리자 - - - 비고 - - - - - {defLoading ? ( - - - - - - ) : filteredDefects.length === 0 ? ( - - - -

등록된 불량유형이 없어요

-
-
- ) : ( - filteredDefects.map((row) => { - const severityLabel = getCatLabel(DEFECT_TABLE, "severity", row.severity); - const severityColor = - severityLabel === "치명적" - ? "destructive" - : severityLabel === "심각" - ? "destructive" - : severityLabel === "보통" - ? "secondary" - : "outline"; - return ( - - setDefChecked((prev) => - prev.includes(row.id) ? prev.filter((id) => id !== row.id) : [...prev, row.id], - ) - } - onDoubleClick={() => openDefEdit(row)} - > - e.stopPropagation()}> - - setDefChecked((prev) => (v ? [...prev, row.id] : prev.filter((id) => id !== row.id))) - } - /> - - {row.defect_code || "-"} - - - {getCatLabel(DEFECT_TABLE, "defect_type", row.defect_type)} - - - {row.defect_name || "-"} - - {row.defect_content || "-"} - - - - {severityLabel || "-"} - - - -
- {row.inspection_type - ? row.inspection_type - .split(",") - .filter(Boolean) - .map((c: string) => ( - - {getCatLabel(DEFECT_TABLE, "inspection_type", c)} - - )) - : "-"} -
-
- -
- {row.apply_target - ? row.apply_target - .split(",") - .filter(Boolean) - .map((t: string) => ( - - {t} - - )) - : "-"} -
-
- - - {getCatLabel(DEFECT_TABLE, "is_active", row.is_active) || "-"} - - - - {row.reg_date || (row.created_date ? row.created_date.slice(0, 10) : "-")} - - {row.manager_id || "-"} - {row.remarks || "-"} -
- ); - }) - )} -
-
-
-
- - {/* ──── 검사장비 탭 ──── */} - -
-
-
- - setEqKeyword(e.target.value)} - /> -
- - {filteredEquipments.length}건 - -
-
- - - -
-
-
- - - - - 0 && eqChecked.length === filteredEquipments.length} - onCheckedChange={(v) => setEqChecked(v ? filteredEquipments.map((r) => r.id) : [])} - /> - - - 장비코드 - - - 장비명 - - - 장비유형 - - - 모델명 - - - 제조사 - - - 설치장소 - - - 최근교정일 - - - 교정주기(개월) - - - 장비상태 - - - 담당자 - - - - - {eqLoading ? ( - - - - - - ) : filteredEquipments.length === 0 ? ( - - - -

등록된 검사장비가 없어요

-
-
- ) : ( - filteredEquipments.map((row) => { - const statusLabel = getCatLabel(EQUIPMENT_TABLE, "equipment_status", row.equipment_status); - const statusColor = - statusLabel === "정상" ? "default" : statusLabel === "폐기" ? "destructive" : "secondary"; - return ( - - setEqChecked((prev) => - prev.includes(row.id) ? prev.filter((id) => id !== row.id) : [...prev, row.id], - ) - } - onDoubleClick={() => openEqEdit(row)} - > - e.stopPropagation()}> - - setEqChecked((prev) => (v ? [...prev, row.id] : prev.filter((id) => id !== row.id))) - } - /> - - {row.equipment_code || "-"} - {row.equipment_name || "-"} - - - {getCatLabel(EQUIPMENT_TABLE, "equipment_type", row.equipment_type) || "-"} - - - {row.model_name || "-"} - {row.manufacturer || "-"} - {row.installation_location || "-"} - {row.last_calibration_date || "-"} - {row.calibration_period ? `${row.calibration_period}개월` : "-"} - - - {statusLabel || "-"} - - - - {userOptions.find((u) => u.code === row.manager_id)?.label || row.manager_id || "-"} - - - ); - }) - )} -
-
-
-
- -
- - {/* ═══════════════════ 검사기준 모달 ═══════════════════ */} - - - - {inspEditMode ? "검사기준 수정" : "검사기준 등록"} - 검사기준 정보를 입력해주세요 - -
- {/* 검사코드 */} -
- - {!inspEditMode && numberingRuleId ? ( - - ) : inspEditMode ? ( - - ) : ( - setInspForm((p) => ({ ...p, inspection_code: e.target.value }))} - placeholder="검사코드 입력" - /> - )} -
- {/* 유형 (다중선택) */} -
- -
- {(catOptions[`${INSPECTION_TABLE}.inspection_type`] || []).map((o) => { - const types: string[] = inspForm.inspection_type - ? inspForm.inspection_type.split(",").filter(Boolean) - : []; - const checked = types.includes(o.code); - return ( -
- { - const next = v ? [...types, o.code] : types.filter((t) => t !== o.code); - setInspForm((p) => ({ ...p, inspection_type: next.join(",") })); - }} - /> - -
- ); - })} -
-
- {/* 검사기준 */} -
- - setInspForm((p) => ({ ...p, inspection_criteria: e.target.value }))} - placeholder="검사기준 입력" - /> -
- {/* 기준상세 */} -
- - setInspForm((p) => ({ ...p, criteria_detail: e.target.value }))} - placeholder="기준상세 입력" - /> -
- {/* 검사항목 */} -
- - setInspForm((p) => ({ ...p, inspection_item: e.target.value }))} - placeholder="검사항목 입력" - /> -
- {/* 검사방법 */} -
- - -
- {/* 판단기준 */} -
- - -
- {/* 단위 */} -
- - -
- {/* 적용구분 */} -
- - -
- {/* 관리자 */} -
- - -
- {/* 비고 */} -
- -