diff --git a/app/dashboard/products/page.tsx b/app/dashboard/products/page.tsx index b0fa90d..e6a6b52 100644 --- a/app/dashboard/products/page.tsx +++ b/app/dashboard/products/page.tsx @@ -1,14 +1,18 @@ -"use client" +"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 { 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"; +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(); @@ -17,7 +21,7 @@ export default function ProductsPage() { const [loading, setLoading] = useState(true); const [modalOpen, setModalOpen] = useState(false); const [editing, setEditing] = useState(false); - const [imagePreview, setImagePreview] = useState(null); + const [imagePreview, setImagePreview] = useState(null); const [productData, setProductData] = useState({ name: "", description: "", @@ -66,7 +70,8 @@ export default function ProductsPage() { index: number ) => { const updatedPricing = [...productData.tieredPricing]; - updatedPricing[index][e.target.name] = e.target.value; + const name = e.target.name as "minQuantity" | "pricePerUnit"; + updatedPricing[index][name] = e.target.valueAsNumber || 0; setProductData({ ...productData, tieredPricing: updatedPricing }); }; @@ -74,30 +79,31 @@ export default function ProductsPage() { 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, + pricePerUnit: + typeof tier.pricePerUnit === "string" + ? parseFloat(tier.pricePerUnit) // Convert string to number + : tier.pricePerUnit, })); - + const productToSave = { ...data, - pricing: adjustedPricing, - imageBase64: imagePreview || "", + 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) => @@ -107,7 +113,7 @@ export default function ProductsPage() { return [...prevProducts, savedProduct]; } }); - + setModalOpen(false); // Close modal after saving } catch (error) { console.error("Error saving product:", error); @@ -122,7 +128,7 @@ export default function ProductsPage() { `${process.env.NEXT_PUBLIC_API_URL}/products/${productId}`, authToken ); - + // Remove the product from the state setProducts((prevProducts) => prevProducts.filter((product) => product._id !== productId) @@ -136,9 +142,9 @@ export default function ProductsPage() { const handleEditProduct = (product: Product) => { setProductData({ ...product, - tieredPricing: product.pricing.map((tier) => ({ + tieredPricing: product.tieredPricing.map(tier => ({ minQuantity: tier.minQuantity, - pricePerUnit: tier.pricePerUnit.toString(), + pricePerUnit: tier.pricePerUnit })), }); setEditing(true); @@ -178,12 +184,12 @@ export default function ProductsPage() { - method._id) - ); + + // Ensure `_id` is always a string + const sanitizedMethods: ShippingMethod[] = fetchedMethods.map((method) => ({ + ...method, + _id: method._id ?? "", // Default to empty string if undefined + })); + + setShippingMethods(sanitizedMethods); } catch (error) { console.error("Error loading shipping options:", error); } finally { @@ -114,7 +108,10 @@ export default function ShippingPage() { }; const handleEditShipping = (shipping: ShippingMethod) => { - setNewShipping({ ...shipping, _id: shipping._id ?? "" }); // Ensure _id is always a string + setNewShipping({ + ...shipping, + _id: shipping._id ?? "", // ✅ Ensure _id is always a string + }); setEditing(true); setModalOpen(true); }; diff --git a/app/dashboard/withdrawal/WithdrawalsPageClient.tsx b/app/dashboard/withdrawal/WithdrawalsPageClient.tsx deleted file mode 100644 index 3505efc..0000000 --- a/app/dashboard/withdrawal/WithdrawalsPageClient.tsx +++ /dev/null @@ -1,201 +0,0 @@ -"use client"; - -import { useState } from "react"; -import Layout from "@/components/kokonutui/layout"; -import { Button } from "@/components/ui/button"; -import { Input } from "@/components/ui/input"; -import { - Select, - SelectTrigger, - SelectValue, - SelectContent, - SelectItem, -} from "@/components/ui/select"; -import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"; -import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from "@/components/ui/table"; -import { Send } from "lucide-react"; - -export default function WithdrawalsPageClient({ balances, withdrawals }) { - const [withdrawalData, setWithdrawalData] = useState({ - currency: "bitcoin", - address: "", - amount: "", - }); - - const handleWithdraw = async () => { - const { currency, address, amount } = withdrawalData; - - if (!currency || !address || !amount || parseFloat(amount) <= 0) { - alert("Please provide valid withdrawal details."); - return; - } - - try { - const authToken = document.cookie.split("authToken=")[1]; - const res = await fetch(`${process.env.NEXT_PUBLIC_API_URL}/withdraw`, { - method: "POST", - headers: { - Authorization: `Bearer ${authToken}`, - "Content-Type": "application/json", - }, - credentials: "include", - body: JSON.stringify({ currency, address, amount }), - }); - - if (!res.ok) throw new Error("Failed to process withdrawal"); - - alert("Withdrawal request submitted successfully!"); - setWithdrawalData({ currency: "bitcoin", address: "", amount: "" }); - } catch (error) { - console.error("Error processing withdrawal:", error); - alert("Failed to process withdrawal. Please try again."); - } - }; - - const handleChange = (e) => { - const { name, value } = e.target; - setWithdrawalData({ ...withdrawalData, [name]: value }); - }; - - return ( - -
-

- Withdraw Funds -

- - {/* Balances Display */} -
- - - Bitcoin Balance - - -

- {balances.bitcoin} BTC -

-
-
- - - - Litecoin Balance - - -

- {balances.litecoin} LTC -

-
-
- - - - Monero Balance - - -

- {balances.monero} XMR -

-
-
-
- - {/* Withdrawal Form */} -
-

- Request Withdrawal -

-
-
- - -
-
- - -
-
- - -
-
- - -
- - {/* Withdrawals History */} -
-

- Withdrawals History -

- - - - Date - Currency - Amount - Address - Status - - - - {withdrawals.length > 0 ? ( - withdrawals.map((withdrawal) => ( - - {withdrawal.date} - {withdrawal.currency.toUpperCase()} - {withdrawal.amount} - {withdrawal.address} - {withdrawal.status} - - )) - ) : ( - - - No withdrawals found. - - - )} - -
-
-
-
- ); -} \ No newline at end of file diff --git a/app/dashboard/withdrawal/page.tsx b/app/dashboard/withdrawal/page.tsx deleted file mode 100644 index 9e751f2..0000000 --- a/app/dashboard/withdrawal/page.tsx +++ /dev/null @@ -1,18 +0,0 @@ -import WithdrawalsPageClient from "./WithdrawalsPageClient"; - -export default async function WithdrawalsPage() { - const authToken = ""; // Fetch token from cookies if needed - const [balancesRes, withdrawalsRes] = await Promise.all([ - fetch(`${process.env.NEXT_PUBLIC_API_URL}/balances`, { - headers: { Authorization: `Bearer ${authToken}` }, - }), - fetch(`${process.env.NEXT_PUBLIC_API_URL}/withdrawals`, { - headers: { Authorization: `Bearer ${authToken}` }, - }), - ]); - - const balances = balancesRes.ok ? await balancesRes.json() : {}; - const withdrawals = withdrawalsRes.ok ? await withdrawalsRes.json() : []; - - return ; -} \ No newline at end of file diff --git a/components/product-modal.tsx b/components/product-modal.tsx index 20ac21f..e854f18 100644 --- a/components/product-modal.tsx +++ b/components/product-modal.tsx @@ -30,16 +30,14 @@ interface ProductModalProps { onClose: () => void; onSave: (productData: Product) => void; productData: Product; - categories: Category[]; // Define categories as an array of Category + categories: Category[]; editing: boolean; - handleChange: ( - e: ChangeEvent - ) => void; + handleChange: (e: ChangeEvent) => void; + setProductData: React.Dispatch>; handleTieredPricingChange: ( e: ChangeEvent, index: number ) => void; - setProductData: React.Dispatch>; } export const ProductModal = ({ @@ -55,10 +53,10 @@ export const ProductModal = ({ }: ProductModalProps) => { const [imagePreview, setImagePreview] = useState(null); - // Update image preview when product data changes (for editing purposes) + // Update image preview when product data changes useEffect(() => { if (productData.image && typeof productData.image === "string") { - setImagePreview(productData.image); // For existing product image + setImagePreview(productData.image); } }, [productData.image]); @@ -73,21 +71,23 @@ export const ProductModal = ({ } }; - // Handle tiered pricing change + // ✅ FIXED: Moved Inside the Component & Used Type Assertion const handleTieredPricingChangeInternal = ( e: ChangeEvent, index: number ) => { const updatedPricing = [...productData.tieredPricing]; - const updatedTier = updatedPricing[index]; + const field = e.target.name as keyof (typeof productData.tieredPricing)[number]; - // Ensure pricePerUnit is a number - const value = e.target.name === "pricePerUnit" - ? parseFloat(e.target.value) || 0 // If value is invalid, default to 0 - : e.target.value; + updatedPricing[index] = { + ...updatedPricing[index], + [field]: e.target.valueAsNumber || 0, + }; - updatedTier[e.target.name] = value; - setProductData({ ...productData, tieredPricing: updatedPricing }); + setProductData({ + ...productData, + tieredPricing: updatedPricing, + }); }; return ( @@ -98,36 +98,21 @@ export const ProductModal = ({ {editing ? "Edit Product" : "Add Product"} +
- +
-