fix: [생산지시] BOM 공정 분류 UI 수정 + 접이식 카드

- BOM types/actions: 필드 매핑 수정 (unit, quantity, unitPrice, totalPrice, nodeName)
- BOM UI: 접이식(collapsible) Card + ChevronDown 토글
- 테이블 컬럼 변경: 품목코드, 품목명, 규격, 단위, 수량, 단가, 금액, 개소
- 중복 key 수정: `${item.id}-${idx}` 패턴 적용
This commit is contained in:
2026-03-05 19:27:01 +09:00
parent 9fc979e135
commit eb18a3facb
3 changed files with 64 additions and 55 deletions

View File

@@ -30,6 +30,7 @@ import {
Circle,
Activity,
Play,
ChevronDown,
} from "lucide-react";
import { PageLayout } from "@/components/organisms/PageLayout";
import { PageHeader } from "@/components/organisms/PageHeader";
@@ -200,6 +201,7 @@ export default function ProductionOrderDetailPage() {
const [isCreateWorkOrderDialogOpen, setIsCreateWorkOrderDialogOpen] = useState(false);
const [isSuccessDialogOpen, setIsSuccessDialogOpen] = useState(false);
const [isCreating, setIsCreating] = useState(false);
const [bomOpen, setBomOpen] = useState(false);
// 데이터 로드
const loadDetail = async () => {
@@ -335,68 +337,69 @@ export default function ProductionOrderDetailPage() {
</Card>
</div>
{/* BOM 품목별 공정 분류 */}
<Card>
<CardHeader>
<CardTitle className="text-base">BOM </CardTitle>
</CardHeader>
<CardContent className="space-y-6">
{detail.bomProcessGroups.length === 0 ? (
<div className="text-center py-8">
<p className="text-muted-foreground text-sm">
BOM .
</p>
{/* BOM 품목별 공정 분류 (접이식) */}
{detail.bomProcessGroups.length > 0 && (
<Card>
<CardHeader
className="cursor-pointer select-none"
onClick={() => setBomOpen((prev) => !prev)}
>
<div className="flex items-center justify-between">
<CardTitle className="text-base">
BOM
<span className="ml-2 text-sm font-normal text-muted-foreground">
({detail.bomProcessGroups.length} )
</span>
</CardTitle>
<ChevronDown
className={`h-5 w-5 text-muted-foreground transition-transform ${
bomOpen ? "rotate-180" : ""
}`}
/>
</div>
) : (
<>
<p className="text-sm font-medium text-muted-foreground border-b pb-2">
</p>
</CardHeader>
{bomOpen && (
<CardContent className="space-y-6 pt-0">
{detail.bomProcessGroups.map((group) => (
<div key={group.processName} className="space-y-2">
<h4 className="text-sm font-semibold">
{group.processName}
{group.sizeSpec && (
<span className="ml-2 text-muted-foreground font-normal">
{group.sizeSpec}
</span>
)}
<h4 className="text-sm font-semibold flex items-center gap-2">
<Badge variant="outline">{group.processName}</Badge>
<span className="text-muted-foreground font-normal text-xs">
{group.items.length}
</span>
</h4>
<div className="border rounded-lg overflow-hidden">
<Table>
<TableHeader>
<TableRow className="bg-gray-50">
<TableHead className="w-[60px] text-center"></TableHead>
<TableHead></TableHead>
<TableHead></TableHead>
<TableHead></TableHead>
<TableHead></TableHead>
<TableHead>LOT NO</TableHead>
<TableHead className="text-right"></TableHead>
<TableHead className="text-center w-[60px]"></TableHead>
<TableHead className="text-center"></TableHead>
<TableHead className="text-right"></TableHead>
<TableHead className="text-right"></TableHead>
<TableHead className="text-right"></TableHead>
<TableHead></TableHead>
</TableRow>
</TableHeader>
<TableBody>
{group.items.map((item, idx) => (
<TableRow key={`${item.id}-${idx}`}>
<TableCell className="text-center font-medium">
{item.itemCode}
<TableCell>
<code className="text-xs bg-gray-100 px-1.5 py-0.5 rounded">
{item.itemCode}
</code>
</TableCell>
<TableCell>{item.itemName}</TableCell>
<TableCell className="text-muted-foreground">
{item.spec || "-"}
</TableCell>
<TableCell>
{item.lotNo ? (
<code className="text-xs bg-gray-100 px-1.5 py-0.5 rounded">
{item.lotNo}
</code>
) : "-"}
</TableCell>
<TableCell className="text-right">
{item.requiredQty > 0 ? formatNumber(item.requiredQty) : "-"}
</TableCell>
<TableCell className="text-center">{item.qty}</TableCell>
<TableCell className="text-center">{item.unit || "-"}</TableCell>
<TableCell className="text-right">{formatNumber(item.quantity)}</TableCell>
<TableCell className="text-right">{formatNumber(item.unitPrice)}</TableCell>
<TableCell className="text-right">{formatNumber(item.totalPrice)}</TableCell>
<TableCell className="text-muted-foreground text-xs">{item.nodeName || "-"}</TableCell>
</TableRow>
))}
</TableBody>
@@ -404,10 +407,10 @@ export default function ProductionOrderDetailPage() {
</div>
</div>
))}
</>
</CardContent>
)}
</CardContent>
</Card>
</Card>
)}
{/* 작업지시서 목록 */}
<Card>

View File

@@ -68,10 +68,12 @@ function transformDetailApiToFrontend(data: ApiProductionOrderDetail): Productio
id: item.id,
itemCode: item.item_code,
itemName: item.item_name,
spec: item.spec,
lotNo: item.lot_no,
requiredQty: item.required_qty,
qty: item.qty,
spec: item.spec || item.specification || '',
unit: item.unit || '',
quantity: item.quantity ?? 0,
unitPrice: item.unit_price ?? 0,
totalPrice: item.total_price ?? 0,
nodeName: item.node_name || '',
})),
})),
};

View File

@@ -90,9 +90,11 @@ export interface ApiBomItem {
item_code: string;
item_name: string;
spec: string;
lot_no: string;
required_qty: number;
qty: number;
unit: string;
quantity: number;
unit_price: number;
total_price: number;
node_name: string;
}
// 프론트 상세 타입
@@ -121,9 +123,11 @@ export interface BomItem {
itemCode: string;
itemName: string;
spec: string;
lotNo: string;
requiredQty: number;
qty: number;
unit: string;
quantity: number;
unitPrice: number;
totalPrice: number;
nodeName: string;
}
// 조회 파라미터