Files
star-erp/resources/js/Pages/Inventory/Traceability/Components/TraceabilitySummary.tsx

151 lines
7.4 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import { Info } from "lucide-react";
import { TraceabilityNode } from "./TreeView";
interface TraceabilitySummaryProps {
data: TraceabilityNode;
direction: 'forward' | 'backward';
}
export function TraceabilitySummary({ data, direction }: TraceabilitySummaryProps) {
if (!data) return null;
// --- Helper to extract unqiue names/codes from children ---
const getUniqueLabels = (nodes: TraceabilityNode[], prefixToStrip: string = '') => {
const labels = nodes
.map(n => n.label.replace(prefixToStrip, '').trim())
.filter(Boolean);
return Array.from(new Set(labels));
};
// --- Logic for Backward Tracing ---
if (direction === 'backward') {
const poNodes = data.children?.filter(c => c.type === 'production_order') || [];
const poNames = getUniqueLabels(poNodes, '生產工單:');
let materialNodes: TraceabilityNode[] = [];
let grNodes: TraceabilityNode[] = [];
poNodes.forEach(po => {
const materials = po.children?.filter(c => c.type === 'material_batch') || [];
materialNodes = [...materialNodes, ...materials];
materials.forEach(mat => {
const grs = mat.children?.filter(c => c.type === 'goods_receipt') || [];
grNodes = [...grNodes, ...grs];
});
});
const materialNames = getUniqueLabels(materialNodes, '原料批號:');
const grNames = getUniqueLabels(grNodes, '進貨單:');
// Handle case where batch is directly from Goods Receipt (no PO)
const directGrNodes = data.children?.filter(c => c.type === 'goods_receipt') || [];
if (directGrNodes.length > 0) {
grNodes = [...grNodes, ...directGrNodes];
const directGrNames = getUniqueLabels(directGrNodes, '進貨單:');
return (
<div className="bg-blue-50/50 border border-blue-100 rounded-xl p-4 mb-6 flex gap-3 text-gray-700 leading-relaxed shadow-sm">
<Info className="w-5 h-5 text-blue-500 shrink-0 mt-0.5" />
<div>
<p>
<strong className="text-gray-900">{data.batch_number}</strong>
{data.product_name && <span> ({data.product_name})</span>}
{directGrNames.map(n => <strong key={n} className="text-gray-900 mx-1">{n}</strong>).reduce((prev, curr) => [prev, '、', curr] as any)}
</p>
</div>
</div>
);
}
return (
<div className="bg-blue-50/50 border border-blue-100 rounded-xl p-4 mb-6 flex gap-3 text-gray-700 leading-relaxed shadow-sm">
<Info className="w-5 h-5 text-blue-500 shrink-0 mt-0.5" />
<div className="space-y-1.5">
<p>
<strong className="text-gray-900">{data.batch_number}</strong>
{data.product_name && <span> ({data.product_name})</span>}
{poNames.length > 0 ? (
<>
{poNames.slice(0, 3).map(n => <strong key={n} className="text-gray-900 mx-1">{n}</strong>).reduce((prev, curr) => [prev, '、', curr] as any)}
{poNames.length > 3 && `${poNames.length} 張工單`}
</>
) : (
'未知的生產工單產出。'
)}
</p>
{materialNodes.length > 0 && (
<p>
使 <strong className="text-gray-900">{materialNodes.length}</strong>
{materialNames.slice(0, 3).map(n => <strong key={n} className="text-gray-900 mx-1">{n}</strong>).reduce((prev, curr) => [prev, '、', curr] as any)}
{materialNames.length > 3 && ' 等'}
</p>
)}
{grNodes.length > 0 && (
<p>
{grNames.slice(0, 3).map(n => <strong key={n} className="text-gray-900 mx-1">{n}</strong>).reduce((prev, curr) => [prev, '、', curr] as any)}
{grNames.length > 3 && `${grNames.length} 張單據`}
</p>
)}
</div>
</div>
);
}
// --- Logic for Forward Tracing ---
if (direction === 'forward') {
const poNodes = data.children?.filter(c => c.type === 'production_order') || [];
const poNames = getUniqueLabels(poNodes, '投入工單:');
let targetNodes: TraceabilityNode[] = [];
let outNodes: TraceabilityNode[] = [];
poNodes.forEach(po => {
const targets = po.children?.filter(c => c.type === 'target_batch') || [];
targetNodes = [...targetNodes, ...targets];
targets.forEach(tgt => {
const outs = tgt.children?.filter(c => c.type === 'outbound_transaction') || [];
outNodes = [...outNodes, ...outs];
});
});
const targetNames = getUniqueLabels(targetNodes, '產出成品:');
const outNames = getUniqueLabels(outNodes, '出庫單據:');
return (
<div className="bg-blue-50/50 border border-blue-100 rounded-xl p-4 mb-6 flex gap-3 text-gray-700 leading-relaxed shadow-sm">
<Info className="w-5 h-5 text-blue-500 shrink-0 mt-0.5" />
<div className="space-y-1.5">
<p>
<strong className="text-gray-900">{data.batch_number}</strong>
{data.product_name && <span> ({data.product_name})</span>}
{poNames.length > 0 ? (
<>
{poNames.slice(0, 3).map(n => <strong key={n} className="text-gray-900 mx-1">{n}</strong>).reduce((prev, curr) => [prev, '、', curr] as any)}
{poNames.length > 3 && `${poNames.length} 張工單`}
</>
) : (
' 未知的生產工單。'
)}
</p>
{targetNodes.length > 0 && (
<p>
{targetNames.slice(0, 3).map(n => <strong key={n} className="text-gray-900 mx-1">{n}</strong>).reduce((prev, curr) => [prev, '、', curr] as any)}
{targetNames.length > 3 && `${targetNodes.length} 批成品`}
</p>
)}
{outNodes.length > 0 && (
<p>
{outNames.slice(0, 3).map(n => <strong key={n} className="text-gray-900 mx-1">{n}</strong>).reduce((prev, curr) => [prev, '、', curr] as any)}
{outNames.length > 3 && `${outNodes.length} 張單據`}
</p>
)}
</div>
</div>
);
}
return null;
}