feat: [수주서] R2 bending_images 기반 이미지 연결 — 작업일지와 동일 구조
- getBendingImageUrl 유틸 import하여 R2 이미지 맵 사용 - 가이드레일/케이스/하단마감재/연기차단재 모두 R2 presigned URL 사용 - productCode + guideType 기반 정확한 이미지 매칭 - OrderDocumentModal에서 bendingImages prop 전달
This commit is contained in:
@@ -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}
|
||||
/>
|
||||
|
||||
@@ -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>
|
||||
|
||||
Reference in New Issue
Block a user