Add balance page and sidebar link for dashboard
Introduces a new balance page displaying cryptocurrency balances and withdrawal functionality. Updates the sidebar configuration to include a link to the balance page with a wallet icon.
This commit is contained in:
168
app/dashboard/balance/page.tsx
Normal file
168
app/dashboard/balance/page.tsx
Normal file
@@ -0,0 +1,168 @@
|
|||||||
|
"use client";
|
||||||
|
|
||||||
|
import { useState } from "react";
|
||||||
|
import Dashboard from "@/components/dashboard/dashboard";
|
||||||
|
import { Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle } from "@/components/ui/card";
|
||||||
|
import { Button } from "@/components/ui/button";
|
||||||
|
import { Wallet, Bitcoin, Coins, DollarSign, ArrowUpRight } from "lucide-react";
|
||||||
|
import { toast } from "sonner";
|
||||||
|
|
||||||
|
// Mock data for balances
|
||||||
|
const mockBalances = {
|
||||||
|
bitcoin: {
|
||||||
|
symbol: "BTC",
|
||||||
|
name: "Bitcoin",
|
||||||
|
balance: 0.054321,
|
||||||
|
usdValue: 2350.45,
|
||||||
|
icon: Bitcoin,
|
||||||
|
color: "text-orange-500",
|
||||||
|
},
|
||||||
|
litecoin: {
|
||||||
|
symbol: "LTC",
|
||||||
|
name: "Litecoin",
|
||||||
|
balance: 12.345678,
|
||||||
|
usdValue: 987.65,
|
||||||
|
icon: Coins,
|
||||||
|
color: "text-blue-500",
|
||||||
|
},
|
||||||
|
monero: {
|
||||||
|
symbol: "XMR",
|
||||||
|
name: "Monero",
|
||||||
|
balance: 5.678901,
|
||||||
|
usdValue: 1123.89,
|
||||||
|
icon: DollarSign,
|
||||||
|
color: "text-orange-600",
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
export default function BalancePage() {
|
||||||
|
const [isWithdrawing, setIsWithdrawing] = useState<{
|
||||||
|
bitcoin: boolean;
|
||||||
|
litecoin: boolean;
|
||||||
|
monero: boolean;
|
||||||
|
}>({
|
||||||
|
bitcoin: false,
|
||||||
|
litecoin: false,
|
||||||
|
monero: false,
|
||||||
|
});
|
||||||
|
|
||||||
|
const handleWithdrawal = async (currency: "bitcoin" | "litecoin" | "monero") => {
|
||||||
|
setIsWithdrawing((prev) => ({ ...prev, [currency]: true }));
|
||||||
|
|
||||||
|
try {
|
||||||
|
// Simulate API call
|
||||||
|
await new Promise((resolve) => setTimeout(resolve, 1500));
|
||||||
|
|
||||||
|
toast.success("Withdrawal Request Submitted", {
|
||||||
|
description: `Your withdrawal request for ${mockBalances[currency].name} has been submitted successfully.`,
|
||||||
|
});
|
||||||
|
} catch (error) {
|
||||||
|
toast.error("Withdrawal Failed", {
|
||||||
|
description: `Failed to submit withdrawal request for ${mockBalances[currency].name}. Please try again.`,
|
||||||
|
});
|
||||||
|
} finally {
|
||||||
|
setIsWithdrawing((prev) => ({ ...prev, [currency]: false }));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const totalUsdValue = Object.values(mockBalances).reduce(
|
||||||
|
(sum, currency) => sum + currency.usdValue,
|
||||||
|
0
|
||||||
|
);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Dashboard>
|
||||||
|
<div className="space-y-6">
|
||||||
|
{/* Header */}
|
||||||
|
<div>
|
||||||
|
<h1 className="text-2xl font-semibold text-gray-900 dark:text-white flex items-center">
|
||||||
|
<Wallet className="mr-2 h-6 w-6" />
|
||||||
|
Balance
|
||||||
|
</h1>
|
||||||
|
<p className="mt-1 text-muted-foreground">
|
||||||
|
View your cryptocurrency balances and request withdrawals
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Total Balance Card */}
|
||||||
|
<Card>
|
||||||
|
<CardHeader>
|
||||||
|
<CardTitle>Total Balance</CardTitle>
|
||||||
|
<CardDescription>Combined value of all cryptocurrencies</CardDescription>
|
||||||
|
</CardHeader>
|
||||||
|
<CardContent>
|
||||||
|
<div className="text-3xl font-bold">
|
||||||
|
${totalUsdValue.toLocaleString("en-US", { minimumFractionDigits: 2, maximumFractionDigits: 2 })}
|
||||||
|
</div>
|
||||||
|
<p className="text-sm text-muted-foreground mt-2">
|
||||||
|
Equivalent in USD
|
||||||
|
</p>
|
||||||
|
</CardContent>
|
||||||
|
</Card>
|
||||||
|
|
||||||
|
{/* Currency Balance Cards */}
|
||||||
|
<div className="grid grid-cols-1 md:grid-cols-3 gap-4">
|
||||||
|
{Object.entries(mockBalances).map(([key, currency]) => {
|
||||||
|
const Icon = currency.icon;
|
||||||
|
return (
|
||||||
|
<Card key={key}>
|
||||||
|
<CardHeader>
|
||||||
|
<div className="flex items-center justify-between">
|
||||||
|
<div className="flex items-center gap-2">
|
||||||
|
<Icon className={`h-5 w-5 ${currency.color}`} />
|
||||||
|
<CardTitle className="text-lg">{currency.name}</CardTitle>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<CardDescription>{currency.symbol}</CardDescription>
|
||||||
|
</CardHeader>
|
||||||
|
<CardContent className="space-y-2">
|
||||||
|
<div>
|
||||||
|
<div className="text-2xl font-bold">
|
||||||
|
{currency.balance.toLocaleString("en-US", {
|
||||||
|
minimumFractionDigits: 6,
|
||||||
|
maximumFractionDigits: 8,
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
<div className="text-sm text-muted-foreground">
|
||||||
|
{currency.symbol}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="pt-2 border-t">
|
||||||
|
<div className="text-lg font-semibold">
|
||||||
|
${currency.usdValue.toLocaleString("en-US", {
|
||||||
|
minimumFractionDigits: 2,
|
||||||
|
maximumFractionDigits: 2,
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
<div className="text-xs text-muted-foreground">USD Value</div>
|
||||||
|
</div>
|
||||||
|
</CardContent>
|
||||||
|
<CardFooter>
|
||||||
|
<Button
|
||||||
|
onClick={() => handleWithdrawal(key as "bitcoin" | "litecoin" | "monero")}
|
||||||
|
disabled={isWithdrawing[key as keyof typeof isWithdrawing]}
|
||||||
|
className="w-full"
|
||||||
|
variant="outline"
|
||||||
|
>
|
||||||
|
{isWithdrawing[key as keyof typeof isWithdrawing] ? (
|
||||||
|
<>
|
||||||
|
<div className="h-4 w-4 border-2 border-current border-t-transparent rounded-full animate-spin mr-2" />
|
||||||
|
Processing...
|
||||||
|
</>
|
||||||
|
) : (
|
||||||
|
<>
|
||||||
|
<ArrowUpRight className="h-4 w-4 mr-2" />
|
||||||
|
Request Withdrawal
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
</Button>
|
||||||
|
</CardFooter>
|
||||||
|
</Card>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</Dashboard>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
import { Home, Package, Box, Truck, Settings, FolderTree, MessageCircle, BarChart3, Tag, Users, TrendingUp, Shield } from "lucide-react"
|
import { Home, Package, Box, Truck, Settings, FolderTree, MessageCircle, BarChart3, Tag, Users, TrendingUp, Shield, Wallet } from "lucide-react"
|
||||||
|
|
||||||
export const sidebarConfig = [
|
export const sidebarConfig = [
|
||||||
{
|
{
|
||||||
@@ -30,6 +30,7 @@ export const sidebarConfig = [
|
|||||||
{ name: "Shipping", href: "/dashboard/shipping", icon: Truck },
|
{ name: "Shipping", href: "/dashboard/shipping", icon: Truck },
|
||||||
{ name: "Promotions", href: "/dashboard/promotions", icon: Tag },
|
{ name: "Promotions", href: "/dashboard/promotions", icon: Tag },
|
||||||
{ name: "Storefront", href: "/dashboard/storefront", icon: Settings },
|
{ name: "Storefront", href: "/dashboard/storefront", icon: Settings },
|
||||||
|
{ name: "Balance", href: "/dashboard/balance", icon: Wallet },
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
{
|
{
|
||||||
"commitHash": "fcba1a8",
|
"commitHash": "928d94c",
|
||||||
"buildTime": "2025-10-30T01:13:38.854Z"
|
"buildTime": "2025-10-31T00:03:09.717Z"
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user