hmm
This commit is contained in:
@@ -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<boolean>(true);
|
||||
const [modalOpen, setModalOpen] = useState<boolean>(false);
|
||||
const [editing, setEditing] = useState<boolean>(false);
|
||||
const [imagePreview, setImagePreview] = useState<string | null>(null);
|
||||
const [imagePreview, setImagePreview] = useState<string | null>(null);
|
||||
const [productData, setProductData] = useState<Product>({
|
||||
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() {
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
<ProductTable
|
||||
<ProductTable
|
||||
products={products}
|
||||
loading={loading}
|
||||
onEdit={handleEditProduct}
|
||||
onDelete={handleDeleteProduct} // Pass handleDeleteProduct
|
||||
getCategoryNameById={getCategoryNameById}
|
||||
getCategoryNameById={getCategoryNameById}
|
||||
/>
|
||||
|
||||
<ProductModal
|
||||
|
||||
@@ -21,17 +21,7 @@ import {
|
||||
updateShippingMethod,
|
||||
} from "@/lib/shippingHelper";
|
||||
|
||||
interface ShippingMethod {
|
||||
_id?: string; // Required after fetching
|
||||
name: string;
|
||||
price: number;
|
||||
}
|
||||
|
||||
interface ShippingData {
|
||||
_id?: string; // Optional for new entry
|
||||
name: string;
|
||||
price: number;
|
||||
}
|
||||
import { ShippingMethod, ShippingData } from "@/lib/types";
|
||||
|
||||
import { ShippingTable } from "@/components/shipping-table"
|
||||
|
||||
@@ -50,10 +40,14 @@ export default function ShippingPage() {
|
||||
try {
|
||||
const authToken = document.cookie.split("Authorization=")[1];
|
||||
const fetchedMethods: ShippingMethod[] = await fetchShippingMethods(authToken);
|
||||
|
||||
setShippingMethods(
|
||||
fetchedMethods.filter((method) => 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);
|
||||
};
|
||||
|
||||
@@ -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 (
|
||||
<Layout>
|
||||
<div className="space-y-6">
|
||||
<h1 className="text-2xl font-semibold text-gray-900 dark:text-white">
|
||||
Withdraw Funds
|
||||
</h1>
|
||||
|
||||
{/* Balances Display */}
|
||||
<div className="grid grid-cols-1 md:grid-cols-3 gap-6">
|
||||
<Card className="shadow-sm bg-white dark:bg-zinc-800 border border-gray-200 dark:border-gray-700">
|
||||
<CardHeader>
|
||||
<CardTitle>Bitcoin Balance</CardTitle>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<p className="text-lg font-semibold text-gray-900 dark:text-white">
|
||||
{balances.bitcoin} BTC
|
||||
</p>
|
||||
</CardContent>
|
||||
</Card>
|
||||
|
||||
<Card className="shadow-sm bg-white dark:bg-zinc-800 border border-gray-200 dark:border-gray-700">
|
||||
<CardHeader>
|
||||
<CardTitle>Litecoin Balance</CardTitle>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<p className="text-lg font-semibold text-gray-900 dark:text-white">
|
||||
{balances.litecoin} LTC
|
||||
</p>
|
||||
</CardContent>
|
||||
</Card>
|
||||
|
||||
<Card className="shadow-sm bg-white dark:bg-zinc-800 border border-gray-200 dark:border-gray-700">
|
||||
<CardHeader>
|
||||
<CardTitle>Monero Balance</CardTitle>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<p className="text-lg font-semibold text-gray-900 dark:text-white">
|
||||
{balances.monero} XMR
|
||||
</p>
|
||||
</CardContent>
|
||||
</Card>
|
||||
</div>
|
||||
|
||||
{/* Withdrawal Form */}
|
||||
<div className="bg-white dark:bg-zinc-800 p-6 rounded-lg shadow border border-gray-200 dark:border-gray-700 space-y-4">
|
||||
<h2 className="text-lg font-semibold text-gray-900 dark:text-white">
|
||||
Request Withdrawal
|
||||
</h2>
|
||||
<div className="space-y-4">
|
||||
<div>
|
||||
<label className="block text-sm font-medium text-gray-700 dark:text-gray-300">
|
||||
Currency
|
||||
</label>
|
||||
<Select
|
||||
value={withdrawalData.currency}
|
||||
onValueChange={(value) =>
|
||||
setWithdrawalData({ ...withdrawalData, currency: value })
|
||||
}
|
||||
>
|
||||
<SelectTrigger className="w-full">
|
||||
<SelectValue placeholder="Select Currency" />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
<SelectItem value="bitcoin">Bitcoin</SelectItem>
|
||||
<SelectItem value="litecoin">Litecoin</SelectItem>
|
||||
<SelectItem value="monero">Monero</SelectItem>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</div>
|
||||
<div>
|
||||
<label className="block text-sm font-medium text-gray-700 dark:text-gray-300">
|
||||
Withdrawal Address
|
||||
</label>
|
||||
<Input
|
||||
name="address"
|
||||
placeholder="Enter the withdrawal address"
|
||||
value={withdrawalData.address}
|
||||
onChange={handleChange}
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<label className="block text-sm font-medium text-gray-700 dark:text-gray-300">
|
||||
Amount
|
||||
</label>
|
||||
<Input
|
||||
name="amount"
|
||||
placeholder="Enter the amount to withdraw"
|
||||
type="number"
|
||||
min="0"
|
||||
step="any"
|
||||
value={withdrawalData.amount}
|
||||
onChange={handleChange}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<Button onClick={handleWithdraw} className="w-full">
|
||||
<Send className="mr-2 h-5 w-5" />
|
||||
Request Withdrawal
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
{/* Withdrawals History */}
|
||||
<div className="bg-white dark:bg-zinc-800 p-6 rounded-lg shadow border border-gray-200 dark:border-gray-700">
|
||||
<h2 className="text-lg font-semibold text-gray-900 dark:text-white mb-4">
|
||||
Withdrawals History
|
||||
</h2>
|
||||
<Table>
|
||||
<TableHeader>
|
||||
<TableRow>
|
||||
<TableHead>Date</TableHead>
|
||||
<TableHead>Currency</TableHead>
|
||||
<TableHead>Amount</TableHead>
|
||||
<TableHead>Address</TableHead>
|
||||
<TableHead>Status</TableHead>
|
||||
</TableRow>
|
||||
</TableHeader>
|
||||
<TableBody>
|
||||
{withdrawals.length > 0 ? (
|
||||
withdrawals.map((withdrawal) => (
|
||||
<TableRow key={withdrawal.id}>
|
||||
<TableCell>{withdrawal.date}</TableCell>
|
||||
<TableCell>{withdrawal.currency.toUpperCase()}</TableCell>
|
||||
<TableCell>{withdrawal.amount}</TableCell>
|
||||
<TableCell>{withdrawal.address}</TableCell>
|
||||
<TableCell>{withdrawal.status}</TableCell>
|
||||
</TableRow>
|
||||
))
|
||||
) : (
|
||||
<TableRow>
|
||||
<TableCell colSpan={5} className="text-center">
|
||||
No withdrawals found.
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
)}
|
||||
</TableBody>
|
||||
</Table>
|
||||
</div>
|
||||
</div>
|
||||
</Layout>
|
||||
);
|
||||
}
|
||||
@@ -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 <WithdrawalsPageClient balances={balances} withdrawals={withdrawals} />;
|
||||
}
|
||||
Reference in New Issue
Block a user