feat: [수주서] R2 bending_images 기반 이미지 연결 — 작업일지와 동일 구조

- getBendingImageUrl 유틸 import하여 R2 이미지 맵 사용
- 가이드레일/케이스/하단마감재/연기차단재 모두 R2 presigned URL 사용
- productCode + guideType 기반 정확한 이미지 매칭
- OrderDocumentModal에서 bendingImages prop 전달
This commit is contained in:
김보곤
2026-03-22 17:09:22 +09:00
parent 85b4510f47
commit 1e21952f7b
2 changed files with 29 additions and 8 deletions

View File

@@ -208,6 +208,7 @@ export function OrderDocumentModal({
motorsLeft={orderDetail?.motors?.left || []}
motorsRight={orderDetail?.motors?.right || []}
bendingParts={orderDetail?.bending_parts || []}
bendingImages={orderDetail?.bending_images}
subsidiaryParts={orderDetail?.subsidiary_parts || []}
categoryCode={orderDetail?.category_code}
/>

View File

@@ -9,6 +9,7 @@
import { useState } from "react";
import { getTodayString } from "@/lib/utils/date";
import { getBendingImageUrl } from "@/components/production/WorkOrders/documents/bending/utils";
import { OrderItem } from "../actions";
import { ProductInfo } from "./OrderDocumentModal";
import { ConstructionApprovalTable } from "@/components/document-system";
@@ -85,6 +86,7 @@ interface SalesOrderDocumentProps {
motorsLeft?: MotorRow[];
motorsRight?: MotorRow[];
bendingParts?: BendingGroup[];
bendingImages?: Record<string, string>;
subsidiaryParts?: SubsidiaryItem[];
categoryCode?: string;
}
@@ -95,9 +97,21 @@ const tdBase = 'border-r border-gray-300 px-1 py-1';
const tdCenter = `${tdBase} text-center`;
const imgPlaceholder = 'flex items-center justify-center border border-dashed border-gray-300 text-gray-400';
/** 절곡품 이미지 렌더링 — image_file_id 있으면 프록시 경유 실제 이미지, 없으면 placeholder */
function BendingImage({ items, height = 'h-20' }: { items: BendingItem[]; height?: string }) {
const fileId = items.find(i => i.image_file_id)?.image_file_id;
/** 절곡품 이미지 렌더링 — R2 bending_images 맵 → 기초관리 files fallback → placeholder */
function BendingImage({ category, productCode, type, bendingImages, fileId, height = 'h-20' }: {
category: 'guiderail' | 'bottombar' | 'smokebarrier' | 'box';
productCode?: string;
type?: 'wall' | 'side' | 'both' | 'bottom' | 'rear';
bendingImages?: Record<string, string>;
fileId?: number | null;
height?: string;
}) {
// 1순위: R2 bending_images 맵 (작업일지와 동일)
const r2Url = getBendingImageUrl(category, productCode || '', type, bendingImages);
if (r2Url) {
return <img src={r2Url} alt="" className={`${height} w-full object-contain`} />;
}
// 2순위: 기초관리 files (file_id 기반 프록시)
if (fileId) {
return <img src={`/api/proxy/files/${fileId}/view`} alt="" className={`${height} w-full object-contain`} />;
}
@@ -127,12 +141,18 @@ export function SalesOrderDocument({
motorsLeft = [],
motorsRight = [],
bendingParts = [],
bendingImages,
subsidiaryParts = [],
}: SalesOrderDocumentProps) {
const [bottomFinishView, setBottomFinishView] = useState<'screen' | 'steel'>('screen');
const motorRows = Math.max(motorsLeft.length, motorsRight.length);
// 첫 번째 제품의 productCode 추출 (이미지 매핑용)
const firstProductCode = productRows[0]?.product_code || 'KSE01';
// 가이드레일 설치 타입 (벽면/측면)
const guideType: 'wall' | 'side' = productRows[0]?.guide_rail?.includes('측면') ? 'side' : 'wall';
// 절곡물 그룹 데이터 추출
const guideRailItems = bendingParts.find(g => g.group === '가이드레일')?.items ?? [];
const caseItems = bendingParts.find(g => g.group === '케이스')?.items ?? [];
@@ -446,7 +466,7 @@ export function SalesOrderDocument({
<tr key={i} className="border-b border-gray-300">
{i === 0 && (
<td className={tdCenter} rowSpan={guideRailItems.length}>
<BendingImage items={guideRailItems} height="h-20" />
<BendingImage category="guiderail" productCode={firstProductCode} type={guideType} bendingImages={bendingImages} height="h-20" />
</td>
)}
<td className={tdCenter}>{item.name}</td>
@@ -475,7 +495,7 @@ export function SalesOrderDocument({
<tr key={i} className="border-b border-gray-300">
{i === 0 && (
<td className={tdCenter} rowSpan={guideSmokeItems.length}>
<BendingImage items={guideSmokeItems} height="h-14" />
<BendingImage category="smokebarrier" bendingImages={bendingImages} height="h-14" />
</td>
)}
<td className={tdCenter}>{item.name}</td>
@@ -513,7 +533,7 @@ export function SalesOrderDocument({
<tr key={i} className="border-b border-gray-300">
{i === 0 && (
<td className={tdCenter} rowSpan={caseItems.length}>
<BendingImage items={caseItems} height="h-24" />
<BendingImage category="box" type="both" bendingImages={bendingImages} height="h-24" />
</td>
)}
<td className={tdCenter}>{item.name}</td>
@@ -542,7 +562,7 @@ export function SalesOrderDocument({
<tr key={i} className="border-b border-gray-300">
{i === 0 && (
<td className={tdCenter} rowSpan={caseSmokeItems.length}>
<BendingImage items={caseSmokeItems} height="h-14" />
<BendingImage category="smokebarrier" bendingImages={bendingImages} height="h-14" />
</td>
)}
<td className={tdCenter}>{item.name}</td>
@@ -580,7 +600,7 @@ export function SalesOrderDocument({
<tr key={i} className="border-b border-gray-300">
{i === 0 && (
<td className={tdCenter} rowSpan={bottomItems.length}>
<BendingImage items={bottomItems} height="h-16" />
<BendingImage category="bottombar" productCode={firstProductCode} bendingImages={bendingImages} height="h-16" />
</td>
)}
<td className={tdCenter}>{item.name}</td>