All checks were successful
ERP-Deploy-Demo / deploy-demo (push) Successful in 1m16s
155 lines
8.3 KiB
TypeScript
155 lines
8.3 KiB
TypeScript
import React, { useState } from 'react';
|
||
import { Head, router } from '@inertiajs/react';
|
||
import AuthenticatedLayout from '@/Layouts/AuthenticatedLayout';
|
||
import { PageProps } from '@/types/global';
|
||
import { Card, CardContent, CardHeader, CardTitle } from '@/Components/ui/card';
|
||
import { Button } from '@/Components/ui/button';
|
||
import { Input } from '@/Components/ui/input';
|
||
import { Label } from '@/Components/ui/label';
|
||
import { TrendingUp, Search, RotateCcw } from 'lucide-react';
|
||
import { RadioGroup, RadioGroupItem } from '@/Components/ui/radio-group';
|
||
import { cn } from '@/lib/utils';
|
||
import TreeView, { TraceabilityNode } from './Components/TreeView';
|
||
import { TraceabilitySummary } from './Components/TraceabilitySummary';
|
||
import { Can } from '@/Components/Permission/Can';
|
||
|
||
interface Props extends PageProps {
|
||
search: {
|
||
batch_number: string | null;
|
||
direction: 'backward' | 'forward';
|
||
};
|
||
result: TraceabilityNode | null;
|
||
}
|
||
|
||
export default function TraceabilityIndex({ search, result }: Props) {
|
||
const [batchNumber, setBatchNumber] = useState(search.batch_number || '');
|
||
const [direction, setDirection] = useState<'backward' | 'forward'>(search.direction || 'backward');
|
||
const [isSearching, setIsSearching] = useState(false);
|
||
|
||
const handleSearch = (e: React.FormEvent) => {
|
||
e.preventDefault();
|
||
if (!batchNumber.trim()) return;
|
||
|
||
setIsSearching(true);
|
||
router.get(
|
||
route('inventory.traceability.index'),
|
||
{ batch_number: batchNumber.trim(), direction },
|
||
{
|
||
preserveState: true,
|
||
preserveScroll: true,
|
||
onFinish: () => setIsSearching(false)
|
||
}
|
||
);
|
||
};
|
||
|
||
return (
|
||
<AuthenticatedLayout
|
||
breadcrumbs={[
|
||
{ label: '報表與分析', href: '#' },
|
||
{ label: '批號溯源', href: route('inventory.traceability.index'), isPage: true },
|
||
]}
|
||
>
|
||
<Head title="批號溯源 - Star ERP" />
|
||
|
||
<div className="container mx-auto p-6 max-w-7xl">
|
||
<div className="mb-6">
|
||
<div className="mb-4">
|
||
<h1 className="text-2xl font-bold text-grey-0 flex items-center gap-2">
|
||
<TrendingUp className="h-6 w-6 text-primary-main" />
|
||
批號溯源
|
||
</h1>
|
||
<p className="text-gray-500 mt-1">
|
||
透過批號追蹤產品的生產履歷,支援從成品追溯原料供應商(逆向),或從原料追查銷售去向(順向)。
|
||
</p>
|
||
</div>
|
||
</div>
|
||
|
||
<Can permission="inventory.traceability.view">
|
||
<Card className="mb-6 bg-white shadow-sm border-gray-200">
|
||
<CardHeader className="pb-3 border-b border-gray-100 bg-gray-50/50">
|
||
<CardTitle className="text-lg flex items-center gap-2 text-gray-800">
|
||
<Search className="h-5 w-5 text-primary-main" />
|
||
查詢條件
|
||
</CardTitle>
|
||
</CardHeader>
|
||
<CardContent className="pt-6">
|
||
<form onSubmit={handleSearch} className="flex flex-col md:flex-row gap-6 items-start md:items-end">
|
||
<div className="flex-1 w-full space-y-1.5">
|
||
<Label htmlFor="batchNumber" className="text-sm font-medium text-grey-1">查詢批號</Label>
|
||
<Input
|
||
id="batchNumber"
|
||
type="text"
|
||
placeholder="請輸入欲查詢的批號 (例如:PROD-TW-20240101-01)"
|
||
value={batchNumber}
|
||
onChange={(e) => setBatchNumber(e.target.value)}
|
||
className="max-w-md w-full"
|
||
/>
|
||
</div>
|
||
|
||
<div className="space-y-3">
|
||
<Label className="text-gray-700 font-medium">追蹤方向</Label>
|
||
<RadioGroup
|
||
value={direction}
|
||
onValueChange={(val: 'backward' | 'forward') => setDirection(val)}
|
||
className="flex space-x-6"
|
||
>
|
||
<div
|
||
className={cn(
|
||
"flex items-center space-x-2 px-4 py-2 rounded-lg border cursor-pointer transition-colors",
|
||
direction === 'backward' ? "bg-primary-lightest border-primary-light" : "bg-gray-50 border-gray-200 hover:bg-gray-100"
|
||
)}
|
||
>
|
||
<RadioGroupItem value="backward" id="backward" />
|
||
<Label htmlFor="backward" className="cursor-pointer flex items-center gap-1.5 font-medium">
|
||
<RotateCcw className="h-4 w-4 text-primary-main" />
|
||
逆向溯源 (成品 ➔ 原料)
|
||
</Label>
|
||
</div>
|
||
<div
|
||
className={cn(
|
||
"flex items-center space-x-2 px-4 py-2 rounded-lg border cursor-pointer transition-colors",
|
||
direction === 'forward' ? "bg-primary-lightest border-primary-light" : "bg-gray-50 border-gray-200 hover:bg-gray-100"
|
||
)}
|
||
>
|
||
<RadioGroupItem value="forward" id="forward" />
|
||
<Label htmlFor="forward" className="cursor-pointer flex items-center gap-1.5 font-medium">
|
||
<TrendingUp className="h-4 w-4 text-primary-main" />
|
||
順向追蹤 (原料 ➔ 去向)
|
||
</Label>
|
||
</div>
|
||
</RadioGroup>
|
||
</div>
|
||
|
||
<Button
|
||
type="submit"
|
||
disabled={isSearching || !batchNumber.trim()}
|
||
className="button-filled-primary min-w-[120px]"
|
||
>
|
||
{isSearching ? '查詢中...' : '開始查詢'}
|
||
</Button>
|
||
</form>
|
||
</CardContent>
|
||
</Card>
|
||
|
||
{search.batch_number && (
|
||
<div className="bg-white rounded-xl border border-gray-200 shadow-sm overflow-hidden p-6 md:p-8">
|
||
{result ? (
|
||
<>
|
||
<TraceabilitySummary data={result} direction={search.direction || 'backward'} />
|
||
<TreeView data={result} />
|
||
</>
|
||
) : (
|
||
<div className="py-16 flex flex-col items-center justify-center text-gray-500">
|
||
<Search className="h-12 w-12 text-gray-300 mb-4" />
|
||
<h3 className="text-lg font-medium text-gray-900 mb-1">找不到符合的批號資料</h3>
|
||
<p>請確認您輸入的批號「{search.batch_number}」是否正確存在於系統中。</p>
|
||
</div>
|
||
)}
|
||
</div>
|
||
)}
|
||
</Can>
|
||
</div>
|
||
</AuthenticatedLayout>
|
||
);
|
||
}
|