Files
sam-react-prod/src/components/items/ItemDetailClient.tsx

399 lines
15 KiB
TypeScript
Raw Normal View History

/**
* Client Component
*
*
*/
'use client';
import { useRouter } from 'next/navigation';
import type { ItemMaster } from '@/types/item';
import { ITEM_TYPE_LABELS, PART_TYPE_LABELS, PART_USAGE_LABELS, PRODUCT_CATEGORY_LABELS } from '@/types/item';
import { Button } from '@/components/ui/button';
import { Badge } from '@/components/ui/badge';
import { Label } from '@/components/ui/label';
import {
Card,
CardContent,
CardHeader,
CardTitle,
} from '@/components/ui/card';
import {
Table,
TableBody,
TableCell,
TableHead,
TableHeader,
TableRow,
} from '@/components/ui/table';
import { ArrowLeft, Edit, Package } from 'lucide-react';
interface ItemDetailClientProps {
item: ItemMaster;
}
/**
* Badge
*/
function getItemTypeBadge(itemType: string) {
const badges: Record<string, { className: string }> = {
FG: { className: 'bg-purple-50 text-purple-700 border-purple-200' },
PT: { className: 'bg-orange-50 text-orange-700 border-orange-200' },
SM: { className: 'bg-green-50 text-green-700 border-green-200' },
RM: { className: 'bg-blue-50 text-blue-700 border-blue-200' },
CS: { className: 'bg-gray-50 text-gray-700 border-gray-200' },
};
const config = badges[itemType] || { className: '' };
return (
<Badge variant="outline" className={config.className}>
{ITEM_TYPE_LABELS[itemType as keyof typeof ITEM_TYPE_LABELS]}
</Badge>
);
}
/**
* ( )
*/
function formatItemCodeForAssembly(item: ItemMaster): string {
return item.itemCode;
}
export default function ItemDetailClient({ item }: ItemDetailClientProps) {
const router = useRouter();
return (
<div className="space-y-6">
{/* 헤더 */}
<div className="flex flex-col md:flex-row md:items-center justify-between gap-4">
<div className="flex items-start gap-3">
<div className="p-2 bg-primary/10 rounded-lg hidden md:block">
<Package className="w-6 h-6 text-primary" />
</div>
<div>
<h1 className="text-xl md:text-2xl"> </h1>
<p className="text-sm text-muted-foreground mt-1">
</p>
</div>
</div>
<div className="flex gap-2">
<Button
type="button"
variant="outline"
onClick={() => router.back()}
>
<ArrowLeft className="w-4 h-4 mr-2" />
</Button>
<Button
type="button"
onClick={() => router.push(`/items/${encodeURIComponent(item.itemCode)}/edit?type=${item.itemType}&id=${item.id}`)}
>
<Edit className="w-4 h-4 mr-2" />
</Button>
</div>
</div>
{/* 기본 정보 */}
<Card>
<CardHeader>
<CardTitle> </CardTitle>
</CardHeader>
<CardContent>
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
<div>
<Label className="text-muted-foreground"></Label>
<p className="mt-1">
<code className="text-sm bg-gray-100 px-2 py-1 rounded">
{formatItemCodeForAssembly(item)}
</code>
</p>
</div>
<div>
<Label className="text-muted-foreground"></Label>
<p className="mt-1 font-medium">{item.itemName}</p>
</div>
<div>
<Label className="text-muted-foreground"></Label>
<p className="mt-1">{getItemTypeBadge(item.itemType)}</p>
</div>
{item.itemType === "PT" && item.partType && (
<div>
<Label className="text-muted-foreground"> </Label>
<p className="mt-1">
<Badge variant="outline" className={
item.partType === 'ASSEMBLY' ? 'bg-blue-50 text-blue-700' :
item.partType === 'BENDING' ? 'bg-purple-50 text-purple-700' :
item.partType === 'PURCHASED' ? 'bg-green-50 text-green-700' :
'bg-gray-50 text-gray-700'
}>
{item.partType === 'ASSEMBLY' ? '조립 부품' :
item.partType === 'BENDING' ? '절곡 부품' :
item.partType === 'PURCHASED' ? '구매 부품' :
item.partType}
</Badge>
</p>
</div>
)}
{item.itemType === "PT" && item.partType === "BENDING" && item.partUsage && (
<div>
<Label className="text-muted-foreground"></Label>
<p className="mt-1">
<Badge variant="outline" className="bg-indigo-50 text-indigo-700">
{item.partUsage === "GUIDE_RAIL" ? "가이드레일용" :
item.partUsage === "BOTTOM_FINISH" ? "하단마감재용" :
item.partUsage === "CASE" ? "케이스용" :
item.partUsage === "DOOR" ? "도어용" :
item.partUsage === "BRACKET" ? "브라켓용" :
item.partUsage === "GENERAL" ? "범용 (공통 부품)" :
item.partUsage}
</Badge>
</p>
</div>
)}
{item.itemType !== "FG" && item.partType !== 'ASSEMBLY' && item.specification && (
<div>
<Label className="text-muted-foreground"></Label>
<p className="mt-1">{item.specification}</p>
</div>
)}
{item.itemType !== "FG" && (
<div>
<Label className="text-muted-foreground"></Label>
<p className="mt-1">
<Badge variant="secondary">{item.unit}</Badge>
</p>
</div>
)}
{/* 버전 정보 */}
<div className="md:col-span-2 lg:col-span-3 pt-4 border-t">
<div className="grid grid-cols-1 md:grid-cols-3 gap-4">
<div>
<Label className="text-muted-foreground"> </Label>
<div className="mt-1">
<Badge variant="secondary">V{item.currentRevision || 0}</Badge>
</div>
</div>
<div>
<Label className="text-muted-foreground"> </Label>
<p className="mt-1">{(item.revisions?.length || 0)}</p>
</div>
</div>
</div>
<div>
<Label className="text-muted-foreground"></Label>
<p className="mt-1 text-sm">{new Date(item.createdAt).toLocaleDateString('ko-KR')}</p>
</div>
</div>
</CardContent>
</Card>
{/* 제품(FG) 전용 정보 */}
{item.itemType === 'FG' && (
<Card>
<CardHeader>
<CardTitle> </CardTitle>
</CardHeader>
<CardContent>
<div className="grid grid-cols-1 md:grid-cols-2 gap-6">
{item.productCategory && (
<div>
<Label className="text-muted-foreground"> </Label>
<p className="mt-1 font-medium">{PRODUCT_CATEGORY_LABELS[item.productCategory]}</p>
</div>
)}
{item.lotAbbreviation && (
<div>
<Label className="text-muted-foreground"> </Label>
<p className="mt-1 font-medium">{item.lotAbbreviation}</p>
</div>
)}
</div>
{item.note && (
<div className="mt-4">
<Label className="text-muted-foreground"></Label>
<p className="mt-1 text-sm whitespace-pre-wrap">{item.note}</p>
</div>
)}
</CardContent>
</Card>
)}
{/* 조립 부품 세부 정보 */}
{item.itemType === 'PT' && item.partType === 'ASSEMBLY' && (
<Card>
<CardHeader>
<CardTitle className="flex items-center gap-2">
<Package className="h-5 w-5" />
</CardTitle>
</CardHeader>
<CardContent>
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-6">
{item.category1 && (
<div>
<Label className="text-muted-foreground"></Label>
<p className="mt-1">
<Badge variant="outline" className="bg-indigo-50 text-indigo-700">
{item.category1 === 'guide_rail' ? '가이드레일' :
item.category1 === 'case' ? '케이스' :
item.category1 === 'bottom_finish' ? '하단마감재' :
item.category1}
</Badge>
</p>
</div>
)}
{item.installationType && (
<div>
<Label className="text-muted-foreground"> </Label>
<p className="mt-1">
<Badge variant="outline" className="bg-green-50 text-green-700">
{item.installationType === 'wall' ? '벽면형 (R)' :
item.installationType === 'side' ? '측면형 (S)' :
item.installationType === 'steel' ? '스틸 (B)' :
item.installationType === 'iron' ? '철재 (T)' :
item.installationType}
</Badge>
</p>
</div>
)}
{item.assemblyType && (
<div>
<Label className="text-muted-foreground"></Label>
<p className="mt-1">
<code className="text-sm bg-gray-100 px-2 py-1 rounded">
{item.assemblyType}
</code>
</p>
</div>
)}
{item.material && (
<div>
<Label className="text-muted-foreground"></Label>
<p className="mt-1">
<Badge variant="outline" className="bg-indigo-50 text-indigo-700">
{item.material}
</Badge>
</p>
</div>
)}
{item.assemblyLength && (
<div>
<Label className="text-muted-foreground"></Label>
<p className="mt-1 font-medium">{item.assemblyLength}mm</p>
</div>
)}
{item.sideSpecWidth && item.sideSpecHeight && (
<div>
<Label className="text-muted-foreground"> </Label>
<p className="mt-1">
{item.sideSpecWidth} × {item.sideSpecHeight}mm
</p>
</div>
)}
</div>
</CardContent>
</Card>
)}
{/* 가이드레일 세부 정보 */}
{item.category3 === "가이드레일" && item.guideRailModelType && (
<Card>
<CardHeader>
<CardTitle> </CardTitle>
</CardHeader>
<CardContent>
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-6">
{item.guideRailModelType && (
<div>
<Label className="text-muted-foreground"></Label>
<p className="mt-1">
<Badge variant="outline" className="bg-orange-50 text-orange-700">
{item.guideRailModelType}
</Badge>
</p>
</div>
)}
{item.guideRailModel && (
<div>
<Label className="text-muted-foreground"></Label>
<p className="mt-1">
<Badge variant="outline" className="bg-orange-50 text-orange-700">
{item.guideRailModel}
</Badge>
</p>
</div>
)}
</div>
</CardContent>
</Card>
)}
{/* BOM 정보 - 절곡 부품은 제외 */}
{(item.itemType === 'FG' || (item.itemType === 'PT' && item.partType !== 'BENDING')) && item.bom && item.bom.length > 0 && (
<Card>
<CardHeader>
<div className="flex items-center justify-between">
<CardTitle className="flex items-center gap-2">
<Package className="h-5 w-5" />
(BOM)
</CardTitle>
<Badge variant="outline" className="bg-blue-50 text-blue-700">
{item.bom.length}
</Badge>
</div>
</CardHeader>
<CardContent>
<div className="border rounded-lg overflow-hidden">
<Table>
<TableHeader>
<TableRow>
<TableHead></TableHead>
<TableHead></TableHead>
<TableHead></TableHead>
<TableHead className="text-right"></TableHead>
<TableHead></TableHead>
</TableRow>
</TableHeader>
<TableBody>
{item.bom.map((line, index) => (
<TableRow key={line.id}>
<TableCell>{index + 1}</TableCell>
<TableCell>
<code className="text-xs bg-gray-100 px-2 py-1 rounded">
{line.childItemCode}
</code>
</TableCell>
<TableCell>
<div className="flex items-center gap-2">
{line.childItemName}
{line.isBending && (
<Badge variant="outline" className="text-xs bg-purple-50 text-purple-700">
</Badge>
)}
</div>
</TableCell>
<TableCell className="text-right">{line.quantity}</TableCell>
<TableCell>{line.unit}</TableCell>
</TableRow>
))}
</TableBody>
</Table>
</div>
</CardContent>
</Card>
)}
</div>
);
}