fix: [production] 절곡 중간검사 데이터 새로고침 시 초기화 버그 수정
- InspectionInputModal: 이전 형식 데이터(products 배열 없음) 로드 시 judgment 기반 제품별 상태 추론 - InspectionInputModal: skipAutoJudgmentRef로 이전 형식 로드 시 auto-judgment 덮어쓰기 방지 - BendingInspectionContent: products/bendingStatus 없을 때 judgment 기반 fallback 추가
This commit is contained in:
@@ -213,6 +213,15 @@ export const BendingInspectionContent = forwardRef<InspectionContentRef, Bending
|
||||
...p,
|
||||
bendingStatus: bendingStatusValue,
|
||||
})));
|
||||
} else if (itemData.judgment) {
|
||||
// 이전 형식 호환: products/bendingStatus 없이 judgment만 있는 경우
|
||||
const inferredStatus: CheckStatus = itemData.judgment === 'pass' ? '양호' : itemData.judgment === 'fail' ? '불량' : null;
|
||||
if (inferredStatus) {
|
||||
setProducts(prev => prev.map(p => ({
|
||||
...p,
|
||||
bendingStatus: inferredStatus,
|
||||
})));
|
||||
}
|
||||
}
|
||||
|
||||
// 부적합 내용 로드
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
* - bending_wip: 재고생산(재공품) 중간검사
|
||||
*/
|
||||
|
||||
import { useState, useEffect, useMemo } from 'react';
|
||||
import { useState, useEffect, useMemo, useRef } from 'react';
|
||||
import {
|
||||
Dialog,
|
||||
DialogContent,
|
||||
@@ -24,6 +24,8 @@ import { Textarea } from '@/components/ui/textarea';
|
||||
import { cn } from '@/lib/utils';
|
||||
import type { InspectionTemplateData, InspectionTemplateSectionItem } from './types';
|
||||
import { formatNumber } from '@/lib/utils/amount';
|
||||
import { getInspectionConfig } from '@/components/production/WorkOrders/actions';
|
||||
import type { InspectionConfigData } from '@/components/production/WorkOrders/actions';
|
||||
|
||||
// 중간검사 공정 타입
|
||||
export type InspectionProcessType =
|
||||
@@ -71,6 +73,8 @@ interface InspectionInputModalProps {
|
||||
templateData?: InspectionTemplateData;
|
||||
/** 작업 아이템의 실제 치수 (reference_attribute 연동용) */
|
||||
workItemDimensions?: { width?: number; height?: number };
|
||||
/** 작업지시 ID (절곡 gap_points API 조회용) */
|
||||
workOrderId?: string;
|
||||
}
|
||||
|
||||
// ===== 절곡 7개 제품 검사 항목 (BendingInspectionContent의 INITIAL_PRODUCTS와 동일 구조) =====
|
||||
@@ -541,6 +545,7 @@ export function InspectionInputModal({
|
||||
onComplete,
|
||||
templateData,
|
||||
workItemDimensions,
|
||||
workOrderId,
|
||||
}: InspectionInputModalProps) {
|
||||
// 템플릿 모드 여부
|
||||
// 절곡(bending)은 7제품 커스텀 폼 사용 → TemplateInspectionContent의 bending 셀 키와 연동
|
||||
@@ -556,14 +561,72 @@ export function InspectionInputModal({
|
||||
// 동적 폼 값 (템플릿 모드용)
|
||||
const [dynamicFormValues, setDynamicFormValues] = useState<Record<string, unknown>>({});
|
||||
|
||||
// 이전 형식 데이터 로드 시 auto-judgment가 judgment를 덮어쓰지 않도록 보호
|
||||
const skipAutoJudgmentRef = useRef(false);
|
||||
|
||||
// 절곡용 간격 포인트 초기화 (레거시 — bending_wip 등에서 사용)
|
||||
const [gapPoints, setGapPoints] = useState<{ left: number | null; right: number | null }[]>(
|
||||
Array(5).fill(null).map(() => ({ left: null, right: null }))
|
||||
);
|
||||
|
||||
// 절곡 API 제품 정의 (gap_points 동적 로딩)
|
||||
const [apiProductDefs, setApiProductDefs] = useState<BendingProductDef[] | null>(null);
|
||||
const effectiveProductDefs = apiProductDefs || BENDING_PRODUCTS;
|
||||
|
||||
// 절곡 7개 제품별 상태 (bending 전용)
|
||||
const [bendingProducts, setBendingProducts] = useState<BendingProductState[]>(createInitialBendingProducts);
|
||||
|
||||
// API에서 절곡 제품 gap_points 동적 로딩
|
||||
useEffect(() => {
|
||||
if (!open || processType !== 'bending' || !workOrderId) return;
|
||||
let cancelled = false;
|
||||
getInspectionConfig(workOrderId).then(result => {
|
||||
if (cancelled) return;
|
||||
if (result.success && result.data?.items?.length) {
|
||||
const displayMap: Record<string, { label: string; len: string; wid: string }> = {
|
||||
guide_rail_wall: { label: '가이드레일 (벽면형)', len: '3000', wid: 'N/A' },
|
||||
guide_rail_side: { label: '가이드레일 (측면형)', len: '3000', wid: 'N/A' },
|
||||
case_box: { label: '케이스 (500X380)', len: '3000', wid: 'N/A' },
|
||||
bottom_bar: { label: '하단마감재 (60X40)', len: '3000', wid: 'N/A' },
|
||||
bottom_l_bar: { label: '하단L-BAR (17X60)', len: '3000', wid: 'N/A' },
|
||||
smoke_w50: { label: '연기차단재 (W50)', len: '3000', wid: '' },
|
||||
smoke_w80: { label: '연기차단재 (W80)', len: '3000', wid: '' },
|
||||
};
|
||||
const defs: BendingProductDef[] = result.data.items.map(item => {
|
||||
const d = displayMap[item.id] || { label: item.name, len: '-', wid: 'N/A' };
|
||||
return {
|
||||
id: item.id,
|
||||
label: d.label,
|
||||
lengthDesign: d.len,
|
||||
widthDesign: d.wid,
|
||||
gapPoints: item.gap_points.map(gp => ({ point: gp.point, design: gp.design_value })),
|
||||
};
|
||||
});
|
||||
setApiProductDefs(defs);
|
||||
}
|
||||
});
|
||||
return () => { cancelled = true; };
|
||||
}, [open, processType, workOrderId]);
|
||||
|
||||
// API 제품 정의 로딩 시 bendingProducts 갱신 (gap 개수 동기화)
|
||||
useEffect(() => {
|
||||
if (!apiProductDefs || processType !== 'bending') return;
|
||||
setBendingProducts(prev => {
|
||||
return apiProductDefs.map((def, idx) => {
|
||||
// 기존 입력값 보존 (ID 매칭 또는 인덱스 폴백)
|
||||
const existing = prev.find(p => p.id === def.id || p.id.replace(/[-_]/g, '') === def.id.replace(/[-_]/g, ''))
|
||||
|| (idx < prev.length ? prev[idx] : undefined);
|
||||
return {
|
||||
id: def.id,
|
||||
bendingStatus: existing?.bendingStatus ?? null,
|
||||
lengthMeasured: existing?.lengthMeasured ?? '',
|
||||
widthMeasured: existing?.widthMeasured ?? '',
|
||||
gapMeasured: def.gapPoints.map((_, gi) => existing?.gapMeasured?.[gi] ?? ''),
|
||||
};
|
||||
});
|
||||
});
|
||||
}, [apiProductDefs, processType]);
|
||||
|
||||
useEffect(() => {
|
||||
if (open) {
|
||||
// initialData가 있으면 기존 저장 데이터로 복원
|
||||
@@ -588,8 +651,10 @@ export function InspectionInputModal({
|
||||
gapPoints: Array<{ point: string; designValue: string; measured: string }>;
|
||||
}> | undefined;
|
||||
if (savedProducts && Array.isArray(savedProducts)) {
|
||||
setBendingProducts(BENDING_PRODUCTS.map((def, idx) => {
|
||||
const saved = savedProducts.find(sp => sp.id === def.id);
|
||||
setBendingProducts(effectiveProductDefs.map((def, idx) => {
|
||||
const saved = savedProducts.find(sp =>
|
||||
sp.id === def.id || sp.id.replace(/[-_]/g, '') === def.id.replace(/[-_]/g, '')
|
||||
) || (idx < savedProducts.length ? savedProducts[idx] : undefined);
|
||||
if (!saved) return { id: def.id, bendingStatus: null, lengthMeasured: '', widthMeasured: '', gapMeasured: def.gapPoints.map(() => '') };
|
||||
return {
|
||||
id: def.id,
|
||||
@@ -599,6 +664,21 @@ export function InspectionInputModal({
|
||||
gapMeasured: def.gapPoints.map((_, gi) => saved.gapPoints?.[gi]?.measured || ''),
|
||||
};
|
||||
}));
|
||||
} else if (processType === 'bending' && initialData.judgment) {
|
||||
// 이전 형식 데이터 호환: products 배열 없이 저장된 경우
|
||||
// judgment 값으로 제품별 상태 추론 (pass → 전체 양호)
|
||||
const restoredStatus: 'good' | 'bad' | null =
|
||||
initialData.judgment === 'pass' ? 'good' : initialData.judgment === 'fail' ? 'bad' : null;
|
||||
setBendingProducts(effectiveProductDefs.map(def => ({
|
||||
id: def.id,
|
||||
bendingStatus: restoredStatus,
|
||||
lengthMeasured: '',
|
||||
widthMeasured: '',
|
||||
gapMeasured: def.gapPoints.map(() => ''),
|
||||
})));
|
||||
// 이전 형식은 lengthMeasured가 없어 autoJudgment가 null이 되므로
|
||||
// 로드된 judgment를 덮어쓰지 않도록 보호
|
||||
skipAutoJudgmentRef.current = true;
|
||||
} else {
|
||||
setBendingProducts(createInitialBendingProducts());
|
||||
}
|
||||
@@ -686,8 +766,12 @@ export function InspectionInputModal({
|
||||
return computeJudgment(processType, formData);
|
||||
}, [useTemplateMode, templateData, dynamicFormValues, workItemDimensions, processType, formData, bendingProducts]);
|
||||
|
||||
// 판정값 자동 동기화
|
||||
// 판정값 자동 동기화 (이전 형식 데이터 로드 시 첫 번째 동기화 건너뜀)
|
||||
useEffect(() => {
|
||||
if (skipAutoJudgmentRef.current) {
|
||||
skipAutoJudgmentRef.current = false;
|
||||
return;
|
||||
}
|
||||
setFormData((prev) => {
|
||||
if (prev.judgment === autoJudgment) return prev;
|
||||
return { ...prev, judgment: autoJudgment };
|
||||
@@ -708,7 +792,7 @@ export function InspectionInputModal({
|
||||
bendingStatus: p.bendingStatus === 'good' ? '양호' : p.bendingStatus === 'bad' ? '불량' : null,
|
||||
lengthMeasured: p.lengthMeasured,
|
||||
widthMeasured: p.widthMeasured,
|
||||
gapPoints: BENDING_PRODUCTS[idx].gapPoints.map((gp, gi) => ({
|
||||
gapPoints: (effectiveProductDefs[idx]?.gapPoints || []).map((gp, gi) => ({
|
||||
point: gp.point,
|
||||
designValue: gp.design,
|
||||
measured: p.gapMeasured[gi] || '',
|
||||
@@ -1008,7 +1092,7 @@ export function InspectionInputModal({
|
||||
{/* ===== 절곡 검사 항목 (7개 제품별) ===== */}
|
||||
{!useTemplateMode && processType === 'bending' && (
|
||||
<div className="space-y-4">
|
||||
{BENDING_PRODUCTS.map((productDef, pIdx) => {
|
||||
{effectiveProductDefs.map((productDef, pIdx) => {
|
||||
const pState = bendingProducts[pIdx];
|
||||
if (!pState) return null;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user