feat(WEB): 절곡 작업일지 bending_info를 work_orders.options으로 이동 + 텍스트 크기 통일
- bending_info 저장 위치: work_order_items → work_orders.options으로 변경 - types.ts: WorkOrderApi에 options 필드 추가, 변환 함수에서 order 레벨 bendingInfo 매핑 - BendingWorkLogContent: items 탐색 대신 order.bendingInfo 직접 참조 - utils.ts: getMaterialMapping에 KSS01/KSS02 SUS 제품 지원 추가 - 4개 섹션 컴포넌트: text-[10px]/text-[8px] → text-xs로 통일 (가독성 개선)
This commit is contained in:
@@ -56,20 +56,20 @@ export function BendingWorkLogContent({ data: order }: BendingWorkLogContentProp
|
||||
}).replace(/\. /g, '-').replace('.', '')
|
||||
: '-';
|
||||
|
||||
// 첫 번째 아이템의 bendingInfo에서 절곡 데이터 추출
|
||||
const firstBendingInfo = items.find(item => item.bendingInfo)?.bendingInfo as BendingInfoExtended | undefined;
|
||||
// 작업지시 레벨의 bendingInfo에서 절곡 데이터 추출
|
||||
const bendingInfo = order.bendingInfo as BendingInfoExtended | undefined;
|
||||
|
||||
// bendingInfo가 없으면 빈 상태 표시
|
||||
const hasBendingData = !!firstBendingInfo?.productCode;
|
||||
const hasBendingData = !!bendingInfo?.productCode;
|
||||
|
||||
// 재질 매핑
|
||||
const mapping = hasBendingData
|
||||
? getMaterialMapping(firstBendingInfo!.productCode, firstBendingInfo!.finishMaterial)
|
||||
? getMaterialMapping(bendingInfo!.productCode, bendingInfo!.finishMaterial)
|
||||
: null;
|
||||
|
||||
// 생산량 합계
|
||||
const summary = hasBendingData && mapping
|
||||
? calculateProductionSummary(firstBendingInfo!, mapping)
|
||||
? calculateProductionSummary(bendingInfo!, mapping)
|
||||
: { susTotal: 0, egiTotal: 0, grandTotal: 0 };
|
||||
|
||||
return (
|
||||
@@ -142,16 +142,16 @@ export function BendingWorkLogContent({ data: order }: BendingWorkLogContentProp
|
||||
<tbody>
|
||||
<tr>
|
||||
<td className="border border-gray-400 p-2">
|
||||
{hasBendingData ? firstBendingInfo!.productCode : '-'}
|
||||
{hasBendingData ? bendingInfo!.productCode : '-'}
|
||||
</td>
|
||||
<td className="border border-gray-400 p-2">
|
||||
{mapping?.bodyMaterial || '-'}
|
||||
</td>
|
||||
<td className="border border-gray-400 p-2">
|
||||
{hasBendingData ? firstBendingInfo!.finishMaterial : '-'}
|
||||
{hasBendingData ? bendingInfo!.finishMaterial : '-'}
|
||||
</td>
|
||||
<td className="border border-gray-400 p-2">
|
||||
{hasBendingData ? firstBendingInfo!.common.type : '-'}
|
||||
{hasBendingData ? bendingInfo!.common.type : '-'}
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
@@ -161,19 +161,19 @@ export function BendingWorkLogContent({ data: order }: BendingWorkLogContentProp
|
||||
{hasBendingData && mapping ? (
|
||||
<>
|
||||
<GuideRailSection
|
||||
bendingInfo={firstBendingInfo!}
|
||||
bendingInfo={bendingInfo!}
|
||||
mapping={mapping}
|
||||
lotNo={order.lotNo}
|
||||
/>
|
||||
<BottomBarSection
|
||||
bendingInfo={firstBendingInfo!}
|
||||
bendingInfo={bendingInfo!}
|
||||
mapping={mapping}
|
||||
/>
|
||||
<ShutterBoxSection
|
||||
bendingInfo={firstBendingInfo!}
|
||||
bendingInfo={bendingInfo!}
|
||||
/>
|
||||
<SmokeBarrierSection
|
||||
bendingInfo={firstBendingInfo!}
|
||||
bendingInfo={bendingInfo!}
|
||||
/>
|
||||
</>
|
||||
) : (
|
||||
|
||||
@@ -37,7 +37,7 @@ export function BottomBarSection({ bendingInfo, mapping }: BottomBarSectionProps
|
||||
|
||||
{/* 우측: 테이블 */}
|
||||
<div className="flex-1">
|
||||
<table className="w-full border-collapse text-[10px]">
|
||||
<table className="w-full border-collapse text-xs">
|
||||
<thead>
|
||||
<tr className="bg-gray-100">
|
||||
<th className="border border-gray-400 px-1 py-0.5">세부품명</th>
|
||||
|
||||
@@ -32,7 +32,7 @@ function PartTable({ title, rows, imageUrl, lotNo, baseSize }: {
|
||||
{/* 좌측: 이미지 + LOT */}
|
||||
<div className="flex-shrink-0 w-48">
|
||||
<img src={imageUrl} alt={title} className="w-full border border-gray-300" />
|
||||
<div className="text-[10px] text-gray-500 mt-1">
|
||||
<div className="text-xs text-gray-500 mt-1">
|
||||
입고&생산 LOT NO:<br />
|
||||
<span className="text-gray-400">_______________</span>
|
||||
</div>
|
||||
@@ -40,7 +40,7 @@ function PartTable({ title, rows, imageUrl, lotNo, baseSize }: {
|
||||
|
||||
{/* 우측: 테이블 */}
|
||||
<div className="flex-1">
|
||||
<table className="w-full border-collapse text-[10px]">
|
||||
<table className="w-full border-collapse text-xs">
|
||||
<thead>
|
||||
<tr className="bg-gray-100">
|
||||
<th className="border border-gray-400 px-1 py-0.5">세부품명</th>
|
||||
|
||||
@@ -41,18 +41,18 @@ function ShutterBoxSubSection({ box, index }: { box: ShutterBoxData; index: numb
|
||||
className="w-full border border-gray-300"
|
||||
/>
|
||||
{/* 치수 텍스트 오버레이 */}
|
||||
<div className="absolute bottom-1 left-1 text-[8px] bg-white/80 px-1 rounded">
|
||||
<div className="absolute bottom-1 left-1 text-xs bg-white/80 px-1 rounded">
|
||||
{box.size} ({box.direction})
|
||||
</div>
|
||||
</div>
|
||||
<div className="text-[10px] text-gray-500 mt-1">
|
||||
<div className="text-xs text-gray-500 mt-1">
|
||||
레일폭: {box.railWidth}mm
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* 우측: 테이블 */}
|
||||
<div className="flex-1">
|
||||
<table className="w-full border-collapse text-[10px]">
|
||||
<table className="w-full border-collapse text-xs">
|
||||
<thead>
|
||||
<tr className="bg-gray-100">
|
||||
<th className="border border-gray-400 px-1 py-0.5">구성요소</th>
|
||||
|
||||
@@ -37,7 +37,7 @@ export function SmokeBarrierSection({ bendingInfo }: SmokeBarrierSectionProps) {
|
||||
|
||||
{/* 우측: 테이블 */}
|
||||
<div className="flex-1">
|
||||
<table className="w-full border-collapse text-[10px]">
|
||||
<table className="w-full border-collapse text-xs">
|
||||
<thead>
|
||||
<tr className="bg-gray-100">
|
||||
<th className="border border-gray-400 px-1 py-0.5">파트</th>
|
||||
|
||||
@@ -75,8 +75,8 @@ export function calcWeight(material: string, width: number, height: number): Wei
|
||||
// ============================================================
|
||||
|
||||
export function getMaterialMapping(productCode: string, finishMaterial: string): MaterialMapping {
|
||||
// Group 1: KQTS01 - SUS 마감
|
||||
if (productCode === 'KQTS01') {
|
||||
// Group 1: KQTS01, KSS01, KSS02 - SUS 전용 제품
|
||||
if (['KQTS01', 'KSS01', 'KSS02'].includes(productCode)) {
|
||||
return {
|
||||
guideRailFinish: 'SUS 1.2T',
|
||||
bodyMaterial: 'EGI 1.55T',
|
||||
@@ -96,13 +96,14 @@ export function getMaterialMapping(productCode: string, finishMaterial: string):
|
||||
bottomBarExtraFinish: isSUS ? 'SUS 1.2T' : '없음',
|
||||
};
|
||||
}
|
||||
// 기타 제품코드 (KSE01, KSS01, KSS02, KWE01 등) - EGI 기본
|
||||
// 기타 제품코드 (KSE01, KWE01 등) - EGI 기본, 마감유형에 따라 SUS 추가
|
||||
const isSUS = finishMaterial?.includes('SUS');
|
||||
return {
|
||||
guideRailFinish: 'EGI 1.55T',
|
||||
bodyMaterial: 'EGI 1.55T',
|
||||
guideRailExtraFinish: '',
|
||||
guideRailExtraFinish: isSUS ? 'SUS 1.2T' : '',
|
||||
bottomBarFinish: 'EGI 1.55T',
|
||||
bottomBarExtraFinish: '없음',
|
||||
bottomBarExtraFinish: isSUS ? 'SUS 1.2T' : '없음',
|
||||
};
|
||||
}
|
||||
|
||||
@@ -119,7 +120,8 @@ export function getBendingImageUrl(
|
||||
): string {
|
||||
switch (category) {
|
||||
case 'guiderail': {
|
||||
const size = ['KQTS01', 'KTE01'].includes(productCode)
|
||||
const isLargeProfile = ['KQTS01', 'KTE01'].includes(productCode);
|
||||
const size = isLargeProfile
|
||||
? (type === 'wall' ? '130x75' : '130x125')
|
||||
: (type === 'wall' ? '120x70' : '120x120');
|
||||
return `${API_BASE}/images/bending/guiderail/guiderail_${productCode}_${type}_${size}.jpg`;
|
||||
|
||||
@@ -206,7 +206,8 @@ export interface WorkOrder {
|
||||
// 공정 진행 상태 (현재 단계)
|
||||
currentStep: number;
|
||||
|
||||
// 절곡 전용 - 전개도 상세
|
||||
// 절곡 전용
|
||||
bendingInfo?: Record<string, unknown>; // 작업지시 레벨 bending_info (work_orders.options)
|
||||
bendingDetails?: BendingDetail[];
|
||||
|
||||
// 이슈
|
||||
@@ -339,6 +340,7 @@ export interface WorkOrderApi {
|
||||
team_id: number | null;
|
||||
scheduled_date: string | null;
|
||||
memo: string | null;
|
||||
options?: { bending_info?: Record<string, unknown>; [key: string]: unknown } | null;
|
||||
is_active: boolean;
|
||||
created_by: number | null;
|
||||
updated_by: number | null;
|
||||
@@ -482,9 +484,7 @@ export function transformApiToFrontend(api: WorkOrderApi): WorkOrder {
|
||||
const si = item.options.slat_info as { length?: number; slat_count?: number; joint_bar?: number; glass_qty?: number };
|
||||
return { length: si.length || 0, slatCount: si.slat_count || 0, jointBar: si.joint_bar || 0, glassQty: si.glass_qty || 0 };
|
||||
})() : undefined,
|
||||
bendingInfo: item.options?.bending_info
|
||||
? (item.options.bending_info as Record<string, unknown>)
|
||||
: undefined,
|
||||
bendingInfo: undefined, // deprecated: work order 레벨로 이동 (order.bendingInfo)
|
||||
materialInputLots: (() => {
|
||||
const inputs = item.material_inputs as Array<{ stock_lot?: { lot_no?: string } }> | undefined;
|
||||
if (!inputs || inputs.length === 0) return undefined;
|
||||
@@ -492,6 +492,7 @@ export function transformApiToFrontend(api: WorkOrderApi): WorkOrder {
|
||||
return lots.length > 0 ? [...new Set(lots)] : undefined;
|
||||
})(),
|
||||
})),
|
||||
bendingInfo: api.options?.bending_info || undefined,
|
||||
bendingDetails: api.bending_detail ? transformBendingDetail(api.bending_detail) : undefined,
|
||||
issues: (api.issues || []).map(issue => ({
|
||||
id: String(issue.id),
|
||||
|
||||
Reference in New Issue
Block a user