327 lines
13 KiB
TypeScript
327 lines
13 KiB
TypeScript
"use client"
|
||
|
||
import * as React from "react"
|
||
import { CheckCircle2, DollarSign, Package, Plus, ShoppingCart, Users } from "lucide-react"
|
||
|
||
import { cn } from "@/lib/utils"
|
||
import { RecentOrdersTable } from "@/components/dashboard/recent-orders-table"
|
||
import { Button } from "@/components/ui/button"
|
||
import { DatePicker, DateRangePicker } from "@/components/ui/date-picker"
|
||
import {
|
||
Dialog,
|
||
DialogContent,
|
||
DialogDescription,
|
||
DialogHeader,
|
||
DialogTitle,
|
||
DialogTrigger,
|
||
} from "@/components/ui/dialog"
|
||
import {
|
||
AlertDialog,
|
||
AlertDialogContent,
|
||
AlertDialogTrigger,
|
||
} from "@/components/ui/alert-dialog"
|
||
import {
|
||
Breadcrumb,
|
||
BreadcrumbEllipsis,
|
||
BreadcrumbItem,
|
||
BreadcrumbLink,
|
||
BreadcrumbList,
|
||
BreadcrumbPage,
|
||
BreadcrumbSeparator,
|
||
} from "@/components/ui/breadcrumb"
|
||
import { StatCard } from "@/components/ui/stat-card"
|
||
import { toast } from "@/components/ui/toast"
|
||
import LineChart from "@/components/ui/line-chart"
|
||
import BarChart from "@/components/ui/bar-chart"
|
||
import PieChart from "@/components/ui/pie-chart"
|
||
|
||
const indigoButton =
|
||
"rounded-full bg-indigo-50 text-indigo-700 hover:bg-indigo-100 focus-visible:ring-indigo-300/40"
|
||
|
||
export default function DashboardPage() {
|
||
const [date, setDate] = React.useState<Date>()
|
||
const [range, setRange] = React.useState<{ from: Date | undefined; to?: Date | undefined }>()
|
||
return (
|
||
<div className="flex flex-col gap-6">
|
||
<div className="flex flex-col gap-5 rounded-2xl bg-white p-4 shadow-sm ring-1 ring-black/5 sm:p-5">
|
||
<div>
|
||
<p className="mb-2 text-sm font-semibold text-slate-500">Breadcrumb</p>
|
||
<div className="flex flex-col gap-3">
|
||
{/* Basic */}
|
||
<Breadcrumb>
|
||
<BreadcrumbList>
|
||
<BreadcrumbItem>
|
||
<BreadcrumbLink href="/dashboard">Home</BreadcrumbLink>
|
||
</BreadcrumbItem>
|
||
<BreadcrumbSeparator />
|
||
<BreadcrumbItem>
|
||
<BreadcrumbLink href="/dashboard/products">Products</BreadcrumbLink>
|
||
</BreadcrumbItem>
|
||
<BreadcrumbSeparator />
|
||
<BreadcrumbItem>
|
||
<BreadcrumbPage>Product Detail</BreadcrumbPage>
|
||
</BreadcrumbItem>
|
||
</BreadcrumbList>
|
||
</Breadcrumb>
|
||
|
||
{/* With ellipsis */}
|
||
<Breadcrumb>
|
||
<BreadcrumbList>
|
||
<BreadcrumbItem>
|
||
<BreadcrumbLink href="/dashboard">Home</BreadcrumbLink>
|
||
</BreadcrumbItem>
|
||
<BreadcrumbSeparator />
|
||
<BreadcrumbItem>
|
||
<BreadcrumbEllipsis />
|
||
</BreadcrumbItem>
|
||
<BreadcrumbSeparator />
|
||
<BreadcrumbItem>
|
||
<BreadcrumbLink href="/dashboard/products">Products</BreadcrumbLink>
|
||
</BreadcrumbItem>
|
||
<BreadcrumbSeparator />
|
||
<BreadcrumbItem>
|
||
<BreadcrumbPage>Edit</BreadcrumbPage>
|
||
</BreadcrumbItem>
|
||
</BreadcrumbList>
|
||
</Breadcrumb>
|
||
</div>
|
||
</div>
|
||
|
||
<div>
|
||
<p className="mb-2 text-sm font-semibold text-slate-500">Variants</p>
|
||
<div className="flex flex-wrap gap-2">
|
||
<Button variant="default">Default</Button>
|
||
<Button variant="outline">Outline</Button>
|
||
<Button variant="secondary">Secondary</Button>
|
||
<Button variant="ghost">Ghost</Button>
|
||
<Button variant="destructive">Destructive</Button>
|
||
<Button variant="success">Success</Button>
|
||
<Button variant="warning">Warning</Button>
|
||
<Button variant="info">Info</Button>
|
||
<Button variant="link">Link</Button>
|
||
</div>
|
||
</div>
|
||
|
||
<div>
|
||
<p className="mb-2 text-sm font-semibold text-slate-500">Sizes</p>
|
||
<div className="flex flex-wrap items-center gap-3">
|
||
<Button size="xs" className={indigoButton}>
|
||
Extra Small
|
||
</Button>
|
||
<Button size="sm" className={indigoButton}>
|
||
Small
|
||
</Button>
|
||
<Button size="default" className={indigoButton}>
|
||
Default
|
||
</Button>
|
||
<Button size="lg" className={indigoButton}>
|
||
Large
|
||
</Button>
|
||
<Button size="lg" className={cn(indigoButton, "h-13 px-8 text-base")}>
|
||
Extra Large
|
||
</Button>
|
||
<Button size="icon-sm" className={indigoButton} aria-label="Add (small)">
|
||
<Plus />
|
||
</Button>
|
||
<Button size="icon" className={indigoButton} aria-label="Add">
|
||
<Plus />
|
||
</Button>
|
||
<Button size="icon-lg" className={indigoButton} aria-label="Add (large)">
|
||
<Plus />
|
||
</Button>
|
||
<Button
|
||
size="icon-lg"
|
||
className={cn(indigoButton, "size-13")}
|
||
aria-label="Add (extra large)"
|
||
>
|
||
<Plus className="size-6" />
|
||
</Button>
|
||
</div>
|
||
</div>
|
||
|
||
<div>
|
||
<p className="mb-2 text-sm font-semibold text-slate-500">Date Picker</p>
|
||
<div className="flex flex-col gap-3 sm:flex-row sm:flex-wrap sm:items-center">
|
||
<div className="flex flex-col gap-1">
|
||
<span className="text-xs text-slate-400">Single date</span>
|
||
<DatePicker
|
||
value={date}
|
||
onChange={setDate}
|
||
placeholder="Select a date"
|
||
/>
|
||
</div>
|
||
<div className="flex flex-col gap-1">
|
||
<span className="text-xs text-slate-400">Date range</span>
|
||
<DateRangePicker
|
||
value={range}
|
||
onChange={setRange}
|
||
placeholder="Select date range"
|
||
/>
|
||
</div>
|
||
</div>
|
||
{(date || range?.from) && (
|
||
<p className="mt-2 text-xs text-slate-500">
|
||
{date && <>Selected: <span className="font-medium text-slate-700">{date.toLocaleDateString()}</span></>}
|
||
{range?.from && (
|
||
<>
|
||
{date && " · "}
|
||
Range: <span className="font-medium text-slate-700">{range.from.toLocaleDateString()}</span>
|
||
{range.to && <> – <span className="font-medium text-slate-700">{range.to.toLocaleDateString()}</span></>}
|
||
</>
|
||
)}
|
||
</p>
|
||
)}
|
||
</div>
|
||
|
||
<div>
|
||
<p className="mb-2 text-sm font-semibold text-slate-500">Modal</p>
|
||
<Dialog>
|
||
<DialogTrigger render={<Button variant="outline">Open Modal</Button>} />
|
||
<DialogContent className="w-[calc(100%-2rem)] sm:max-w-md">
|
||
<DialogHeader className="items-center gap-3 px-2 pt-4 text-center sm:px-4">
|
||
<div className="flex size-14 items-center justify-center rounded-full bg-emerald-50">
|
||
<CheckCircle2 className="size-7 text-emerald-600" />
|
||
</div>
|
||
<DialogTitle className="text-lg font-bold">Order confirmed</DialogTitle>
|
||
<DialogDescription>
|
||
Your order has been placed successfully and is now being processed.
|
||
</DialogDescription>
|
||
</DialogHeader>
|
||
|
||
<div className="flex justify-center px-2 pb-2 sm:px-4">
|
||
<Button size="lg" className="w-full rounded-full sm:w-auto sm:px-10">
|
||
Continue
|
||
</Button>
|
||
</div>
|
||
</DialogContent>
|
||
</Dialog>
|
||
</div>
|
||
|
||
<div>
|
||
<p className="mb-2 text-sm font-semibold text-slate-500">Toast</p>
|
||
<div className="flex flex-wrap gap-2">
|
||
<Button variant="outline" onClick={() => toast({ title: "Default toast message" })}>
|
||
Default
|
||
</Button>
|
||
<Button variant="success" onClick={() => toast.success("Success!", "Your changes have been saved.")}>
|
||
Success
|
||
</Button>
|
||
<Button variant="destructive" onClick={() => toast.error("Error", "Something went wrong. Please try again.")}>
|
||
Error
|
||
</Button>
|
||
<Button variant="warning" onClick={() => toast.warning("Warning", "This action cannot be undone.")}>
|
||
Warning
|
||
</Button>
|
||
<Button variant="info" onClick={() => toast.info("Info", "Your session will expire in 5 minutes.")}>
|
||
Info
|
||
</Button>
|
||
<Button
|
||
variant="secondary"
|
||
onClick={() =>
|
||
toast({
|
||
title: "With Action",
|
||
description: "Do you want to undo this change?",
|
||
actionLabel: "Undo",
|
||
onAction: () => toast.success("Undone!", "Change has been reverted."),
|
||
})
|
||
}
|
||
>
|
||
With Action
|
||
</Button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div className="flex flex-col gap-5 rounded-2xl bg-white p-4 shadow-sm ring-1 ring-black/5 sm:p-5">
|
||
<div>
|
||
<p className="mb-2 text-sm font-semibold text-slate-500">Alert Dialogs</p>
|
||
<div className="flex flex-wrap gap-2">
|
||
<AlertDialog>
|
||
<AlertDialogTrigger render={<Button variant="info">Info</Button>} />
|
||
<AlertDialogContent
|
||
variant="info"
|
||
title="Update available"
|
||
description="A new version is ready. Reload the page to apply the latest changes."
|
||
confirmLabel="Reload"
|
||
onConfirm={() => toast.info("Reloading...", "Applying the latest update.")}
|
||
/>
|
||
</AlertDialog>
|
||
|
||
<AlertDialog>
|
||
<AlertDialogTrigger render={<Button variant="success">Success</Button>} />
|
||
<AlertDialogContent
|
||
variant="success"
|
||
title="Order confirmed"
|
||
description="Your order has been placed successfully and is now being processed."
|
||
confirmLabel="Continue"
|
||
cancelLabel="View order"
|
||
onConfirm={() => toast.success("Done!", "Redirecting to dashboard.")}
|
||
/>
|
||
</AlertDialog>
|
||
|
||
<AlertDialog>
|
||
<AlertDialogTrigger render={<Button variant="warning">Warning</Button>} />
|
||
<AlertDialogContent
|
||
variant="warning"
|
||
title="Unsaved changes"
|
||
description="You have unsaved changes. Leaving this page will discard them."
|
||
confirmLabel="Leave anyway"
|
||
onConfirm={() => toast.warning("Changes discarded")}
|
||
/>
|
||
</AlertDialog>
|
||
|
||
<AlertDialog>
|
||
<AlertDialogTrigger render={<Button variant="destructive">Delete</Button>} />
|
||
<AlertDialogContent
|
||
variant="destructive"
|
||
title="Delete record?"
|
||
description="This action is permanent and cannot be undone. All associated data will be removed."
|
||
confirmLabel="Delete"
|
||
onConfirm={() => toast.error("Deleted", "The record has been permanently removed.")}
|
||
/>
|
||
</AlertDialog>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div className="grid grid-cols-1 gap-4 sm:grid-cols-2 xl:grid-cols-4">
|
||
<StatCard label="Total Revenue" value={45231.89} icon={DollarSign} />
|
||
<StatCard label="Orders" value={1284} icon={ShoppingCart} />
|
||
<StatCard label="Customers" value={892} icon={Users} />
|
||
<StatCard label="Low Stock Items" value={16} icon={Package} />
|
||
</div>
|
||
|
||
<RecentOrdersTable />
|
||
|
||
<div className="mt-6 grid grid-cols-1 gap-4 lg:grid-cols-3">
|
||
<div className="rounded-2xl bg-white p-4 shadow-sm ring-1 ring-black/5">
|
||
<h3 className="mb-2 text-sm font-semibold text-slate-600">Sales (Line)</h3>
|
||
<div className="h-56">
|
||
<LineChart
|
||
labels={["Jan", "Feb", "Mar", "Apr", "May", "Jun"]}
|
||
datasets={[{ label: "Sales", data: [120, 200, 150, 220, 180, 260] }]}
|
||
/>
|
||
</div>
|
||
</div>
|
||
|
||
<div className="rounded-2xl bg-white p-4 shadow-sm ring-1 ring-black/5">
|
||
<h3 className="mb-2 text-sm font-semibold text-slate-600">Revenue (Bar)</h3>
|
||
<div className="h-56">
|
||
<BarChart
|
||
labels={["Q1", "Q2", "Q3", "Q4"]}
|
||
datasets={[{ label: "Revenue", data: [30000, 42000, 36000, 48000], backgroundColor: "#60a5fa" }]}
|
||
/>
|
||
</div>
|
||
</div>
|
||
|
||
<div className="rounded-2xl bg-white p-4 shadow-sm ring-1 ring-black/5">
|
||
<h3 className="mb-2 text-sm font-semibold text-slate-600">Product Mix (Pie)</h3>
|
||
<div className="h-56">
|
||
<PieChart labels={["A","B","C"]} data={[45, 30, 25]} />
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
)
|
||
}
|