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:
NotII
2025-10-31 00:09:34 +00:00
parent 928d94cecd
commit 28e292abb3
3 changed files with 172 additions and 3 deletions

View 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>
);
}

View File

@@ -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 = [
{
@@ -30,6 +30,7 @@ export const sidebarConfig = [
{ name: "Shipping", href: "/dashboard/shipping", icon: Truck },
{ name: "Promotions", href: "/dashboard/promotions", icon: Tag },
{ name: "Storefront", href: "/dashboard/storefront", icon: Settings },
{ name: "Balance", href: "/dashboard/balance", icon: Wallet },
],
},
{

View File

@@ -1,4 +1,4 @@
{
"commitHash": "fcba1a8",
"buildTime": "2025-10-30T01:13:38.854Z"
"commitHash": "928d94c",
"buildTime": "2025-10-31T00:03:09.717Z"
}