Files
star-erp/resources/js/Components/Product/ProductTable.tsx
sky121113 4fa87925a2
All checks were successful
Koori-ERP-Deploy-System / deploy-demo (push) Has been skipped
Koori-ERP-Deploy-System / deploy-production (push) Successful in 1m8s
UI優化: 全系統狀態標籤 (StatusBadge) 統一化重構完成 (Phase 3 & 4)
2026-02-13 13:16:05 +08:00

244 lines
9.4 KiB
TypeScript
Raw Permalink 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 {
Table,
TableBody,
TableCell,
TableHead,
TableHeader,
TableRow,
} from "@/Components/ui/table";
import { Button } from "@/Components/ui/button";
import { StatusBadge } from "@/Components/shared/StatusBadge";
import { Pencil, Trash2, ArrowUpDown, ArrowUp, ArrowDown, Eye } from "lucide-react";
import {
Tooltip,
TooltipContent,
TooltipProvider,
TooltipTrigger,
} from "@/Components/ui/tooltip";
import {
AlertDialog,
AlertDialogAction,
AlertDialogCancel,
AlertDialogContent,
AlertDialogDescription,
AlertDialogFooter,
AlertDialogHeader,
AlertDialogTitle,
AlertDialogTrigger,
} from "@/Components/ui/alert-dialog";
import { Can } from "@/Components/Permission/Can";
import { Link } from "@inertiajs/react";
import type { Product } from "@/Pages/Product/Index";
interface ProductTableProps {
products: Product[];
onDelete: (id: string) => void;
startIndex: number;
sortField: string | null;
sortDirection: "asc" | "desc" | null;
onSort: (field: string) => void;
}
export default function ProductTable({
products,
onDelete,
startIndex,
sortField,
sortDirection,
onSort,
}: ProductTableProps) {
// const [barcodeDialogOpen, setBarcodeDialogOpen] = useState(false);
// const [selectedProduct, setSelectedProduct] = useState<Product | null>(null);
const SortIcon = ({ field }: { field: string }) => {
if (sortField !== field) {
return <ArrowUpDown className="h-4 w-4 text-muted-foreground ml-1" />;
}
if (sortDirection === "asc") {
return <ArrowUp className="h-4 w-4 text-primary ml-1" />;
}
if (sortDirection === "desc") {
return <ArrowDown className="h-4 w-4 text-primary ml-1" />;
}
return <ArrowUpDown className="h-4 w-4 text-muted-foreground ml-1" />;
};
// 查看條碼
/*
const handleViewBarcode = (product: Product) => {
setSelectedProduct(product);
setBarcodeDialogOpen(true);
};
*/
return (
<>
<div className="bg-white rounded-lg shadow-sm border">
<Table>
<TableHeader className="bg-gray-50">
<TableRow>
<TableHead className="w-[50px] text-center">#</TableHead>
<TableHead className="w-[150px]"></TableHead>
<TableHead>
<button onClick={() => onSort("name")} className="flex items-center hover:text-gray-900">
<SortIcon field="name" />
</button>
</TableHead>
<TableHead>
<button onClick={() => onSort("category_id")} className="flex items-center hover:text-gray-900">
<SortIcon field="category_id" />
</button>
</TableHead>
<TableHead>
<button onClick={() => onSort("base_unit_id")} className="flex items-center hover:text-gray-900">
<SortIcon field="base_unit_id" />
</button>
</TableHead>
<TableHead></TableHead>
<TableHead className="w-[200px]"></TableHead>
<TableHead></TableHead>
<TableHead className="text-center"></TableHead>
<TableHead className="text-center"></TableHead>
</TableRow>
</TableHeader>
<TableBody>
{products.length === 0 ? (
<TableRow>
<TableCell colSpan={9} className="text-center py-8 text-gray-500">
</TableCell>
</TableRow>
) : (
products.map((product, index) => (
<TableRow key={product.id}>
<TableCell className="text-gray-500 font-medium text-center">
{startIndex + index}
</TableCell>
<TableCell className="font-mono text-sm text-gray-700">
{product.barcode || "-"}
</TableCell>
<TableCell>
<div className="flex flex-col">
<div className="flex items-center gap-2">
<span className="font-medium text-grey-0">{product.name}</span>
{product.brand && <StatusBadge variant="neutral" className="text-[10px] h-4 px-1">{product.brand}</StatusBadge>}
</div>
<span className="text-xs text-gray-400 font-mono">: {product.code}</span>
</div>
</TableCell>
<TableCell>
<StatusBadge variant="neutral">
{product.category?.name || '-'}
</StatusBadge>
</TableCell>
<TableCell>{product.baseUnit?.name || '-'}</TableCell>
<TableCell>
{product.largeUnit ? (
<span className="text-sm text-gray-500">
1 {product.largeUnit?.name} = {Number(product.conversionRate)} {product.baseUnit?.name}
</span>
) : (
'-'
)}
</TableCell>
<TableCell className="max-w-[200px]">
<TooltipProvider delayDuration={300}>
<Tooltip>
<TooltipTrigger asChild>
<div className="truncate text-gray-600 cursor-help">
{product.specification || '-'}
</div>
</TooltipTrigger>
{product.specification && (
<TooltipContent className="max-w-xs break-all">
<p className="whitespace-pre-wrap">{product.specification}</p>
</TooltipContent>
)}
</Tooltip>
</TooltipProvider>
</TableCell>
<TableCell>
<span className="text-sm text-gray-600">{product.location || '-'}</span>
</TableCell>
<TableCell className="text-center">
{product.is_active ? (
<StatusBadge variant="success"></StatusBadge>
) : (
<StatusBadge variant="neutral"></StatusBadge>
)}
</TableCell>
<TableCell className="text-center">
<div className="flex justify-center gap-2">
<Link href={route("products.show", product.id)}>
<Button
variant="outline"
size="sm"
className="button-outlined-primary"
title="查看詳情"
>
<Eye className="h-4 w-4" />
</Button>
</Link>
<Can permission="products.edit">
<Link href={route("products.edit", product.id)}>
<Button
variant="outline"
size="sm"
className="button-outlined-primary"
title="編輯"
>
<Pencil className="h-4 w-4" />
</Button>
</Link>
</Can>
<Can permission="products.delete">
<AlertDialog>
<AlertDialogTrigger asChild>
<Button variant="outline" size="sm" className="button-outlined-error">
<Trash2 className="h-4 w-4" />
</Button>
</AlertDialogTrigger>
<AlertDialogContent>
<AlertDialogHeader>
<AlertDialogTitle></AlertDialogTitle>
<AlertDialogDescription>
{product.name}
</AlertDialogDescription>
</AlertDialogHeader>
<AlertDialogFooter>
<AlertDialogCancel></AlertDialogCancel>
<AlertDialogAction
onClick={() => onDelete(product.id)}
className="bg-red-600 hover:bg-red-700"
>
</AlertDialogAction>
</AlertDialogFooter>
</AlertDialogContent>
</AlertDialog>
</Can>
</div>
</TableCell>
</TableRow>
))
)}
</TableBody>
</Table>
</div>
{/* 條碼查看對話框 - Temporarily disabled */}
{/*
{selectedProduct && (
<BarcodeViewDialog
open={barcodeDialogOpen}
onOpenChange={setBarcodeDialogOpen}
productName={selectedProduct.name}
productCode={selectedProduct.code}
barcodeValue={selectedProduct.code}
/>
)}
*/}
</>
);
}