From 7527841fe0e3b3512a5c37f56035eb508dc23666 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B6=8C=ED=98=81=EC=84=B1?= Date: Thu, 26 Feb 2026 21:38:38 +0900 Subject: [PATCH] =?UTF-8?q?feat:=20[=EA=B2=80=EC=82=AC=EB=AC=B8=EC=84=9C]?= =?UTF-8?q?=20TemplateInspectionContent=20=EC=A0=88=EA=B3=A1(bending)=20sa?= =?UTF-8?q?ve/restore=20=EC=A7=80=EC=9B=90?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - documentRecords prop 추가 (document_data EAV 레코드 복원용) - getInspectionData()에 bending 분기 추가: 구성품별 field_key 인코딩 (b{idx}_ok/ng, b{idx}_p{pt}_n1, b{idx}_n{n}, b{idx}_judgment, b{idx}_value) - 비-bending 모드 기존 로직 guard 추가 (if !isBending) - useEffect로 documentRecords에서 bending cellValues 복원 로직 구현 --- .../documents/TemplateInspectionContent.tsx | 182 +++++++++++++++++- 1 file changed, 180 insertions(+), 2 deletions(-) diff --git a/src/components/production/WorkOrders/documents/TemplateInspectionContent.tsx b/src/components/production/WorkOrders/documents/TemplateInspectionContent.tsx index 3f42cf38..0aac454a 100644 --- a/src/components/production/WorkOrders/documents/TemplateInspectionContent.tsx +++ b/src/components/production/WorkOrders/documents/TemplateInspectionContent.tsx @@ -54,6 +54,14 @@ interface TemplateInspectionContentProps { readOnly?: boolean; workItems?: WorkItemData[]; inspectionDataMap?: InspectionDataMap; + /** 기존 document_data EAV 레코드 (문서 로딩 시 복원용) */ + documentRecords?: Array<{ + section_id: number | null; + column_id: number | null; + row_index: number; + field_key: string; + field_value: string | null; + }>; } // ===== 유틸 ===== @@ -301,7 +309,7 @@ function SectionImage({ section }: { section: { id: number; title?: string; name // ===== 컴포넌트 ===== export const TemplateInspectionContent = forwardRef( - function TemplateInspectionContent({ data: order, template, readOnly = false, workItems, inspectionDataMap }, ref) { + function TemplateInspectionContent({ data: order, template, readOnly = false, workItems, inspectionDataMap, documentRecords }, ref) { const fullDate = getFullDate(); const { primaryAssignee } = getOrderInfo(order); @@ -486,6 +494,84 @@ export const TemplateInspectionContent = forwardRef { + if (!isBending || !documentRecords || documentRecords.length === 0 || bendingProducts.length === 0) return; + + const initial: Record = {}; + + // field_key 패턴: b{productIdx}_ok, b{productIdx}_ng, b{productIdx}_p{pointIdx}_n1, b{productIdx}_n{n}, b{productIdx}_judgment + for (const rec of documentRecords) { + const fk = rec.field_key; + if (!fk.startsWith('b')) continue; + const val = rec.field_value; + if (val == null) continue; + + // b{productIdx}_ok / b{productIdx}_ng → check status + const checkMatch = fk.match(/^b(\d+)_(ok|ng)$/); + if (checkMatch && rec.column_id) { + const productIdx = parseInt(checkMatch[1], 10); + const cellKey = `b-${productIdx}-${rec.column_id}`; + if (checkMatch[2] === 'ok' && val === 'OK') { + initial[cellKey] = { ...initial[cellKey], status: 'good' }; + } else if (checkMatch[2] === 'ng' && val === 'NG') { + initial[cellKey] = { ...initial[cellKey], status: 'bad' }; + } + continue; + } + + // b{productIdx}_p{pointIdx}_n1 → gap measurement + const gapMatch = fk.match(/^b(\d+)_p(\d+)_n(\d+)$/); + if (gapMatch && rec.column_id) { + const productIdx = parseInt(gapMatch[1], 10); + const pointIdx = parseInt(gapMatch[2], 10); + const cellKey = `b-${productIdx}-p${pointIdx}-${rec.column_id}`; + initial[cellKey] = { measurements: [val, '', ''] }; + continue; + } + + // b{productIdx}_n{n} → complex measurement (길이/너비) + const complexMatch = fk.match(/^b(\d+)_n(\d+)$/); + if (complexMatch && rec.column_id) { + const productIdx = parseInt(complexMatch[1], 10); + const mIdx = parseInt(complexMatch[2], 10) - 1; + const cellKey = `b-${productIdx}-${rec.column_id}`; + const prev = initial[cellKey]?.measurements || ['', '', '']; + const m: [string, string, string] = [...prev] as [string, string, string]; + m[mIdx] = val; + initial[cellKey] = { ...initial[cellKey], measurements: m }; + continue; + } + + // b{productIdx}_judgment → skip (자동 계산, 복원 불필요) + if (fk.match(/^b\d+_judgment$/)) continue; + + // b{productIdx}_value → fallback value + const valMatch = fk.match(/^b(\d+)_value$/); + if (valMatch && rec.column_id) { + const productIdx = parseInt(valMatch[1], 10); + const cellKey = `b-${productIdx}-${rec.column_id}`; + initial[cellKey] = { value: val }; + continue; + } + } + + // overall_result, remark 복원 + for (const rec of documentRecords) { + if (rec.field_key === 'overall_result' && rec.field_value) { + // overallResult는 자동 계산이므로 별도 처리 불필요 + } + if (rec.field_key === 'remark' && rec.field_value) { + setInadequateContent(rec.field_value); + } + } + + if (Object.keys(initial).length > 0) { + setCellValues(prev => ({ ...prev, ...initial })); + } + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [documentRecords, isBending, bendingProducts]); + const updateCell = (key: string, update: Partial) => { setCellValues(prev => ({ ...prev, @@ -563,7 +649,99 @@ export const TemplateInspectionContent = forwardRef { + // Bending 모드: 구성품별 데이터 (개소 단위, field_key에 구성품/포인트 인코딩) + if (isBending && bendingProducts.length > 0) { + bendingProducts.forEach((product, productIdx) => { + for (const col of template.columns) { + const label = col.label.trim(); + const isGapCol = col.id === gapColumnId; + + // text 컬럼 (분류/제품명, 타입) → bendingInfo에서 동적 생성이므로 저장 불필요 + if (col.column_type === 'text') continue; + + // 판정 컬럼 → 자동 계산 결과 저장 + if (isJudgmentColumn(label)) { + const judgment = getBendingProductJudgment(productIdx); + if (judgment) { + records.push({ + section_id: null, column_id: col.id, row_index: 0, + field_key: `b${productIdx}_judgment`, + field_value: judgment === '적' ? 'OK' : 'NG', + }); + } + continue; + } + + // 간격 컬럼 (per-point 데이터) + if (isGapCol) { + product.gapPoints.forEach((_gp, pointIdx) => { + const cellKey = `b-${productIdx}-p${pointIdx}-${col.id}`; + const cell = cellValues[cellKey]; + if (cell?.measurements?.[0]) { + records.push({ + section_id: null, column_id: col.id, row_index: 0, + field_key: `b${productIdx}_p${pointIdx}_n1`, + field_value: cell.measurements[0], + }); + } + }); + continue; + } + + // 비간격 merged 컬럼 + const cellKey = `b-${productIdx}-${col.id}`; + const cell = cellValues[cellKey]; + + // check 컬럼 (절곡상태) + if (col.column_type === 'check') { + records.push({ + section_id: null, column_id: col.id, row_index: 0, + field_key: `b${productIdx}_ok`, + field_value: cell?.status === 'good' ? 'OK' : '', + }); + records.push({ + section_id: null, column_id: col.id, row_index: 0, + field_key: `b${productIdx}_ng`, + field_value: cell?.status === 'bad' ? 'NG' : '', + }); + continue; + } + + // complex 컬럼 (길이/너비 측정) + if (col.column_type === 'complex' && col.sub_labels) { + let inputIdx = 0; + for (const sl of col.sub_labels) { + const slLower = sl.toLowerCase(); + if (slLower.includes('도면') || slLower.includes('기준')) continue; + if (slLower.includes('point') || slLower.includes('포인트')) continue; + const n = inputIdx + 1; + const val = cell?.measurements?.[inputIdx] || null; + if (val) { + records.push({ + section_id: null, column_id: col.id, row_index: 0, + field_key: `b${productIdx}_n${n}`, + field_value: val, + }); + } + inputIdx++; + } + continue; + } + + // fallback + if (cell?.value) { + records.push({ + section_id: null, column_id: col.id, row_index: 0, + field_key: `b${productIdx}_value`, + field_value: cell.value, + }); + } + } + }); + } + + // 비-Bending 모드: 개소(WorkItem)별 데이터 + if (!isBending) effectiveWorkItems.forEach((wi, rowIdx) => { for (const col of template.columns) { // 일련번호 컬럼 → 저장 (mng show에서 표시용) if (isSerialColumn(col.label)) {