This commit is contained in:
NotII
2025-05-23 10:01:32 +01:00
parent 308a816736
commit 10d7307725
6 changed files with 77 additions and 16 deletions

View File

@@ -266,6 +266,32 @@ export default function ProductsPage() {
} }
}; };
// Handle toggle product enabled status
const handleToggleEnabled = async (productId: string, enabled: boolean) => {
try {
setLoading(true);
await clientFetch(`/products/${productId}`, {
method: "PATCH",
body: JSON.stringify({ enabled }),
});
// Update the local state
setProducts(products.map(product =>
product._id === productId
? { ...product, enabled }
: product
));
toast.success(`Product ${enabled ? 'enabled' : 'disabled'} successfully`);
setLoading(false);
} catch (error) {
console.error(error);
toast.error("Failed to update product status");
setLoading(false);
}
};
return ( return (
<Layout> <Layout>
<div className="space-y-6"> <div className="space-y-6">
@@ -315,6 +341,7 @@ export default function ProductsPage() {
loading={loading} loading={loading}
onEdit={handleEditProduct} onEdit={handleEditProduct}
onDelete={handleDeleteProduct} onDelete={handleDeleteProduct}
onToggleEnabled={handleToggleEnabled}
getCategoryNameById={getCategoryNameById} getCategoryNameById={getCategoryNameById}
/> />

View File

@@ -21,6 +21,7 @@ import { toast } from "sonner";
import type React from "react"; import type React from "react";
import { Plus } from "lucide-react"; import { Plus } from "lucide-react";
import { apiRequest } from "@/lib/api"; import { apiRequest } from "@/lib/api";
import { Switch } from "@/components/ui/switch";
type CategorySelectProps = { type CategorySelectProps = {
categories: { _id: string; name: string; parentId?: string }[]; categories: { _id: string; name: string; parentId?: string }[];
@@ -200,34 +201,54 @@ const ProductBasicInfo: React.FC<{
setProductData: React.Dispatch<React.SetStateAction<ProductData>>; setProductData: React.Dispatch<React.SetStateAction<ProductData>>;
onAddCategory: (newCategory: { _id: string; name: string; parentId?: string }) => void; onAddCategory: (newCategory: { _id: string; name: string; parentId?: string }) => void;
}> = ({ productData, handleChange, categories, setProductData, onAddCategory }) => ( }> = ({ productData, handleChange, categories, setProductData, onAddCategory }) => (
<div className="grid gap-4 mb-4"> <div className="space-y-6">
<div> <div>
<label htmlFor="name" className="text-sm font-medium"> <label htmlFor="name" className="text-sm font-medium">
Name Product Name
</label> </label>
<Input <Input
id="name" id="name"
name="name" name="name"
required value={productData.name}
value={productData.name || ""}
onChange={handleChange} onChange={handleChange}
placeholder="Product name" placeholder="Enter product name"
/> />
</div> </div>
<div> <div>
<label className="text-sm font-medium">Description</label> <label htmlFor="description" className="text-sm font-medium">
<Textarea Description
</label>
<textarea
id="description" id="description"
name="description" name="description"
rows={3} value={productData.description}
value={productData.description || ""}
onChange={handleChange} onChange={handleChange}
placeholder="Product description" placeholder="Enter product description"
className="w-full min-h-[100px] rounded-md border border-input bg-background px-3 py-2 text-sm ring-offset-background placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50"
/> />
</div> </div>
{/* Stock Management Section */} <div className="bg-background rounded-lg border border-border p-4">
<h3 className="text-sm font-medium mb-4">Product Status</h3>
<div className="flex items-center space-x-2">
<Switch
id="enabled"
checked={productData.enabled !== false}
onCheckedChange={(checked) => {
setProductData({
...productData,
enabled: checked
});
}}
/>
<label htmlFor="enabled" className="text-sm">
Enable Product
</label>
</div>
</div>
<div className="bg-background rounded-lg border border-border p-4"> <div className="bg-background rounded-lg border border-border p-4">
<h3 className="text-sm font-medium mb-4">Stock Management</h3> <h3 className="text-sm font-medium mb-4">Stock Management</h3>

View File

@@ -3,12 +3,14 @@ import { Edit, Trash, AlertTriangle, CheckCircle, AlertCircle } from "lucide-rea
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";
interface ProductTableProps { interface ProductTableProps {
products: Product[]; products: Product[];
loading: boolean; loading: boolean;
onEdit: (product: Product) => void; onEdit: (product: Product) => void;
onDelete: (productId: string) => void; onDelete: (productId: string) => void;
onToggleEnabled: (productId: string, enabled: boolean) => void;
getCategoryNameById: (categoryId: string) => string; getCategoryNameById: (categoryId: string) => string;
} }
@@ -17,6 +19,7 @@ const ProductTable = ({
loading, loading,
onEdit, onEdit,
onDelete, onDelete,
onToggleEnabled,
getCategoryNameById getCategoryNameById
}: ProductTableProps) => { }: ProductTableProps) => {
@@ -47,6 +50,7 @@ const ProductTable = ({
<TableHead className="text-center">Category</TableHead> <TableHead className="text-center">Category</TableHead>
<TableHead className="text-center">Unit</TableHead> <TableHead className="text-center">Unit</TableHead>
<TableHead className="text-center">Stock</TableHead> <TableHead className="text-center">Stock</TableHead>
<TableHead className="text-center">Enabled</TableHead>
<TableHead className="text-right">Actions</TableHead> <TableHead className="text-right">Actions</TableHead>
</TableRow> </TableRow>
</TableHeader> </TableHeader>
@@ -59,6 +63,7 @@ const ProductTable = ({
<TableCell>Loading...</TableCell> <TableCell>Loading...</TableCell>
<TableCell>Loading...</TableCell> <TableCell>Loading...</TableCell>
<TableCell>Loading...</TableCell> <TableCell>Loading...</TableCell>
<TableCell>Loading...</TableCell>
</TableRow> </TableRow>
)) ))
) : sortedProducts.length > 0 ? ( ) : sortedProducts.length > 0 ? (
@@ -81,6 +86,12 @@ const ProductTable = ({
<Badge variant="outline" className="text-xs">Not Tracked</Badge> <Badge variant="outline" className="text-xs">Not Tracked</Badge>
)} )}
</TableCell> </TableCell>
<TableCell className="text-center">
<Switch
checked={product.enabled !== false}
onCheckedChange={(checked) => onToggleEnabled(product._id as string, checked)}
/>
</TableCell>
<TableCell className="text-right flex justify-end space-x-1"> <TableCell className="text-right flex justify-end space-x-1">
<Button variant="ghost" size="sm" onClick={() => onEdit(product)}> <Button variant="ghost" size="sm" onClick={() => onEdit(product)}>
<Edit className="h-4 w-4" /> <Edit className="h-4 w-4" />
@@ -98,7 +109,7 @@ const ProductTable = ({
)) ))
) : ( ) : (
<TableRow> <TableRow>
<TableCell colSpan={5} className="h-24 text-center"> <TableCell colSpan={6} className="h-24 text-center">
No products found. No products found.
</TableCell> </TableCell>
</TableRow> </TableRow>

View File

@@ -44,6 +44,7 @@ export interface Product {
stockStatus?: 'in_stock' | 'low_stock' | 'out_of_stock' stockStatus?: 'in_stock' | 'low_stock' | 'out_of_stock'
unitType: string unitType: string
category: string category: string
enabled?: boolean
pricing: PricingTier[] pricing: PricingTier[]
image?: string | File | null image?: string | File | null
} }

View File

@@ -4,6 +4,7 @@ export interface Product {
description: string; description: string;
unitType: string; unitType: string;
category: string; category: string;
enabled?: boolean;
// Stock management fields // Stock management fields
stockTracking?: boolean; stockTracking?: boolean;
currentStock?: number; currentStock?: number;

View File

@@ -1,4 +1,4 @@
{ {
"commitHash": "02e0900", "commitHash": "308a816",
"buildTime": "2025-05-19T00:35:36.962Z" "buildTime": "2025-05-23T06:40:22.513Z"
} }