shipd 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (145) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +205 -0
  3. package/dist/index.d.ts +1 -0
  4. package/dist/index.js +1366 -0
  5. package/docs-template/README.md +255 -0
  6. package/docs-template/[slug]/[subslug]/page.tsx +1242 -0
  7. package/docs-template/[slug]/page.tsx +422 -0
  8. package/docs-template/api/page.tsx +47 -0
  9. package/docs-template/components/docs/docs-category-page.tsx +162 -0
  10. package/docs-template/components/docs/docs-code-card.tsx +135 -0
  11. package/docs-template/components/docs/docs-header.tsx +69 -0
  12. package/docs-template/components/docs/docs-nav.ts +95 -0
  13. package/docs-template/components/docs/docs-sidebar.tsx +112 -0
  14. package/docs-template/components/docs/docs-toc.tsx +38 -0
  15. package/docs-template/components/ui/badge.tsx +47 -0
  16. package/docs-template/components/ui/button.tsx +60 -0
  17. package/docs-template/components/ui/card.tsx +93 -0
  18. package/docs-template/components/ui/sheet.tsx +140 -0
  19. package/docs-template/documentation/page.tsx +80 -0
  20. package/docs-template/layout.tsx +27 -0
  21. package/docs-template/lib/utils.ts +7 -0
  22. package/docs-template/page.tsx +360 -0
  23. package/package.json +66 -0
  24. package/template/.env.example +45 -0
  25. package/template/README.md +239 -0
  26. package/template/app/api/auth/[...all]/route.ts +4 -0
  27. package/template/app/api/chat/route.ts +16 -0
  28. package/template/app/api/subscription/route.ts +25 -0
  29. package/template/app/api/upload-image/route.ts +64 -0
  30. package/template/app/blog/[slug]/page.tsx +314 -0
  31. package/template/app/blog/page.tsx +107 -0
  32. package/template/app/dashboard/_components/chart-interactive.tsx +289 -0
  33. package/template/app/dashboard/_components/chatbot.tsx +39 -0
  34. package/template/app/dashboard/_components/mode-toggle.tsx +46 -0
  35. package/template/app/dashboard/_components/navbar.tsx +84 -0
  36. package/template/app/dashboard/_components/section-cards.tsx +102 -0
  37. package/template/app/dashboard/_components/sidebar.tsx +90 -0
  38. package/template/app/dashboard/_components/subscribe-button.tsx +49 -0
  39. package/template/app/dashboard/billing/page.tsx +277 -0
  40. package/template/app/dashboard/chat/page.tsx +73 -0
  41. package/template/app/dashboard/cli/page.tsx +260 -0
  42. package/template/app/dashboard/layout.tsx +24 -0
  43. package/template/app/dashboard/page.tsx +216 -0
  44. package/template/app/dashboard/payment/_components/manage-subscription.tsx +22 -0
  45. package/template/app/dashboard/payment/page.tsx +126 -0
  46. package/template/app/dashboard/settings/page.tsx +613 -0
  47. package/template/app/dashboard/upload/page.tsx +324 -0
  48. package/template/app/error.tsx +78 -0
  49. package/template/app/favicon.ico +0 -0
  50. package/template/app/globals.css +126 -0
  51. package/template/app/layout.tsx +135 -0
  52. package/template/app/not-found.tsx +45 -0
  53. package/template/app/page.tsx +28 -0
  54. package/template/app/pricing/_component/pricing-table.tsx +276 -0
  55. package/template/app/pricing/page.tsx +23 -0
  56. package/template/app/privacy-policy/page.tsx +280 -0
  57. package/template/app/robots.txt +12 -0
  58. package/template/app/sign-in/page.tsx +228 -0
  59. package/template/app/sign-up/page.tsx +243 -0
  60. package/template/app/sitemap.ts +62 -0
  61. package/template/app/success/page.tsx +123 -0
  62. package/template/app/terms-of-service/page.tsx +212 -0
  63. package/template/auth-schema.ts +47 -0
  64. package/template/components/homepage/cli-workflow-section.tsx +138 -0
  65. package/template/components/homepage/features-section.tsx +150 -0
  66. package/template/components/homepage/footer.tsx +53 -0
  67. package/template/components/homepage/hero-section.tsx +112 -0
  68. package/template/components/homepage/integrations.tsx +124 -0
  69. package/template/components/homepage/navigation.tsx +116 -0
  70. package/template/components/homepage/news-section.tsx +82 -0
  71. package/template/components/homepage/testimonials-section.tsx +34 -0
  72. package/template/components/logos/BetterAuth.tsx +21 -0
  73. package/template/components/logos/NeonPostgres.tsx +41 -0
  74. package/template/components/logos/Nextjs.tsx +72 -0
  75. package/template/components/logos/Polar.tsx +7 -0
  76. package/template/components/logos/TailwindCSS.tsx +27 -0
  77. package/template/components/logos/index.ts +6 -0
  78. package/template/components/logos/shadcnui.tsx +8 -0
  79. package/template/components/provider.tsx +8 -0
  80. package/template/components/ui/avatar.tsx +53 -0
  81. package/template/components/ui/badge.tsx +46 -0
  82. package/template/components/ui/button.tsx +59 -0
  83. package/template/components/ui/card.tsx +92 -0
  84. package/template/components/ui/chart.tsx +353 -0
  85. package/template/components/ui/checkbox.tsx +32 -0
  86. package/template/components/ui/dialog.tsx +135 -0
  87. package/template/components/ui/dropdown-menu.tsx +257 -0
  88. package/template/components/ui/form.tsx +167 -0
  89. package/template/components/ui/input.tsx +21 -0
  90. package/template/components/ui/label.tsx +24 -0
  91. package/template/components/ui/progress.tsx +31 -0
  92. package/template/components/ui/resizable.tsx +56 -0
  93. package/template/components/ui/select.tsx +185 -0
  94. package/template/components/ui/separator.tsx +28 -0
  95. package/template/components/ui/sheet.tsx +139 -0
  96. package/template/components/ui/skeleton.tsx +13 -0
  97. package/template/components/ui/sonner.tsx +25 -0
  98. package/template/components/ui/switch.tsx +31 -0
  99. package/template/components/ui/tabs.tsx +66 -0
  100. package/template/components/ui/textarea.tsx +18 -0
  101. package/template/components/ui/toggle-group.tsx +73 -0
  102. package/template/components/ui/toggle.tsx +47 -0
  103. package/template/components/ui/tooltip.tsx +61 -0
  104. package/template/components/user-profile.tsx +139 -0
  105. package/template/components.json +21 -0
  106. package/template/db/drizzle.ts +14 -0
  107. package/template/db/migrations/0000_worried_rawhide_kid.sql +77 -0
  108. package/template/db/migrations/meta/0000_snapshot.json +494 -0
  109. package/template/db/migrations/meta/_journal.json +13 -0
  110. package/template/db/schema.ts +85 -0
  111. package/template/drizzle.config.ts +13 -0
  112. package/template/emails/components/layout.tsx +181 -0
  113. package/template/emails/password-reset.tsx +67 -0
  114. package/template/emails/payment-failed.tsx +167 -0
  115. package/template/emails/subscription-confirmation.tsx +129 -0
  116. package/template/emails/welcome.tsx +100 -0
  117. package/template/eslint.config.mjs +16 -0
  118. package/template/hooks/use-mobile.ts +21 -0
  119. package/template/lib/auth-client.ts +8 -0
  120. package/template/lib/auth.ts +276 -0
  121. package/template/lib/email.ts +118 -0
  122. package/template/lib/polar-products.ts +49 -0
  123. package/template/lib/subscription.ts +148 -0
  124. package/template/lib/upload-image.ts +28 -0
  125. package/template/lib/utils.ts +6 -0
  126. package/template/middleware.ts +30 -0
  127. package/template/next-env.d.ts +5 -0
  128. package/template/next.config.ts +27 -0
  129. package/template/package.json +99 -0
  130. package/template/postcss.config.mjs +5 -0
  131. package/template/public/add.png +0 -0
  132. package/template/public/favicon.svg +4 -0
  133. package/template/public/file.svg +1 -0
  134. package/template/public/globe.svg +1 -0
  135. package/template/public/iphone.png +0 -0
  136. package/template/public/logo.png +0 -0
  137. package/template/public/next.svg +1 -0
  138. package/template/public/polar-sh.svg +1 -0
  139. package/template/public/shadcn-ui.svg +1 -0
  140. package/template/public/site.webmanifest +21 -0
  141. package/template/public/vercel.svg +1 -0
  142. package/template/public/window.svg +1 -0
  143. package/template/tailwind.config.ts +89 -0
  144. package/template/template.config.json +138 -0
  145. package/template/tsconfig.json +27 -0
@@ -0,0 +1,289 @@
1
+ "use client";
2
+
3
+ import * as React from "react";
4
+ import { Area, AreaChart, CartesianGrid, XAxis } from "recharts";
5
+
6
+ import { useIsMobile } from "@/hooks/use-mobile";
7
+ import {
8
+ Card,
9
+ CardAction,
10
+ CardContent,
11
+ CardDescription,
12
+ CardHeader,
13
+ CardTitle,
14
+ } from "@/components/ui/card";
15
+ import {
16
+ ChartConfig,
17
+ ChartContainer,
18
+ ChartTooltip,
19
+ ChartTooltipContent,
20
+ } from "@/components/ui/chart";
21
+ import {
22
+ Select,
23
+ SelectContent,
24
+ SelectItem,
25
+ SelectTrigger,
26
+ SelectValue,
27
+ } from "@/components/ui/select";
28
+ import { ToggleGroup, ToggleGroupItem } from "@/components/ui/toggle-group";
29
+
30
+ export const description = "An interactive area chart";
31
+
32
+ const chartData = [
33
+ { date: "2024-04-01", desktop: 222, mobile: 150 },
34
+ { date: "2024-04-02", desktop: 97, mobile: 180 },
35
+ { date: "2024-04-03", desktop: 167, mobile: 120 },
36
+ { date: "2024-04-04", desktop: 242, mobile: 260 },
37
+ { date: "2024-04-05", desktop: 373, mobile: 290 },
38
+ { date: "2024-04-06", desktop: 301, mobile: 340 },
39
+ { date: "2024-04-07", desktop: 245, mobile: 180 },
40
+ { date: "2024-04-08", desktop: 409, mobile: 320 },
41
+ { date: "2024-04-09", desktop: 59, mobile: 110 },
42
+ { date: "2024-04-10", desktop: 261, mobile: 190 },
43
+ { date: "2024-04-11", desktop: 327, mobile: 350 },
44
+ { date: "2024-04-12", desktop: 292, mobile: 210 },
45
+ { date: "2024-04-13", desktop: 342, mobile: 380 },
46
+ { date: "2024-04-14", desktop: 137, mobile: 220 },
47
+ { date: "2024-04-15", desktop: 120, mobile: 170 },
48
+ { date: "2024-04-16", desktop: 138, mobile: 190 },
49
+ { date: "2024-04-17", desktop: 446, mobile: 360 },
50
+ { date: "2024-04-18", desktop: 364, mobile: 410 },
51
+ { date: "2024-04-19", desktop: 243, mobile: 180 },
52
+ { date: "2024-04-20", desktop: 89, mobile: 150 },
53
+ { date: "2024-04-21", desktop: 137, mobile: 200 },
54
+ { date: "2024-04-22", desktop: 224, mobile: 170 },
55
+ { date: "2024-04-23", desktop: 138, mobile: 230 },
56
+ { date: "2024-04-24", desktop: 387, mobile: 290 },
57
+ { date: "2024-04-25", desktop: 215, mobile: 250 },
58
+ { date: "2024-04-26", desktop: 75, mobile: 130 },
59
+ { date: "2024-04-27", desktop: 383, mobile: 420 },
60
+ { date: "2024-04-28", desktop: 122, mobile: 180 },
61
+ { date: "2024-04-29", desktop: 315, mobile: 240 },
62
+ { date: "2024-04-30", desktop: 454, mobile: 380 },
63
+ { date: "2024-05-01", desktop: 165, mobile: 220 },
64
+ { date: "2024-05-02", desktop: 293, mobile: 310 },
65
+ { date: "2024-05-03", desktop: 247, mobile: 190 },
66
+ { date: "2024-05-04", desktop: 385, mobile: 420 },
67
+ { date: "2024-05-05", desktop: 481, mobile: 390 },
68
+ { date: "2024-05-06", desktop: 498, mobile: 520 },
69
+ { date: "2024-05-07", desktop: 388, mobile: 300 },
70
+ { date: "2024-05-08", desktop: 149, mobile: 210 },
71
+ { date: "2024-05-09", desktop: 227, mobile: 180 },
72
+ { date: "2024-05-10", desktop: 293, mobile: 330 },
73
+ { date: "2024-05-11", desktop: 335, mobile: 270 },
74
+ { date: "2024-05-12", desktop: 197, mobile: 240 },
75
+ { date: "2024-05-13", desktop: 197, mobile: 160 },
76
+ { date: "2024-05-14", desktop: 448, mobile: 490 },
77
+ { date: "2024-05-15", desktop: 473, mobile: 380 },
78
+ { date: "2024-05-16", desktop: 338, mobile: 400 },
79
+ { date: "2024-05-17", desktop: 499, mobile: 420 },
80
+ { date: "2024-05-18", desktop: 315, mobile: 350 },
81
+ { date: "2024-05-19", desktop: 235, mobile: 180 },
82
+ { date: "2024-05-20", desktop: 177, mobile: 230 },
83
+ { date: "2024-05-21", desktop: 82, mobile: 140 },
84
+ { date: "2024-05-22", desktop: 81, mobile: 120 },
85
+ { date: "2024-05-23", desktop: 252, mobile: 290 },
86
+ { date: "2024-05-24", desktop: 294, mobile: 220 },
87
+ { date: "2024-05-25", desktop: 201, mobile: 250 },
88
+ { date: "2024-05-26", desktop: 213, mobile: 170 },
89
+ { date: "2024-05-27", desktop: 420, mobile: 460 },
90
+ { date: "2024-05-28", desktop: 233, mobile: 190 },
91
+ { date: "2024-05-29", desktop: 78, mobile: 130 },
92
+ { date: "2024-05-30", desktop: 340, mobile: 280 },
93
+ { date: "2024-05-31", desktop: 178, mobile: 230 },
94
+ { date: "2024-06-01", desktop: 178, mobile: 200 },
95
+ { date: "2024-06-02", desktop: 470, mobile: 410 },
96
+ { date: "2024-06-03", desktop: 103, mobile: 160 },
97
+ { date: "2024-06-04", desktop: 439, mobile: 380 },
98
+ { date: "2024-06-05", desktop: 88, mobile: 140 },
99
+ { date: "2024-06-06", desktop: 294, mobile: 250 },
100
+ { date: "2024-06-07", desktop: 323, mobile: 370 },
101
+ { date: "2024-06-08", desktop: 385, mobile: 320 },
102
+ { date: "2024-06-09", desktop: 438, mobile: 480 },
103
+ { date: "2024-06-10", desktop: 155, mobile: 200 },
104
+ { date: "2024-06-11", desktop: 92, mobile: 150 },
105
+ { date: "2024-06-12", desktop: 492, mobile: 420 },
106
+ { date: "2024-06-13", desktop: 81, mobile: 130 },
107
+ { date: "2024-06-14", desktop: 426, mobile: 380 },
108
+ { date: "2024-06-15", desktop: 307, mobile: 350 },
109
+ { date: "2024-06-16", desktop: 371, mobile: 310 },
110
+ { date: "2024-06-17", desktop: 475, mobile: 520 },
111
+ { date: "2024-06-18", desktop: 107, mobile: 170 },
112
+ { date: "2024-06-19", desktop: 341, mobile: 290 },
113
+ { date: "2024-06-20", desktop: 408, mobile: 450 },
114
+ { date: "2024-06-21", desktop: 169, mobile: 210 },
115
+ { date: "2024-06-22", desktop: 317, mobile: 270 },
116
+ { date: "2024-06-23", desktop: 480, mobile: 530 },
117
+ { date: "2024-06-24", desktop: 132, mobile: 180 },
118
+ { date: "2024-06-25", desktop: 141, mobile: 190 },
119
+ { date: "2024-06-26", desktop: 434, mobile: 380 },
120
+ { date: "2024-06-27", desktop: 448, mobile: 490 },
121
+ { date: "2024-06-28", desktop: 149, mobile: 200 },
122
+ { date: "2024-06-29", desktop: 103, mobile: 160 },
123
+ { date: "2024-06-30", desktop: 446, mobile: 400 },
124
+ ];
125
+
126
+ const chartConfig = {
127
+ visitors: {
128
+ label: "Visitors",
129
+ },
130
+ desktop: {
131
+ label: "Desktop",
132
+ color: "var(--primary)",
133
+ },
134
+ mobile: {
135
+ label: "Mobile",
136
+ color: "var(--primary)",
137
+ },
138
+ } satisfies ChartConfig;
139
+
140
+ export function ChartAreaInteractive() {
141
+ const isMobile = useIsMobile();
142
+ const [timeRange, setTimeRange] = React.useState("90d");
143
+
144
+ React.useEffect(() => {
145
+ if (isMobile) {
146
+ setTimeRange("7d");
147
+ }
148
+ }, [isMobile]);
149
+
150
+ const filteredData = chartData.filter((item) => {
151
+ const date = new Date(item.date);
152
+ const referenceDate = new Date("2024-06-30");
153
+ let daysToSubtract = 90;
154
+ if (timeRange === "30d") {
155
+ daysToSubtract = 30;
156
+ } else if (timeRange === "7d") {
157
+ daysToSubtract = 7;
158
+ }
159
+ const startDate = new Date(referenceDate);
160
+ startDate.setDate(startDate.getDate() - daysToSubtract);
161
+ return date >= startDate;
162
+ });
163
+
164
+ return (
165
+ <Card className="@container/card">
166
+ <CardHeader>
167
+ <CardTitle>Total Visitors</CardTitle>
168
+ <CardDescription>
169
+ <span className="hidden @[540px]/card:block">
170
+ Total for the last 3 months
171
+ </span>
172
+ <span className="@[540px]/card:hidden">Last 3 months</span>
173
+ </CardDescription>
174
+ <CardAction>
175
+ <ToggleGroup
176
+ type="single"
177
+ value={timeRange}
178
+ onValueChange={setTimeRange}
179
+ variant="outline"
180
+ className="hidden *:data-[slot=toggle-group-item]:!px-4 @[767px]/card:flex"
181
+ >
182
+ <ToggleGroupItem value="90d">Last 3 months</ToggleGroupItem>
183
+ <ToggleGroupItem value="30d">Last 30 days</ToggleGroupItem>
184
+ <ToggleGroupItem value="7d">Last 7 days</ToggleGroupItem>
185
+ </ToggleGroup>
186
+ <Select value={timeRange} onValueChange={setTimeRange}>
187
+ <SelectTrigger
188
+ className="flex w-40 **:data-[slot=select-value]:block **:data-[slot=select-value]:truncate @[767px]/card:hidden"
189
+ size="sm"
190
+ aria-label="Select a value"
191
+ >
192
+ <SelectValue placeholder="Last 3 months" />
193
+ </SelectTrigger>
194
+ <SelectContent className="rounded-xl">
195
+ <SelectItem value="90d" className="rounded-lg">
196
+ Last 3 months
197
+ </SelectItem>
198
+ <SelectItem value="30d" className="rounded-lg">
199
+ Last 30 days
200
+ </SelectItem>
201
+ <SelectItem value="7d" className="rounded-lg">
202
+ Last 7 days
203
+ </SelectItem>
204
+ </SelectContent>
205
+ </Select>
206
+ </CardAction>
207
+ </CardHeader>
208
+ <CardContent className="px-2 pt-4 sm:px-6 sm:pt-6">
209
+ <ChartContainer
210
+ config={chartConfig}
211
+ className="aspect-auto h-[250px] w-full"
212
+ >
213
+ <AreaChart data={filteredData}>
214
+ <defs>
215
+ <linearGradient id="fillDesktop" x1="0" y1="0" x2="0" y2="1">
216
+ <stop
217
+ offset="5%"
218
+ stopColor="var(--color-desktop)"
219
+ stopOpacity={1.0}
220
+ />
221
+ <stop
222
+ offset="95%"
223
+ stopColor="var(--color-desktop)"
224
+ stopOpacity={0.1}
225
+ />
226
+ </linearGradient>
227
+ <linearGradient id="fillMobile" x1="0" y1="0" x2="0" y2="1">
228
+ <stop
229
+ offset="5%"
230
+ stopColor="var(--color-mobile)"
231
+ stopOpacity={0.8}
232
+ />
233
+ <stop
234
+ offset="95%"
235
+ stopColor="var(--color-mobile)"
236
+ stopOpacity={0.1}
237
+ />
238
+ </linearGradient>
239
+ </defs>
240
+ <CartesianGrid vertical={false} />
241
+ <XAxis
242
+ dataKey="date"
243
+ tickLine={false}
244
+ axisLine={false}
245
+ tickMargin={8}
246
+ minTickGap={32}
247
+ tickFormatter={(value) => {
248
+ const date = new Date(value);
249
+ return date.toLocaleDateString("en-US", {
250
+ month: "short",
251
+ day: "numeric",
252
+ });
253
+ }}
254
+ />
255
+ <ChartTooltip
256
+ cursor={false}
257
+ defaultIndex={isMobile ? -1 : 10}
258
+ content={
259
+ <ChartTooltipContent
260
+ labelFormatter={(value) => {
261
+ return new Date(value).toLocaleDateString("en-US", {
262
+ month: "short",
263
+ day: "numeric",
264
+ });
265
+ }}
266
+ indicator="dot"
267
+ />
268
+ }
269
+ />
270
+ <Area
271
+ dataKey="mobile"
272
+ type="natural"
273
+ fill="url(#fillMobile)"
274
+ stroke="var(--color-mobile)"
275
+ stackId="a"
276
+ />
277
+ <Area
278
+ dataKey="desktop"
279
+ type="natural"
280
+ fill="url(#fillDesktop)"
281
+ stroke="var(--color-desktop)"
282
+ stackId="a"
283
+ />
284
+ </AreaChart>
285
+ </ChartContainer>
286
+ </CardContent>
287
+ </Card>
288
+ );
289
+ }
@@ -0,0 +1,39 @@
1
+ "use client";
2
+ import { Input } from "@/components/ui/input";
3
+ import { Bot, X } from "lucide-react";
4
+ import { useState } from "react";
5
+
6
+ export default function Chatbot() {
7
+ const [open, setOpen] = useState(false);
8
+ return (
9
+ <div className="absolute bottom-4 right-4 z-[99]">
10
+ <div
11
+ className="rounded-full bg-black/10 cursor-pointer border p-3"
12
+ onClick={() => setOpen(!open)}
13
+ >
14
+ <Bot className="w-4 h-4 transition-transform hover:scale-125 hover:rotate-12 duration-300 ease-in-out" />
15
+ </div>
16
+ {open && (
17
+ <div className="absolute bottom-12 right-4 w-80 z-[99] dark:bg-black bg-white">
18
+ <div className="flex flex-col items-start justify-between gap-3 rounded-lg border h-96 shadow-lg p-4">
19
+ <div className="w-full">
20
+ <div className="flex items-center justify-between">
21
+ <h3 className="text-lg font-semibold">Nextjs Starter Kit</h3>
22
+ <X
23
+ className="w-4 h-4 hover:cursor-pointer"
24
+ onClick={() => setOpen(false)}
25
+ />
26
+ </div>
27
+ <p className="text-sm text-muted-foreground mt-1 mb-4">
28
+ Ask me anything about Nextjs Starter Kit
29
+ </p>
30
+ </div>
31
+ <div className="flex items-end justify-center gap-2 w-full">
32
+ <Input className="w-full" placeholder="Ask me anything" />
33
+ </div>
34
+ </div>
35
+ </div>
36
+ )}
37
+ </div>
38
+ );
39
+ }
@@ -0,0 +1,46 @@
1
+ "use client"
2
+
3
+ import { useEffect, useState } from "react";
4
+ import { Moon, Sun } from "lucide-react";
5
+ import { useTheme } from "next-themes";
6
+
7
+ import { Button } from "@/components/ui/button";
8
+
9
+ export default function ModeToggle() {
10
+ const { theme, setTheme } = useTheme();
11
+ const [mounted, setMounted] = useState(false);
12
+
13
+ // After mounting, we have access to the theme
14
+ useEffect(() => setMounted(true), []);
15
+
16
+ if (!mounted) {
17
+ // Render nothing on the server and until the theme is mounted
18
+ return null;
19
+ }
20
+
21
+ return (
22
+ <div>
23
+ {theme === "dark" ? (
24
+ <Button
25
+ variant="ghost"
26
+ className="hover:bg-[#ff5722]/10 hover:text-[#ff5722]"
27
+ size="icon"
28
+ onClick={() => setTheme("light")}
29
+ >
30
+ <Sun className="w-5 h-5" />
31
+ <span className="sr-only">Switch to light mode</span>
32
+ </Button>
33
+ ) : (
34
+ <Button
35
+ variant="ghost"
36
+ size="icon"
37
+ className="hover:bg-[#ff5722]/10 hover:text-[#ff5722]"
38
+ onClick={() => setTheme("dark")}
39
+ >
40
+ <Moon className="w-5 h-5" />
41
+ <span className="sr-only">Switch to dark mode</span>
42
+ </Button>
43
+ )}
44
+ </div>
45
+ );
46
+ }
@@ -0,0 +1,84 @@
1
+ "use client";
2
+
3
+ import { Button } from "@/components/ui/button";
4
+ import { Dialog, DialogClose } from "@/components/ui/dialog";
5
+ import { Separator } from "@/components/ui/separator";
6
+ import {
7
+ SheetContent,
8
+ SheetHeader,
9
+ SheetTitle,
10
+ SheetTrigger,
11
+ } from "@/components/ui/sheet";
12
+ import UserProfile from "@/components/user-profile";
13
+ import {
14
+ CreditCard,
15
+ HomeIcon,
16
+ Settings,
17
+ Terminal,
18
+ } from "lucide-react";
19
+ import Link from "next/link";
20
+ import { ReactNode } from "react";
21
+ import ModeToggle from "./mode-toggle";
22
+
23
+ export default function DashboardTopNav({ children }: { children: ReactNode }) {
24
+ return (
25
+ <div className="flex flex-col">
26
+ <header className="flex h-14 lg:h-[52px] items-center gap-4 border-b px-3">
27
+ <Dialog>
28
+ <SheetTrigger className="min-[1024px]:hidden p-2 transition">
29
+ <Link prefetch={true} href="/dashboard">
30
+ <span className="sr-only">Home</span>
31
+ </Link>
32
+ </SheetTrigger>
33
+ <SheetContent side="left">
34
+ <SheetHeader>
35
+ <Link prefetch={true} href="/">
36
+ <SheetTitle>SaaS Scaffold</SheetTitle>
37
+ </Link>
38
+ </SheetHeader>
39
+ <div className="flex flex-col space-y-3 mt-[1rem]">
40
+ <DialogClose asChild>
41
+ <Link prefetch={true} href="/dashboard">
42
+ <Button variant="outline" className="w-full">
43
+ <HomeIcon className="mr-2 h-4 w-4" />
44
+ Overview
45
+ </Button>
46
+ </Link>
47
+ </DialogClose>
48
+ <DialogClose asChild>
49
+ <Link prefetch={true} href="/dashboard/cli">
50
+ <Button variant="outline" className="w-full">
51
+ <Terminal className="mr-2 h-4 w-4" />
52
+ CLI Access
53
+ </Button>
54
+ </Link>
55
+ </DialogClose>
56
+ <DialogClose asChild>
57
+ <Link prefetch={true} href="/dashboard/billing">
58
+ <Button variant="outline" className="w-full">
59
+ <CreditCard className="mr-2 h-4 w-4" />
60
+ Billing
61
+ </Button>
62
+ </Link>
63
+ </DialogClose>
64
+ <Separator className="my-3" />
65
+ <DialogClose asChild>
66
+ <Link prefetch={true} href="/dashboard/settings">
67
+ <Button variant="outline" className="w-full">
68
+ <Settings className="mr-2 h-4 w-4" />
69
+ Settings
70
+ </Button>
71
+ </Link>
72
+ </DialogClose>
73
+ </div>
74
+ </SheetContent>
75
+ </Dialog>
76
+ <div className="flex justify-center items-center gap-2 ml-auto">
77
+ <ModeToggle />
78
+ <UserProfile mini={true} />
79
+ </div>
80
+ </header>
81
+ {children}
82
+ </div>
83
+ );
84
+ }
@@ -0,0 +1,102 @@
1
+ import { IconTrendingUp } from "@tabler/icons-react";
2
+
3
+ import { Badge } from "@/components/ui/badge";
4
+ import {
5
+ Card,
6
+ CardAction,
7
+ CardDescription,
8
+ CardFooter,
9
+ CardHeader,
10
+ CardTitle,
11
+ } from "@/components/ui/card";
12
+
13
+ export function SectionCards() {
14
+ return (
15
+ <div className="*:data-[slot=card]:from-primary/5 *:data-[slot=card]:to-card dark:*:data-[slot=card]:bg-card grid grid-cols-1 gap-4 *:data-[slot=card]:bg-gradient-to-t *:data-[slot=card]:shadow-xs @xl/main:grid-cols-2 @5xl/main:grid-cols-4">
16
+ <Card className="@container/card">
17
+ <CardHeader>
18
+ <CardDescription>Projects Generated</CardDescription>
19
+ <CardTitle className="text-2xl font-semibold tabular-nums @[250px]/card:text-3xl">
20
+ 1,247
21
+ </CardTitle>
22
+ <CardAction>
23
+ <Badge variant="outline">
24
+ <IconTrendingUp />
25
+ +18.2%
26
+ </Badge>
27
+ </CardAction>
28
+ </CardHeader>
29
+ <CardFooter className="flex-col items-start gap-1.5 text-sm">
30
+ <div className="line-clamp-1 flex gap-2 font-medium">
31
+ Growing adoption rate <IconTrendingUp className="size-4" />
32
+ </div>
33
+ <div className="text-muted-foreground">
34
+ Projects created this month
35
+ </div>
36
+ </CardFooter>
37
+ </Card>
38
+ <Card className="@container/card">
39
+ <CardHeader>
40
+ <CardDescription>GitHub Stars</CardDescription>
41
+ <CardTitle className="text-2xl font-semibold tabular-nums @[250px]/card:text-3xl">
42
+ 324
43
+ </CardTitle>
44
+ <CardAction>
45
+ <Badge variant="outline">
46
+ <IconTrendingUp />
47
+ +45%
48
+ </Badge>
49
+ </CardAction>
50
+ </CardHeader>
51
+ <CardFooter className="flex-col items-start gap-1.5 text-sm">
52
+ <div className="line-clamp-1 flex gap-2 font-medium">
53
+ Community is growing <IconTrendingUp className="size-4" />
54
+ </div>
55
+ <div className="text-muted-foreground">
56
+ Star us on GitHub
57
+ </div>
58
+ </CardFooter>
59
+ </Card>
60
+ <Card className="@container/card">
61
+ <CardHeader>
62
+ <CardDescription>Active Developers</CardDescription>
63
+ <CardTitle className="text-2xl font-semibold tabular-nums @[250px]/card:text-3xl">
64
+ 892
65
+ </CardTitle>
66
+ <CardAction>
67
+ <Badge variant="outline">
68
+ <IconTrendingUp />
69
+ +24.3%
70
+ </Badge>
71
+ </CardAction>
72
+ </CardHeader>
73
+ <CardFooter className="flex-col items-start gap-1.5 text-sm">
74
+ <div className="line-clamp-1 flex gap-2 font-medium">
75
+ Strong community engagement <IconTrendingUp className="size-4" />
76
+ </div>
77
+ <div className="text-muted-foreground">Weekly active users</div>
78
+ </CardFooter>
79
+ </Card>
80
+ <Card className="@container/card">
81
+ <CardHeader>
82
+ <CardDescription>Feature Adoption</CardDescription>
83
+ <CardTitle className="text-2xl font-semibold tabular-nums @[250px]/card:text-3xl">
84
+ 78%
85
+ </CardTitle>
86
+ <CardAction>
87
+ <Badge variant="outline">
88
+ <IconTrendingUp />
89
+ +8.5%
90
+ </Badge>
91
+ </CardAction>
92
+ </CardHeader>
93
+ <CardFooter className="flex-col items-start gap-1.5 text-sm">
94
+ <div className="line-clamp-1 flex gap-2 font-medium">
95
+ Users love optional features <IconTrendingUp className="size-4" />
96
+ </div>
97
+ <div className="text-muted-foreground">Avg features per project</div>
98
+ </CardFooter>
99
+ </Card>
100
+ </div>
101
+ );
102
+ }
@@ -0,0 +1,90 @@
1
+ "use client";
2
+
3
+ import UserProfile from "@/components/user-profile";
4
+ import clsx from "clsx";
5
+ import {
6
+ CreditCard,
7
+ HomeIcon,
8
+ LucideIcon,
9
+ Settings,
10
+ } from "lucide-react";
11
+ import Link from "next/link";
12
+ import { usePathname, useRouter } from "next/navigation";
13
+
14
+ interface NavItem {
15
+ label: string;
16
+ href: string;
17
+ icon: LucideIcon;
18
+ }
19
+
20
+ const navItems: NavItem[] = [
21
+ {
22
+ label: "Overview",
23
+ href: "/dashboard",
24
+ icon: HomeIcon,
25
+ },
26
+ {
27
+ label: "Billing",
28
+ href: "/dashboard/billing",
29
+ icon: CreditCard,
30
+ },
31
+ ];
32
+
33
+ export default function DashboardSideBar() {
34
+ const pathname = usePathname();
35
+ const router = useRouter();
36
+
37
+ return (
38
+ <div className="min-[1024px]:block hidden w-64 border-r h-full bg-background">
39
+ <div className="flex h-full flex-col">
40
+ <div className="flex h-[3.45rem] items-center border-b px-4">
41
+ <Link
42
+ prefetch={true}
43
+ className="flex items-center font-semibold hover:cursor-pointer"
44
+ href="/"
45
+ >
46
+ <span>SaaS Scaffold</span>
47
+ </Link>
48
+ </div>
49
+
50
+ <nav className="flex flex-col h-full justify-between items-start w-full space-y-1">
51
+ <div className="w-full space-y-1 p-4">
52
+ {navItems.map((item) => (
53
+ <div
54
+ key={item.href}
55
+ onClick={() => router.push(item.href)}
56
+ className={clsx(
57
+ "flex items-center gap-2 w-full rounded-lg px-3 py-2 text-sm font-medium transition-colors hover:cursor-pointer",
58
+ pathname === item.href
59
+ ? "bg-primary/10 text-primary hover:bg-primary/20"
60
+ : "text-muted-foreground hover:bg-muted hover:text-foreground",
61
+ )}
62
+ >
63
+ <item.icon className="h-4 w-4" />
64
+ {item.label}
65
+ </div>
66
+ ))}
67
+ </div>
68
+
69
+ <div className="flex flex-col gap-2 w-full">
70
+ <div className="px-4">
71
+ <div
72
+ onClick={() => router.push("/dashboard/settings")}
73
+ className={clsx(
74
+ "flex items-center w-full gap-2 rounded-lg px-3 py-2 text-sm font-medium transition-colors hover:cursor-pointer",
75
+ pathname === "/dashboard/settings"
76
+ ? "bg-primary/10 text-primary hover:bg-primary/20"
77
+ : "text-muted-foreground hover:bg-muted hover:text-foreground",
78
+ )}
79
+ >
80
+ <Settings className="h-4 w-4" />
81
+ Settings
82
+ </div>
83
+ </div>
84
+ <UserProfile />
85
+ </div>
86
+ </nav>
87
+ </div>
88
+ </div>
89
+ );
90
+ }