Shadcnspace Builder
blockdashboard-shell-01source: ok · refreshed
Dashboard Shell 01
A modern dashboard shell with a sidebar and header, ideal for admin dashboards to organize and navigate through different sections.
npx shadcn add "https://builder.shadcnspace.com/r/dashboard-shell-01.json"free
Where it goes
navbarsidebardashboard-shellkanban
Use cases
dashboard
Tags auto-generated by rules on 9/24/2026 — empty means no evidence, not lack of support.
Dependencies
Bundle weight · mediumiconslucide-react
chartsrecharts
animationmotion
Registry dependencies
sidebarscroll-areacardbuttonbadgechartcollapsibledropdown-menuseparatorcheckboxprogresstable
Files9 · source cached 10/2/2026, 1:27:05 AM
src/components/shadcn-space/blocks/dashboard-shell-01/page.tsx
import AppSidebar from "@/components/shadcn-space/blocks/dashboard-shell-01/app-sidebar"
import StatisticsBlock from "@/components/shadcn-space/blocks/dashboard-shell-01/statistics"
import SalesOverviewChart from "@/components/shadcn-space/blocks/dashboard-shell-01/sales-overview-chart"
import EarningReportChart from "@/components/shadcn-space/blocks/dashboard-shell-01/earning-report-chart"
import TopProductTable from "@/components/shadcn-space/blocks/dashboard-shell-01/top-product-table"
import SalesByCountryWidget from "@/components/shadcn-space/blocks/dashboard-shell-01/salesbycountrywidget"
export default function Page() {
return (
<AppSidebar>
<div className="grid grid-cols-12 gap-6 p-6">
<div className="col-span-12">
<StatisticsBlock />
</div>
<div className="xl:col-span-8 col-span-12">
<SalesOverviewChart />
</div>
<div className="xl:col-span-4 col-span-12">
<EarningReportChart />
</div>
<div className="xl:col-span-8 col-span-12">
<TopProductTable />
</div>
<div className="xl:col-span-4 col-span-12">
<SalesByCountryWidget />
</div>
</div>
</AppSidebar>
)
}src/components/shadcn-space/blocks/dashboard-shell-01/salesbycountrywidget.tsx
'use client'
import React, { useRef } from 'react'
import { Separator } from '@/components/ui/separator'
import {
DropdownMenu,
DropdownMenuContent,
DropdownMenuItem,
DropdownMenuTrigger,
} from '@/components/ui/dropdown-menu'
import { Icon } from '@iconify/react'
import { Card, CardTitle, CardHeader, CardContent, CardAction } from '@/components/ui/card'
import { motion, useInView } from 'motion/react'
import { cn } from '@/lib/utils'
import { Badge } from '@/components/ui/badge'
const DEFAULT_DROPDOWN_ITEMS = [
{ title: 'Action', link: '#' },
{ title: 'Another action', link: '#' },
{ title: 'Something else', link: '#' },
]
const DEFAULT_TRANS_DATA = [
{
img: 'https://images.shadcnspace.com/assets/flags/flag-us.svg',
title: 'PayPal Transfer',
country: 'United states',
rank: '$8,567k',
badgeData: "+4.7%",
badgeBG: "bg-teal-400/10"
},
{
img: 'https://images.shadcnspace.com/assets/flags/flag-brazil.svg',
title: 'Wallet',
country: 'Brazil',
rank: '$2,415k',
badgeData: "-1.7%",
badgeBG: "bg-orange-400/10"
},
{
img: 'https://images.shadcnspace.com/assets/flags/flag-india.svg',
title: 'Credit Card',
country: 'India',
rank: '$865k',
badgeData: "+4.7%",
badgeBG: "bg-teal-400/10"
},
{
img: 'https://images.shadcnspace.com/assets/flags/flag-australia.svg',
title: 'Bank Transfer',
country: 'Australia',
rank: '$745k',
badgeData: "-1.7%",
badgeBG: "bg-orange-400/10"
},
{
img: 'https://images.shadcnspace.com/assets/flags/flag-france.svg',
title: 'Refund',
country: 'France',
rank: '$45',
badgeData: "+4.7%",
badgeBG: "bg-teal-400/10"
},
{
img: 'https://images.shadcnspace.com/assets/flags/flag-china.svg',
title: 'Refund',
country: 'China',
rank: '$12k',
badgeData: "+4.7%",
badgeBG: "bg-teal-400/10"
},
]
interface TransactionProps {
img: string
title: string
country: string
rank: string
badgeData: string
badgeBG: string
}
interface DropdownItemProps {
title: string
link?: string
}
interface WidgetProps {
recentTransData?: TransactionProps[]
dropdownItems?: DropdownItemProps[]
}
const SalesByCountryWidget = ({
recentTransData = DEFAULT_TRANS_DATA,
dropdownItems = DEFAULT_DROPDOWN_ITEMS,
}: WidgetProps) => {
const ref = useRef(null)
const isInView = useInView(ref, { once: true, amount: 0.2 })
return (
<Card>
<CardHeader className='flex items-center justify-between'>
<CardTitle className='text-lg font-medium text-foreground'>
Sales by Countries
</CardTitle>
<CardAction>
<DropdownMenu>
<DropdownMenuTrigger className='outline-none'>
<span className='h-9 w-9 flex justify-center items-center rounded-full hover:bg-accent hover:text-accent-foreground cursor-pointer'>
<Icon icon="solar:menu-dots-bold" width={22} height={22} className='rotate-90' />
</span>
</DropdownMenuTrigger>
<DropdownMenuContent>
{dropdownItems.map((item, index) => (
<DropdownMenuItem key={index} className='font-normal cursor-pointer'>
{item.link ? (
<a href={item.link} className="w-full">
{item.title}
</a>
) : (
<span className='w-full justify-start'>
{item.title}
</span>
)}
</DropdownMenuItem>
))}
</DropdownMenuContent>
</DropdownMenu>
</CardAction>
</CardHeader>
<CardContent className='px-0'>
<motion.div
ref={ref}
className='flex flex-col gap-3'
initial='hidden'
animate={isInView ? 'visible' : 'hidden'}
variants={{
visible: {
transition: {
staggerChildren: 0.1,
delayChildren: 0.2,
},
},
}}>
{recentTransData.map((item, index) => (
<React.Fragment key={index}>
<motion.div
className='flex gap-3 items-center px-6'
variants={{
hidden: { opacity: 0, y: 20 },
visible: { opacity: 1, y: 0 },
}}
transition={{
type: 'spring',
stiffness: 300,
damping: 24,
}}
whileHover={{ scale: 1.02, x: 4 }}
whileTap={{ scale: 0.98 }}>
<motion.div
className={cn(`w-8 h-8 rounded-full flex justify-center items-center overflow-hidden`)}
whileHover={{ rotate: 5, scale: 1.1 }}
transition={{ type: 'spring', stiffness: 400 }}>
<img src={item.img} alt='icon' width={32} height={32} />
</motion.div>
<div className="flex items-center justify-between flex-1">
<div>
<h5 className='text-base font-medium text-foreground'>{item.rank}</h5>
<p className='text-sm font-normal tracking-wide text-muted-foreground'>
{item.country}
</p>
</div>
<Badge className={cn(`${item.badgeBG}`, "text-muted-foreground")}>
{item.badgeData}
</Badge>
</div>
</motion.div>
{index < recentTransData.length - 1 && <Separator />}
</React.Fragment>
))}
</motion.div>
</CardContent>
</Card>
)
}
export default SalesByCountryWidgetsrc/components/shadcn-space/blocks/dashboard-shell-01/site-header.tsx
import { SidebarTrigger } from "@/components/ui/sidebar"
import UserDropdown from "@/components/shadcn-space/blocks/dashboard-shell-01/user-dropdown";
import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar";
import NotificationDropdown from "@/components/shadcn-space/blocks/dashboard-shell-01/notification-dropdown";
import { BellRing, SearchIcon } from "lucide-react";
import { InputGroup, InputGroupAddon, InputGroupInput } from "@/components/ui/input-group";
export function SiteHeader() {
return (
<div className="flex w-full items-center justify-between">
<div className="flex items-center gap-2">
<SidebarTrigger className="-ml-1" />
<InputGroup>
<InputGroupInput placeholder="Search" />
<InputGroupAddon>
<SearchIcon />
</InputGroupAddon>
</InputGroup>
</div>
<div className="flex items-center gap-3">
<NotificationDropdown
defaultOpen={false}
align="center"
trigger={
<div className="rounded-full p-2 hover:bg-accent relative before:absolute before:bottom-0 before:left-1/2 before:z-10 before:w-2 before:h-2 before:rounded-full before:bg-red-500 before:top-1">
<BellRing className="size-4" />
</div>
}
/>
<UserDropdown
defaultOpen={false}
align="center"
trigger={
<div className="rounded-full">
<Avatar className="size-8 cursor-pointer">
<AvatarImage
src="https://images.shadcnspace.com/assets/profiles/user-11.jpg"
alt="David McMichael"
/>
<AvatarFallback>DM</AvatarFallback>
</Avatar>
</div>
}
/>
</div>
</div>
)
}
src/components/shadcn-space/blocks/dashboard-shell-01/top-product-table.tsx
"use client";
import { LucideIcon, AppWindowMac, HandMetal, Megaphone, Contrast, Brush } from 'lucide-react';
import { FolderPlus, FolderPen, FolderMinus } from 'lucide-react';
import { EllipsisVertical } from "lucide-react";
import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from "@/components/ui/table";
import { Checkbox } from "@/components/ui/checkbox";
import { Progress } from "@/components/ui/progress";
import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuTrigger } from "@/components/ui/dropdown-menu";
import { Card, CardHeader, CardTitle, CardContent, CardDescription } from "@/components/ui/card";
import { cn } from "@/lib/utils";
interface TableAction {
icon: LucideIcon;
listtitle: string;
}
interface ProjectData {
project: string;
date: string;
budget: string;
icon: LucideIcon;
iconcolor: string;
iconbg: string;
avatar: string;
name: string;
handle: string;
progress: number;
progressColor: string;
}
const TopProductTable = () => {
const tableActionData: TableAction[] = [
{ icon: FolderPlus, listtitle: "Add" },
{ icon: FolderPen, listtitle: "Edit" },
{ icon: FolderMinus, listtitle: "Delete" },
];
const checkboxTableData: ProjectData[] = [
{
project: 'Web App Project',
date: "04 June 2026",
budget: '12,000',
icon: AppWindowMac,
iconcolor: 'text-orange-400',
iconbg: 'bg-orange-400/20',
avatar: "https://images.shadcnspace.com/assets/profiles/user-11.jpg",
name: 'Olivia Rhye',
handle: 'olivia@ui.com',
progress: 60,
progressColor: '**:data-[slot=progress-indicator]:bg-orange-400'
},
{
project: 'MaterialM Admin',
date: "09 January 2026",
budget: '8000',
icon: HandMetal,
iconcolor: 'text-sky-400',
iconbg: 'bg-sky-400/20',
avatar: "https://images.shadcnspace.com/assets/profiles/user-8.jpg",
name: 'Barbara Steele',
handle: 'steele@ui.com',
progress: 30,
progressColor: '**:data-[slot=progress-indicator]:bg-blue-500'
},
{
project: 'Digital Marketing',
date: "15 April 2026",
budget: '15,000',
icon: Megaphone,
iconcolor: 'text-teal-400',
iconbg: 'bg-teal-400/20',
avatar: "https://images.shadcnspace.com/assets/profiles/user-3.jpg",
name: 'Leonard Gordon',
handle: 'olivia@ui.com',
progress: 45,
progressColor: '**:data-[slot=progress-indicator]:bg-amber-300'
},
{
project: 'Shadcn Space Design',
date: "30 March 2026",
budget: '1000',
icon: Contrast,
iconcolor: 'text-red-500',
iconbg: 'bg-red-500/20',
avatar: "https://images.shadcnspace.com/assets/profiles/user-4.jpg",
name: 'Evelyn Pope',
handle: 'steele@ui.com',
progress: 37,
progressColor: '**:data-[slot=progress-indicator]:bg-red-500'
},
{
project: 'Graphic Design',
date: "23 October 2026",
budget: '7000',
icon: Brush,
iconcolor: 'text-blue-500',
iconbg: 'bg-blue-500/20',
avatar: "https://images.shadcnspace.com/assets/profiles/user-5.jpg",
name: 'Tommy Garza',
handle: 'olivia@ui.com',
progress: 87,
progressColor: '**:data-[slot=progress-indicator]:bg-teal-400'
},
{
project: 'Digital Marketing',
date: "15 April 2026",
budget: '15,000',
icon: Megaphone,
iconcolor: 'text-teal-400',
iconbg: 'bg-teal-400/20',
avatar: "https://images.shadcnspace.com/assets/profiles/user-3.jpg",
name: 'Leonard Gordon',
handle: 'olivia@ui.com',
progress: 45,
progressColor: '**:data-[slot=progress-indicator]:bg-amber-300'
},
];
return (
<Card className="w-full border-0 overflow-hidden pb-0 h-full">
<CardHeader>
<CardTitle>Top Projects</CardTitle>
<CardDescription>Checkout the statistics of top projects</CardDescription>
</CardHeader>
<CardContent className="px-0">
<div className="overflow-x-auto">
<Table className="min-w-2xl">
<TableHeader>
<TableRow className="hover:bg-transparent!">
<TableHead className="p-3 ps-6">#</TableHead>
<TableHead className="p-2">Project Name</TableHead>
<TableHead className="p-2">Budget</TableHead>
<TableHead className="p-2">Manager</TableHead>
<TableHead className="p-2">Progress</TableHead>
<TableHead className="p-3 pe-6 flex justify-end">Action</TableHead>
</TableRow>
</TableHeader>
<TableBody className="divide-y divide-border dark:divide-darkborder">
{checkboxTableData.map((item, index) => (
<TableRow key={index}>
{/* Checkbox */}
<TableCell className="whitespace-nowrap p-3 ps-6">
<Checkbox className="data-[state=checked]:bg-blue-500 dark:data-[state=checked]:bg-blue-500 data-[state=checked]:border-blue-500 dark:data-[state=checked]:border-blue-500 cursor-pointer" />
</TableCell>
{/* project */}
<TableCell className="whitespace-nowrap">
<div className="flex items-center gap-2">
<div className={cn("h-9 w-9 rounded-full flex items-center justify-center", item.iconbg)}>
<item.icon width={18} height={18} className={cn(item.iconcolor)} />
</div>
<div className="">
<h6 className="text-sm font-medium">{item.project}</h6>
<p className="text-xs text-muted-foreground">{item.date}</p>
</div>
</div>
</TableCell>
{/* Status Badge */}
<TableCell className="whitespace-nowrap">
<p className="text-sm text-foreground">
${item.budget}
</p>
</TableCell>
{/* Customer */}
<TableCell className="whitespace-nowrap">
<div className="flex gap-3 items-center">
<img
src={item.avatar}
alt="icon"
className="h-9 w-9 rounded-full"
width={36}
height={36}
/>
<div className="truncate line-clamp-2 max-w-56">
<h6 className="text-base! font-normal!">{item.name}</h6>
<p className="text-sm text-muted-foreground">{item.handle}</p>
</div>
</div>
</TableCell>
{/* Progress */}
<TableCell className="whitespace-nowrap">
<Progress
value={item.progress}
className={cn("w-full h-1", `${item.progressColor}`)}
/>
</TableCell>
{/* Dropdown Menu */}
<TableCell className="whitespace-nowrap p-3 pe-6">
<div className="flex items-center justify-end">
<DropdownMenu>
<DropdownMenuTrigger>
<span className="flex justify-center items-center rounded-full p-2 hover:bg-muted cursor-pointer">
<EllipsisVertical width={16} height={16} />
</span>
</DropdownMenuTrigger>
<DropdownMenuContent align="end">
{tableActionData.map((action, idx) => (
<DropdownMenuItem key={idx} className="group flex gap-3 hover:bg-accent! cursor-pointer">
<action.icon />
<span>{action.listtitle}</span>
</DropdownMenuItem>
))}
</DropdownMenuContent>
</DropdownMenu>
</div>
</TableCell>
</TableRow>
))}
</TableBody>
</Table>
</div>
</CardContent>
</Card>
);
};
export default TopProductTable;
src/components/shadcn-space/blocks/dashboard-shell-01/statistics.tsx
import { Badge } from "@/components/ui/badge";
import { Button } from "@/components/ui/button";
import { Card, CardContent } from "@/components/ui/card";
import { Separator } from "@/components/ui/separator";
import { ArrowRight, CalendarDays, Handbag, LucideIcon } from "lucide-react";
import { cn } from "@/lib/utils";
type DashboardMetric = {
label: string;
value: string;
percentage: string;
isPositive?: boolean;
};
type MainDashboardData = {
title: string;
description: string;
metrics: DashboardMetric[];
};
type StatItem = {
title: string;
value: string;
percentage: string;
icon: LucideIcon;
isPositive?: boolean;
};
type StatisticsBlockProps = {
mainDashboard?: MainDashboardData;
secondaryStats?: StatItem[];
};
const mainDashboardData: MainDashboardData = {
title: "Analytics Dashboard",
description: "Check all the statistics",
metrics: [
{
label: "Earnings",
value: "$27,850",
percentage: "+18%",
isPositive: true,
},
{
label: "Expense",
value: "$18,453",
percentage: "-5%",
isPositive: false,
},
],
};
const secondaryStatsData: StatItem[] = [
{
title: "Weekly Sales",
value: "$4,587",
percentage: "+18%",
icon: CalendarDays,
isPositive: true,
},
{
title: "Purchase Orders",
value: "230",
percentage: "+18%",
icon: Handbag,
isPositive: true,
},
];
const StatisticsBlock = ({
mainDashboard = mainDashboardData,
secondaryStats = secondaryStatsData,
}: StatisticsBlockProps) => {
return (
<div className="grid grid-cols-12 gap-6 h-full">
<div className="col-span-12 xl:col-span-6 h-full">
<Card className="p-0 ring-0 border rounded-2xl relative h-full">
<CardContent className="p-0">
<div className="ps-6 py-4 flex flex-col gap-9 justify-between">
<div>
<p className="text-lg font-medium text-card-foreground">
{mainDashboard.title}
</p>
<p className="text-xs font-normal text-muted-foreground">
{mainDashboard.description}
</p>
</div>
<div className="flex items-center gap-6">
{mainDashboard.metrics.map((metric, index) => (
<div key={index} className="flex items-center gap-6">
<div>
<p className="text-xs font-normal text-muted-foreground">
{metric.label}
</p>
<div className="flex items-center gap-1">
<p className="text-2xl font-medium text-card-foreground">
{metric.value}
</p>
<Badge
className={cn(
"font-normal text-muted-foreground",
metric.isPositive
? "bg-teal-400/10 "
: "bg-red-500/10"
)}
>
{metric.percentage}
</Badge>
</div>
</div>
{index < mainDashboard.metrics.length - 1 && (
<Separator
orientation="vertical"
className={"h-12"}
/>
)}
</div>
))}
</div>
</div>
{/* image */}
<img
src="https://images.shadcnspace.com/assets/backgrounds/stats-01.webp"
alt="user-img"
width={211}
height={168}
className="absolute bottom-0 right-0 hidden sm:block"
/>
</CardContent>
</Card>
</div>
{secondaryStats.map((stat, index) => (
<div
key={index}
className="col-span-12 sm:col-span-6 xl:col-span-3"
>
<Card className="p-6 ring-0 border rounded-2xl">
<CardContent className="p-0 flex items-start justify-between">
<div className="flex flex-col gap-5 justify-between">
<div className="flex flex-col gap-1">
<p className="text-lg font-medium text-card-foreground">
{stat.title}
</p>
<div className="flex items-center gap-2">
<p className="text-2xl font-medium text-card-foreground">
{stat.value}
</p>
<Badge
className={cn(
"font-normal text-muted-foreground",
stat.isPositive !== false
? "bg-teal-400/10"
: "bg-red-500/10"
)}
>
{stat.percentage}
</Badge>
</div>
</div>
{/* button */}
<Button
variant={"outline"}
className={"flex items-center gap-1.5 w-fit rounded-xl"}
>
<span>See Report</span>
<ArrowRight size={16} />
</Button>
</div>
<div className="p-3 rounded-full outline">
<stat.icon size={16} />
</div>
</CardContent>
</Card>
</div>
))}
</div>
);
};
export default StatisticsBlock;
src/components/shadcn-space/blocks/dashboard-shell-01/sales-overview-chart.tsx
"use client";
import {
Card,
CardHeader,
CardTitle,
CardContent,
} from "@/components/ui/card";
import {
ChartContainer,
ChartTooltip,
ChartTooltipContent,
type ChartConfig,
} from "@/components/ui/chart"
import { Badge } from "@/components/ui/badge";
import { cn } from "@/lib/utils";
import { Bar, BarChart, CartesianGrid, XAxis, YAxis } from "recharts"
const chartData = [
{ month: "Jan", expense: 31, profit: 31, earning: 31 },
{ month: "Feb", expense: 83, profit: 83, earning: 83 },
{ month: "Mar", expense: 53, profit: 53, earning: 53 },
{ month: "Apr", expense: 36, profit: 36, earning: 36 },
{ month: "May", expense: 64, profit: 64, earning: 64 },
{ month: "Jun", expense: 47, profit: 47, earning: 47 },
{ month: "Jul", expense: 95, profit: 95, earning: 95 },
{ month: "Aug", expense: 69, profit: 69, earning: 69 },
{ month: "Sep", expense: 29, profit: 29, earning: 29 },
{ month: "Oct", expense: 73, profit: 73, earning: 73 },
{ month: "Nov", expense: 27, profit: 27, earning: 27 },
{ month: "Dec", expense: 53, profit: 53, earning: 53 },
]
const chartConfig = {
expense: {
label: "Expense",
color: "var(--color-blue-500)",
},
profit: {
label: "Profit",
color: "var(--color-sky-400)",
},
earning: {
label: "Earning",
color: "rgba(56, 189, 248, 0.5)",
},
} satisfies ChartConfig
export default function SalesOverviewChart() {
const Countries = [
{
id: 1,
title: "Earning",
color: "bg-sky-400/50",
},
{
id: 2,
title: "Profit",
color: "bg-sky-400",
},
{
id: 3,
title: "Expense",
color: "bg-blue-500",
}
];
return (
<Card className="w-full">
<CardHeader className="flex sm:flex-row flex-col justify-between sm:items-center items-start gap-3">
<div className="flex flex-col gap-1">
<CardTitle className="text-lg font-medium">Sales Overview</CardTitle>
<div className="flex items-center gap-2">
<h3 className="text-3xl font-medium text-card-foreground">$386.53K</h3>
<Badge
className={cn(
"bg-teal-400/10 text-muted-foreground shadow-none"
)}
>
+18%
</Badge>
<span className="text-xs text-muted-foreground">
than last year
</span>
</div>
</div>
<div className="flex items-center gap-3">
{Countries.map((item) => (
<div key={item.id} className="flex items-center gap-2">
<span className={cn("w-2.5 h-2.5 rounded-full", item.color)} />
<p className="text-sm text-muted-foreground">{item.title}</p>
</div>
))}
</div>
</CardHeader>
<CardContent>
<ChartContainer config={chartConfig} className="h-[300px] w-full">
<BarChart accessibilityLayer data={chartData}>
<CartesianGrid vertical={false} strokeDasharray="3 3" stroke="rgba(144, 164, 174, 0.3)" />
<XAxis
dataKey="month"
tickLine={false}
tickMargin={10}
axisLine={false}
tickFormatter={(value) => value.slice(0, 3)}
fontSize={12}
/>
<YAxis
tickLine={false}
axisLine={false}
tickMargin={10}
fontSize={12}
tickFormatter={(value) => `${value / 10}k`}
domain={[0, 100]}
ticks={[0, 50, 100, 150, 200, 250, 300]}
/>
<ChartTooltip content={<ChartTooltipContent />} />
<Bar
dataKey="expense"
stackId="a"
fill="var(--color-expense)"
radius={[0, 0, 4, 4]}
barSize={20}
/>
<Bar
dataKey="profit"
stackId="a"
fill="var(--color-profit)"
radius={[0, 0, 0, 0]}
barSize={20}
/>
<Bar
dataKey="earning"
stackId="a"
fill="var(--color-earning)"
radius={[4, 4, 0, 0]}
barSize={20}
/>
</BarChart>
</ChartContainer>
</CardContent>
</Card>
);
}src/components/shadcn-space/blocks/dashboard-shell-01/nav-main.tsx
"use client"
import { ChevronRight } from "lucide-react";
import { Collapsible, CollapsibleTrigger, CollapsibleContent } from "@/components/ui/collapsible";
import { SidebarGroup, SidebarGroupLabel, SidebarMenu, SidebarMenuButton, SidebarMenuItem, SidebarMenuSub, SidebarMenuSubItem, SidebarMenuSubButton } from "@/components/ui/sidebar";
import { NavItem } from "@/components/shadcn-space/blocks/dashboard-shell-01/app-sidebar";
import { cn } from "@/lib/utils";
import { usePathname } from "next/navigation";
export function NavMain({ items }: { items: NavItem[] }) {
const pathname = usePathname();
// Recursive render function
const renderItem = (item: NavItem) => {
// Section label
if (item.isSection && item.label) {
return (
<SidebarGroup key={item.label} className="p-0 pt-5 first:pt-0">
<SidebarGroupLabel className="p-0 text-xs font-medium uppercase text-sidebar-foreground">
{item.label}
</SidebarGroupLabel>
</SidebarGroup>
);
}
const hasChildren = !!item.children?.length;
// Item with children → collapsible
if (hasChildren && item.title) {
return (
<SidebarGroup key={item.title} className="p-0">
<SidebarMenu>
<Collapsible>
<SidebarMenuItem>
<CollapsibleTrigger
render={
<SidebarMenuButton
tooltip={item.title}
className="rounded-xl text-sm px-3 py-2 h-9"
>
{item.icon && <item.icon size={16} />}
<span>{item.title}</span>
<ChevronRight className="ml-auto transition-transform duration-200 collapsible/button-[aria-expanded='true']:rotate-90" />
</SidebarMenuButton>} className="w-full collapsible/button"
/>
<CollapsibleContent>
<SidebarMenuSub className="me-0 pe-0">
{item.children!.map(renderItemSub)}
</SidebarMenuSub>
</CollapsibleContent>
</SidebarMenuItem>
</Collapsible>
</SidebarMenu>
</SidebarGroup>
);
}
// Item without children
if (item.title) {
return (
<SidebarGroup key={item.title} className="p-0">
<SidebarMenu>
<SidebarMenuItem>
<SidebarMenuButton
tooltip={item.title}
className={cn("rounded-lg text-sm px-3 py-2 h-9 ", pathname === item.href ? "bg-primary hover:bg-primary dark:bg-blue-500 text-white dark:hover:bg-blue-500 hover:text-white" : "")}
>
{item.icon && <item.icon />}
<a href={item.href} className="w-full">
{item.title}
</a>
</SidebarMenuButton>
</SidebarMenuItem>
</SidebarMenu>
</SidebarGroup>
);
}
return null;
};
// Recursive render function for sub-items
const renderItemSub = (item: NavItem) => {
const hasChildren = !!item.children?.length;
if (hasChildren && item.title) {
return (
<SidebarMenuSubItem key={item.title}>
<Collapsible>
<CollapsibleTrigger className="w-full">
<SidebarMenuSubButton className="rounded-xl text-sm px-3 py-2 h-9">
{item.icon && <item.icon />}
<span>{item.title}</span>
<ChevronRight className="ml-auto transition-transform duration-200 data-[state=open]:rotate-90" />
</SidebarMenuSubButton>
</CollapsibleTrigger>
<CollapsibleContent>
<SidebarMenuSub className="me-0 pe-0">
{item.children!.map(renderItemSub)}
</SidebarMenuSub>
</CollapsibleContent>
</Collapsible>
</SidebarMenuSubItem>
);
}
if (item.title) {
return (
<SidebarMenuSubItem key={item.title} className="w-full">
<SidebarMenuSubButton
className="w-full"
render={<a href={item.href}>{item.title}</a>}
/>
</SidebarMenuSubItem>
);
}
return null;
};
return <>{items.map(renderItem)}</>;
}
src/components/shadcn-space/blocks/dashboard-shell-01/earning-report-chart.tsx
"use client";
import * as React from "react"
import { Label, Pie, PieChart } from "recharts"
import { Card, CardHeader, CardTitle, CardContent } from "@/components/ui/card";
import { Badge } from "@/components/ui/badge";
import {
ChartConfig,
ChartContainer,
ChartTooltip,
ChartTooltipContent,
} from "@/components/ui/chart"
import { cn } from "@/lib/utils";
const chartData = [
{ browser: "Website", visitors: 60, fill: "var(--color-blue-500)" },
{ browser: "Marketplace", visitors: 20, fill: "var(--color-sky-400)" },
{ browser: "Affiliate", visitors: 20, fill: "rgba(56, 189, 248, 0.5)" },
]
const chartConfig = {
visitors: {
label: "Visitors",
},
Website: {
label: "Website",
color: "var(--color-blue-500)",
},
Marketplace: {
label: "Marketplace",
color: "var(--color-sky-400)",
},
Affiliate: {
label: "Affiliate",
color: "var(--color-blue-500)",
},
} satisfies ChartConfig
export default function EarningReportChart() {
const totalVisitors = React.useMemo(() => {
return chartData.reduce((acc, curr) => acc + curr.visitors, 0)
}, [])
const CustomerSegmentation = [
{
id: 1,
customer: "Website ",
tagColor: "muted-foreground",
borderColor: "bg-blue-500",
badgeColor: "bg-teal-400/10",
earning: 18356,
growthPercentage: "+4.7%",
},
{
id: 2,
customer: "Marketplace",
tagColor: "muted-foreground",
borderColor: "bg-sky-400",
badgeColor: "bg-teal-400/10",
earning: 4590,
growthPercentage: "+2.1%",
},
{
id: 3,
customer: "Affiliate",
tagColor: "muted-foreground",
borderColor: "bg-sky-400/50",
badgeColor: "bg-teal-400/10",
earning: 4385,
growthPercentage: "-1.7%",
},
];
return (
<Card className="h-full w-full">
<CardHeader>
<CardTitle>
<h4 className="text-lg font-semibold">Earning Reports</h4>
</CardTitle>
</CardHeader>
<CardContent className="flex flex-col justify-between gap-2 flex-1">
<ChartContainer
config={chartConfig}
className="aspect-square max-h-[250px]"
>
<PieChart>
<ChartTooltip
cursor={false}
content={<ChartTooltipContent hideLabel />}
/>
<Pie
data={chartData}
dataKey="visitors"
nameKey="browser"
innerRadius={65}
strokeWidth={50}
>
<Label
content={({ viewBox }) => {
if (viewBox && "cx" in viewBox && "cy" in viewBox) {
return (
<text
x={viewBox.cx}
y={viewBox.cy}
textAnchor="middle"
dominantBaseline="middle"
>
<tspan
x={viewBox.cx}
y={(viewBox.cy || 0) - 10}
className="fill-muted-foreground text-sm"
>
Total
</tspan>
<tspan
x={viewBox.cx}
y={(viewBox.cy || 0) + 15}
className="fill-foreground text-xl font-medium"
>
$27,850
</tspan>
</text>
)
}
}}
/>
</Pie>
</PieChart>
</ChartContainer>
<div className="flex flex-col gap-3">
{CustomerSegmentation.map((item) => (
<div key={item.id} className="flex items-center justify-between">
<div className="flex items-center gap-2">
<div className={cn(item.borderColor, "w-1 h-4 rounded-full")}></div>
<h6 className={cn("text-sm font-medium leading-tight")}>
{item.customer}
</h6>
</div>
<div className="flex items-center gap-1">
<h6 className="text-sm font-medium">${item.earning}</h6>
<Badge
className={cn(
item.badgeColor,
`text-${item.tagColor}`,
"shadow-none"
)}
>
{item.growthPercentage}
</Badge>
</div>
</div>
))}
</div>
</CardContent>
</Card>
);
}
src/components/shadcn-space/blocks/dashboard-shell-01/app-sidebar.tsx
"use client";
import React from "react";
import { Sidebar, SidebarContent, SidebarHeader, SidebarMenu, SidebarMenuItem, SidebarProvider, SidebarTrigger, } from "@/components/ui/sidebar";
import { ScrollArea } from "@/components/ui/scroll-area";
import { Card, CardContent } from "@/components/ui/card";
import { Button } from "@/components/ui/button";
import Logo from "@/assets/logo/logo";
import { NavMain } from "@/components/shadcn-space/blocks/dashboard-shell-01/nav-main";
import { AlignStartVertical, ChartPie, CircleUserRound, ClipboardList, LucideIcon, Notebook, NotepadText, Table, TextInitial, Ticket, } from "lucide-react";
import { SiteHeader } from "@/components/shadcn-space/blocks/dashboard-shell-01/site-header";
import SimpleBar from "simplebar-react";
import 'simplebar-react/dist/simplebar.min.css'
export type NavItem = {
label?: string;
isSection?: boolean;
title?: string;
icon?: LucideIcon;
href?: string;
children?: NavItem[];
};
export const navData: NavItem[] = [
// Dashboards Section
{ label: "Dashboards", isSection: true },
{ title: "Analytics", icon: ChartPie, href: "/block/dashboard-shell-01/preview" },
{ title: "CRM Dashboard", icon: ClipboardList, href: "#" },
// Pages Section
{ label: "Pages", isSection: true },
{ title: "Tables", icon: Table, href: "#" },
{ title: "Forms", icon: ClipboardList, href: "#" },
{ title: "User Profile", icon: CircleUserRound, href: "#" },
// Apps Section
{ label: "Apps", isSection: true },
{ title: "Notes", icon: Notebook, href: "#" },
{ title: "Tickets", icon: Ticket, href: "#" },
{
title: "Blogs",
icon: TextInitial,
children: [
{ title: "Blog Post", href: "#" },
{ title: "Blog Detail", href: "#" },
{ title: "Blog Edit", href: "#" },
{ title: "Blog Create", href: "#" },
{ title: "Manage Blogs", href: "#" },
],
},
// Form Elements Section
{ label: "Form Elements", isSection: true },
{
title: "Shadcn Forms",
icon: NotepadText,
children: [
{ title: "Button", href: "#" },
{ title: "Input", href: "#" },
{ title: "Select", href: "#" },
{ title: "Checkbox", href: "#" },
{ title: "Radio", href: "#" },
],
},
{
title: "Form layouts",
icon: AlignStartVertical,
children: [
{ title: "Forms Horizontal", href: "#" },
{ title: "Forms Vertical", href: "#" },
{ title: "Forms Validation", href: "#" },
{ title: "Forms Examples", href: "#" },
{ title: "Forms Wizard", href: "#" },
],
},
];
/* -------------------------------------------------------------------------- */
/* Page */
/* -------------------------------------------------------------------------- */
const AppSidebar = ({ children }: { children: React.ReactNode }) => {
return (
<SidebarProvider>
<Sidebar className="py-4 px-0 bg-background">
<div className="flex flex-col gap-6 bg-background">
{/* ---------------- Header ---------------- */}
<SidebarHeader className="py-0 px-4">
<SidebarMenu>
<SidebarMenuItem>
<a href="#" className="w-full h-full">
<Logo />
</a>
</SidebarMenuItem>
</SidebarMenu>
</SidebarHeader>
{/* ---------------- Content ---------------- */}
<SidebarContent className="overflow-hidden gap-0">
<SimpleBar autoHide={true} className="h-[calc(100vh-348px)] border-b border-border">
<div className="px-4">
<NavMain items={navData} />
</div>
</SimpleBar>
{/* card */}
<div className="pt-4 px-4">
<Card className="shadow-none ring-0 bg-blue-500/10 px-4 py-6">
<CardContent className="p-0 flex flex-col gap-3 items-center">
<img
src="https://images.shadcnspace.com/assets/backgrounds/download-img.png"
alt="sidebar-img"
width={74}
height={74}
className="h-20 w-20"
/>
<div className="flex flex-col gap-4 items-center">
<div>
<p className="text-base font-semibold text-card-foreground text-center">
Grab Pro Now
</p>
<p className="text-sm font-regular text-muted-foreground text-center">
Customize your admin
</p>
</div>
<Button className="w-fit px-4 py-2 shadow-none cursor-pointer rounded-xl bg-blue-500 font-medium hover:bg-blue-500/80">
Get Premium
</Button>
</div>
</CardContent>
</Card>
</div>
</SidebarContent>
</div>
</Sidebar>
{/* ---------------- Main ---------------- */}
<div className="flex flex-1 flex-col">
<header className="sticky top-0 z-50 flex items-center border-b px-6 py-3 bg-background">
<SiteHeader />
</header>
<main className="flex-1">
{children}
</main>
</div>
</SidebarProvider>
);
};
export default AppSidebar;
More from Shadcnspace Builder
About Us 01 - Impact MetricsblockAccordion-01 - DefaultcomponentAccordion-02 - Multi-levelcomponentAnimated Text-01 - Shiny TextcomponentAnimated Text-02 - Gradient TextcomponentAnimated Text-03 - TypingcomponentAnimated Text-04 - Rolling TextcomponentAnimated Text-05 - Shimmer Textcomponent
Browse all components in this library →