Files
sam-react-prod/src/components/outbound/VehicleDispatchManagement/VehicleDispatchDetail.tsx
유병철 00a6209347 feat: 레이아웃/출하/생산/회계/대시보드 전반 개선
- HeaderFavoritesBar 대폭 개선
- Sidebar/AuthenticatedLayout 소폭 수정
- ShipmentCreate, VehicleDispatch 출하 관련 개선
- WorkOrderCreate/Edit, WorkerScreen 생산 관련 개선
- InspectionCreate 자재 입고검사 개선
- DailyReport, VendorDetail 회계 수정
- CEO 대시보드: CardManagement/DailyProduction/DailyAttendance 섹션 개선
- useCEODashboard, expense transformer 정비
- DocumentViewer, PDF generate route 소폭 수정
- bill-prototype 개발 페이지 추가
- mockData 불필요 데이터 제거
2026-03-05 13:35:48 +09:00

155 lines
5.3 KiB
TypeScript

'use client';
/**
* 배차차량 상세 페이지
* 3개 섹션: 기본정보, 배차정보, 배송비정보
*/
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 { IntegratedDetailTemplate } from '@/components/templates/IntegratedDetailTemplate';
import { vehicleDispatchConfig } from './vehicleDispatchConfig';
import { getVehicleDispatchById } from './actions';
import {
VEHICLE_DISPATCH_STATUS_LABELS,
VEHICLE_DISPATCH_STATUS_STYLES,
FREIGHT_COST_LABELS,
FREIGHT_COST_STYLES,
} from './types';
import type { VehicleDispatchDetail as VehicleDispatchDetailType } from './types';
import { formatNumber as formatAmount } from '@/lib/utils/amount';
interface VehicleDispatchDetailProps {
id: string;
}
export function VehicleDispatchDetail({ id }: VehicleDispatchDetailProps) {
const router = useRouter();
// API 데이터 상태
const [detail, setDetail] = useState<VehicleDispatchDetailType | null>(null);
const [isLoading, setIsLoading] = useState(true);
const [_error, setError] = useState<string | null>(null);
// API 데이터 로드
const loadData = useCallback(async () => {
setIsLoading(true);
setError(null);
try {
const result = await getVehicleDispatchById(id);
if (result.success && result.data) {
setDetail(result.data);
} else {
setError(result.error || '배차차량 정보를 찾을 수 없습니다.');
}
} catch (err) {
console.error('[VehicleDispatchDetail] loadData error:', err);
setError('데이터를 불러오는 중 오류가 발생했습니다.');
} finally {
setIsLoading(false);
}
}, [id]);
useEffect(() => {
loadData();
}, [loadData]);
const handleEdit = useCallback(() => {
router.push(`/ko/outbound/vehicle-dispatches/${id}?mode=edit`);
}, [id, router]);
// 정보 필드 렌더링 헬퍼
const renderInfoField = (label: string, value: React.ReactNode, className?: string) => (
<div className={className}>
<div className="text-sm text-muted-foreground mb-1">{label}</div>
<div className="font-medium">{value || '-'}</div>
</div>
);
// 컨텐츠 렌더링
const renderViewContent = useCallback((_data: Record<string, unknown>) => {
if (!detail) return null;
return (
<div className="space-y-6">
{/* 카드 1: 기본 정보 */}
<Card>
<CardHeader>
<CardTitle className="text-base"> </CardTitle>
</CardHeader>
<CardContent>
<div className="grid grid-cols-2 md:grid-cols-4 gap-6">
{renderInfoField('배차번호', detail.dispatchNo)}
{renderInfoField('로트번호', detail.lotNo || detail.shipmentNo)}
{renderInfoField('현장명', detail.siteName)}
{renderInfoField('수주처', detail.orderCustomer)}
{renderInfoField(
'운임비용',
<Badge className={FREIGHT_COST_STYLES[detail.freightCostType]}>
{FREIGHT_COST_LABELS[detail.freightCostType]}
</Badge>
)}
{renderInfoField(
'상태',
<Badge className={VEHICLE_DISPATCH_STATUS_STYLES[detail.status]}>
{VEHICLE_DISPATCH_STATUS_LABELS[detail.status]}
</Badge>
)}
{renderInfoField('작성자', detail.writer)}
</div>
</CardContent>
</Card>
{/* 카드 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>
</Card>
{/* 카드 3: 배송비 정보 */}
<Card>
<CardHeader>
<CardTitle className="text-base"> </CardTitle>
</CardHeader>
<CardContent>
<div className="grid grid-cols-2 md:grid-cols-3 gap-6">
{renderInfoField('공급가액', `${formatAmount(detail.supplyAmount)}`)}
{renderInfoField('부가세', `${formatAmount(detail.vat)}`)}
{renderInfoField(
'합계',
<span className="text-lg font-bold">{formatAmount(detail.totalAmount)}</span>
)}
</div>
</CardContent>
</Card>
</div>
);
}, [detail]);
return (
<IntegratedDetailTemplate
config={vehicleDispatchConfig}
mode="view"
initialData={(detail ?? undefined) as Record<string, unknown> | undefined}
itemId={id}
isLoading={isLoading}
onEdit={handleEdit}
renderView={renderViewContent}
/>
);
}