Files
star-erp/resources/js/Components/Product/ProductTable.tsx

209 lines
7.6 KiB
TypeScript
Raw Normal View History

2025-12-30 15:03:19 +08:00
import {
Table,
TableBody,
TableCell,
TableHead,
TableHeader,
TableRow,
} from "@/Components/ui/table";
import { Button } from "@/Components/ui/button";
import { Badge } from "@/Components/ui/badge";
import { Pencil, Trash2, ArrowUpDown, ArrowUp, ArrowDown } from "lucide-react";
import {
AlertDialog,
AlertDialogAction,
AlertDialogCancel,
AlertDialogContent,
AlertDialogDescription,
AlertDialogFooter,
AlertDialogHeader,
AlertDialogTitle,
AlertDialogTrigger,
} from "@/Components/ui/alert-dialog";
import { Can } from "@/Components/Permission/Can";
2025-12-30 15:03:19 +08:00
import type { Product } from "@/Pages/Product/Index";
interface ProductTableProps {
products: Product[];
onEdit: (product: Product) => void;
onDelete: (id: number) => void;
startIndex: number;
sortField: string | null;
sortDirection: "asc" | "desc" | null;
onSort: (field: string) => void;
}
export default function ProductTable({
products,
onEdit,
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">
2025-12-30 15:03:19 +08:00
<TableRow>
<TableHead className="w-[50px] text-center">#</TableHead>
<TableHead>
<button onClick={() => onSort("code")} className="flex items-center hover:text-gray-900">
<SortIcon field="code" />
2025-12-30 15:03:19 +08:00
</button>
</TableHead>
<TableHead>
<button onClick={() => onSort("name")} className="flex items-center hover:text-gray-900">
2025-12-30 15:03:19 +08:00
<SortIcon field="name" />
</button>
</TableHead>
<TableHead>
<button onClick={() => onSort("category_id")} className="flex items-center hover:text-gray-900">
2025-12-30 15:03:19 +08:00
<SortIcon field="category_id" />
</button>
</TableHead>
2026-01-08 12:00:36 +08:00
<TableHead>
<button onClick={() => onSort("base_unit_id")} className="flex items-center hover:text-gray-900">
2026-01-08 12:00:36 +08:00
<SortIcon field="base_unit_id" />
</button>
</TableHead>
2025-12-30 15:03:19 +08:00
<TableHead></TableHead>
<TableHead className="text-center"></TableHead>
</TableRow>
</TableHeader>
<TableBody>
{products.length === 0 ? (
<TableRow>
<TableCell colSpan={7} 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.code}
</TableCell>
<TableCell>
<div className="flex flex-col">
<span className="font-medium">{product.name}</span>
{product.brand && <span className="text-xs text-gray-400">{product.brand}</span>}
</div>
</TableCell>
<TableCell>
<Badge variant="outline">
{product.category?.name || '-'}
</Badge>
</TableCell>
2026-01-08 12:00:36 +08:00
<TableCell>{product.base_unit?.name || '-'}</TableCell>
2025-12-30 15:03:19 +08:00
<TableCell>
2026-01-08 12:00:36 +08:00
{product.large_unit ? (
2025-12-30 15:03:19 +08:00
<span className="text-sm text-gray-500">
2026-01-08 12:00:36 +08:00
1 {product.large_unit?.name} = {Number(product.conversion_rate)} {product.base_unit?.name}
2025-12-30 15:03:19 +08:00
</span>
) : (
'-'
)}
</TableCell>
<TableCell className="text-center">
<div className="flex justify-center gap-2">
{/*
<Button
variant="ghost"
size="sm"
onClick={() => handleViewBarcode(product)}
className="h-8 px-2 text-primary hover:text-primary-dark hover:bg-primary-lightest"
>
<Eye className="h-4 w-4" />
</Button>
*/}
<Can permission="products.edit">
<Button
variant="outline"
size="sm"
onClick={() => onEdit(product)}
className="button-outlined-primary"
>
<Pencil className="h-4 w-4" />
</Button>
</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>
2025-12-30 15:03:19 +08:00
</div>
</TableCell>
</TableRow>
))
)}
</TableBody>
</Table>
</div>
{/* 條碼查看對話框 - Temporarily disabled */}
{/*
{selectedProduct && (
<BarcodeViewDialog
open={barcodeDialogOpen}
onOpenChange={setBarcodeDialogOpen}
productName={selectedProduct.name}
productCode={selectedProduct.code}
barcodeValue={selectedProduct.code}
/>
)}
*/}
</>
);
}