feat(WEB): 자재/출고/생산/품질/단가 기능 대폭 개선 및 신규 페이지 추가

자재관리:
- 입고관리 재고조정 다이얼로그 신규 추가, 상세/목록 기능 확장
- 재고현황 컴포넌트 리팩토링

출고관리:
- 출하관리 생성/수정/목록/상세 개선
- 차량배차관리 상세/수정/목록 기능 보강

생산관리:
- 작업지시서 WIP 생산 모달 신규 추가
- 벤딩WIP/슬랫조인트바 검사 콘텐츠 신규 추가
- 작업자화면 기능 대폭 확장 (카드/목록 개선)
- 검사성적서 모달 개선

품질관리:
- 실적보고서 관리 페이지 신규 추가
- 검사관리 문서/타입/목데이터 개선

단가관리:
- 단가배포 페이지 및 컴포넌트 신규 추가
- 단가표 관리 페이지 및 컴포넌트 신규 추가

공통:
- 권한 시스템 추가 개선 (PermissionContext, usePermission, PermissionGuard)
- 메뉴 폴링 훅 개선, 레이아웃 수정
- 모바일 줌/패닝 CSS 수정
- locale 유틸 추가

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
유병철
2026-02-04 12:46:19 +09:00
parent 17c16028b1
commit c1b63b850a
70 changed files with 6832 additions and 384 deletions

View File

@@ -50,10 +50,6 @@ import {
getLogisticsOptions,
getVehicleTonnageOptions,
} from './actions';
import {
FREIGHT_COST_LABELS,
DELIVERY_METHOD_LABELS,
} from './types';
import type {
ShipmentCreateFormData,
DeliveryMethod,
@@ -83,10 +79,12 @@ const deliveryMethodOptions: { value: DeliveryMethod; label: string }[] = [
{ value: 'self_pickup', label: '직접수령' },
];
// 운임비용 옵션
const freightCostOptions: { value: FreightCostType; label: string }[] = Object.entries(
FREIGHT_COST_LABELS
).map(([value, label]) => ({ value: value as FreightCostType, label }));
// 운임비용 옵션 (선불, 착불, 없음)
const freightCostOptions: { value: FreightCostType; label: string }[] = [
{ value: 'prepaid', label: '선불' },
{ value: 'collect', label: '착불' },
{ value: 'none', label: '없음' },
];
// 빈 배차 행 생성
function createEmptyDispatch(): VehicleDispatch {
@@ -111,7 +109,7 @@ export function ShipmentCreate() {
priority: 'normal',
deliveryMethod: 'direct_dispatch',
shipmentDate: '',
freightCost: undefined,
freightCost: 'none',
receiver: '',
receiverContact: '',
zipCode: '',
@@ -229,9 +227,22 @@ export function ShipmentCreate() {
if (validationErrors.length > 0) setValidationErrors([]);
}, [validationErrors]);
// 배송방식에 따라 운임비용 '없음' 고정 여부 판단
const isFreightCostLocked = (method: DeliveryMethod) =>
method === 'direct_dispatch' || method === 'self_pickup';
// 폼 입력 핸들러
const handleInputChange = (field: keyof ShipmentCreateFormData, value: string) => {
setFormData(prev => ({ ...prev, [field]: value }));
if (field === 'deliveryMethod') {
const method = value as DeliveryMethod;
if (isFreightCostLocked(method)) {
setFormData(prev => ({ ...prev, deliveryMethod: method, freightCost: 'none' as FreightCostType }));
} else {
setFormData(prev => ({ ...prev, deliveryMethod: method }));
}
} else {
setFormData(prev => ({ ...prev, [field]: value }));
}
if (validationErrors.length > 0) setValidationErrors([]);
};
@@ -455,7 +466,7 @@ export function ShipmentCreate() {
<Select
value={formData.freightCost || ''}
onValueChange={(value) => handleInputChange('freightCost', value)}
disabled={isSubmitting}
disabled={isSubmitting || isFreightCostLocked(formData.deliveryMethod)}
>
<SelectTrigger>
<SelectValue placeholder="선택" />
@@ -549,7 +560,7 @@ export function ShipmentCreate() {
<TableRow>
<TableHead></TableHead>
<TableHead></TableHead>
<TableHead></TableHead>
<TableHead></TableHead>
<TableHead></TableHead>
<TableHead></TableHead>
<TableHead></TableHead>

View File

@@ -338,18 +338,16 @@ export function ShipmentDetail({ id }: ShipmentDetailProps) {
</CardHeader>
<CardContent>
<div className="grid grid-cols-2 md:grid-cols-4 gap-6">
{renderInfoField('출고번호', detail.shipmentNo)}
{renderInfoField('로트번호', detail.lotNo)}
{renderInfoField('현장명', detail.siteName)}
{renderInfoField('수주처', detail.customerName)}
{renderInfoField('거래등급', detail.customerGrade)}
{renderInfoField('작성자', detail.registrant)}
{renderInfoField(
'상태',
<Badge className={SHIPMENT_STATUS_STYLES[detail.status]}>
{SHIPMENT_STATUS_LABELS[detail.status]}
</Badge>
)}
{renderInfoField('작성자', detail.registrant)}
</div>
</CardContent>
</Card>
@@ -408,7 +406,7 @@ export function ShipmentDetail({ id }: ShipmentDetailProps) {
<TableRow>
<TableHead></TableHead>
<TableHead></TableHead>
<TableHead></TableHead>
<TableHead></TableHead>
<TableHead></TableHead>
<TableHead></TableHead>
<TableHead></TableHead>

View File

@@ -52,7 +52,6 @@ import {
import {
SHIPMENT_STATUS_LABELS,
SHIPMENT_STATUS_STYLES,
FREIGHT_COST_LABELS,
} from './types';
import type {
ShipmentDetail,
@@ -79,10 +78,12 @@ const deliveryMethodOptions: { value: DeliveryMethod; label: string }[] = [
{ value: 'self_pickup', label: '직접수령' },
];
// 운임비용 옵션
const freightCostOptions: { value: FreightCostType; label: string }[] = Object.entries(
FREIGHT_COST_LABELS
).map(([value, label]) => ({ value: value as FreightCostType, label }));
// 운임비용 옵션 (선불, 착불, 없음)
const freightCostOptions: { value: FreightCostType; label: string }[] = [
{ value: 'prepaid', label: '선불' },
{ value: 'collect', label: '착불' },
{ value: 'none', label: '없음' },
];
// 빈 배차 행 생성
function createEmptyDispatch(): VehicleDispatch {
@@ -174,12 +175,13 @@ export function ShipmentEdit({ id }: ShipmentEditProps) {
setDetail(shipmentDetail);
// 폼 초기값 설정
const lockedFreight = shipmentDetail.deliveryMethod === 'direct_dispatch' || shipmentDetail.deliveryMethod === 'self_pickup';
setFormData({
scheduledDate: shipmentDetail.scheduledDate,
shipmentDate: shipmentDetail.shipmentDate || '',
priority: shipmentDetail.priority,
deliveryMethod: shipmentDetail.deliveryMethod,
freightCost: shipmentDetail.freightCost,
freightCost: lockedFreight ? 'none' : shipmentDetail.freightCost,
receiver: shipmentDetail.receiver || '',
receiverContact: shipmentDetail.receiverContact || '',
zipCode: shipmentDetail.zipCode || '',
@@ -223,9 +225,22 @@ export function ShipmentEdit({ id }: ShipmentEditProps) {
loadData();
}, [loadData]);
// 배송방식에 따라 운임비용 '없음' 고정 여부 판단
const isFreightCostLocked = (method: DeliveryMethod) =>
method === 'direct_dispatch' || method === 'self_pickup';
// 폼 입력 핸들러
const handleInputChange = (field: keyof ShipmentEditFormData, value: string | number | undefined) => {
setFormData(prev => ({ ...prev, [field]: value }));
if (field === 'deliveryMethod') {
const method = value as DeliveryMethod;
if (isFreightCostLocked(method)) {
setFormData(prev => ({ ...prev, deliveryMethod: method, freightCost: 'none' as FreightCostType }));
} else {
setFormData(prev => ({ ...prev, deliveryMethod: method }));
}
} else {
setFormData(prev => ({ ...prev, [field]: value }));
}
if (validationErrors.length > 0) setValidationErrors([]);
};
@@ -375,10 +390,6 @@ export function ShipmentEdit({ id }: ShipmentEditProps) {
</CardHeader>
<CardContent>
<div className="grid grid-cols-2 md:grid-cols-4 gap-6">
<div className="space-y-1">
<Label className="text-muted-foreground"></Label>
<div className="font-medium">{detail.shipmentNo}</div>
</div>
<div className="space-y-1">
<Label className="text-muted-foreground"></Label>
<div className="font-medium">{detail.lotNo}</div>
@@ -388,12 +399,12 @@ export function ShipmentEdit({ id }: ShipmentEditProps) {
<div className="font-medium">{detail.siteName}</div>
</div>
<div className="space-y-1">
<Label className="text-muted-foreground"></Label>
<Label className="text-muted-foreground"></Label>
<div className="font-medium">{detail.customerName}</div>
</div>
<div className="space-y-1">
<Label className="text-muted-foreground"></Label>
<div className="font-medium">{detail.customerGrade || '-'}</div>
<Label className="text-muted-foreground"></Label>
<div className="font-medium">{detail.orderer || '-'}</div>
</div>
<div className="space-y-1">
<Label className="text-muted-foreground"></Label>
@@ -454,7 +465,7 @@ export function ShipmentEdit({ id }: ShipmentEditProps) {
key={`freight-${formData.freightCost}`}
value={formData.freightCost || ''}
onValueChange={(value) => handleInputChange('freightCost', value)}
disabled={isSubmitting}
disabled={isSubmitting || isFreightCostLocked(formData.deliveryMethod)}
>
<SelectTrigger>
<SelectValue placeholder="선택" />
@@ -548,7 +559,7 @@ export function ShipmentEdit({ id }: ShipmentEditProps) {
<TableRow>
<TableHead></TableHead>
<TableHead></TableHead>
<TableHead></TableHead>
<TableHead></TableHead>
<TableHead></TableHead>
<TableHead></TableHead>
<TableHead></TableHead>

View File

@@ -231,23 +231,17 @@ export function ShipmentList() {
icon: Plus,
},
// 테이블 컬럼 (18개 - 출고번호/로트번호 통합)
// 테이블 컬럼 (11개)
columns: [
{ key: 'no', label: '번호', className: 'w-[50px] text-center' },
{ key: 'shipmentNo', label: '출고번호/로트번호', className: 'min-w-[160px]' },
{ key: 'scheduledDate', label: '출고예정일', className: 'w-[100px] text-center' },
{ key: 'lotNo', label: '로트번호', className: 'min-w-[120px]' },
{ key: 'siteName', label: '현장명', className: 'min-w-[100px]' },
{ key: 'orderCustomer', label: '수주처', className: 'min-w-[100px]' },
{ key: 'customerGrade', label: '거래등급', className: 'w-[80px] text-center' },
{ key: 'receiver', label: '수신자', className: 'w-[80px] text-center' },
{ key: 'receiverAddress', label: '수신주소', className: 'min-w-[140px]' },
{ key: 'receiverCompany', label: '수신처', className: 'min-w-[100px]' },
{ key: 'status', label: '상태', className: 'w-[80px] text-center' },
{ key: 'dispatch', label: '배차', className: 'w-[80px] text-center' },
{ key: 'arrivalDateTime', label: '입차일시', className: 'w-[130px] text-center' },
{ key: 'tonnage', label: '톤수', className: 'w-[70px] text-center' },
{ key: 'unloadingNo', label: '하차번호', className: 'w-[90px] text-center' },
{ key: 'driverContact', label: '기사연락처', className: 'min-w-[110px] text-center' },
{ key: 'writer', label: '작성자', className: 'w-[80px] text-center' },
{ key: 'shipmentDate', label: '출고일', className: 'w-[100px] text-center' },
],
@@ -292,7 +286,7 @@ export function ShipmentList() {
// 통계 카드
stats,
// 테이블 행 렌더링 (19개 컬럼)
// 테이블 행 렌더링 (11개 컬럼)
renderTableRow: (
item: ShipmentItem,
index: number,
@@ -312,16 +306,9 @@ export function ShipmentList() {
/>
</TableCell>
<TableCell className="text-center text-muted-foreground">{globalIndex}</TableCell>
<TableCell className="font-medium">
<div>{item.shipmentNo}</div>
{item.lotNo && item.lotNo !== item.shipmentNo && (
<div className="text-xs text-muted-foreground">{item.lotNo}</div>
)}
</TableCell>
<TableCell className="text-center">{item.scheduledDate}</TableCell>
<TableCell className="font-medium">{item.lotNo || item.shipmentNo || '-'}</TableCell>
<TableCell className="max-w-[100px] truncate">{item.siteName}</TableCell>
<TableCell>{item.orderCustomer || item.customerName || '-'}</TableCell>
<TableCell className="text-center">{item.customerGrade || '-'}</TableCell>
<TableCell className="text-center">{item.receiver || '-'}</TableCell>
<TableCell className="max-w-[140px] truncate">{item.receiverAddress || '-'}</TableCell>
<TableCell>{item.receiverCompany || '-'}</TableCell>
@@ -331,10 +318,6 @@ export function ShipmentList() {
</Badge>
</TableCell>
<TableCell className="text-center">{item.dispatch || item.deliveryMethodLabel || '-'}</TableCell>
<TableCell className="text-center">{item.arrivalDateTime || '-'}</TableCell>
<TableCell className="text-center">{item.tonnage || '-'}</TableCell>
<TableCell className="text-center">{item.unloadingNo || '-'}</TableCell>
<TableCell className="text-center">{item.driverContact || '-'}</TableCell>
<TableCell className="text-center">{item.writer || item.manager || '-'}</TableCell>
<TableCell className="text-center">{item.shipmentDate || '-'}</TableCell>
</TableRow>
@@ -373,11 +356,14 @@ export function ShipmentList() {
}
infoGrid={
<div className="grid grid-cols-2 gap-x-4 gap-y-3">
<InfoField label="출고번호/로트번호" value={item.shipmentNo || item.lotNo} />
<InfoField label="수주처" value={item.orderCustomer || item.customerName} />
<InfoField label="출고예정일" value={item.scheduledDate} />
<InfoField label="배송방식" value={item.deliveryMethodLabel || DELIVERY_METHOD_LABELS[item.deliveryMethod]} />
<InfoField label="로트번호" value={item.lotNo || item.shipmentNo} />
<InfoField label="현장명" value={item.siteName} />
<InfoField label="수주처" value={item.orderCustomer || item.customerName || '-'} />
<InfoField label="수신자" value={item.receiver || '-'} />
<InfoField label="수신주소" value={item.receiverAddress || '-'} />
<InfoField label="수신처" value={item.receiverCompany || '-'} />
<InfoField label="배차" value={item.dispatch || item.deliveryMethodLabel || '-'} />
<InfoField label="작성자" value={item.writer || item.manager || '-'} />
<InfoField label="출고일" value={item.shipmentDate || '-'} />
</div>
}

View File

@@ -41,11 +41,12 @@ export const PRIORITY_STYLES: Record<ShipmentPriority, string> = {
};
// 운임비용 타입
export type FreightCostType = 'prepaid' | 'collect' | 'free' | 'negotiable';
export type FreightCostType = 'prepaid' | 'collect' | 'free' | 'negotiable' | 'none';
export const FREIGHT_COST_LABELS: Record<FreightCostType, string> = {
prepaid: '선불',
collect: '착불',
none: '없음',
free: '무료',
negotiable: '협의',
};
@@ -137,6 +138,8 @@ export interface ShipmentItem {
writer?: string; // 작성자
shipmentDate?: string; // 출고일
shipmentTime?: string; // 출고시간 (캘린더용)
orderer?: string; // 수주자
createdAt?: string; // 작성일
}
// 출고 품목
@@ -162,6 +165,7 @@ export interface ShipmentDetail {
customerGrade: string; // 거래등급
status: ShipmentStatus; // 상태
registrant?: string; // 작성자
orderer?: string; // 수주자
// 수주/배송 정보
scheduledDate: string; // 출고 예정일

View File

@@ -9,6 +9,14 @@ import { useState, useCallback, useEffect } from 'react';
import { useRouter } from 'next/navigation';
import { Badge } from '@/components/ui/badge';
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
import {
Table,
TableBody,
TableCell,
TableHead,
TableHeader,
TableRow,
} from '@/components/ui/table';
import { IntegratedDetailTemplate } from '@/components/templates/IntegratedDetailTemplate';
import { vehicleDispatchConfig } from './vehicleDispatchConfig';
import { getVehicleDispatchById } from './actions';
@@ -87,7 +95,7 @@ export function VehicleDispatchDetail({ id }: VehicleDispatchDetailProps) {
<CardContent>
<div className="grid grid-cols-2 md:grid-cols-4 gap-6">
{renderInfoField('배차번호', detail.dispatchNo)}
{renderInfoField('출고번호', detail.shipmentNo)}
{renderInfoField('로트번호', detail.lotNo || detail.shipmentNo)}
{renderInfoField('현장명', detail.siteName)}
{renderInfoField('수주처', detail.orderCustomer)}
{renderInfoField(
@@ -107,20 +115,34 @@ export function VehicleDispatchDetail({ id }: VehicleDispatchDetailProps) {
</CardContent>
</Card>
{/* 카드 2: 배차 정보 */}
{/* 카드 2: 배차 정보 (테이블 형태) */}
<Card>
<CardHeader>
<CardTitle className="text-base"> </CardTitle>
</CardHeader>
<CardContent>
<div className="grid grid-cols-2 md:grid-cols-3 gap-6">
{renderInfoField('물류업체', detail.logisticsCompany)}
{renderInfoField('입차일시', detail.arrivalDateTime)}
{renderInfoField('톤수', detail.tonnage)}
{renderInfoField('차량번호', detail.vehicleNo)}
{renderInfoField('기사연락처', detail.driverContact)}
{renderInfoField('비고', detail.remarks || '-')}
</div>
<CardContent className="p-0">
<Table>
<TableHeader>
<TableRow>
<TableHead></TableHead>
<TableHead></TableHead>
<TableHead></TableHead>
<TableHead></TableHead>
<TableHead></TableHead>
<TableHead></TableHead>
</TableRow>
</TableHeader>
<TableBody>
<TableRow>
<TableCell>{detail.logisticsCompany || '-'}</TableCell>
<TableCell>{detail.arrivalDateTime || '-'}</TableCell>
<TableCell>{detail.tonnage || '-'}</TableCell>
<TableCell>{detail.vehicleNo || '-'}</TableCell>
<TableCell>{detail.driverContact || '-'}</TableCell>
<TableCell>{detail.remarks || '-'}</TableCell>
</TableRow>
</TableBody>
</Table>
</CardContent>
</Card>

View File

@@ -202,8 +202,8 @@ export function VehicleDispatchEdit({ id }: VehicleDispatchEditProps) {
<div className="font-medium">{detail.dispatchNo}</div>
</div>
<div className="space-y-1">
<Label className="text-muted-foreground"></Label>
<div className="font-medium">{detail.shipmentNo}</div>
<Label className="text-muted-foreground"></Label>
<div className="font-medium">{detail.lotNo || detail.shipmentNo}</div>
</div>
<div className="space-y-1">
<Label className="text-muted-foreground"></Label>
@@ -275,11 +275,11 @@ export function VehicleDispatchEdit({ id }: VehicleDispatchEditProps) {
/>
</div>
<div className="space-y-2">
<Label></Label>
<Label></Label>
<Input
value={formData.tonnage}
onChange={(e) => handleInputChange('tonnage', e.target.value)}
placeholder="예: 3.5톤"
placeholder="예: 3.5 톤"
disabled={isSubmitting}
/>
</div>

View File

@@ -163,23 +163,19 @@ export function VehicleDispatchList() {
onEndDateChange: setEndDate,
},
// 테이블 컬럼
// 테이블 컬럼 (13개)
columns: [
{ key: 'no', label: '번호', className: 'w-[50px] text-center' },
{ key: 'no', label: 'No.', className: 'w-[50px] text-center' },
{ key: 'dispatchNo', label: '배차번호', className: 'min-w-[130px]' },
{ key: 'shipmentNo', label: '출고번호', className: 'min-w-[130px]' },
{ key: 'lotNo', label: '로트번호', className: 'min-w-[120px]' },
{ key: 'siteName', label: '현장명', className: 'min-w-[100px]' },
{ key: 'orderCustomer', label: '수주처', className: 'min-w-[100px]' },
{ key: 'logisticsCompany', label: '물류업체', className: 'min-w-[90px]' },
{ key: 'tonnage', label: '톤수', className: 'w-[70px] text-center' },
{ key: 'supplyAmount', label: '공급가액', className: 'w-[100px] text-right' },
{ key: 'vat', label: '부가세', className: 'w-[90px] text-right' },
{ key: 'totalAmount', label: '합계', className: 'w-[100px] text-right' },
{ key: 'freightCostType', label: '선/착불', className: 'w-[70px] text-center' },
{ key: 'vehicleNo', label: '차량번호', className: 'min-w-[100px]' },
{ key: 'driverContact', label: '기사연락처', className: 'min-w-[110px]' },
{ key: 'writer', label: '작성자', className: 'w-[80px] text-center' },
{ key: 'arrivalDateTime', label: '입차일시', className: 'w-[130px] text-center' },
{ key: 'status', label: '상태', className: 'w-[80px] text-center' },
{ key: 'remarks', label: '비고', className: 'min-w-[100px]' },
],
@@ -201,15 +197,14 @@ export function VehicleDispatchList() {
itemsPerPage: ITEMS_PER_PAGE,
// 검색
searchPlaceholder: '배차번호, 출고번호, 현장명, 수주처, 차량번호 검색...',
searchPlaceholder: '배차번호, 로트번호, 현장명, 수주처 검색...',
searchFilter: (item: VehicleDispatchItem, search: string) => {
const s = search.toLowerCase();
return (
item.dispatchNo.toLowerCase().includes(s) ||
item.shipmentNo.toLowerCase().includes(s) ||
(item.lotNo || item.shipmentNo).toLowerCase().includes(s) ||
item.siteName.toLowerCase().includes(s) ||
item.orderCustomer.toLowerCase().includes(s) ||
item.vehicleNo.toLowerCase().includes(s)
item.orderCustomer.toLowerCase().includes(s)
);
},
@@ -235,31 +230,29 @@ export function VehicleDispatchList() {
onCheckedChange={handlers.onToggle}
/>
</TableCell>
<TableCell className="text-center text-muted-foreground">{globalIndex}</TableCell>
<TableCell className="font-medium">{item.dispatchNo}</TableCell>
<TableCell>{item.shipmentNo}</TableCell>
<TableCell className="max-w-[100px] truncate">{item.siteName}</TableCell>
<TableCell>{item.orderCustomer}</TableCell>
<TableCell>{item.logisticsCompany}</TableCell>
<TableCell className="text-center">{item.tonnage}</TableCell>
<TableCell className="text-right">{formatAmount(item.supplyAmount)}</TableCell>
<TableCell className="text-right">{formatAmount(item.vat)}</TableCell>
<TableCell className="text-right font-medium">{formatAmount(item.totalAmount)}</TableCell>
<TableCell className="text-center">
<Badge className={`text-xs ${FREIGHT_COST_STYLES[item.freightCostType]}`}>
{FREIGHT_COST_LABELS[item.freightCostType]}
</Badge>
<TableCell className="w-[50px] text-center text-muted-foreground">{globalIndex}</TableCell>
<TableCell className="min-w-[130px] font-medium">{item.dispatchNo}</TableCell>
<TableCell className="min-w-[120px]">{item.lotNo || item.shipmentNo}</TableCell>
<TableCell className="min-w-[100px] truncate">{item.siteName}</TableCell>
<TableCell className="min-w-[100px]">{item.orderCustomer}</TableCell>
<TableCell className="min-w-[90px]">{item.logisticsCompany}</TableCell>
<TableCell className="w-[100px] text-right">{formatAmount(item.supplyAmount || 0)}</TableCell>
<TableCell className="w-[90px] text-right">{formatAmount(item.vat || 0)}</TableCell>
<TableCell className="w-[100px] text-right font-medium">{formatAmount(item.totalAmount || 0)}</TableCell>
<TableCell className="w-[70px] text-center">
{item.freightCostType ? (
<Badge className={`text-xs ${FREIGHT_COST_STYLES[item.freightCostType]}`}>
{FREIGHT_COST_LABELS[item.freightCostType]}
</Badge>
) : '-'}
</TableCell>
<TableCell>{item.vehicleNo}</TableCell>
<TableCell>{item.driverContact}</TableCell>
<TableCell className="text-center">{item.writer}</TableCell>
<TableCell className="text-center">{item.arrivalDateTime}</TableCell>
<TableCell className="text-center">
<TableCell className="w-[80px] text-center">{item.writer || '-'}</TableCell>
<TableCell className="w-[80px] text-center">
<Badge className={`text-xs ${VEHICLE_DISPATCH_STATUS_STYLES[item.status]}`}>
{VEHICLE_DISPATCH_STATUS_LABELS[item.status]}
</Badge>
</TableCell>
<TableCell className="max-w-[100px] truncate">{item.remarks || '-'}</TableCell>
<TableCell className="min-w-[100px] truncate">{item.remarks || '-'}</TableCell>
</TableRow>
);
},
@@ -296,17 +289,16 @@ export function VehicleDispatchList() {
}
infoGrid={
<div className="grid grid-cols-2 gap-x-4 gap-y-3">
<InfoField label="출고번호" value={item.shipmentNo} />
<InfoField label="로트번호" value={item.lotNo || item.shipmentNo} />
<InfoField label="수주처" value={item.orderCustomer} />
<InfoField label="물류업체" value={item.logisticsCompany} />
<InfoField label="톤수" value={item.tonnage} />
<InfoField label="공급가액" value={`${formatAmount(item.supplyAmount)}`} />
<InfoField label="합계" value={`${formatAmount(item.totalAmount)}`} />
<InfoField
label="선/착불"
value={FREIGHT_COST_LABELS[item.freightCostType]}
/>
<InfoField label="차량번호" value={item.vehicleNo} />
<InfoField label="입차일시" value={item.arrivalDateTime} />
<InfoField label="작성자" value={item.writer} />
</div>
}
actions={

View File

@@ -35,6 +35,7 @@ export interface VehicleDispatchItem {
id: string;
dispatchNo: string; // 배차번호
shipmentNo: string; // 출고번호
lotNo?: string; // 로트번호
siteName: string; // 현장명
orderCustomer: string; // 수주처
logisticsCompany: string; // 물류업체
@@ -57,6 +58,7 @@ export interface VehicleDispatchDetail {
// 기본 정보
dispatchNo: string; // 배차번호
shipmentNo: string; // 출고번호
lotNo?: string; // 로트번호
siteName: string; // 현장명
orderCustomer: string; // 수주처
freightCostType: FreightCostType; // 운임비용