Update product-table.tsx

This commit is contained in:
g
2026-01-12 06:54:28 +00:00
parent 211cdc71f9
commit 7c7db0fc09

View File

@@ -1,5 +1,4 @@
import { Table,
Table,
TableBody, TableBody,
TableCell, TableCell,
TableHead, TableHead,
@@ -14,11 +13,15 @@ import {
AlertCircle, AlertCircle,
Calculator, Calculator,
Copy, Copy,
PackageOffset,
Archive
} from "lucide-react"; } from "lucide-react";
import { Button } from "@/components/ui/button"; import { Button } from "@/components/ui/button";
import { Product } from "@/models/products"; import { Product } from "@/models/products";
import { Badge } from "@/components/ui/badge"; import { Badge } from "@/components/ui/badge";
import { Switch } from "@/components/ui/switch"; import { Switch } from "@/components/ui/switch";
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
import { motion, AnimatePresence } from "framer-motion";
interface ProductTableProps { interface ProductTableProps {
products: Product[]; products: Product[];
@@ -68,35 +71,53 @@ const ProductTable = ({
} }
}; };
const renderProductRow = (product: Product, isDisabled: boolean = false) => ( const renderProductRow = (product: Product, index: number, isDisabled: boolean = false) => (
<TableRow <motion.tr
key={product._id} key={product._id}
className={`transition-colors hover:bg-gray-50 dark:hover:bg-zinc-800/70 ${isDisabled ? "opacity-60" : ""}`} initial={{ opacity: 0, y: 10 }}
animate={{ opacity: 1, y: 0 }}
exit={{ opacity: 0 }}
transition={{ duration: 0.2, delay: index * 0.05 }}
className={`group hover:bg-muted/40 border-b border-border/50 transition-colors ${isDisabled ? "opacity-60 bg-muted/20" : ""}`}
> >
<TableCell> <TableCell className="font-medium">
<div className="font-medium truncate max-w-[180px]">{product.name}</div> <div className="flex items-center gap-2">
<div className="hidden sm:block text-sm text-muted-foreground mt-1"> <div className="h-8 w-8 rounded bg-muted/50 flex items-center justify-center text-muted-foreground">
{product.image ? (
<img src={product.image} alt={product.name} className="h-full w-full object-cover rounded" />
) : (
<span className="text-xs font-bold">{product.name.charAt(0).toUpperCase()}</span>
)}
</div>
<div>
<div className="truncate max-w-[180px]">{product.name}</div>
<div className="sm:hidden text-xs text-muted-foreground">
{getCategoryNameById(product.category)} {getCategoryNameById(product.category)}
</div> </div>
</div>
</div>
</TableCell> </TableCell>
<TableCell className="hidden sm:table-cell text-center"> <TableCell className="hidden sm:table-cell text-center">
<Badge variant="outline" className="font-normal bg-background/50">
{getCategoryNameById(product.category)} {getCategoryNameById(product.category)}
</Badge>
</TableCell> </TableCell>
<TableCell className="hidden md:table-cell text-center"> <TableCell className="hidden md:table-cell text-center text-muted-foreground text-sm">
{product.unitType} {product.unitType}
</TableCell> </TableCell>
<TableCell className="text-center"> <TableCell className="text-center">
{product.stockTracking ? ( {product.stockTracking ? (
<div className="flex items-center justify-center gap-1"> <div className="flex items-center justify-center gap-1.5">
{getStockIcon(product)} {getStockIcon(product)}
<span className="text-sm"> <span className={`text-sm font-medium ${product.stockStatus === 'out_of_stock' ? 'text-destructive' :
{product.currentStock !== undefined ? product.currentStock : 0}{" "} product.stockStatus === 'low_stock' ? 'text-amber-500' : 'text-foreground'
{product.unitType} }`}>
{product.currentStock !== undefined ? product.currentStock : 0}
</span> </span>
</div> </div>
) : ( ) : (
<Badge variant="outline" className="text-xs"> <Badge variant="secondary" className="text-[10px] h-5 px-1.5 text-muted-foreground bg-muted/50">
Not Tracked Unlimited
</Badge> </Badge>
)} )}
</TableCell> </TableCell>
@@ -106,17 +127,19 @@ const ProductTable = ({
onCheckedChange={(checked) => onCheckedChange={(checked) =>
onToggleEnabled(product._id as string, checked) onToggleEnabled(product._id as string, checked)
} }
className="data-[state=checked]:bg-primary"
/> />
</TableCell> </TableCell>
<TableCell className="text-right flex justify-end space-x-1"> <TableCell className="text-right">
<div className="flex items-center justify-end gap-1">
{onProfitAnalysis && ( {onProfitAnalysis && (
<Button <Button
variant="ghost" variant="ghost"
size="sm" size="icon"
onClick={() => onClick={() =>
onProfitAnalysis(product._id as string, product.name) onProfitAnalysis(product._id as string, product.name)
} }
className="text-green-600 hover:text-green-700 hover:bg-green-50 dark:hover:bg-green-950/20" className="h-8 w-8 text-emerald-500 hover:text-emerald-600 hover:bg-emerald-500/10"
title="Profit Analysis" title="Profit Analysis"
> >
<Calculator className="h-4 w-4" /> <Calculator className="h-4 w-4" />
@@ -125,9 +148,9 @@ const ProductTable = ({
{onClone && ( {onClone && (
<Button <Button
variant="ghost" variant="ghost"
size="sm" size="icon"
onClick={() => onClone(product)} onClick={() => onClone(product)}
className="text-blue-600 hover:text-blue-700 hover:bg-blue-50 dark:hover:bg-blue-950/20" className="h-8 w-8 text-blue-500 hover:text-blue-600 hover:bg-blue-500/10"
title="Clone Listing" title="Clone Listing"
> >
<Copy className="h-4 w-4" /> <Copy className="h-4 w-4" />
@@ -135,28 +158,30 @@ const ProductTable = ({
)} )}
<Button <Button
variant="ghost" variant="ghost"
size="sm" size="icon"
onClick={() => onEdit(product)} onClick={() => onEdit(product)}
className="h-8 w-8 text-muted-foreground hover:text-foreground"
title="Edit Product" title="Edit Product"
> >
<Edit className="h-4 w-4" /> <Edit className="h-4 w-4" />
</Button> </Button>
<Button <Button
variant="ghost" variant="ghost"
size="sm" size="icon"
onClick={() => onDelete(product._id as string)} onClick={() => onDelete(product._id as string)}
className="text-red-500 hover:text-red-600 hover:bg-red-50 dark:hover:bg-red-950/20" className="h-8 w-8 text-muted-foreground hover:text-destructive hover:bg-destructive/10"
title="Delete Product" title="Delete Product"
> >
<Trash className="h-4 w-4" /> <Trash className="h-4 w-4" />
</Button> </Button>
</div>
</TableCell> </TableCell>
</TableRow> </motion.tr>
); );
const renderTableHeader = () => ( const renderTableHeader = () => (
<TableHeader className="bg-gray-50 dark:bg-zinc-800/50"> <TableHeader className="bg-muted/50 sticky top-0 z-10">
<TableRow className="hover:bg-transparent"> <TableRow className="hover:bg-transparent border-border/50">
<TableHead className="w-[200px]">Product</TableHead> <TableHead className="w-[200px]">Product</TableHead>
<TableHead className="hidden sm:table-cell text-center"> <TableHead className="hidden sm:table-cell text-center">
Category Category
@@ -166,57 +191,86 @@ const ProductTable = ({
<TableHead className="hidden lg:table-cell text-center"> <TableHead className="hidden lg:table-cell text-center">
Enabled Enabled
</TableHead> </TableHead>
<TableHead className="text-right">Actions</TableHead> <TableHead className="text-right pr-6">Actions</TableHead>
</TableRow> </TableRow>
</TableHeader> </TableHeader>
); );
return ( return (
<div className="space-y-6"> <div className="space-y-8">
{/* Enabled Products Table */} {/* Enabled Products Table */}
<div className="rounded-lg border dark:border-zinc-700 shadow-sm overflow-hidden"> <Card className="border-border/40 bg-background/50 backdrop-blur-sm shadow-sm overflow-hidden">
<Table className="relative"> <CardHeader className="py-4 px-6 border-b border-border/50 bg-muted/30">
<CardTitle className="text-lg font-medium flex items-center gap-2">
<CheckCircle className="h-5 w-5 text-primary" />
Active Products
<Badge variant="secondary" className="ml-2 bg-background/80 backdrop-blur-sm">
{sortedEnabledProducts.length}
</Badge>
</CardTitle>
</CardHeader>
<CardContent className="p-0">
<div className="max-h-[600px] overflow-auto">
<Table>
{renderTableHeader()} {renderTableHeader()}
<TableBody> <TableBody>
<AnimatePresence mode="popLayout">
{loading ? ( {loading ? (
Array.from({ length: 1 }).map((_, index) => ( <TableRow>
<TableRow key={index}> <TableCell colSpan={6} className="h-32 text-center text-muted-foreground">
<TableCell>Loading...</TableCell> Loading products...
<TableCell>Loading...</TableCell> </TableCell>
<TableCell>Loading...</TableCell>
<TableCell>Loading...</TableCell>
<TableCell>Loading...</TableCell>
<TableCell>Loading...</TableCell>
</TableRow> </TableRow>
))
) : sortedEnabledProducts.length > 0 ? ( ) : sortedEnabledProducts.length > 0 ? (
sortedEnabledProducts.map((product) => renderProductRow(product)) sortedEnabledProducts.map((product, index) => renderProductRow(product, index))
) : ( ) : (
<TableRow> <TableRow>
<TableCell colSpan={6} className="h-24 text-center"> <TableCell colSpan={6} className="h-32 text-center text-muted-foreground">
No enabled products found. <div className="flex flex-col items-center justify-center gap-2">
<PackageOffset className="h-8 w-8 opacity-50" />
<p>No active products found</p>
</div>
</TableCell> </TableCell>
</TableRow> </TableRow>
)} )}
</AnimatePresence>
</TableBody> </TableBody>
</Table> </Table>
</div> </div>
</CardContent>
</Card>
{/* Disabled Products Section */} {/* Disabled Products Section */}
{!loading && disabledProducts.length > 0 && ( {!loading && disabledProducts.length > 0 && (
<div className="rounded-lg border dark:border-zinc-700 shadow-sm overflow-hidden bg-gray-50/30 dark:bg-zinc-900/30"> <Card className="border-border/40 bg-background/30 backdrop-blur-sm shadow-sm overflow-hidden opacity-90">
<Table className="relative"> <CardHeader className="py-4 px-6 border-b border-border/50 bg-muted/20">
<CardTitle className="text-lg font-medium flex items-center gap-2 text-muted-foreground">
<Archive className="h-5 w-5" />
Archived / Disabled
<Badge variant="outline" className="ml-2">
{sortedDisabledProducts.length}
</Badge>
</CardTitle>
</CardHeader>
<CardContent className="p-0">
<div className="max-h-[400px] overflow-auto">
<Table>
{renderTableHeader()} {renderTableHeader()}
<TableBody> <TableBody>
{sortedDisabledProducts.map((product) => <AnimatePresence mode="popLayout">
renderProductRow(product, true), {sortedDisabledProducts.map((product, index) =>
renderProductRow(product, index, true),
)} )}
</AnimatePresence>
</TableBody> </TableBody>
</Table> </Table>
</div> </div>
</CardContent>
</Card>
)} )}
</div> </div>
); );
}; };
export default ProductTable; export default ProductTable;