├── .eslintrc
├── .gitignore
├── .prettierrc
├── README.md
├── app
├── (guest)
│ ├── layout.tsx
│ ├── login
│ │ └── page.tsx
│ ├── pages
│ │ └── error
│ │ │ ├── 404
│ │ │ └── page.tsx
│ │ │ └── 500
│ │ │ └── page.tsx
│ └── register
│ │ └── page.tsx
├── dashboard
│ ├── default
│ │ ├── cards
│ │ │ ├── chat.tsx
│ │ │ ├── metric.tsx
│ │ │ ├── payment-method.tsx
│ │ │ ├── payment.tsx
│ │ │ ├── subscriptions.tsx
│ │ │ ├── theme-members.tsx
│ │ │ └── total-revenue.tsx
│ │ └── page.tsx
│ ├── error.tsx
│ ├── layout.tsx
│ └── pages
│ │ ├── settings
│ │ ├── layout.tsx
│ │ ├── page.tsx
│ │ ├── profile-form.tsx
│ │ └── sidebar-nav.tsx
│ │ └── users
│ │ ├── data-table.tsx
│ │ ├── data.json
│ │ └── page.tsx
├── favicon.ico
├── globals.scss
└── layout.tsx
├── components.json
├── components
├── anchor.tsx
├── card-action-menus.tsx
├── date-range-picker.tsx
├── icon.tsx
├── icons.tsx
├── layout
│ ├── header.tsx
│ ├── logo.tsx
│ ├── search.tsx
│ └── sidebar.tsx
├── main-layout.tsx
├── providers.tsx
├── theme-provider.tsx
├── ui
│ ├── accordion.tsx
│ ├── avatar.tsx
│ ├── badge.tsx
│ ├── breadcrumb.tsx
│ ├── button.tsx
│ ├── calendar.tsx
│ ├── card.tsx
│ ├── chart.tsx
│ ├── checkbox.tsx
│ ├── collapsible.tsx
│ ├── command.tsx
│ ├── dialog.tsx
│ ├── dropdown-menu.tsx
│ ├── form.tsx
│ ├── input.tsx
│ ├── label.tsx
│ ├── pagination.tsx
│ ├── popover.tsx
│ ├── progress.tsx
│ ├── radio-group.tsx
│ ├── scroll-area.tsx
│ ├── select.tsx
│ ├── separator.tsx
│ ├── sheet.tsx
│ ├── switch.tsx
│ ├── table.tsx
│ ├── tabs.tsx
│ ├── textarea.tsx
│ └── tooltip.tsx
└── user-avatar.tsx
├── lib
├── routes-config.tsx
└── utils.ts
├── middleware.ts
├── next.config.mjs
├── package-lock.json
├── package.json
├── postcss.config.mjs
├── public
├── github.png
├── images
│ ├── 404.svg
│ ├── 500.svg
│ ├── avatars
│ │ ├── 1.png
│ │ ├── 10.png
│ │ ├── 11.png
│ │ ├── 12.png
│ │ ├── 2.png
│ │ ├── 3.png
│ │ ├── 4.png
│ │ ├── 5.png
│ │ ├── 6.png
│ │ ├── 7.png
│ │ ├── 8.png
│ │ └── 9.png
│ └── cover.png
├── logo.png
├── preview.png
└── seo.jpg
├── tailwind.config.ts
└── tsconfig.json
/.eslintrc:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "next/core-web-vitals",
3 | }
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
2 |
3 | # dependencies
4 | /node_modules
5 | /.pnp
6 | .pnp.js
7 | .yarn/install-state.gz
8 |
9 | # testing
10 | /coverage
11 |
12 | # next.js
13 | /.next/
14 | /out/
15 |
16 | # production
17 | /build
18 |
19 | # misc
20 | .DS_Store
21 | *.pem
22 |
23 | # debug
24 | npm-debug.log*
25 | yarn-debug.log*
26 | yarn-error.log*
27 |
28 | # local env files
29 | .env*.local
30 |
31 | # vercel
32 | .vercel
33 |
34 | # typescript
35 | *.tsbuildinfo
36 | next-env.d.ts
37 |
--------------------------------------------------------------------------------
/.prettierrc:
--------------------------------------------------------------------------------
1 | {
2 | "semi": true,
3 | "tabWidth": 2,
4 | "printWidth": 100,
5 | "singleQuote": false,
6 | "trailingComma": "none",
7 | "jsxBracketSameLine": true,
8 | "plugins": ["prettier-plugin-tailwindcss"]
9 | }
10 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
Shadcn UI Kit
10 |
11 |
12 | Shadcn UI Kit is a comprehensive collection of ready-to-use admin dashboards, website templates, and customizable components.
13 |
14 |
15 | Home Page
16 | •
17 | Dashboards
18 | •
19 | Templates
20 | •
21 | Free
22 |
23 |
24 |
25 |
26 | ## 💎 About Shadcn UI Kit
27 |
28 | **Shadcn UI Kit** is a comprehensive and versatile collection of ready-to-use admin dashboards, website templates, and fully customizable components designed for modern web applications. It goes beyond standard UI libraries by offering enhanced functionality, greater design flexibility, and a seamless user experience. Whether you're building complex admin panels or sleek landing pages, Shadcn UI Kit provides the tools you need to create visually appealing and highly functional interfaces with ease.
29 |
30 |
31 |
32 | ## 🪄 Get Lifetime Access (PRO)
33 |
34 | Get lifetime use of the premium version of Shadcn UI Kit with hundreds of UI components, dashboards, website templates and pre-built pages. Free updates, newly added components and templates are also included.
35 |
36 | | Free Version | [Shadcn UI Kit PRO](https://shadcnuikit.com/pricing) |
37 | | -------------- | ---------------------------------------------------- |
38 | | 1 Dashboard | ✔ 10 Dashboards |
39 | | 5+ Pages | ✔ 50+ Pages |
40 | | 1 Color Scheme | ✔ 10+ Web Apps |
41 | | | ✔ 100+ Premium Components |
42 | | | ✔ Premium Templates |
43 | | | ✔ 5+ Color Schemes |
44 | | | ✔ Theme Customization |
45 | | | ✔ Dark/Light Mode 🌙 |
46 | | | ✔ LTR/RTL Support |
47 | | | ✔ New Sidebar |
48 | | | ✔ Multiple Layouts |
49 | | | ✔ and more.. |
50 |
51 | ✅ [Click here](https://shadcnuikit.com/pricing) to get the Shadcn UI Kit and review it in detail
52 |
53 | ## ✉️ Contact
54 |
55 | Toby Belhome - [@TobyBelhome](https://x.com/TobyBelhome)
56 |
57 | (back to top)
58 |
--------------------------------------------------------------------------------
/app/(guest)/layout.tsx:
--------------------------------------------------------------------------------
1 | import Providers from "@/components/providers";
2 |
3 | export default function GuestLayout({
4 | children
5 | }: Readonly<{
6 | children: React.ReactNode;
7 | }>) {
8 | return {children};
9 | }
10 |
--------------------------------------------------------------------------------
/app/(guest)/login/page.tsx:
--------------------------------------------------------------------------------
1 | import { Button } from "@/components/ui/button";
2 | import { Input } from "@/components/ui/input";
3 | import { Label } from "@/components/ui/label";
4 | import { generateMeta } from "@/lib/utils";
5 | import { GithubIcon } from "lucide-react";
6 | import Link from "next/link";
7 |
8 | export async function generateMetadata() {
9 | return generateMeta({
10 | title: "Login Page - Shadcn UI Kit Free",
11 | description:
12 | "A login form with email and password. There's an option to login with Google and a link to sign up if you don't have an account.",
13 | canonical: "/login"
14 | });
15 | }
16 |
17 | export default function LoginPageV1() {
18 | return (
19 |
20 |
21 |

22 |
23 |
24 |
25 |
26 |
27 |
Welcome back
28 |
Please sign in to your account
29 |
30 |
31 |
74 |
75 |
76 |
77 |
80 |
81 | or continue with
82 |
83 |
84 |
85 |
86 |
107 |
111 |
112 |
113 |
114 | Don't have an account?{" "}
115 |
116 | Sign up
117 |
118 |
119 |
120 |
121 |
122 |
123 | );
124 | }
125 |
--------------------------------------------------------------------------------
/app/(guest)/pages/error/404/page.tsx:
--------------------------------------------------------------------------------
1 | import { Button } from "@/components/ui/button";
2 | import { generateMeta } from "@/lib/utils";
3 | import { ArrowRight } from "lucide-react";
4 |
5 | export async function generateMetadata() {
6 | return generateMeta({
7 | title: "404 Page - Shadcn UI Kit Free",
8 | description:
9 | "This is an example of a template for 404 error pages. Built with technologies like Tailwind CSS, Next.js, React and Shadcn.",
10 | canonical: "/pages/error/404"
11 | });
12 | }
13 |
14 | export default function Error404() {
15 | return (
16 |
17 |
18 |
404
19 |
20 | Page not found
21 |
22 |
23 | Sorry, we couldn’t find the page you’re looking for.
24 |
25 |
26 |
27 |
30 |
31 |
32 |
33 |
34 |

39 |
40 |
41 | );
42 | }
43 |
--------------------------------------------------------------------------------
/app/(guest)/pages/error/500/page.tsx:
--------------------------------------------------------------------------------
1 | import { generateMeta } from "@/lib/utils";
2 |
3 | export async function generateMetadata() {
4 | return generateMeta({
5 | title: "500 Page - Shadcn UI Kit Free",
6 | description:
7 | "This is an example of a template for 500 error pages. Built with technologies like Tailwind CSS, Next.js, React and Shadcn.",
8 | canonical: "/pages/error/404"
9 | });
10 | }
11 |
12 | export default function Error404() {
13 | return (
14 |
15 |
16 |
500
17 |
18 | Server Error
19 |
20 |
21 | There seems to be a connection problem between the server and the website.
22 |
23 |
24 |
25 |
26 |

31 |
32 |
33 | );
34 | }
35 |
--------------------------------------------------------------------------------
/app/(guest)/register/page.tsx:
--------------------------------------------------------------------------------
1 | import { Button } from "@/components/ui/button";
2 | import { Input } from "@/components/ui/input";
3 | import { Label } from "@/components/ui/label";
4 | import { generateMeta } from "@/lib/utils";
5 | import { GithubIcon } from "lucide-react";
6 |
7 | export async function generateMetadata() {
8 | return generateMeta({
9 | title: "Register Page - Shadcn UI Kit Free",
10 | description:
11 | "A login form with email and password. There's an option to login with Google and a link to sign up if you don't have an account.",
12 | canonical: "/register"
13 | });
14 | }
15 |
16 | export default function LoginPageV1() {
17 | return (
18 |
19 |
20 |

21 |
22 |
23 |
24 |
25 |
26 |
Register
27 |
28 | Create a new account to access the dashboard.
29 |
30 |
31 |
32 |
83 |
84 |
85 |
86 |
89 |
90 | or continue with
91 |
92 |
93 |
94 |
95 |
116 |
120 |
121 |
122 |
123 | Already have an account?{" "}
124 |
125 | Log in
126 |
127 |
128 |
129 |
130 |
131 |
132 | );
133 | }
134 |
--------------------------------------------------------------------------------
/app/dashboard/default/cards/chat.tsx:
--------------------------------------------------------------------------------
1 | "use client";
2 |
3 | import * as React from "react";
4 | import { Check, Plus, Send } from "lucide-react";
5 |
6 | import { cn } from "@/lib/utils";
7 | import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar";
8 | import { Button } from "@/components/ui/button";
9 | import {
10 | Card,
11 | CardContent,
12 | CardFooter,
13 | CardHeader,
14 | } from "@/components/ui/card";
15 | import {
16 | Command,
17 | CommandEmpty,
18 | CommandGroup,
19 | CommandInput,
20 | CommandItem,
21 | CommandList,
22 | } from "@/components/ui/command";
23 | import {
24 | Dialog,
25 | DialogContent,
26 | DialogDescription,
27 | DialogFooter,
28 | DialogHeader,
29 | DialogTitle,
30 | } from "@/components/ui/dialog";
31 | import { Input } from "@/components/ui/input";
32 | import {
33 | Tooltip,
34 | TooltipContent,
35 | TooltipProvider,
36 | TooltipTrigger,
37 | } from "@/components/ui/tooltip";
38 |
39 | const users = [
40 | {
41 | name: "Olivia Martin",
42 | email: "m@example.com",
43 | avatar: "/avatars/01.png",
44 | },
45 | {
46 | name: "Isabella Nguyen",
47 | email: "isabella.nguyen@email.com",
48 | avatar: "/avatars/03.png",
49 | },
50 | {
51 | name: "Emma Wilson",
52 | email: "emma@example.com",
53 | avatar: "/avatars/05.png",
54 | },
55 | {
56 | name: "Jackson Lee",
57 | email: "lee@example.com",
58 | avatar: "/avatars/02.png",
59 | },
60 | {
61 | name: "William Kim",
62 | email: "will@email.com",
63 | avatar: "/avatars/04.png",
64 | },
65 | ] as const;
66 |
67 | type User = (typeof users)[number];
68 |
69 | export function ChatCard() {
70 | const [open, setOpen] = React.useState(false);
71 | const [selectedUsers, setSelectedUsers] = React.useState([]);
72 |
73 | const [messages, setMessages] = React.useState([
74 | {
75 | role: "agent",
76 | content: "Hi, how can I help you today?",
77 | },
78 | {
79 | role: "user",
80 | content: "Hey, I'm having trouble with my account.",
81 | },
82 | {
83 | role: "agent",
84 | content: "What seems to be the problem?",
85 | },
86 | {
87 | role: "user",
88 | content: "I can't log in.",
89 | },
90 | ]);
91 | const [input, setInput] = React.useState("");
92 | const inputLength = input.trim().length;
93 |
94 | return (
95 | <>
96 |
97 |
98 |
99 |
100 |
101 | OM
102 |
103 |
104 |
Sofia Davis
105 |
m@example.com
106 |
107 |
108 |
109 |
110 |
111 |
120 |
121 | New message
122 |
123 |
124 |
125 |
126 |
127 | {messages.map((message, index) => (
128 |
137 | {message.content}
138 |
139 | ))}
140 |
141 |
142 |
143 |
171 |
172 |
173 |
256 | >
257 | );
258 | }
259 |
--------------------------------------------------------------------------------
/app/dashboard/default/cards/metric.tsx:
--------------------------------------------------------------------------------
1 | "use client";
2 |
3 | import { Line, LineChart, ResponsiveContainer, Tooltip } from "recharts";
4 |
5 | import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card";
6 | import { ExportButton } from "@/components/card-action-menus";
7 |
8 | const data = [
9 | {
10 | average: 400,
11 | today: 240
12 | },
13 | {
14 | average: 300,
15 | today: 139
16 | },
17 | {
18 | average: 200,
19 | today: 980
20 | },
21 | {
22 | average: 278,
23 | today: 390
24 | },
25 | {
26 | average: 189,
27 | today: 480
28 | },
29 | {
30 | average: 239,
31 | today: 380
32 | },
33 | {
34 | average: 349,
35 | today: 430
36 | }
37 | ];
38 |
39 | export default function MetricCard({ className }: { className?: string }) {
40 | return (
41 |
42 |
43 |
44 |
45 | Exercise Minutes
46 |
47 | Your exercise minutes are ahead of where you normally are.
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
64 | {
66 | if (active && payload && payload.length) {
67 | return (
68 |
69 |
70 |
71 |
72 | Average
73 |
74 |
75 | {payload[0].value}
76 |
77 |
78 |
79 |
80 | Today
81 |
82 | {payload[1].value}
83 |
84 |
85 |
86 | );
87 | }
88 |
89 | return null;
90 | }}
91 | />
92 |
108 |
123 |
124 |
125 |
126 |
127 |
128 | );
129 | }
130 |
--------------------------------------------------------------------------------
/app/dashboard/default/cards/payment-method.tsx:
--------------------------------------------------------------------------------
1 | "use client";
2 |
3 | import { Icons } from "@/components/icons";
4 | import { Button } from "@/components/ui/button";
5 | import {
6 | Card,
7 | CardContent,
8 | CardDescription,
9 | CardFooter,
10 | CardHeader,
11 | CardTitle,
12 | } from "@/components/ui/card";
13 | import { Input } from "@/components/ui/input";
14 | import { Label } from "@/components/ui/label";
15 | import { RadioGroup, RadioGroupItem } from "@/components/ui/radio-group";
16 | import {
17 | Select,
18 | SelectContent,
19 | SelectItem,
20 | SelectTrigger,
21 | SelectValue,
22 | } from "@/components/ui/select";
23 |
24 | export function PaymentMethodCard() {
25 | return (
26 |
27 |
28 | Payment Method
29 |
30 | Add a new payment method to your account.
31 |
32 |
33 |
34 |
35 |
36 |
42 |
61 |
62 |
63 |
64 |
70 |
77 |
78 |
79 |
80 |
86 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 |
107 |
108 |
109 |
110 |
129 |
130 |
131 |
132 |
144 |
145 |
146 |
147 |
148 |
149 |
150 |
151 |
152 |
153 |
154 |
155 | );
156 | }
157 |
--------------------------------------------------------------------------------
/app/dashboard/default/cards/payment.tsx:
--------------------------------------------------------------------------------
1 | "use client";
2 |
3 | import * as React from "react";
4 |
5 | import {
6 | ColumnDef,
7 | ColumnFiltersState,
8 | SortingState,
9 | VisibilityState,
10 | flexRender,
11 | getCoreRowModel,
12 | getFilteredRowModel,
13 | getPaginationRowModel,
14 | getSortedRowModel,
15 | useReactTable
16 | } from "@tanstack/react-table";
17 |
18 | import { Button } from "@/components/ui/button";
19 | import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card";
20 | import { Checkbox } from "@/components/ui/checkbox";
21 | import {
22 | DropdownMenu,
23 | DropdownMenuCheckboxItem,
24 | DropdownMenuContent,
25 | DropdownMenuItem,
26 | DropdownMenuLabel,
27 | DropdownMenuSeparator,
28 | DropdownMenuTrigger
29 | } from "@/components/ui/dropdown-menu";
30 | import { Input } from "@/components/ui/input";
31 | import {
32 | Table,
33 | TableBody,
34 | TableCell,
35 | TableHead,
36 | TableHeader,
37 | TableRow
38 | } from "@/components/ui/table";
39 | import { ChevronDownIcon, ChevronsUpDown, Ellipsis } from "lucide-react";
40 | import { CardOptionsMenu } from "@/components/card-action-menus";
41 |
42 | const data: Payment[] = [
43 | {
44 | id: "m5gr84i9",
45 | amount: 316,
46 | status: "success",
47 | email: "ken99@yahoo.com"
48 | },
49 | {
50 | id: "3u1reuv4",
51 | amount: 242,
52 | status: "success",
53 | email: "Abe45@gmail.com"
54 | },
55 | {
56 | id: "derv1ws0",
57 | amount: 837,
58 | status: "processing",
59 | email: "Monserrat44@gmail.com"
60 | },
61 | {
62 | id: "5kma53ae",
63 | amount: 874,
64 | status: "success",
65 | email: "Silas22@gmail.com"
66 | },
67 | {
68 | id: "bhqecj4p",
69 | amount: 721,
70 | status: "failed",
71 | email: "carmella@hotmail.com"
72 | }
73 | ];
74 |
75 | export type Payment = {
76 | id: string;
77 | amount: number;
78 | status: "pending" | "processing" | "success" | "failed";
79 | email: string;
80 | };
81 |
82 | export const columns: ColumnDef[] = [
83 | {
84 | id: "select",
85 | header: ({ table }) => (
86 | table.toggleAllPageRowsSelected(!!value)}
91 | aria-label="Select all"
92 | />
93 | ),
94 | cell: ({ row }) => (
95 | row.toggleSelected(!!value)}
98 | aria-label="Select row"
99 | />
100 | ),
101 | enableSorting: false,
102 | enableHiding: false
103 | },
104 | {
105 | accessorKey: "status",
106 | header: "Status",
107 | cell: ({ row }) => {row.getValue("status")}
108 | },
109 | {
110 | accessorKey: "email",
111 | header: ({ column }) => {
112 | return (
113 |
119 | );
120 | },
121 | cell: ({ row }) => {row.getValue("email")}
122 | },
123 | {
124 | accessorKey: "amount",
125 | header: () => Amount
,
126 | cell: ({ row }) => {
127 | const amount = parseFloat(row.getValue("amount"));
128 |
129 | // Format the amount as a dollar amount
130 | const formatted = new Intl.NumberFormat("en-US", {
131 | style: "currency",
132 | currency: "USD"
133 | }).format(amount);
134 |
135 | return {formatted}
;
136 | }
137 | },
138 | {
139 | id: "actions",
140 | enableHiding: false,
141 | cell: ({ row }) => {
142 | const payment = row.original;
143 |
144 | return (
145 |
146 |
147 |
151 |
152 |
153 | Actions
154 | navigator.clipboard.writeText(payment.id)}>
155 | Copy payment ID
156 |
157 |
158 | View customer
159 | View payment details
160 |
161 |
162 | );
163 | }
164 | }
165 | ];
166 |
167 | export function PaymentsCard({ className }: { className?: string }) {
168 | const [sorting, setSorting] = React.useState([]);
169 | const [columnFilters, setColumnFilters] = React.useState([]);
170 | const [columnVisibility, setColumnVisibility] = React.useState({});
171 | const [rowSelection, setRowSelection] = React.useState({});
172 |
173 | const table = useReactTable({
174 | data,
175 | columns,
176 | onSortingChange: setSorting,
177 | onColumnFiltersChange: setColumnFilters,
178 | getCoreRowModel: getCoreRowModel(),
179 | getPaginationRowModel: getPaginationRowModel(),
180 | getSortedRowModel: getSortedRowModel(),
181 | getFilteredRowModel: getFilteredRowModel(),
182 | onColumnVisibilityChange: setColumnVisibility,
183 | onRowSelectionChange: setRowSelection,
184 | state: {
185 | sorting,
186 | columnFilters,
187 | columnVisibility,
188 | rowSelection
189 | }
190 | });
191 |
192 | return (
193 |
194 |
195 |
196 | Payments
197 | Manage your payments.
198 |
199 |
200 |
201 |
202 |
203 | table.getColumn("email")?.setFilterValue(event.target.value)}
207 | className="max-w-sm"
208 | />
209 |
210 |
211 |
214 |
215 |
216 | {table
217 | .getAllColumns()
218 | .filter((column) => column.getCanHide())
219 | .map((column) => {
220 | return (
221 | column.toggleVisibility(!!value)}>
226 | {column.id}
227 |
228 | );
229 | })}
230 |
231 |
232 |
233 |
234 |
235 |
236 | {table.getHeaderGroups().map((headerGroup) => (
237 |
238 | {headerGroup.headers.map((header) => {
239 | return (
240 |
241 | {header.isPlaceholder
242 | ? null
243 | : flexRender(header.column.columnDef.header, header.getContext())}
244 |
245 | );
246 | })}
247 |
248 | ))}
249 |
250 |
251 | {table.getRowModel().rows?.length ? (
252 | table.getRowModel().rows.map((row) => (
253 |
254 | {row.getVisibleCells().map((cell) => (
255 |
256 | {flexRender(cell.column.columnDef.cell, cell.getContext())}
257 |
258 | ))}
259 |
260 | ))
261 | ) : (
262 |
263 |
264 | No results.
265 |
266 |
267 | )}
268 |
269 |
270 |
271 |
272 |
273 | {table.getFilteredSelectedRowModel().rows.length} of{" "}
274 | {table.getFilteredRowModel().rows.length} row(s) selected.
275 |
276 |
277 |
284 |
291 |
292 |
293 |
294 |
295 | );
296 | }
297 |
--------------------------------------------------------------------------------
/app/dashboard/default/cards/subscriptions.tsx:
--------------------------------------------------------------------------------
1 | "use client";
2 |
3 | import { Bar, BarChart, Line, LineChart, ResponsiveContainer } from "recharts";
4 |
5 | import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
6 |
7 | const data = [
8 | {
9 | revenue: 10400,
10 | subscription: 240,
11 | },
12 | {
13 | revenue: 14405,
14 | subscription: 300,
15 | },
16 | {
17 | revenue: 9400,
18 | subscription: 200,
19 | },
20 | {
21 | revenue: 8200,
22 | subscription: 278,
23 | },
24 | {
25 | revenue: 7000,
26 | subscription: 189,
27 | },
28 | {
29 | revenue: 9600,
30 | subscription: 239,
31 | },
32 | {
33 | revenue: 11244,
34 | subscription: 278,
35 | },
36 | {
37 | revenue: 26475,
38 | subscription: 189,
39 | },
40 | ];
41 |
42 | export default function SubscriptionsCard() {
43 | return (
44 |
45 |
46 | Subscriptions
47 |
48 |
49 | +2350
50 | +180.1% from last month
51 |
52 |
53 |
54 |
64 |
65 |
66 |
67 |
68 |
69 | );
70 | }
71 |
--------------------------------------------------------------------------------
/app/dashboard/default/cards/theme-members.tsx:
--------------------------------------------------------------------------------
1 | import { ChevronDownIcon, ChevronsDownIcon } from "lucide-react";
2 |
3 | import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar";
4 | import { Button } from "@/components/ui/button";
5 | import {
6 | Card,
7 | CardContent,
8 | CardDescription,
9 | CardHeader,
10 | CardTitle,
11 | } from "@/components/ui/card";
12 | import {
13 | Command,
14 | CommandEmpty,
15 | CommandGroup,
16 | CommandInput,
17 | CommandItem,
18 | CommandList,
19 | } from "@/components/ui/command";
20 | import {
21 | Popover,
22 | PopoverContent,
23 | PopoverTrigger,
24 | } from "@/components/ui/popover";
25 |
26 | export default function TeamMembersCard() {
27 | return (
28 |
29 |
30 | Team Members
31 |
32 | Invite your team members to collaborate.
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 | OM
41 |
42 |
43 |
Sofia Davis
44 |
m@example.com
45 |
46 |
47 |
48 |
49 |
53 |
54 |
55 |
56 |
57 |
58 | No roles found.
59 |
60 |
61 | Viewer
62 |
63 | Can view and comment.
64 |
65 |
66 |
67 | Developer
68 |
69 | Can view, comment and edit.
70 |
71 |
72 |
73 | Billing
74 |
75 | Can view, comment and manage billing.
76 |
77 |
78 |
79 | Owner
80 |
81 | Admin-level access to all resources.
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 | JL
95 |
96 |
97 |
Jackson Lee
98 |
p@example.com
99 |
100 |
101 |
102 |
103 |
107 |
108 |
109 |
110 |
111 |
112 | No roles found.
113 |
114 |
115 | Viewer
116 |
117 | Can view and comment.
118 |
119 |
120 |
121 | Developer
122 |
123 | Can view, comment and edit.
124 |
125 |
126 |
127 | Billing
128 |
129 | Can view, comment and manage billing.
130 |
131 |
132 |
133 | Owner
134 |
135 | Admin-level access to all resources.
136 |
137 |
138 |
139 |
140 |
141 |
142 |
143 |
144 |
145 |
146 | );
147 | }
148 |
--------------------------------------------------------------------------------
/app/dashboard/default/cards/total-revenue.tsx:
--------------------------------------------------------------------------------
1 | "use client";
2 |
3 | import { Bar, BarChart, Line, LineChart, ResponsiveContainer } from "recharts";
4 |
5 | import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
6 |
7 | const data = [
8 | {
9 | revenue: 10400,
10 | subscription: 240,
11 | },
12 | {
13 | revenue: 14405,
14 | subscription: 300,
15 | },
16 | {
17 | revenue: 9400,
18 | subscription: 200,
19 | },
20 | {
21 | revenue: 8200,
22 | subscription: 278,
23 | },
24 | {
25 | revenue: 7000,
26 | subscription: 189,
27 | },
28 | {
29 | revenue: 9600,
30 | subscription: 239,
31 | },
32 | {
33 | revenue: 11244,
34 | subscription: 278,
35 | },
36 | {
37 | revenue: 26475,
38 | subscription: 189,
39 | },
40 | ];
41 |
42 | export default function TotalRevenueCard() {
43 | return (
44 |
45 |
46 | Total Revenue
47 |
48 |
49 | $15,231.89
50 | +20.1% from last month
51 |
52 |
53 |
62 |
77 |
78 |
79 |
80 |
81 |
82 | );
83 | }
84 |
--------------------------------------------------------------------------------
/app/dashboard/default/page.tsx:
--------------------------------------------------------------------------------
1 | import CalendarDateRangePicker from "@/components/date-range-picker";
2 | import TeamMembersCard from "./cards/theme-members";
3 | import SubscriptionsCard from "./cards/subscriptions";
4 | import TotalRevenueCard from "./cards/total-revenue";
5 | import { ChatCard } from "./cards/chat";
6 | import { Button } from "@/components/ui/button";
7 | import MetricCard from "./cards/metric";
8 | import { PaymentsCard } from "./cards/payment";
9 | import { PaymentMethodCard } from "./cards/payment-method";
10 | import { generateMeta } from "@/lib/utils";
11 |
12 | export async function generateMetadata() {
13 | return generateMeta({
14 | title: "Dashboard - Shadcn UI Kit Free",
15 | description:
16 | "The default dashboard template, built with React and Tailwind CSS, offers a sleek and efficient interface for monitoring key data and user interactions.",
17 | canonical: "/default"
18 | });
19 | }
20 |
21 | export default function Page() {
22 | return (
23 | <>
24 |
25 |
Dashboard
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
42 |
43 |
44 | >
45 | );
46 | }
47 |
--------------------------------------------------------------------------------
/app/dashboard/error.tsx:
--------------------------------------------------------------------------------
1 | "use client"; // Error components must be Client Components
2 |
3 | import { Button } from "@/components/ui/button";
4 | import { useEffect } from "react";
5 |
6 | export default function Error({
7 | error,
8 | reset
9 | }: {
10 | error: Error & { digest?: string };
11 | reset: () => void;
12 | }) {
13 | useEffect(() => {
14 | // Log the error to an error reporting service
15 | console.error("-->", error);
16 | }, [error]);
17 |
18 | return (
19 |
20 |
21 |
Oops!
22 |
Something went wrong!
23 |
24 |
25 |
26 | );
27 | }
28 |
--------------------------------------------------------------------------------
/app/dashboard/layout.tsx:
--------------------------------------------------------------------------------
1 | import MainLayout from "@/components/main-layout";
2 |
3 | export default function AuthLayout({
4 | children
5 | }: Readonly<{
6 | children: React.ReactNode;
7 | }>) {
8 | return {children};
9 | }
10 |
--------------------------------------------------------------------------------
/app/dashboard/pages/settings/layout.tsx:
--------------------------------------------------------------------------------
1 | import { Separator } from "@/components/ui/separator";
2 | import { SidebarNav } from "./sidebar-nav";
3 | import { generateMeta } from "@/lib/utils";
4 |
5 | export async function generateMetadata() {
6 | return generateMeta({
7 | title: "Settings Page",
8 | description:
9 | "Example of settings page and form created using react-hook-form and Zod validator. Built with Tailwind CSS and React.",
10 | canonical: "/pages/settings"
11 | });
12 | }
13 |
14 | const sidebarNavItems = [
15 | {
16 | title: "Profile",
17 | href: "/dashboard/pages/settings"
18 | },
19 | {
20 | title: "Account",
21 | href: "#"
22 | },
23 | {
24 | title: "Appearance",
25 | href: "#"
26 | },
27 | {
28 | title: "Notifications",
29 | href: "#"
30 | },
31 | {
32 | title: "Display",
33 | href: "#"
34 | }
35 | ];
36 |
37 | export default function SettingsLayout({ children }: { children: React.ReactNode }) {
38 | return (
39 | <>
40 |
41 |
Settings
42 |
43 | Manage your account settings and set e-mail preferences.
44 |
45 |
46 |
47 |
48 |
51 |
{children}
52 |
53 | >
54 | );
55 | }
56 |
--------------------------------------------------------------------------------
/app/dashboard/pages/settings/page.tsx:
--------------------------------------------------------------------------------
1 | import { Separator } from "@/components/ui/separator";
2 | import { ProfileForm } from "./profile-form";
3 |
4 | export default function SettingsProfilePage() {
5 | return (
6 |
7 |
8 |
Profile
9 |
10 | This is how others will see you on the site.
11 |
12 |
13 |
14 |
15 |
16 | );
17 | }
18 |
--------------------------------------------------------------------------------
/app/dashboard/pages/settings/profile-form.tsx:
--------------------------------------------------------------------------------
1 | "use client";
2 |
3 | import Link from "next/link";
4 | import { zodResolver } from "@hookform/resolvers/zod";
5 | import { useFieldArray, useForm } from "react-hook-form";
6 | import { z } from "zod";
7 |
8 | import { cn } from "@/lib/utils";
9 | import { Button } from "@/components/ui/button";
10 | import {
11 | Form,
12 | FormControl,
13 | FormDescription,
14 | FormField,
15 | FormItem,
16 | FormLabel,
17 | FormMessage
18 | } from "@/components/ui/form";
19 | import { Input } from "@/components/ui/input";
20 | import {
21 | Select,
22 | SelectContent,
23 | SelectItem,
24 | SelectTrigger,
25 | SelectValue
26 | } from "@/components/ui/select";
27 | import { Textarea } from "@/components/ui/textarea";
28 |
29 | const profileFormSchema = z.object({
30 | username: z
31 | .string()
32 | .min(2, {
33 | message: "Username must be at least 2 characters."
34 | })
35 | .max(30, {
36 | message: "Username must not be longer than 30 characters."
37 | }),
38 | email: z
39 | .string({
40 | required_error: "Please select an email to display."
41 | })
42 | .email(),
43 | bio: z.string().max(160).min(4),
44 | urls: z
45 | .array(
46 | z.object({
47 | value: z.string().url({ message: "Please enter a valid URL." })
48 | })
49 | )
50 | .optional()
51 | });
52 |
53 | type ProfileFormValues = z.infer;
54 |
55 | // This can come from your database or API.
56 | const defaultValues: Partial = {
57 | bio: "I own a computer.",
58 | urls: [{ value: "https://shadcn.com" }, { value: "http://twitter.com/shadcn" }]
59 | };
60 |
61 | export function ProfileForm() {
62 | const form = useForm({
63 | resolver: zodResolver(profileFormSchema),
64 | defaultValues,
65 | mode: "onChange"
66 | });
67 |
68 | const { fields, append } = useFieldArray({
69 | name: "urls",
70 | control: form.control
71 | });
72 |
73 | function onSubmit(data: ProfileFormValues) {}
74 |
75 | return (
76 |
172 |
173 | );
174 | }
175 |
--------------------------------------------------------------------------------
/app/dashboard/pages/settings/sidebar-nav.tsx:
--------------------------------------------------------------------------------
1 | "use client";
2 |
3 | import Link from "next/link";
4 | import { usePathname } from "next/navigation";
5 |
6 | import { cn } from "@/lib/utils";
7 | import { buttonVariants } from "@/components/ui/button";
8 |
9 | interface SidebarNavProps extends React.HTMLAttributes {
10 | items: {
11 | href: string;
12 | title: string;
13 | }[];
14 | }
15 |
16 | export function SidebarNav({ className, items, ...props }: SidebarNavProps) {
17 | const pathname = usePathname();
18 |
19 | return (
20 |
36 | );
37 | }
38 |
--------------------------------------------------------------------------------
/app/dashboard/pages/users/data.json:
--------------------------------------------------------------------------------
1 | [
2 | {
3 | "id": 1,
4 | "name": "Stern Thireau",
5 | "email": "sthireau0@prlog.org",
6 | "country": "Portugal",
7 | "role": "Construction Foreman",
8 | "image": "/images/avatars/1.png",
9 | "status": "active",
10 | "plan_name": "Basic"
11 | },
12 | {
13 | "id": 2,
14 | "name": "Ford McKibbin",
15 | "email": "fmckibbin1@slate.com",
16 | "country": "Mexico",
17 | "role": "Project Manager",
18 | "image": "/images/avatars/2.png",
19 | "status": "pending",
20 | "plan_name": "Team"
21 | },
22 | {
23 | "id": 3,
24 | "name": "Foss Roglieri",
25 | "email": "froglieri2@xing.com",
26 | "country": "Brazil",
27 | "role": "Construction Expeditor",
28 | "image": "/images/avatars/3.png",
29 | "status": "active",
30 | "plan_name": "Basic"
31 | },
32 | {
33 | "id": 4,
34 | "name": "Maurits Elgey",
35 | "email": "melgey3@blogger.com",
36 | "country": "Poland",
37 | "role": "Construction Manager",
38 | "image": "/images/avatars/4.png",
39 | "status": "active",
40 | "plan_name": "Enterprise"
41 | },
42 | {
43 | "id": 5,
44 | "name": "Gun Kaasmann",
45 | "email": "gkaasmann4@economist.com",
46 | "country": "Russia",
47 | "role": "Construction Foreman",
48 | "image": "/images/avatars/5.png",
49 | "status": "pending",
50 | "plan_name": "Team"
51 | },
52 | {
53 | "id": 6,
54 | "name": "Edmund McCrae",
55 | "email": "emccrae5@woothemes.com",
56 | "country": "Poland",
57 | "role": "Project Manager",
58 | "image": "/images/avatars/6.png",
59 | "status": "pending",
60 | "plan_name": "Team"
61 | },
62 | {
63 | "id": 7,
64 | "name": "Samuel Totman",
65 | "email": "stotman6@wisc.edu",
66 | "country": "France",
67 | "role": "Surveyor",
68 | "image": "/images/avatars/7.png",
69 | "status": "active",
70 | "plan_name": "Basic"
71 | },
72 | {
73 | "id": 8,
74 | "name": "Patsy Cuardall",
75 | "email": "pcuardall7@barnesandnoble.com",
76 | "country": "Indonesia",
77 | "role": "Architect",
78 | "image": "/images/avatars/8.png",
79 | "status": "inactive",
80 | "plan_name": "Basic"
81 | },
82 | {
83 | "id": 9,
84 | "name": "Barnaby Carl",
85 | "email": "bcarl8@alexa.com",
86 | "country": "Peru",
87 | "role": "Subcontractor",
88 | "image": "/images/avatars/9.png",
89 | "status": "active",
90 | "plan_name": "Basic"
91 | },
92 | {
93 | "id": 10,
94 | "name": "Mary Stivens",
95 | "email": "mstivens9@facebook.com",
96 | "country": "Russia",
97 | "role": "Construction Manager",
98 | "image": "/images/avatars/10.png",
99 | "status": "active",
100 | "plan_name": "Basic"
101 | },
102 | {
103 | "id": 11,
104 | "name": "Averil Caccavella",
105 | "email": "acaccavellaa@shinystat.com",
106 | "country": "China",
107 | "role": "Construction Foreman",
108 | "image": "/images/avatars/11.png",
109 | "status": "active",
110 | "plan_name": "Basic"
111 | },
112 | {
113 | "id": 12,
114 | "name": "Beulah Varian",
115 | "email": "bvarianb@amazon.co.jp",
116 | "country": "Palestinian Territory",
117 | "role": "Architect",
118 | "image": "/images/avatars/1.png",
119 | "status": "inactive",
120 | "plan_name": "Enterprise"
121 | },
122 | {
123 | "id": 13,
124 | "name": "Modesta Handy",
125 | "email": "mhandyc@cloudflare.com",
126 | "country": "Russia",
127 | "role": "Engineer",
128 | "image": "/images/avatars/2.png",
129 | "status": "active",
130 | "plan_name": "Basic"
131 | },
132 | {
133 | "id": 14,
134 | "name": "Neal Sievewright",
135 | "email": "nsievewrightd@microsoft.com",
136 | "country": "Philippines",
137 | "role": "Project Manager",
138 | "image": "/images/avatars/3.png",
139 | "status": "active",
140 | "plan_name": "Enterprise"
141 | },
142 | {
143 | "id": 15,
144 | "name": "Sisely Bonson",
145 | "email": "sbonsone@mozilla.org",
146 | "country": "Morocco",
147 | "role": "Architect",
148 | "image": "/images/avatars/4.png",
149 | "status": "active",
150 | "plan_name": "Enterprise"
151 | },
152 | {
153 | "id": 16,
154 | "name": "Charlena Maccaddie",
155 | "email": "cmaccaddief@csmonitor.com",
156 | "country": "Japan",
157 | "role": "Construction Worker",
158 | "image": "/images/avatars/5.png",
159 | "status": "active",
160 | "plan_name": "Basic"
161 | },
162 | {
163 | "id": 17,
164 | "name": "Durward Guenther",
165 | "email": "dguentherg@howstuffworks.com",
166 | "country": "Portugal",
167 | "role": "Electrician",
168 | "image": "/images/avatars/6.png",
169 | "status": "inactive",
170 | "plan_name": "Enterprise"
171 | },
172 | {
173 | "id": 18,
174 | "name": "Rheta Wathall",
175 | "email": "rwathallh@alexa.com",
176 | "country": "China",
177 | "role": "Supervisor",
178 | "image": "/images/avatars/7.png",
179 | "status": "active",
180 | "plan_name": "Basic"
181 | },
182 | {
183 | "id": 19,
184 | "name": "Hugibert Cuttings",
185 | "email": "hcuttingsi@dropbox.com",
186 | "country": "Sweden",
187 | "role": "Construction Foreman",
188 | "image": "/images/avatars/8.png",
189 | "status": "inactive",
190 | "plan_name": "Basic"
191 | },
192 | {
193 | "id": 20,
194 | "name": "Riane Winchcum",
195 | "email": "rwinchcumj@people.com.cn",
196 | "country": "Philippines",
197 | "role": "Electrician",
198 | "image": "/images/avatars/9.png",
199 | "status": "active",
200 | "plan_name": "Basic"
201 | },
202 | {
203 | "id": 21,
204 | "name": "Maurise Reinmar",
205 | "email": "mreinmark@vk.com",
206 | "country": "Finland",
207 | "role": "Project Manager",
208 | "image": "/images/avatars/10.png",
209 | "status": "pending",
210 | "plan_name": "Basic"
211 | },
212 | {
213 | "id": 22,
214 | "name": "Karita Flight",
215 | "email": "kflightl@mail.ru",
216 | "country": "France",
217 | "role": "Architect",
218 | "image": "/images/avatars/11.png",
219 | "status": "active",
220 | "plan_name": "Team"
221 | },
222 | {
223 | "id": 23,
224 | "name": "Innis Gillyett",
225 | "email": "igillyettm@canalblog.com",
226 | "country": "Ukraine",
227 | "role": "Estimator",
228 | "image": "/images/avatars/1.png",
229 | "status": "active",
230 | "plan_name": "Basic"
231 | },
232 | {
233 | "id": 24,
234 | "name": "Teriann Hamflett",
235 | "email": "thamflettn@gmpg.org",
236 | "country": "Indonesia",
237 | "role": "Estimator",
238 | "image": "/images/avatars/2.png",
239 | "status": "active",
240 | "plan_name": "Basic"
241 | },
242 | {
243 | "id": 25,
244 | "name": "Wynn McCrossan",
245 | "email": "wmccrossano@nationalgeographic.com",
246 | "country": "Poland",
247 | "role": "Construction Expeditor",
248 | "image": "/images/avatars/3.png",
249 | "status": "active",
250 | "plan_name": "Basic"
251 | },
252 | {
253 | "id": 26,
254 | "name": "Thane Peake",
255 | "email": "tpeakep@state.gov",
256 | "country": "China",
257 | "role": "Estimator",
258 | "image": "/images/avatars/4.png",
259 | "status": "active",
260 | "plan_name": "Team"
261 | },
262 | {
263 | "id": 27,
264 | "name": "Kalila Corradino",
265 | "email": "kcorradinoq@sciencedaily.com",
266 | "country": "Germany",
267 | "role": "Construction Manager",
268 | "image": "/images/avatars/5.png",
269 | "status": "pending",
270 | "plan_name": "Team"
271 | },
272 | {
273 | "id": 28,
274 | "name": "Patton Scatchar",
275 | "email": "pscatcharr@t-online.de",
276 | "country": "Sweden",
277 | "role": "Project Manager",
278 | "image": "/images/avatars/6.png",
279 | "status": "active",
280 | "plan_name": "Basic"
281 | },
282 | {
283 | "id": 29,
284 | "name": "Leona Margetts",
285 | "email": "lmargettss@dailymail.co.uk",
286 | "country": "Indonesia",
287 | "role": "Construction Expeditor",
288 | "image": "/images/avatars/7.png",
289 | "status": "active",
290 | "plan_name": "Team"
291 | },
292 | {
293 | "id": 30,
294 | "name": "Frieda Caught",
295 | "email": "fcaughtt@smugmug.com",
296 | "country": "China",
297 | "role": "Construction Foreman",
298 | "image": "/images/avatars/8.png",
299 | "status": "active",
300 | "plan_name": "Basic"
301 | },
302 | {
303 | "id": 31,
304 | "name": "Birch Shakle",
305 | "email": "bshakleu@ucoz.ru",
306 | "country": "Indonesia",
307 | "role": "Supervisor",
308 | "image": "/images/avatars/9.png",
309 | "status": "active",
310 | "plan_name": "Basic"
311 | },
312 | {
313 | "id": 32,
314 | "name": "Alessandro Roon",
315 | "email": "aroonv@vkontakte.ru",
316 | "country": "United States",
317 | "role": "Construction Worker",
318 | "image": "/images/avatars/10.png",
319 | "status": "active",
320 | "plan_name": "Basic"
321 | },
322 | {
323 | "id": 33,
324 | "name": "Oliviero Moran",
325 | "email": "omoranw@chronoengine.com",
326 | "country": "Liberia",
327 | "role": "Estimator",
328 | "image": "/images/avatars/11.png",
329 | "status": "active",
330 | "plan_name": "Basic"
331 | },
332 | {
333 | "id": 34,
334 | "name": "Frederich Tethcote",
335 | "email": "ftethcotex@guardian.co.uk",
336 | "country": "China",
337 | "role": "Engineer",
338 | "image": "/images/avatars/1.png",
339 | "status": "active",
340 | "plan_name": "Basic"
341 | },
342 | {
343 | "id": 35,
344 | "name": "Elonore Boase",
345 | "email": "eboasey@networksolutions.com",
346 | "country": "Guatemala",
347 | "role": "Construction Manager",
348 | "image": "/images/avatars/2.png",
349 | "status": "inactive",
350 | "plan_name": "Basic"
351 | },
352 | {
353 | "id": 36,
354 | "name": "Arny Sidebotton",
355 | "email": "asidebottonz@marketwatch.com",
356 | "country": "France",
357 | "role": "Estimator",
358 | "image": "/images/avatars/3.png",
359 | "status": "pending",
360 | "plan_name": "Basic"
361 | },
362 | {
363 | "id": 37,
364 | "name": "Karolina Lawlie",
365 | "email": "klawlie10@bloglines.com",
366 | "country": "Peru",
367 | "role": "Estimator",
368 | "image": "/images/avatars/4.png",
369 | "status": "inactive",
370 | "plan_name": "Basic"
371 | },
372 | {
373 | "id": 38,
374 | "name": "Andrew Langlands",
375 | "email": "alanglands11@deliciousdays.com",
376 | "country": "Russia",
377 | "role": "Engineer",
378 | "image": "/images/avatars/5.png",
379 | "status": "active",
380 | "plan_name": "Basic"
381 | },
382 | {
383 | "id": 39,
384 | "name": "Don Doughton",
385 | "email": "ddoughton12@reddit.com",
386 | "country": "Portugal",
387 | "role": "Architect",
388 | "image": "/images/avatars/6.png",
389 | "status": "active",
390 | "plan_name": "Basic"
391 | },
392 | {
393 | "id": 40,
394 | "name": "Letta Ofer",
395 | "email": "lofer13@merriam-webster.com",
396 | "country": "Bolivia",
397 | "role": "Construction Foreman",
398 | "image": "/images/avatars/7.png",
399 | "status": "active",
400 | "plan_name": "Basic"
401 | }
402 | ]
403 |
--------------------------------------------------------------------------------
/app/dashboard/pages/users/page.tsx:
--------------------------------------------------------------------------------
1 | import { promises as fs } from "fs";
2 | import path from "path";
3 |
4 | import Link from "next/link";
5 | import { Button } from "@/components/ui/button";
6 | import { PlusCircledIcon } from "@radix-ui/react-icons";
7 | import UsersDataTable from "./data-table";
8 | import { generateMeta } from "@/lib/utils";
9 |
10 | export async function generateMetadata() {
11 | return generateMeta({
12 | title: "Users - Shadcn UI Kit Free",
13 | description:
14 | "A list of users created using the Tanstack Table. Tailwind is built on CSS and React.",
15 | canonical: "/pages/users"
16 | });
17 | }
18 |
19 | async function getUsers() {
20 | const data = await fs.readFile(path.join(process.cwd(), "app/dashboard/pages/users/data.json"));
21 | return JSON.parse(data.toString());
22 | }
23 |
24 | export default async function Page() {
25 | const users = await getUsers();
26 |
27 | return (
28 | <>
29 |
30 |
Users
31 |
36 |
37 |
38 | >
39 | );
40 | }
41 |
--------------------------------------------------------------------------------
/app/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bundui/shadcn-dashboard-free/d794529f65db7588ce0b69ca8bb1d0c91511b6a3/app/favicon.ico
--------------------------------------------------------------------------------
/app/globals.scss:
--------------------------------------------------------------------------------
1 | @tailwind base;
2 | @tailwind components;
3 | @tailwind utilities;
4 |
5 | @layer base {
6 | :root {
7 | --background: 0 0% 100%;
8 | --foreground: 20 14.3% 4.1%;
9 | --card: 0 0% 100%;
10 | --card-foreground: 20 14.3% 4.1%;
11 | --popover: 0 0% 100%;
12 | --popover-foreground: 20 14.3% 4.1%;
13 | --primary: 24 9.8% 10%;
14 | --primary-foreground: 60 9.1% 97.8%;
15 | --secondary: 60 4.8% 95.9%;
16 | --secondary-foreground: 24 9.8% 10%;
17 | --muted: 60 4.8% 95.9%;
18 | --muted-foreground: 25 5.3% 44.7%;
19 | --accent: 60 4.8% 95.9%;
20 | --accent-foreground: 24 9.8% 10%;
21 | --destructive: 0 84.2% 60.2%;
22 | --destructive-foreground: 60 9.1% 97.8%;
23 | --border: 20 5.9% 90%;
24 | --input: 20 5.9% 90%;
25 | --ring: 20 14.3% 4.1%;
26 | --radius: 0.5rem;
27 |
28 | --chart-1: 359 2% 40%;
29 | --chart-2: 240 1% 74%;
30 | --chart-3: 240 1% 58%;
31 | --chart-4: 240 1% 42%;
32 | --chart-5: 240 2% 26%;
33 |
34 | --sidebar-width: 19rem;
35 | }
36 | }
37 |
38 | @layer base {
39 | * {
40 | @apply border-border;
41 | }
42 | body {
43 | @apply overflow-x-hidden bg-muted/70 text-foreground;
44 | }
45 | }
46 |
--------------------------------------------------------------------------------
/app/layout.tsx:
--------------------------------------------------------------------------------
1 | import { Inter } from "next/font/google";
2 | import "./globals.scss";
3 |
4 | const inter = Inter({ subsets: ["latin"] });
5 |
6 | export default function RootLayout({
7 | children
8 | }: Readonly<{
9 | children: React.ReactNode;
10 | }>) {
11 | return (
12 |
13 | {children}
14 |
15 | );
16 | }
17 |
--------------------------------------------------------------------------------
/components.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "https://ui.shadcn.com/schema.json",
3 | "style": "default",
4 | "rsc": true,
5 | "tsx": true,
6 | "tailwind": {
7 | "config": "tailwind.config.ts",
8 | "css": "app/globals.css",
9 | "baseColor": "slate",
10 | "cssVariables": true,
11 | "prefix": ""
12 | },
13 | "aliases": {
14 | "components": "@/components",
15 | "utils": "@/lib/utils"
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/components/anchor.tsx:
--------------------------------------------------------------------------------
1 | "use client";
2 |
3 | import { cn } from "@/lib/utils";
4 | import Link from "next/link";
5 | import { usePathname } from "next/navigation";
6 | import { ComponentProps } from "react";
7 |
8 | type AnchorProps = ComponentProps & {
9 | absolute?: boolean;
10 | activeClassName?: string;
11 | disabled?: boolean;
12 | };
13 |
14 | export default function Anchor({
15 | absolute,
16 | className = "",
17 | activeClassName = "",
18 | disabled,
19 | children,
20 | ...props
21 | }: AnchorProps) {
22 | const path = usePathname();
23 | const isMatch = absolute
24 | ? props.href.toString().split("/")[1] == path.split("/")[1]
25 | : path === props.href;
26 |
27 | if (disabled)
28 | return (
29 | {children}
30 | );
31 | return (
32 |
33 | {children}
34 |
35 | );
36 | }
37 |
--------------------------------------------------------------------------------
/components/card-action-menus.tsx:
--------------------------------------------------------------------------------
1 | import {
2 | DropdownMenu,
3 | DropdownMenuItem,
4 | DropdownMenuContent,
5 | DropdownMenuTrigger
6 | } from "@/components/ui/dropdown-menu";
7 | import { Button } from "@/components/ui/button";
8 | import { Download, Ellipsis } from "lucide-react";
9 |
10 | export function CardOptionsMenu() {
11 | return (
12 |
13 |
14 |
15 |
18 |
19 |
20 | View detail
21 | Download
22 | Refresh
23 |
24 |
25 |
26 | );
27 | }
28 |
29 | export function ExportButton() {
30 | return (
31 |
32 |
33 |
34 |
37 |
38 |
39 | Excel
40 | PDF
41 |
42 |
43 |
44 | );
45 | }
46 |
--------------------------------------------------------------------------------
/components/date-range-picker.tsx:
--------------------------------------------------------------------------------
1 | "use client";
2 |
3 | import * as React from "react";
4 | import { addDays, format } from "date-fns";
5 | import { DateRange } from "react-day-picker";
6 |
7 | import { cn } from "@/lib/utils";
8 | import {
9 | Popover,
10 | PopoverContent,
11 | PopoverTrigger,
12 | } from "@/components/ui/popover";
13 | import { Button } from "@/components/ui/button";
14 | import { CalendarIcon } from "lucide-react";
15 | import { Calendar } from "@/components/ui/calendar";
16 |
17 | export default function CalendarDateRangePicker({
18 | className,
19 | }: React.HTMLAttributes) {
20 | const [date, setDate] = React.useState({
21 | from: new Date(),
22 | to: addDays(new Date(), 20),
23 | });
24 |
25 | return (
26 |
27 |
28 |
29 |
51 |
52 |
53 |
61 |
62 |
63 |
64 | );
65 | }
66 |
--------------------------------------------------------------------------------
/components/icon.tsx:
--------------------------------------------------------------------------------
1 | import { icons } from "lucide-react";
2 |
3 | type IconProps = {
4 | name: string;
5 | className?: string;
6 | };
7 |
8 | type IconType = React.ComponentType>;
9 |
10 | type IconsType = {
11 | [key: string]: IconType;
12 | };
13 |
14 | const iconMap: IconsType = icons;
15 |
16 | const Icon: React.FC = ({ name, className }) => {
17 | const LucideIcon = iconMap[name];
18 |
19 | if (!LucideIcon) {
20 | return null;
21 | }
22 |
23 | return ;
24 | };
25 |
26 | export default Icon;
27 |
--------------------------------------------------------------------------------
/components/layout/header.tsx:
--------------------------------------------------------------------------------
1 | import { LockIcon, Menu } from "lucide-react";
2 | import {
3 | DropdownMenu,
4 | DropdownMenuContent,
5 | DropdownMenuItem,
6 | DropdownMenuLabel,
7 | DropdownMenuSeparator,
8 | DropdownMenuTrigger
9 | } from "@/components/ui/dropdown-menu";
10 | import { Button } from "@/components/ui/button";
11 | import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card";
12 | import { Sheet, SheetContent, SheetTrigger } from "@/components/ui/sheet";
13 | import Search from "./search";
14 | import Logo from "./logo";
15 | import { SidebarNavLink } from "./sidebar";
16 | import { page_routes } from "@/lib/routes-config";
17 | import { Fragment } from "react";
18 | import Link from "next/link";
19 |
20 | export default function Header() {
21 | return (
22 |
23 |
24 |
25 |
26 |
30 |
31 |
32 |
33 |
45 |
46 |
47 |
48 | Get Shadcn UI Kit Pro
49 |
50 | Need more pages and components? Then you can get the pro.
51 |
52 |
53 |
54 |
62 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 | My Account
83 |
84 | Settings
85 | Support
86 |
87 | Logout
88 |
89 |
90 |
91 |
92 | );
93 | }
94 |
--------------------------------------------------------------------------------
/components/layout/logo.tsx:
--------------------------------------------------------------------------------
1 | import { cn } from "@/lib/utils";
2 | import Link from "next/link";
3 | import { Badge } from "../ui/badge";
4 |
5 | type LogoProps = {
6 | className?: string;
7 | };
8 |
9 | export default function Logo({ className }: LogoProps) {
10 | return (
11 |
12 |
13 | Shadcn UI Kit Free
14 |
15 | );
16 | }
17 |
--------------------------------------------------------------------------------
/components/layout/search.tsx:
--------------------------------------------------------------------------------
1 | "use client";
2 |
3 | import React from "react";
4 | import { CommandIcon, SearchIcon, icons } from "lucide-react";
5 | import { Input } from "@/components/ui/input";
6 | import { page_routes } from "@/lib/routes-config";
7 | import { useEffect, useState } from "react";
8 |
9 | import {
10 | CommandDialog,
11 | CommandEmpty,
12 | CommandGroup,
13 | CommandInput,
14 | CommandItem,
15 | CommandList,
16 | CommandSeparator
17 | } from "@/components/ui/command";
18 | import { useRouter } from "next/navigation";
19 |
20 | type CommandItemProps = {
21 | item: {
22 | title: string;
23 | href: string;
24 | icon?: string;
25 | };
26 | };
27 |
28 | export default function Search() {
29 | const [open, setOpen] = useState(false);
30 | const router = useRouter();
31 |
32 | useEffect(() => {
33 | const down = (e: KeyboardEvent) => {
34 | if (e.key === "k" && (e.metaKey || e.ctrlKey)) {
35 | e.preventDefault();
36 | setOpen((open) => !open);
37 | }
38 | };
39 | document.addEventListener("keydown", down);
40 | return () => document.removeEventListener("keydown", down);
41 | }, []);
42 |
43 | const CommandItemComponent: React.FC = ({ item }) => {
44 | // @ts-expect-error
45 | const LucideIcon = icons[item.icon];
46 |
47 | return (
48 | {
50 | setOpen(false);
51 | router.push(item.href);
52 | }}>
53 | {item.icon && }
54 | {item.title}
55 |
56 | );
57 | };
58 |
59 | return (
60 |
61 |
62 |
63 |
setOpen(true)}
68 | />
69 |
70 |
71 | k
72 |
73 |
74 |
75 |
76 |
77 | No results found.
78 | {page_routes.map((route) => (
79 |
80 |
81 | {route.items.map((item, key) => (
82 |
83 | ))}
84 |
85 |
86 |
87 | ))}
88 |
89 |
90 |
91 | );
92 | }
93 |
--------------------------------------------------------------------------------
/components/layout/sidebar.tsx:
--------------------------------------------------------------------------------
1 | import { Fragment } from "react";
2 | import Link from "next/link";
3 |
4 | import { ScrollArea } from "@/components/ui/scroll-area";
5 | import { page_routes } from "@/lib/routes-config";
6 | import Anchor from "../anchor";
7 | import Logo from "./logo";
8 | import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "../ui/card";
9 | import { Button } from "../ui/button";
10 | import { Collapsible, CollapsibleContent, CollapsibleTrigger } from "@/components/ui/collapsible";
11 | import Icon from "../icon";
12 | import { ChevronDown, LockIcon } from "lucide-react";
13 | import { Badge } from "../ui/badge";
14 |
15 | type SidebarNavLinkProps = {
16 | item: {
17 | title: string;
18 | href: string;
19 | icon?: string;
20 | isComing?: boolean;
21 | };
22 | };
23 |
24 | export const SidebarNavLink: React.FC = ({ item }: SidebarNavLinkProps) => {
25 | return (
26 |
30 | {item.icon && }
31 | {item.title}
32 | {item.isComing && (
33 |
34 | Coming
35 |
36 | )}
37 |
38 | );
39 | };
40 |
41 | export default function Sidebar() {
42 | return (
43 |
44 |
45 |
46 | {page_routes.map((route) => (
47 |
48 | {route.title}
49 |
50 | {route.items.map((item, key) => {
51 | return (
52 |
53 | {item.items?.length ? (
54 |
55 |
56 | {item.icon && }
57 | {item.title}
58 |
59 |
60 |
61 |
62 | {item.items.map((item, key) => (
63 |
64 | ))}
65 |
66 |
67 |
68 | ) : (
69 |
70 | )}
71 |
72 | );
73 | })}
74 |
75 |
76 | ))}
77 |
78 |
79 |
80 | Get Shadcn UI Kit Pro
81 |
82 | Need more pages and components? Then you can get the pro.
83 |
84 |
85 |
86 |
94 |
99 |
100 |
101 |
102 |
103 |
104 | );
105 | }
106 |
--------------------------------------------------------------------------------
/components/main-layout.tsx:
--------------------------------------------------------------------------------
1 | "use client";
2 |
3 | import Sidebar from "./layout/sidebar";
4 | import Header from "./layout/header";
5 | import { cn } from "@/lib/utils";
6 |
7 | export default function MainLayout({ children }: { children: React.ReactNode }) {
8 | return (
9 | <>
10 |
11 |
12 |
13 |
14 | {children}
15 |
16 |
17 | >
18 | );
19 | }
20 |
--------------------------------------------------------------------------------
/components/providers.tsx:
--------------------------------------------------------------------------------
1 | "use client";
2 |
3 | import { ThemeProvider } from "next-themes";
4 |
5 | export default function Providers({ children }: { children: React.ReactNode }) {
6 | return (
7 |
8 | {children}
9 |
10 | );
11 | }
12 |
--------------------------------------------------------------------------------
/components/theme-provider.tsx:
--------------------------------------------------------------------------------
1 | 'use client';
2 |
3 | import * as React from 'react';
4 | import { ThemeProvider as NextThemesProvider } from 'next-themes';
5 | import { type ThemeProviderProps } from 'next-themes/dist/types';
6 |
7 | export function ThemeProvider({ children, ...props }: ThemeProviderProps) {
8 | return {children};
9 | }
10 |
--------------------------------------------------------------------------------
/components/ui/accordion.tsx:
--------------------------------------------------------------------------------
1 | "use client";
2 |
3 | import * as React from "react";
4 | import * as AccordionPrimitive from "@radix-ui/react-accordion";
5 | import { ChevronDown } from "lucide-react";
6 |
7 | import { cn } from "@/lib/utils";
8 |
9 | const Accordion = AccordionPrimitive.Root;
10 |
11 | const AccordionItem = React.forwardRef<
12 | React.ElementRef,
13 | React.ComponentPropsWithoutRef
14 | >(({ className, ...props }, ref) => (
15 |
20 | ));
21 | AccordionItem.displayName = "AccordionItem";
22 |
23 | const AccordionTrigger = React.forwardRef<
24 | React.ElementRef,
25 | React.ComponentPropsWithoutRef
26 | >(({ className, children, ...props }, ref) => (
27 |
28 | svg]:rotate-180",
32 | className,
33 | )}
34 | {...props}
35 | >
36 | {children}
37 |
38 |
39 |
40 | ));
41 | AccordionTrigger.displayName = AccordionPrimitive.Trigger.displayName;
42 |
43 | const AccordionContent = React.forwardRef<
44 | React.ElementRef,
45 | React.ComponentPropsWithoutRef
46 | >(({ className, children, ...props }, ref) => (
47 |
52 | {children}
53 |
54 | ));
55 |
56 | AccordionContent.displayName = AccordionPrimitive.Content.displayName;
57 |
58 | export { Accordion, AccordionItem, AccordionTrigger, AccordionContent };
59 |
--------------------------------------------------------------------------------
/components/ui/avatar.tsx:
--------------------------------------------------------------------------------
1 | "use client";
2 |
3 | import * as React from "react";
4 | import * as AvatarPrimitive from "@radix-ui/react-avatar";
5 |
6 | import { cn } from "@/lib/utils";
7 | import { cva, VariantProps } from "class-variance-authority";
8 |
9 | const Avatar = React.forwardRef<
10 | React.ElementRef,
11 | React.ComponentPropsWithoutRef
12 | >(({ className, ...props }, ref) => (
13 |
18 | ));
19 | Avatar.displayName = AvatarPrimitive.Root.displayName;
20 |
21 | const AvatarImage = React.forwardRef<
22 | React.ElementRef,
23 | React.ComponentPropsWithoutRef
24 | >(({ className, ...props }, ref) => (
25 |
30 | ));
31 | AvatarImage.displayName = AvatarPrimitive.Image.displayName;
32 |
33 | const AvatarFallback = React.forwardRef<
34 | React.ElementRef,
35 | React.ComponentPropsWithoutRef
36 | >(({ className, ...props }, ref) => (
37 |
45 | ));
46 | AvatarFallback.displayName = AvatarPrimitive.Fallback.displayName;
47 |
48 | const indicatorVariants = cva("w-3 h-3 absolute rounded-full end-0 bottom-0", {
49 | variants: {
50 | variant: {
51 | success: "bg-green-400",
52 | danger: "bg-red-400",
53 | warning: "bg-orange-400"
54 | }
55 | }
56 | });
57 |
58 | export interface AvatarIndicatorProps
59 | extends React.HTMLAttributes,
60 | VariantProps {
61 | variant?: "success" | "danger" | "warning" | null;
62 | }
63 |
64 | const AvatarIndicator = React.forwardRef(
65 | ({ variant, className, ...props }, ref) => (
66 |
67 | )
68 | );
69 | AvatarIndicator.displayName = AvatarPrimitive.Fallback.displayName;
70 |
71 | export { Avatar, AvatarImage, AvatarFallback, AvatarIndicator };
72 |
--------------------------------------------------------------------------------
/components/ui/badge.tsx:
--------------------------------------------------------------------------------
1 | import * as React from "react";
2 | import { cva, type VariantProps } from "class-variance-authority";
3 |
4 | import { cn } from "@/lib/utils";
5 |
6 | const badgeVariants = cva(
7 | "inline-flex items-center rounded-full border px-2.5 py-0.5 text-xs font-semibold transition-colors focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2",
8 | {
9 | variants: {
10 | variant: {
11 | default: "border-transparent bg-primary text-primary-foreground hover:bg-primary/80",
12 | secondary:
13 | "border-transparent bg-secondary text-secondary-foreground hover:bg-secondary/80",
14 | destructive:
15 | "border-transparent bg-red-100 text-red-500 hover:bg-destructive/80 dark:bg-red-950 dark:text-white",
16 | success:
17 | "border-transparent bg-green-100 text-green-foreground hover:bg-green/80 dark:bg-green-950",
18 | warning:
19 | "border-transparent bg-orange-100 text-orange-foreground hover:bg-orange/80 dark:bg-orange-950",
20 | outline: "text-foreground"
21 | }
22 | },
23 | defaultVariants: {
24 | variant: "default"
25 | }
26 | }
27 | );
28 |
29 | export interface BadgeProps
30 | extends React.HTMLAttributes,
31 | VariantProps {}
32 |
33 | function Badge({ className, variant, ...props }: BadgeProps) {
34 | return ;
35 | }
36 |
37 | export { Badge, badgeVariants };
38 |
--------------------------------------------------------------------------------
/components/ui/breadcrumb.tsx:
--------------------------------------------------------------------------------
1 | import * as React from "react";
2 | import { Slot } from "@radix-ui/react-slot";
3 | import { ChevronRight, MoreHorizontal } from "lucide-react";
4 |
5 | import { cn } from "@/lib/utils";
6 |
7 | const Breadcrumb = React.forwardRef<
8 | HTMLElement,
9 | React.ComponentPropsWithoutRef<"nav"> & {
10 | separator?: React.ReactNode;
11 | }
12 | >(({ ...props }, ref) => );
13 | Breadcrumb.displayName = "Breadcrumb";
14 |
15 | const BreadcrumbList = React.forwardRef<
16 | HTMLOListElement,
17 | React.ComponentPropsWithoutRef<"ol">
18 | >(({ className, ...props }, ref) => (
19 |
27 | ));
28 | BreadcrumbList.displayName = "BreadcrumbList";
29 |
30 | const BreadcrumbItem = React.forwardRef<
31 | HTMLLIElement,
32 | React.ComponentPropsWithoutRef<"li">
33 | >(({ className, ...props }, ref) => (
34 |
39 | ));
40 | BreadcrumbItem.displayName = "BreadcrumbItem";
41 |
42 | const BreadcrumbLink = React.forwardRef<
43 | HTMLAnchorElement,
44 | React.ComponentPropsWithoutRef<"a"> & {
45 | asChild?: boolean;
46 | }
47 | >(({ asChild, className, ...props }, ref) => {
48 | const Comp = asChild ? Slot : "a";
49 |
50 | return (
51 |
56 | );
57 | });
58 | BreadcrumbLink.displayName = "BreadcrumbLink";
59 |
60 | const BreadcrumbPage = React.forwardRef<
61 | HTMLSpanElement,
62 | React.ComponentPropsWithoutRef<"span">
63 | >(({ className, ...props }, ref) => (
64 |
72 | ));
73 | BreadcrumbPage.displayName = "BreadcrumbPage";
74 |
75 | const BreadcrumbSeparator = ({
76 | children,
77 | className,
78 | ...props
79 | }: React.ComponentProps<"li">) => (
80 | svg]:size-3.5", className)}
84 | {...props}
85 | >
86 | {children ?? }
87 |
88 | );
89 | BreadcrumbSeparator.displayName = "BreadcrumbSeparator";
90 |
91 | const BreadcrumbEllipsis = ({
92 | className,
93 | ...props
94 | }: React.ComponentProps<"span">) => (
95 |
101 |
102 | More
103 |
104 | );
105 | BreadcrumbEllipsis.displayName = "BreadcrumbElipssis";
106 |
107 | export {
108 | Breadcrumb,
109 | BreadcrumbList,
110 | BreadcrumbItem,
111 | BreadcrumbLink,
112 | BreadcrumbPage,
113 | BreadcrumbSeparator,
114 | BreadcrumbEllipsis,
115 | };
116 |
--------------------------------------------------------------------------------
/components/ui/button.tsx:
--------------------------------------------------------------------------------
1 | import * as React from "react";
2 | import { Slot } from "@radix-ui/react-slot";
3 | import { cva, type VariantProps } from "class-variance-authority";
4 |
5 | import { cn } from "@/lib/utils";
6 |
7 | const buttonVariants = cva(
8 | "inline-flex items-center justify-center whitespace-nowrap rounded-md text-sm font-medium ring-offset-background transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50",
9 | {
10 | variants: {
11 | variant: {
12 | default: "bg-primary text-primary-foreground hover:bg-primary/90",
13 | destructive: "bg-destructive text-destructive-foreground hover:bg-destructive/90",
14 | outline: "border border-input bg-background hover:bg-accent hover:text-accent-foreground",
15 | secondary: "bg-secondary text-secondary-foreground hover:bg-secondary/80",
16 | ghost: "hover:bg-accent hover:text-accent-foreground",
17 | link: "text-primary underline-offset-4 hover:underline"
18 | },
19 | size: {
20 | default: "h-10 px-4 py-2",
21 | sm: "h-9 rounded-md px-3",
22 | lg: "h-11 rounded-md px-8",
23 | icon: "h-10 w-10"
24 | }
25 | },
26 | defaultVariants: {
27 | variant: "default",
28 | size: "default"
29 | }
30 | }
31 | );
32 |
33 | export interface ButtonProps
34 | extends React.ButtonHTMLAttributes,
35 | VariantProps {
36 | asChild?: boolean;
37 | }
38 |
39 | const Button = React.forwardRef(
40 | ({ className, variant, size, asChild = false, ...props }, ref) => {
41 | const Comp = asChild ? Slot : "button";
42 | return (
43 |
44 | );
45 | }
46 | );
47 | Button.displayName = "Button";
48 |
49 | export { Button, buttonVariants };
50 |
--------------------------------------------------------------------------------
/components/ui/calendar.tsx:
--------------------------------------------------------------------------------
1 | "use client";
2 |
3 | import * as React from "react";
4 | import { ChevronLeft, ChevronRight } from "lucide-react";
5 | import { DayPicker } from "react-day-picker";
6 |
7 | import { cn } from "@/lib/utils";
8 | import { buttonVariants } from "@/components/ui/button";
9 |
10 | export type CalendarProps = React.ComponentProps;
11 |
12 | function Calendar({
13 | className,
14 | classNames,
15 | showOutsideDays = true,
16 | ...props
17 | }: CalendarProps) {
18 | return (
19 | ,
58 | IconRight: ({ ...props }) => ,
59 | }}
60 | {...props}
61 | />
62 | );
63 | }
64 | Calendar.displayName = "Calendar";
65 |
66 | export { Calendar };
67 |
--------------------------------------------------------------------------------
/components/ui/card.tsx:
--------------------------------------------------------------------------------
1 | import * as React from "react";
2 |
3 | import { cn } from "@/lib/utils";
4 |
5 | const Card = React.forwardRef>(
6 | ({ className, ...props }, ref) => (
7 |
12 | )
13 | );
14 | Card.displayName = "Card";
15 |
16 | const CardHeader = React.forwardRef>(
17 | ({ className, ...props }, ref) => (
18 |
19 | )
20 | );
21 | CardHeader.displayName = "CardHeader";
22 |
23 | const CardTitle = React.forwardRef>(
24 | ({ className, ...props }, ref) => (
25 |
30 | )
31 | );
32 | CardTitle.displayName = "CardTitle";
33 |
34 | const CardDescription = React.forwardRef<
35 | HTMLParagraphElement,
36 | React.HTMLAttributes
37 | >(({ className, ...props }, ref) => (
38 |
39 | ));
40 | CardDescription.displayName = "CardDescription";
41 |
42 | const CardContent = React.forwardRef>(
43 | ({ className, ...props }, ref) => (
44 |
45 | )
46 | );
47 | CardContent.displayName = "CardContent";
48 |
49 | const CardFooter = React.forwardRef>(
50 | ({ className, ...props }, ref) => (
51 |
52 | )
53 | );
54 | CardFooter.displayName = "CardFooter";
55 |
56 | export { Card, CardHeader, CardFooter, CardTitle, CardDescription, CardContent };
57 |
--------------------------------------------------------------------------------
/components/ui/chart.tsx:
--------------------------------------------------------------------------------
1 | "use client";
2 |
3 | import * as React from "react";
4 | import * as RechartsPrimitive from "recharts";
5 |
6 | import { cn } from "@/lib/utils";
7 |
8 | // Format: { THEME_NAME: CSS_SELECTOR }
9 | const THEMES = { light: "", dark: ".dark" } as const;
10 |
11 | export type ChartConfig = {
12 | [k in string]: {
13 | label?: React.ReactNode;
14 | icon?: React.ComponentType;
15 | } & (
16 | | { color?: string; theme?: never }
17 | | { color?: never; theme: Record }
18 | );
19 | };
20 |
21 | type ChartContextProps = {
22 | config: ChartConfig;
23 | };
24 |
25 | const ChartContext = React.createContext(null);
26 |
27 | function useChart() {
28 | const context = React.useContext(ChartContext);
29 |
30 | if (!context) {
31 | throw new Error("useChart must be used within a ");
32 | }
33 |
34 | return context;
35 | }
36 |
37 | const ChartContainer = React.forwardRef<
38 | HTMLDivElement,
39 | React.ComponentProps<"div"> & {
40 | config: ChartConfig;
41 | children: React.ComponentProps<
42 | typeof RechartsPrimitive.ResponsiveContainer
43 | >["children"];
44 | }
45 | >(({ id, className, children, config, ...props }, ref) => {
46 | const uniqueId = React.useId();
47 | const chartId = `chart-${id || uniqueId.replace(/:/g, "")}`;
48 |
49 | return (
50 |
51 |
60 |
61 |
62 | {children}
63 |
64 |
65 |
66 | );
67 | });
68 | ChartContainer.displayName = "Chart";
69 |
70 | const ChartStyle = ({ id, config }: { id: string; config: ChartConfig }) => {
71 | const colorConfig = Object.entries(config).filter(
72 | ([_, config]) => config.theme || config.color,
73 | );
74 |
75 | if (!colorConfig.length) {
76 | return null;
77 | }
78 |
79 | return (
80 |