This commit is contained in:
g
2025-02-07 04:43:47 +00:00
parent d3e19c7e09
commit 6c4f3f0866
94 changed files with 11777 additions and 0 deletions

View File

@@ -0,0 +1,203 @@
"use client"
import { useState, useEffect, ChangeEvent } from "react";
import { useRouter } from "next/navigation";
import Layout from "@/components/kokonutui/layout";
import { Button } from "@/components/ui/button";
import { Product } from "@/models/products";
import { Plus } from "lucide-react";
import { fetchProductData, saveProductData, deleteProductData } from "@/lib/productData";
import { ProductModal } from "@/components/product-modal";
import ProductTable from "@/components/product-table";
export default function ProductsPage() {
const router = useRouter();
const [products, setProducts] = useState<Product[]>([]);
const [categories, setCategories] = useState<any[]>([]);
const [loading, setLoading] = useState<boolean>(true);
const [modalOpen, setModalOpen] = useState<boolean>(false);
const [editing, setEditing] = useState<boolean>(false);
const [imagePreview, setImagePreview] = useState<string | null>(null);
const [productData, setProductData] = useState<Product>({
name: "",
description: "",
unitType: "pcs",
category: "",
tieredPricing: [{ minQuantity: 1, pricePerUnit: 0 }],
image: null,
});
// Fetch products and categories
useEffect(() => {
const fetchDataAsync = async () => {
try {
const authToken = document.cookie.split("Authorization=")[1];
const [fetchedProducts, fetchedCategories] = await Promise.all([
fetchProductData(
`${process.env.NEXT_PUBLIC_API_URL}/products`,
authToken
),
fetchProductData(
`${process.env.NEXT_PUBLIC_API_URL}/categories`,
authToken
),
]);
setProducts(fetchedProducts);
setCategories(fetchedCategories);
} catch (error) {
console.error("Error loading data:", error);
} finally {
setLoading(false);
}
};
fetchDataAsync();
}, []);
// Handle input changes
const handleChange = (
e: ChangeEvent<HTMLInputElement | HTMLTextAreaElement>
) => setProductData({ ...productData, [e.target.name]: e.target.value });
// Handle tiered pricing changes
const handleTieredPricingChange = (
e: ChangeEvent<HTMLInputElement>,
index: number
) => {
const updatedPricing = [...productData.tieredPricing];
updatedPricing[index][e.target.name] = e.target.value;
setProductData({ ...productData, tieredPricing: updatedPricing });
};
// Save product data after modal form submission
const handleSaveProduct = async (data: Product) => {
const adjustedPricing = data.tieredPricing.map((tier) => ({
minQuantity: tier.minQuantity,
pricePerUnit: typeof tier.pricePerUnit === "string"
? parseFloat(tier.pricePerUnit) // Convert string to number
: tier.pricePerUnit,
}));
const productToSave = {
...data,
pricing: adjustedPricing,
imageBase64: imagePreview || "",
};
try {
const authToken = document.cookie.split("Authorization=")[1];
const apiUrl = editing
? `${process.env.NEXT_PUBLIC_API_URL}/products/${data._id}`
: `${process.env.NEXT_PUBLIC_API_URL}/products`;
const savedProduct = await saveProductData(
apiUrl,
productToSave,
authToken,
editing ? "PUT" : "POST"
);
setProducts((prevProducts) => {
if (editing) {
return prevProducts.map((product) =>
product._id === savedProduct._id ? savedProduct : product
);
} else {
return [...prevProducts, savedProduct];
}
});
setModalOpen(false); // Close modal after saving
} catch (error) {
console.error("Error saving product:", error);
}
};
// Handle delete product
const handleDeleteProduct = async (productId: string) => {
const authToken = document.cookie.split("Authorization=")[1];
try {
await deleteProductData(
`${process.env.NEXT_PUBLIC_API_URL}/products/${productId}`,
authToken
);
// Remove the product from the state
setProducts((prevProducts) =>
prevProducts.filter((product) => product._id !== productId)
);
} catch (error) {
console.error("Error deleting product:", error);
}
};
// Edit product
const handleEditProduct = (product: Product) => {
setProductData({
...product,
tieredPricing: product.pricing.map((tier) => ({
minQuantity: tier.minQuantity,
pricePerUnit: tier.pricePerUnit.toString(),
})),
});
setEditing(true);
setModalOpen(true);
};
// Reset product data when adding a new product
const handleAddNewProduct = () => {
setProductData({
name: "",
description: "",
unitType: "pcs",
category: "",
tieredPricing: [{ minQuantity: 1, pricePerUnit: 0 }],
image: null,
});
setEditing(false);
setModalOpen(true);
};
// Get category name by ID
const getCategoryNameById = (categoryId: string): string => {
const category = categories.find((cat) => cat._id === categoryId);
return category ? category.name : "Unknown Category";
};
return (
<Layout>
<div className="space-y-6">
<div className="flex items-center justify-between">
<h1 className="text-2xl font-semibold text-gray-900 dark:text-white">
Product Inventory
</h1>
<Button onClick={handleAddNewProduct}>
<Plus className="mr-2 h-5 w-5" />
Add Product
</Button>
</div>
<ProductTable
products={products}
loading={loading}
onEdit={handleEditProduct}
onDelete={handleDeleteProduct} // Pass handleDeleteProduct
getCategoryNameById={getCategoryNameById}
/>
<ProductModal
open={modalOpen}
onClose={() => setModalOpen(false)}
onSave={handleSaveProduct}
productData={productData}
categories={categories}
editing={editing}
handleChange={handleChange}
handleTieredPricingChange={handleTieredPricingChange}
setProductData={setProductData}
/>
</div>
</Layout>
);
}