# Web3 dashboard

A shadcn/ui block from the [@watermelon](https://ui.watermelon.sh/?utm_source=shadcn.exchange) registry, listed on [shadcn.exchange](https://shadcn.exchange/r/watermelon/web3-dashboard).

- Page, with a live preview: https://shadcn.exchange/r/watermelon/web3-dashboard
- Screenshot: https://shadcn.exchange/shot/watermelon/web3-dashboard/light.png
- Registry item: https://registry.watermelon.sh/r/web3-dashboard.json
- Listed: 2026-10-13, source last changed 2026-10-13

## Install

```bash
npx shadcn@latest add https://shadcn.exchange/r/watermelon/web3-dashboard.json
```

Run it in a project that has shadcn/ui set up. It adds the files below and installs what they depend on.

## Dependencies

- npm packages: `@tabler/icons-react`, `class-variance-authority`, `lucide-react`, `next-themes`, `react-icons`, `recharts`
- Registry items: `avatar`, `badge`, `button`, `card`, `dropdown-menu`, `input`, `select`, `separator`, `sheet`, `sidebar`, `skeleton`, `table`, `tabs`, `tooltip`, `utils`

## Files

### `src/components/dashboards/web3-dashboard/components/web3/asset-badge.tsx`

```tsx
import { cn } from "@/lib/utils";

type AssetBadgeProps = {
  symbol: string;
  color: string;
  size?: "sm" | "md";
};

export function AssetBadge({ symbol, color, size = "md" }: AssetBadgeProps) {
  const text = symbol === "Bitcoin" ? "B" : symbol.slice(0, 1);

  return (
    <span
      className={cn(
        "inline-flex shrink-0 items-center justify-center rounded-full text-xs text-white  shadow-[inset_0_1px_2px_1px_rgba(255,255,255,0.2),inset_0px_-1px_2px_1px_rgba(0,0,0,0.08)] ",
        size === "sm" ? "size-6" : "size-9",
        color,
        symbol === "ETH" || symbol === "Ethereum" ? "text-slate-700" : ""
      )}
    >
      {text}
    </span>
  );
}
```

### `src/components/dashboards/web3-dashboard/components/web3/asset-table-card.tsx`

```tsx
"use client";

import * as React from "react";
import { ArrowRight, ChevronsUpDown, MoreVertical, Plus } from "lucide-react";
import { Button } from "@/components/ui/button";
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
import {
  Table,
  TableBody,
  TableCell,
  TableHead,
  TableHeader,
  TableRow,
} from "@/components/ui/table";
import { AssetBadge } from "./asset-badge";

type Asset = {
  asset: string;
  balance: string;
  value: string;
  color: string;
};

type AssetTableCardProps = {
  title: string;
  assets: Asset[];
  action: string;
  valueLabel: string;
  external?: boolean;
};

export function AssetTableCard({
  title,
  assets,
  action,
  valueLabel,
  external,
}: AssetTableCardProps) {
  const [sortField, setSortField] = React.useState<"balance" | "value" | null>(
    null,
  );
  const [sortDir, setSortDir] = React.useState<"asc" | "desc">("desc");

  const handleSort = (field: "balance" | "value") => {
    if (sortField === field) {
      if (sortDir === "desc") {
        setSortDir("asc");
      } else {
        setSortField(null);
        setSortDir("desc");
      }
    } else {
      setSortField(field);
      setSortDir("desc");
    }
  };

  const sortedAssets = React.useMemo(() => {
    if (!sortField) return assets;

    return [...assets].sort((a, b) => {
      const parseValue = (val: string) => {
        return parseFloat(val.replace(/[^0-9.-]+/g, "")) || 0;
      };

      const valA = parseValue(a[sortField]);
      const valB = parseValue(b[sortField]);

      if (sortDir === "asc") {
        return valA - valB;
      } else {
        return valB - valA;
      }
    });
  }, [assets, sortField, sortDir]);

  return (
    <Card className="rounded-2xl bg-card dark:bg-muted shadow-primary ring-0 py-4 p-2 gap-2">
      <CardHeader className="flex items-center justify-between px-2  ">
        <CardTitle className="text-lg font-medium">{title}</CardTitle>
        <Button variant="ghost" size="icon-sm" aria-label={`${title} menu`}>
          <MoreVertical className="size-4" />
        </Button>
      </CardHeader>
      <CardContent className="px-4 p-2 rounded-lg bg-muted/40 dark:bg-black">
        <div className="border rounded-md overflow-hidden ">
          <Table>
            <TableHeader className="bg-muted rounded-md">
              <TableRow className="hover:bg-muted rounded-md   ">
                <TableHead className="h-9 text-sm font-medium">Asset</TableHead>
                <TableHead
                  className="h-9 text-sm font-medium cursor-pointer select-none transition-colors hover:text-foreground"
                  onClick={() => handleSort("balance")}
                >
                  <span className="inline-flex items-center gap-1">
                    Balance
                    <ChevronsUpDown
                      className={`size-4 ${sortField === "balance" ? "text-foreground" : "text-muted-foreground"}`}
                    />
                  </span>
                </TableHead>
                <TableHead
                  className="h-9 text-sm font-medium cursor-pointer select-none transition-colors hover:text-foreground"
                  onClick={() => handleSort("value")}
                >
                  <span className="inline-flex items-center gap-1">
                    {valueLabel}
                    <ChevronsUpDown
                      className={`size-4 ${sortField === "value" ? "text-foreground" : "text-muted-foreground"}`}
                    />
                  </span>
                </TableHead>
              </TableRow>
            </TableHeader>
            <TableBody>
              {sortedAssets.map((asset) => (
                <TableRow
                  key={asset.asset}
                  className="border-border/70 border-y bg-white dark:bg-black hover:dark:bg-muted"
                >
                  <TableCell className="h-12 bg-transparent">
                    <span className="inline-flex items-center gap-3 font-medium ">
                      <AssetBadge
                        symbol={asset.asset}
                        color={asset.color}
                        size="sm"
                      />
                      {asset.asset}
                    </span>
                  </TableCell>
                  <TableCell className="h-12 font-medium">
                    {asset.balance}
                  </TableCell>
                  <TableCell className="h-12 font-medium">
                    {asset.value}
                  </TableCell>
                </TableRow>
              ))}
            </TableBody>
          </Table>
        </div>
        <Button
          variant="outline"
          className="mt-4 h-9 w-full bg-muted dark:bg-muted font-normal"
        >
          {action}
          {external ? (
            <ArrowRight className="size-4" />
          ) : (
            <Plus className="size-4" />
          )}
        </Button>
      </CardContent>
    </Card>
  );
}
```

### `src/components/dashboards/web3-dashboard/components/web3/charts.tsx`

```tsx
"use client";

import {
  Area,
  AreaChart,
  CartesianGrid,
  ResponsiveContainer,
  XAxis,
  YAxis,
} from "recharts";
import { apyData } from "../../data";

export function PortfolioChart({ data }: { data: object[] }) {
  return (
    <ResponsiveContainer width="100%" height="100%">
      <AreaChart data={data}>
        <defs>
          <linearGradient id="portfolioFill" x1="0" y1="0" x2="0" y2="1">
            <stop offset="0%" stopColor="var(--chart-1)" stopOpacity={0.55} />
            <stop offset="100%" stopColor="var(--chart-1)" stopOpacity={0.02} />
          </linearGradient>
        </defs>
        <CartesianGrid stroke="var(--border)" strokeOpacity={0.25} vertical />
        <XAxis
          dataKey="time"
          axisLine={false}
          tickLine={false}
          interval="preserveStartEnd"
          tick={{ fill: "var(--muted-foreground)", fontSize: 13 }}
        />
        <YAxis
          axisLine={false}
          tickLine={false}
          width={54}
          ticks={[0, 50000, 100000, 150000, 200000, 250000, 300000]}
          tickFormatter={(value) => `${Number(value) / 1000}K`}
          tick={{ fill: "var(--muted-foreground)", fontSize: 13 }}
        />
        <Area
          type="linear"
          dataKey="value"
          stroke="var(--chart-1)"
          strokeWidth={4}
          fill="url(#portfolioFill)"
          activeDot={false}
          dot={false}
        />
      </AreaChart>
    </ResponsiveContainer>
  );
}

export function ApySparkline() {
  return (
    <ResponsiveContainer width="100%" height="100%">
      <AreaChart
        data={apyData}
        margin={{ top: 8, right: 0, left: 0, bottom: 0 }}
      >
        <defs>
          <linearGradient id="apyFill" x1="0" y1="0" x2="0" y2="1">
            <stop
              offset="0%"
              stopColor="var(--color-emerald-400)"
              stopOpacity={0.45}
            />
            <stop
              offset="100%"
              stopColor="var(--color-emerald-400)"
              stopOpacity={0}
            />
          </linearGradient>
        </defs>
        <XAxis dataKey="month" hide />
        <Area
          type="monotone"
          dataKey="value"
          stroke="var(--color-emerald-400)"
          strokeWidth={2}
          fill="url(#apyFill)"
          dot={false}
          activeDot={false}
        />
      </AreaChart>
    </ResponsiveContainer>
  );
}
```

### `src/components/dashboards/web3-dashboard/components/web3/logo-icons.tsx`

```tsx
export default function LogoIcon({ className }: { className?: string }) {
  return (
    <svg
      className={className}
      width="785"
      height="506"
      viewBox="0 0 785 506"
      fill="currentColor"
      xmlns="http://www.w3.org/2000/svg"
    >
      <path
        d="M392.688 104.141C299.504 104.141 291.288 234.316 261.147 234.316C231.007 234.316 230.996 0 121.38 0C-30.726 0 -49.9098 505.63 121.38 505.63C196.742 505.63 218.672 442.598 252.926 442.598C287.179 442.598 322.801 505.63 392.688 505.63C462.575 505.63 496.832 442.598 531.085 442.598C565.339 442.598 587.269 505.63 662.631 505.63C833.921 505.63 814.737 0 662.631 0C553.015 0 553.004 234.316 522.864 234.316C492.723 234.316 485.872 104.141 392.688 104.141Z"
        fill="currentColor"
      />
      <circle cx="515.226" cy="332.977" r="46.5892" fill="black" />
      <circle cx="268.575" cy="332.976" r="46.5892" fill="black" />
    </svg>
  );
}
```

### `src/components/dashboards/web3-dashboard/components/web3/markets-panel.tsx`

```tsx
import { ArrowUpRight } from "lucide-react";
import { GoTriangleUp } from "react-icons/go";
import { Button } from "@/components/ui/button";
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
import { AssetBadge } from "./asset-badge";
import { markets } from "../../data";

export function MarketsPanel() {
  return (
    <Card className="rounded-2xl bg-card shadow-priamry dark:bg-muted p-2 ring-0 shadow-primary gap-1">
      <CardHeader className="flex items-center justify-between px-2 py-0.5">
        <CardTitle className="text-lg font-medium">Top Markets</CardTitle>
        <Button variant="ghost" size="sm" className="h-7 gap-2 px-1 text-sm font-normal text-muted-foreground">
          View all
          <ArrowUpRight className="size-4" />
        </Button>
      </CardHeader>
      <CardContent className="px-2 bg-muted/20 dark:bg-black rounded-xl h-full shadow-primary">
        <div className="">
          {markets.map((market) => (
            <div key={market.name} className="flex items-center gap-4 py-4">
              <AssetBadge symbol={market.name} color={market.color} />
              <span className="flex-1 text-lg font-medium">{market.name}</span>
              <span className="flex items-center gap-1 text-sm font-medium text-emerald-400">
                <GoTriangleUp className="size-4 fill-current" />
                {market.change}
              </span>
            </div>
          ))}
        </div>
      </CardContent>
    </Card>
  );
}
```

### `src/components/dashboards/web3-dashboard/components/web3/portfolio-panel.tsx`

```tsx
import { GoTriangleUp } from "react-icons/go";
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs";
import { PortfolioChart } from "./charts";
import { portfolioDataRanges } from "../../data";

const ranges = ["1D", "7D", "1M", "All"];

export function PortfolioPanel() {
  return (
    <Card className="rounded-2xl ring-0 shadow-primary bg-card dark:bg-muted p-2 xl:col-span-3 gap-2">
      <Tabs defaultValue="All" className="w-full flex flex-col h-full">
        <CardHeader className="flex flex-row items-center justify-between p-0">
          <CardTitle className="text-lg font-medium px-2 truncate line-clamp-1">
            Portfolio Performance
          </CardTitle>
          <TabsList className="bg-transparent gap-2">
            {ranges.map((range) => (
              <TabsTrigger
                key={range}
                value={range}
                className="h-7 px-3 text-sm rounded-md data-[state=active]:bg-muted/40 dark:data-[state=active]:bg-black data-[state=active]:text-primary data-[state=inactive]:text-muted-foreground data-[state=active]:shadow-none border-0"
              >
                {range}
              </TabsTrigger>
            ))}
          </TabsList>
        </CardHeader>
        <CardContent className="p-2 mt-2 bg-muted/20 dark:bg-card rounded-xl flex-1 shadow-primary overflow-hidden">
          <div className="px-4 flex-1">
            <div className="text-3xl font-medium tracking-wide">$84,520</div>
            <div className="mt-2 flex items-center gap-1 text-sm font-medium text-emerald-400">
              <GoTriangleUp className="size-4 fill-current" />
              <span>6.8% this month</span>
            </div>
          </div>
          <div className="h-80 pt-5 relative">
            {ranges.map((range) => (
              <TabsContent
                key={range}
                value={range}
                className="absolute inset-0 mt-0 pt-5 data-[state=inactive]:hidden"
              >
                <PortfolioChart
                  data={
                    portfolioDataRanges[
                      range as keyof typeof portfolioDataRanges
                    ]
                  }
                />
              </TabsContent>
            ))}
          </div>
        </CardContent>
      </Tabs>
    </Card>
  );
}
```

### `src/components/dashboards/web3-dashboard/components/web3/sidebar.tsx`

```tsx
import { ChevronDown } from "lucide-react";
import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar";
import { ThemeToggle } from "../ui/theme-toggle";
import {
  DropdownMenu,
  DropdownMenuContent,
  DropdownMenuItem,
  DropdownMenuLabel,
  DropdownMenuSeparator,
  DropdownMenuTrigger,
} from "@/components/ui/dropdown-menu";
import {
  Sidebar,
  SidebarContent,
  SidebarFooter,
  SidebarGroup,
  SidebarGroupContent,
  SidebarGroupLabel,
  SidebarHeader,
  SidebarMenu,
  SidebarMenuButton,
  SidebarMenuItem,
  SidebarSeparator,
  SidebarTrigger,
} from "@/components/ui/sidebar";
import { navSections, utilityItems } from "../../data";
import { cn } from "@/lib/utils";
import LogoIcon from "./logo-icons";

export function DashboardSidebar() {
  return (
    <Sidebar collapsible="icon" className="overflow-hidden border-r">
      <SidebarHeader className="p-3">
        <SidebarMenu>
          <SidebarMenuItem className="flex items-center gap-2">
            <SidebarMenuButton
              asChild
              size="lg"
              className="h-11 px-2 group-data-[collapsible=icon]:hidden group-data-[collapsible=icon]:p-0"
            >
              <a href="#">
                <LogoIcon className="text-primary size-8" />
                <span className="text-lg font-medium">Watermelon</span>
              </a>
            </SidebarMenuButton>
            <SidebarTrigger className=" " />
          </SidebarMenuItem>
        </SidebarMenu>
      </SidebarHeader>

      <SidebarContent className="gap-4 p-3">
        {navSections.map((section) => (
          <SidebarGroup key={section.title} className="p-0">
            <SidebarGroupLabel className="h-8 px-1 text-sm">
              {section.title}
            </SidebarGroupLabel>
            <SidebarGroupContent>
              <SidebarMenu className="gap-1">
                {section.items.map((item) => (
                  <SidebarMenuItem key={item.label}>
                    <SidebarMenuButton
                      asChild
                      isActive={item.active}
                      tooltip={item.label}
                      className={cn(
                        "h-9 gap-3 rounded-lg px-3 text-sm font-medium",
                        item.active
                          ? "bg-sidebar-accent text-sidebar-accent-foreground shadow-primary"
                          : "text-muted-foreground",
                      )}
                    >
                      <a href="#">
                        <item.icon
                          className={cn(
                            "size-4",
                            item.active ? "text-primary" : "",
                          )}
                        />
                        <span>{item.label}</span>
                      </a>
                    </SidebarMenuButton>
                  </SidebarMenuItem>
                ))}
              </SidebarMenu>
            </SidebarGroupContent>
          </SidebarGroup>
        ))}
      </SidebarContent>
      <SidebarSeparator />

      <SidebarFooter className="gap-3 px-3 pb-3">
        <SidebarMenu className="gap-1">
          {utilityItems.map((item) => (
            <SidebarMenuItem key={item.label}>
              <SidebarMenuButton
                asChild
                tooltip={item.label}
                className="text-muted-foreground h-9 gap-3 rounded-lg px-3 text-sm"
              >
                <a href="#">
                  <item.icon className="size-4" />
                  <span>{item.label}</span>
                </a>
              </SidebarMenuButton>
            </SidebarMenuItem>
          ))}
          <SidebarMenuItem className="flex items-center gap-0 group-data-[collapsible=icon]:p-0">
            <DropdownMenu>
              <DropdownMenuTrigger asChild>
                <SidebarMenuButton
                  tooltip="Connected account"
                  className="flex h-11 items-center gap-3 rounded-md group-data-[collapsible=icon]:justify-center group-data-[collapsible=icon]:p-0"
                >
                  <Avatar className="size-6 shrink-0">
                    <AvatarImage
                      src={"https://assets.watermelon.sh/wm_ben.png"}
                      className=""
                    />

                    <AvatarFallback className="bg-primary text-primary-foreground text-xs font-semibold">
                      VP
                    </AvatarFallback>
                  </Avatar>
                  <span className="flex-1 overflow-hidden text-left text-sm font-medium text-ellipsis whitespace-nowrap group-data-[collapsible=icon]:hidden">
                    Vansh Patel
                  </span>
                  <ChevronDown className="text-muted-foreground size-4 shrink-0 group-data-[collapsible=icon]:hidden" />
                </SidebarMenuButton>
              </DropdownMenuTrigger>
              <DropdownMenuContent
                className="w-56"
                align="end"
                side="right"
                sideOffset={16}
              >
                <DropdownMenuLabel>My Account</DropdownMenuLabel>
                <DropdownMenuSeparator />
                <DropdownMenuItem>Profile</DropdownMenuItem>
                <DropdownMenuItem>Settings</DropdownMenuItem>
                <DropdownMenuSeparator />
                <DropdownMenuItem variant="destructive">
                  Logout
                </DropdownMenuItem>
              </DropdownMenuContent>
            </DropdownMenu>
            <div className="group-data-[collapsible=icon]:hidden">
              <ThemeToggle />
            </div>
          </SidebarMenuItem>
        </SidebarMenu>
      </SidebarFooter>
    </Sidebar>
  );
}
```

### `src/components/dashboards/web3-dashboard/components/web3/stat-card.tsx`

```tsx
import { GoTriangleUp, GoTriangleDown } from "react-icons/go";
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
import { ApySparkline } from "./charts";
import { stats } from "../../data";
import { cn } from "@/lib/utils";

export function StatGrid() {
  return (
    <section className="grid gap-3 sm:grid-cols-2 xl:grid-cols-4">
      {stats.map((stat) => (
        <Card
          key={stat.label}
          className="min-h-28 rounded-2xl bg-card py-4 ring-0 shadow-primary dark:bg-muted"
        >
          <CardHeader className="flex items-center gap-2 px-4 pb-2">
            <div
              className={cn(
                " rounded-full shadow-[inset_0_1px_8px_1px_rgba(255,255,255,0.6),inset_0_-1px_8px_2px_rgba(0,0,0,0.3)] flex items-center justify-center p-2 ",
                stat.tone,
              )}
            >
              <stat.icon className={cn("size-5 text-black")} />
            </div>
            <CardTitle className="text-sm font-medium">{stat.label}</CardTitle>
          </CardHeader>
          <CardContent className="px-4  flex flex-col mt-auto  ">
            <div className="text-3xl font-nomral tracking-wide ">
              {stat.value}
            </div>
            <div className=" ">
              {stat.compactChart ? (
                <div className="mt-2 h-10">
                  <ApySparkline />
                </div>
              ) : (
                <div
                  className={cn("mt-2 flex items-center gap-1 text-sm", {
                    "text-emerald-400": stat.detail.startsWith("+"),
                    "text-red-400": stat.detail.startsWith("-"),
                  })}
                >
                  {stat.detail.startsWith("+") || stat.detail.startsWith("-") ? (
                    <>
                      {stat.detail.startsWith("+") ? (
                        <GoTriangleUp className="size-4 fill-current" />
                      ) : (
                        <GoTriangleDown className="size-4 fill-current" />
                      )}
                      <span className="">{stat.detail}</span>
                    </>
                  ) : (
                    <span className="">
                      {stat.detail.split(/(\d+(?:\.\d+)?)/).map((part, index) => {
                        if (!part) return null;
                        return /^\d+(?:\.\d+)?$/.test(part) ? (
                          <span key={index} className="text-foreground">
                            {part}
                          </span>
                        ) : (
                          <span key={index} className="text-muted-foreground">
                            {part}
                          </span>
                        );
                      })}
                    </span>
                  )}
                </div>
              )}
            </div>
          </CardContent>
        </Card>
      ))}
    </section>
  );
}
```

### `src/components/dashboards/web3-dashboard/components/web3/topbar.tsx`

```tsx
"use client";

import * as React from "react";
import { ArrowUpRight, Plus, Repeat2, Search } from "lucide-react";

import { Badge } from "@/components/ui/badge";
import { Button } from "@/components/ui/button";
import { Input } from "@/components/ui/input";
import { SidebarTrigger } from "@/components/ui/sidebar";
import {
  Select,
  SelectContent,
  SelectItem,
  SelectTrigger,
  SelectValue,
} from "@/components/ui/select";

export function DashboardTopbar() {
  const [network, setNetwork] = React.useState("ethereum");

  return (
    <header className="flex min-h-16 items-center justify-between gap-4">
      <div className="flex w-full max-w-sm items-center gap-2">
        <SidebarTrigger className="shrink-0 lg:hidden" />
        <div className="relative flex-1">
          <Search className="text-muted-foreground absolute top-1/2 left-3 size-4 -translate-y-1/2" />
          <Input
            aria-label="Search assets"
            className="border-border bg-muted h-9 w-full rounded-lg pl-9 text-sm"
            placeholder="search assets, pools, transaction..."
          />
        </div>
      </div>

      <div className="hidden items-center gap-2 md:flex">
        <Select value={network} onValueChange={setNetwork}>
          <SelectTrigger className="bg-muted min-h-9 w-[210px] gap-3">
            <SelectValue placeholder="Select Network" />
          </SelectTrigger>
          <SelectContent>
            <SelectItem value="ethereum">
              <div className="flex items-center gap-2">
                <Badge className="flex size-5 items-center justify-center rounded-full bg-indigo-500 p-0 text-white">
                  E
                </Badge>
                Ethereum Mainnet
              </div>
            </SelectItem>
            <SelectItem value="polygon">
              <div className="flex items-center gap-2">
                <Badge className="flex size-5 items-center justify-center rounded-full bg-purple-500 p-0 text-white">
                  P
                </Badge>
                Polygon
              </div>
            </SelectItem>
            <SelectItem value="arbitrum">
              <div className="flex items-center gap-2">
                <Badge className="flex size-5 items-center justify-center rounded-full bg-blue-500 p-0 text-white">
                  A
                </Badge>
                Arbitrum
              </div>
            </SelectItem>
            <SelectItem value="optimism">
              <div className="flex items-center gap-2">
                <Badge className="flex size-5 items-center justify-center rounded-full bg-red-500 p-0 text-white">
                  O
                </Badge>
                Optimism
              </div>
            </SelectItem>
          </SelectContent>
        </Select>
        <Button variant="outline" className="bg-muted h-9">
          Deposit
          <Plus className="size-4" />
        </Button>
        <Button variant="outline" className="bg-muted h-9">
          Withdraw
          <ArrowUpRight className="size-4" />
        </Button>
        <Button className="bg-primary text-primary-foreground h-9 px-5 shadow-[inset_0_1px_6px_2px_rgba(255,255,255,0.1),inset_0_-1px_6px_2px_rgba(0,0,0,0.1)]">
          Swap
          <Repeat2 className="size-4" />
        </Button>
      </div>
    </header>
  );
}
```

### `src/components/dashboards/web3-dashboard/dashboard-layout.tsx`

```tsx

import { DashboardSidebar } from './components/web3/sidebar';
import './dashboard.css';

import { SidebarInset, SidebarProvider } from '@/components/ui/sidebar';
import type { CSSProperties } from 'react';

export default function DashboardLayout({children}:{children:React.ReactNode}) {
  return (
    <SidebarProvider
      className="web3-dashboard bg-background text-foreground min-h-screen font-sans"
      style={
        {
          '--sidebar-width': 'calc(var(--spacing) * 64)',
          '--sidebar-width-icon': 'calc(var(--spacing) * 14)',
        } as CSSProperties
      }
    >
      <DashboardSidebar />
      <SidebarInset>
        {children}
      </SidebarInset>
    </SidebarProvider>
  );
}
```

### `src/components/dashboards/web3-dashboard/dashboard.css`

```css
.web3-dashboard {

  --background: oklch(0.975 0.002 247.839);
  --foreground: oklch(0.145 0 0);
  --card: oklch(1 0 0);
  --card-foreground: oklch(0.145 0 0);
  --popover: oklch(1 0 0);
  --popover-foreground: oklch(0.145 0 0);
  --primary: oklch(89.7% 0.196 126.665);
  --primary-foreground: oklch(0.145 0 0);
  --secondary: oklch(0.967 0.001 286.375);
  --secondary-foreground: oklch(0.21 0.006 285.885);
  --muted: oklch(0.94 0 0);
  --muted-foreground: oklch(0.556 0 0);
  --accent: oklch(0.97 0 0);
  --accent-foreground: oklch(0.205 0 0);
  --destructive: oklch(0.577 0.245 27.325);
  --border: oklch(0.922 0 0);
  --input: oklch(0.922 0 0);
  --ring: oklch(0.708 0 0);
  --chart-1: oklch(0.897 0.196 126.665);
  --chart-2: oklch(0.768 0.233 130.85);
  --chart-3: oklch(0.648 0.2 131.684);
  --chart-4: oklch(0.532 0.157 131.589);
  --chart-5: oklch(0.453 0.124 130.933);
  --radius: 0.625rem;
  --sidebar: oklch(0.99 0 0);
  --sidebar-foreground: oklch(0.145 0 0);
  --sidebar-primary: oklch(0.648 0.2 131.684);
  --sidebar-primary-foreground: oklch(0.986 0.031 120.757);
  --sidebar-accent: oklch(0.97 0 0);
  --sidebar-accent-foreground: oklch(0.205 0 0);
  --sidebar-border: oklch(0.922 0 0);
  --sidebar-ring: oklch(0.708 0 0);
  --shadow-primary:
    0px 0px 0px 0.5px rgba(0, 0, 0, 0.06), 0px 1px 2px -1px rgba(0, 0, 0, 0.06),
    0px 2px 4px 0px rgba(0, 0, 0, 0.04),
    inset 0px 0px 0px 1px rgba(255, 255, 255, 0.3);

  --radius-sm: calc(var(--radius) * 0.6);
  --radius-md: calc(var(--radius) * 0.8);
  --radius-lg: var(--radius);
  --radius-xl: calc(var(--radius) * 1.4);
  --radius-2xl: calc(var(--radius) * 1.8);
  --radius-3xl: calc(var(--radius) * 2.2);
  --radius-4xl: calc(var(--radius) * 2.6);
  background-color: var(--background);
  color: var(--foreground);
}

.web3-dashboard.dark,
.dark .web3-dashboard {
  --background: oklch(0.115 0 0);
  --foreground: oklch(0.985 0 0);
  --card: oklch(0.145 0 0);
  --card-foreground: oklch(0.985 0 0);
  --popover: oklch(0.205 0 0);
  --popover-foreground: oklch(0.985 0 0);
  --primary: oklch(89.7% 0.196 126.665);
  --primary-foreground: oklch(0.405 0.101 131.063);
  --secondary: oklch(0.274 0.006 286.033);
  --secondary-foreground: oklch(0.985 0 0);
  --muted: oklch(0.215 0 0);
  --muted-foreground: oklch(0.708 0 0);
  --accent: oklch(0.269 0 0);
  --accent-foreground: oklch(0.985 0 0);
  --destructive: oklch(0.704 0.191 22.216);
  --border: oklch(1 0 0 / 10%);
  --input: oklch(1 0 0 / 15%);
  --ring: oklch(0.556 0 0);
  --chart-1: oklch(0.897 0.196 126.665);
  --chart-2: oklch(0.768 0.233 130.85);
  --chart-3: oklch(0.648 0.2 131.684);
  --chart-4: oklch(0.532 0.157 131.589);
  --chart-5: oklch(0.453 0.124 130.933);
  --sidebar: oklch(0.135 0 0);
  --sidebar-foreground: oklch(0.985 0 0);
  --sidebar-primary: oklch(0.768 0.233 130.85);
  --sidebar-primary-foreground: oklch(0.274 0.072 132.109);
  --sidebar-accent: oklch(0.269 0 0);
  --sidebar-accent-foreground: oklch(0.985 0 0);
  --sidebar-border: oklch(1 0 0 / 10%);
  --sidebar-ring: oklch(0.556 0 0);
  --shadow-primary:
    0px 0px 0px 1px rgba(255, 255, 255, 0.06),
    0px 1px 2px -1px rgba(0, 0, 0, 0.5), 0px 2px 4px 0px rgba(0, 0, 0, 0.5),
    inset 0px 0px 0px 0px rgba(255, 255, 255, 0);
}

.web3-dashboard .shadow-primary {
  box-shadow: var(--shadow-primary);
}
```

### `src/components/dashboards/web3-dashboard/dashboardView.tsx`

```tsx

import { AssetTableCard } from './components/web3/asset-table-card';
import { borrowedAssets, netWorthAssets } from './data';
import { DashboardTopbar } from './components/web3/topbar';
import { MarketsPanel } from './components/web3/markets-panel';
import { PortfolioPanel } from './components/web3/portfolio-panel';
import { StatGrid } from './components/web3/stat-card';
export default function DashboardView() {
  return (
    <main className="flex-1 px-4 pb-7 lg:px-8">
      <DashboardTopbar />
      <div className="mx-auto grid gap-4">
        <StatGrid />
        <section className="grid gap-3 xl:grid-cols-4">
          <PortfolioPanel />
          <MarketsPanel />
        </section>
        <section className="grid gap-3 xl:grid-cols-2">
          <AssetTableCard
            title="Net Worth"
            assets={netWorthAssets}
            action="Deposit"
            valueLabel="Value"
          />
          <AssetTableCard
            title="Borrowed Assets"
            assets={borrowedAssets}
            action="Borrow More"
            valueLabel="APY"
            external
          />
        </section>
      </div>
    </main>
  );
}
```

### `src/components/dashboards/web3-dashboard/data.ts`

```ts
import {
  ArrowDown,
  ArrowUpRight,
  Bell,
  ChartNoAxesColumn,
  CircleGauge,
  Coins,
  FileClock,
  Grid2X2,
  LockKeyhole,
  Repeat2,
  Settings,
  User,
  WalletCards,
} from "lucide-react";

export const navSections = [
  {
    title: "Main",
    items: [
      { label: "Overview", icon: Grid2X2, active: true },
      { label: "Swap / LP", icon: Repeat2 },
      { label: "Global Markets", icon: CircleGauge },
      { label: "Staking", icon: ArrowDown },
      { label: "Vaults", icon: LockKeyhole },
      { label: "Leaderboard", icon: ChartNoAxesColumn },
    ],
  },
  {
    title: "System",
    items: [
      { label: "Changelog", icon: User },
      { label: "Report", icon: FileClock },
    ],
  },
];

export const utilityItems = [
  { label: "Notifications", icon: Bell },
  { label: "Settings", icon: Settings },
];

export const stats = [
  {
    label: "Net Worth",
    value: "$86,265",
    detail: "+12.4%",
    icon: WalletCards,
    tone: "bg-lime-400",
  },
  {
    label: "Total Supplied",
    value: "$49,654",
    detail: "Across 5 assets",
    icon: Coins,
    tone: "bg-emerald-400",
  },
  {
    label: "Total Borrowed",
    value: "$18,920",
    detail: "3 open positions",
    icon: ArrowDown,
    tone: "bg-orange-400",
  },
  {
    label: "Net APY",
    value: "+8.75%",
    detail: "",
    icon: ArrowUpRight,
    tone: "bg-sky-400",
    compactChart: true,
  },
];

export const portfolioDataRanges = {
  "1D": [
    { time: "10:00", value: 250000 },
    { time: "11:00", value: 252000 },
    { time: "12:00", value: 248000 },
    { time: "13:00", value: 255000 },
    { time: "14:00", value: 258000 },
    { time: "15:00", value: 260000 },
  ],
  "7D": [
    { time: "Mon", value: 240000 },
    { time: "Tue", value: 245000 },
    { time: "Wed", value: 235000 },
    { time: "Thu", value: 250000 },
    { time: "Fri", value: 255000 },
    { time: "Sat", value: 258000 },
    { time: "Sun", value: 260000 },
  ],
  "1M": [
    { time: "Week 1", value: 210000 },
    { time: "Week 2", value: 230000 },
    { time: "Week 3", value: 245000 },
    { time: "Week 4", value: 260000 },
  ],
  All: [
    { time: "Jan", value: 22000 },
    { time: "Feb", value: 52000 },
    { time: "Mar", value: 76000 },
    { time: "Apr", value: 76000 },
    { time: "May", value: 76000 },
    { time: "Jun", value: 66000 },
    { time: "Jul", value: 59000 },
    { time: "Aug", value: 91000 },
    { time: "Sep", value: 127000 },
    { time: "Oct", value: 196000 },
    { time: "Nov", value: 217000 },
    { time: "Now", value: 260000 },
  ],
};

export const apyData = [
  { month: "Oct", value: 5.8 },
  { month: "Nov", value: 6.6 },
  { month: "Dec", value: 6.2 },
  { month: "Jan", value: 6.4 },
  { month: "Feb", value: 6.9 },
  { month: "Mar", value: 7.6 },
  { month: "Apr", value: 7.8 },
];

export const markets = [
  { name: "Bitcoin", symbol: "BTC", change: "4.50%", color: "bg-orange-400" },
  { name: "Ethereum", symbol: "ETH", change: "3.20%", color: "bg-sky-300" },
  { name: "Ripple", symbol: "XRP", change: "2.85%", color: "bg-blue-500" },
  { name: "Litecoin", symbol: "LTC", change: "1.75%", color: "bg-blue-800" },
  { name: "Cardano", symbol: "ADA", change: "2.10%", color: "bg-red-500" },
  { name: "Dogecoin", symbol: "DOGE", change: "2.10%", color: "bg-red-800" },
];

export const netWorthAssets = [
  { asset: "BTC", balance: "2.4K", value: "$8,000", color: "bg-orange-400" },
  { asset: "ETH", balance: "2.4K", value: "$2,500", color: "bg-slate-100" },
  { asset: "USDC", balance: "13.5K", value: "$13,500", color: "bg-blue-500" },
  { asset: "LTC", balance: "8.9K", value: "$8,000", color: "bg-orange-400" },
];

export const borrowedAssets = [
  { asset: "USDC", balance: "$2,400", value: "4.50%", color: "bg-blue-500" },
  { asset: "ETH", balance: "$8,000", value: "4.50%", color: "bg-slate-100" },
  { asset: "BTC", balance: "$6,500", value: "18.6%", color: "bg-orange-400" },
  { asset: "LTC", balance: "$300", value: "12.7%", color: "bg-orange-400" },
];
```

### `src/components/dashboards/web3-dashboard/demo.tsx`

```tsx
import DashboardView from "./dashboardView"
import DashboardLayout from "./dashboard-layout"

export default function Web3DashboardDemo() {
    return (
        <DashboardLayout>
            <DashboardView />
        </DashboardLayout>
    )
}
```

### `src/components/dashboards/web3-dashboard/web3-dashboard.mdx`

```mdx
---
title: Web3 Dashboard
slug: web3-dashboard
category: dashboard
image: 'https://assets.watermelon.sh/components/task-management-dashboard.webp'
video: 'https://assets.watermelon.sh/components/task-management-dashboard.mp4'
description: >-
  A unified task management dashboard interface, displaying tasks sorted by
  priority and category.
featured: false
dependencies:
  - lucide-react
  - tabler/icons-react
install:
  - >-
    npx shadcn@latest add
    https://registry.watermelon.sh/r/web3-dashboard.json
componentNumber: 15
---
```

### `src/components/dashboards/web3-dashboard/components/ui/theme-toggle.tsx`

```tsx
"use client";
import { Moon, Sun } from "lucide-react";
import { useTheme } from "next-themes";

import { Button } from "@/components/ui/button";

export function ThemeToggle() {
  const { setTheme, theme } = useTheme();

  return (
    <Button
      variant="ghost"
      size="icon"
      onClick={() => setTheme(theme === "light" ? "dark" : "light")}
      aria-label="Toggle theme"
    >
      <Sun className="h-[1.2rem] w-[1.2rem] rotate-0 scale-100 transition-all dark:-rotate-90 dark:scale-0" />
      <Moon className="absolute h-[1.2rem] w-[1.2rem] rotate-90 scale-0 transition-all dark:rotate-0 dark:scale-100" />
      <span className="sr-only">Toggle theme</span>
    </Button>
  );
}
```
