diff --git a/src/components/production/WorkOrders/documents/BendingWorkLogContent.tsx b/src/components/production/WorkOrders/documents/BendingWorkLogContent.tsx index 99f90d10..ea74e6d5 100644 --- a/src/components/production/WorkOrders/documents/BendingWorkLogContent.tsx +++ b/src/components/production/WorkOrders/documents/BendingWorkLogContent.tsx @@ -30,9 +30,10 @@ import { ProductionSummarySection } from './bending/ProductionSummarySection'; interface BendingWorkLogContentProps { data: WorkOrder; lotNoMap?: Record; // BD-{prefix}-{lengthCode} → LOT NO + bendingImages?: Record; // R2 presigned URL 맵 } -export function BendingWorkLogContent({ data: order, lotNoMap }: BendingWorkLogContentProps) { +export function BendingWorkLogContent({ data: order, lotNoMap, bendingImages }: BendingWorkLogContentProps) { const today = new Date().toLocaleDateString('ko-KR', { year: 'numeric', month: '2-digit', @@ -166,19 +167,23 @@ export function BendingWorkLogContent({ data: order, lotNoMap }: BendingWorkLogC mapping={mapping} lotNo={order.lotNo} lotNoMap={lotNoMap} + bendingImages={bendingImages} /> ) : ( diff --git a/src/components/production/WorkOrders/documents/bending/BottomBarSection.tsx b/src/components/production/WorkOrders/documents/bending/BottomBarSection.tsx index 75eb6c2f..18863e72 100644 --- a/src/components/production/WorkOrders/documents/bending/BottomBarSection.tsx +++ b/src/components/production/WorkOrders/documents/bending/BottomBarSection.tsx @@ -14,9 +14,10 @@ interface BottomBarSectionProps { bendingInfo: BendingInfoExtended; mapping: MaterialMapping; lotNoMap?: Record; + bendingImages?: Record; } -export function BottomBarSection({ bendingInfo, mapping, lotNoMap }: BottomBarSectionProps) { +export function BottomBarSection({ bendingInfo, mapping, lotNoMap, bendingImages }: BottomBarSectionProps) { const rows = buildBottomBarRows(bendingInfo.bottomBar, mapping, bendingInfo.productCode); if (rows.length === 0) return null; @@ -30,7 +31,7 @@ export function BottomBarSection({ bendingInfo, mapping, lotNoMap }: BottomBarSe {/* 좌측: 이미지 */}
하단마감재 diff --git a/src/components/production/WorkOrders/documents/bending/GuideRailSection.tsx b/src/components/production/WorkOrders/documents/bending/GuideRailSection.tsx index 5be79ff5..97bfa3db 100644 --- a/src/components/production/WorkOrders/documents/bending/GuideRailSection.tsx +++ b/src/components/production/WorkOrders/documents/bending/GuideRailSection.tsx @@ -14,7 +14,8 @@ interface GuideRailSectionProps { bendingInfo: BendingInfoExtended; mapping: MaterialMapping; lotNo: string; - lotNoMap?: Record; // BD-{prefix}-{lengthCode} → LOT NO + lotNoMap?: Record; + bendingImages?: Record; } function PartTable({ title, rows, imageUrl, lotNo: _lotNo, baseSize, lotNoMap }: { @@ -76,7 +77,7 @@ function PartTable({ title, rows, imageUrl, lotNo: _lotNo, baseSize, lotNoMap }: ); } -export function GuideRailSection({ bendingInfo, mapping, lotNo, lotNoMap }: GuideRailSectionProps) { +export function GuideRailSection({ bendingInfo, mapping, lotNo, lotNoMap, bendingImages }: GuideRailSectionProps) { const { wall, side } = bendingInfo.guideRail; const productCode = bendingInfo.productCode; @@ -100,7 +101,7 @@ export function GuideRailSection({ bendingInfo, mapping, lotNo, lotNoMap }: Guid ; + bendingImages?: Record; } -function ShutterBoxSubSection({ box, index, lotNoMap }: { box: ShutterBoxData; index: number; lotNoMap?: Record }) { +function ShutterBoxSubSection({ box, index, lotNoMap, bendingImages }: { box: ShutterBoxData; index: number; lotNoMap?: Record; bendingImages?: Record }) { const rows = buildShutterBoxRows(box); if (rows.length === 0) return null; @@ -37,7 +38,7 @@ function ShutterBoxSubSection({ box, index, lotNoMap }: { box: ShutterBoxData; i
{`셔터박스 @@ -92,7 +93,7 @@ function ShutterBoxSubSection({ box, index, lotNoMap }: { box: ShutterBoxData; i ); } -export function ShutterBoxSection({ bendingInfo, lotNoMap }: ShutterBoxSectionProps) { +export function ShutterBoxSection({ bendingInfo, lotNoMap, bendingImages }: ShutterBoxSectionProps) { const boxes = bendingInfo.shutterBox; if (!boxes || boxes.length === 0) return null; @@ -103,7 +104,7 @@ export function ShutterBoxSection({ bendingInfo, lotNoMap }: ShutterBoxSectionPr
{boxes.map((box, idx) => ( - + ))}
); diff --git a/src/components/production/WorkOrders/documents/bending/SmokeBarrierSection.tsx b/src/components/production/WorkOrders/documents/bending/SmokeBarrierSection.tsx index 8e6f1eb9..123ea478 100644 --- a/src/components/production/WorkOrders/documents/bending/SmokeBarrierSection.tsx +++ b/src/components/production/WorkOrders/documents/bending/SmokeBarrierSection.tsx @@ -14,9 +14,10 @@ import { buildSmokeBarrierRows, getBendingImageUrl, fmt, fmtWeight, lookupLotNo interface SmokeBarrierSectionProps { bendingInfo: BendingInfoExtended; lotNoMap?: Record; + bendingImages?: Record; } -export function SmokeBarrierSection({ bendingInfo, lotNoMap }: SmokeBarrierSectionProps) { +export function SmokeBarrierSection({ bendingInfo, lotNoMap, bendingImages }: SmokeBarrierSectionProps) { const rows = buildSmokeBarrierRows(bendingInfo.smokeBarrier); if (rows.length === 0) return null; @@ -30,7 +31,7 @@ export function SmokeBarrierSection({ bendingInfo, lotNoMap }: SmokeBarrierSecti {/* 좌측: 이미지 */}
연기차단재 diff --git a/src/components/production/WorkOrders/documents/bending/utils.ts b/src/components/production/WorkOrders/documents/bending/utils.ts index 3bcc51ef..e948f55e 100644 --- a/src/components/production/WorkOrders/documents/bending/utils.ts +++ b/src/components/production/WorkOrders/documents/bending/utils.ts @@ -127,11 +127,42 @@ export function getMaterialMapping(productCode: string, finishMaterial: string): const API_BASE = process.env.NEXT_PUBLIC_API_URL || 'https://api.sam.kr'; -export function getBendingImageUrl( +/** + * 절곡 이미지 키 생성 (bending_images 맵 조회용) + */ +export function getBendingImageKey( category: 'guiderail' | 'bottombar' | 'smokebarrier' | 'box', productCode: string, type?: 'wall' | 'side' | 'both' | 'bottom' | 'rear', ): string { + switch (category) { + case 'guiderail': { + const isLargeProfile = ['KQTS01', 'KTE01'].includes(productCode); + const size = isLargeProfile + ? (type === 'wall' ? '130x75' : '130x125') + : (type === 'wall' ? '120x70' : '120x120'); + return `guiderail_${productCode}_${type}_${size}`; + } + case 'bottombar': + return `bottombar_${productCode}`; + case 'smokebarrier': + return 'smokeban'; + case 'box': + return `box_${type || 'both'}`; + default: + return ''; + } +} + +export function getBendingImageUrl( + category: 'guiderail' | 'bottombar' | 'smokebarrier' | 'box', + productCode: string, + type?: 'wall' | 'side' | 'both' | 'bottom' | 'rear', + bendingImages?: Record, +): string { + const key = getBendingImageKey(category, productCode, type); + if (bendingImages?.[key]) return bendingImages[key]; + // fallback: API 서버 직접 (레거시) switch (category) { case 'guiderail': { const isLargeProfile = ['KQTS01', 'KTE01'].includes(productCode); diff --git a/src/components/production/WorkerScreen/WorkLogModal.tsx b/src/components/production/WorkerScreen/WorkLogModal.tsx index 6723d4a7..34aa98ee 100644 --- a/src/components/production/WorkerScreen/WorkLogModal.tsx +++ b/src/components/production/WorkerScreen/WorkLogModal.tsx @@ -63,6 +63,7 @@ export function WorkLogModal({ const [isLoading, setIsLoading] = useState(false); const [isSaving, setIsSaving] = useState(false); const [error, setError] = useState(null); + const [bendingImages, setBendingImages] = useState>({}); const contentWrapperRef = useRef(null); // Lazy Snapshot 대상 문서 ID const [snapshotDocumentId, setSnapshotDocumentId] = useState(null); @@ -129,6 +130,10 @@ export function WorkLogModal({ if (lotsResult.success) { setMaterialLots(lotsResult.data); } + // bending_images 맵 저장 + if (workLogResult.success && workLogResult.data?.bending_images) { + setBendingImages(workLogResult.data.bending_images); + } // Lazy Snapshot: 문서가 있고 rendered_html이 없으면 스냅샷 대상 if (workLogResult.success && workLogResult.data?.document) { const doc = workLogResult.data.document as { id?: number; rendered_html?: string | null }; @@ -147,6 +152,7 @@ export function WorkLogModal({ // 모달 닫힐 때 상태 초기화 setOrder(null); setMaterialLots([]); + setBendingImages({}); setSnapshotDocumentId(null); setError(null); } @@ -250,7 +256,7 @@ export function WorkLogModal({ lotNoMap[lot.item_code] = lot.lot_no; } } - return ; + return ; } default: return ; diff --git a/src/components/production/WorkerScreen/actions.ts b/src/components/production/WorkerScreen/actions.ts index df71d8ad..99664948 100644 --- a/src/components/production/WorkerScreen/actions.ts +++ b/src/components/production/WorkerScreen/actions.ts @@ -771,6 +771,7 @@ export async function getWorkLog( document: Record | null; auto_values: Record; work_stats: Record; + bending_images: Record; }; error?: string; }> { @@ -779,6 +780,7 @@ export async function getWorkLog( document: Record | null; auto_values: Record; work_stats: Record; + bending_images: Record; }>({ url: `${API_URL}/api/v1/work-orders/${workOrderId}/work-log`, errorMessage: '작업일지 조회에 실패했습니다.',