@victusvinceere/saas-core 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 (64) hide show
  1. package/dist/auth/index.d.mts +40 -0
  2. package/dist/auth/index.d.ts +40 -0
  3. package/dist/auth/index.js +147 -0
  4. package/dist/auth/index.js.map +1 -0
  5. package/dist/auth/index.mjs +111 -0
  6. package/dist/auth/index.mjs.map +1 -0
  7. package/dist/authorization/index.d.mts +78 -0
  8. package/dist/authorization/index.d.ts +78 -0
  9. package/dist/authorization/index.js +137 -0
  10. package/dist/authorization/index.js.map +1 -0
  11. package/dist/authorization/index.mjs +104 -0
  12. package/dist/authorization/index.mjs.map +1 -0
  13. package/dist/components/auth/index.d.mts +26 -0
  14. package/dist/components/auth/index.d.ts +26 -0
  15. package/dist/components/auth/index.js +733 -0
  16. package/dist/components/auth/index.js.map +1 -0
  17. package/dist/components/auth/index.mjs +696 -0
  18. package/dist/components/auth/index.mjs.map +1 -0
  19. package/dist/components/dashboard/index.d.mts +32 -0
  20. package/dist/components/dashboard/index.d.ts +32 -0
  21. package/dist/components/dashboard/index.js +440 -0
  22. package/dist/components/dashboard/index.js.map +1 -0
  23. package/dist/components/dashboard/index.mjs +401 -0
  24. package/dist/components/dashboard/index.mjs.map +1 -0
  25. package/dist/components/ui/index.d.mts +351 -0
  26. package/dist/components/ui/index.d.ts +351 -0
  27. package/dist/components/ui/index.js +14342 -0
  28. package/dist/components/ui/index.js.map +1 -0
  29. package/dist/components/ui/index.mjs +14173 -0
  30. package/dist/components/ui/index.mjs.map +1 -0
  31. package/dist/config/index.d.mts +45 -0
  32. package/dist/config/index.d.ts +45 -0
  33. package/dist/config/index.js +71 -0
  34. package/dist/config/index.js.map +1 -0
  35. package/dist/config/index.mjs +44 -0
  36. package/dist/config/index.mjs.map +1 -0
  37. package/dist/hooks/index.d.mts +20 -0
  38. package/dist/hooks/index.d.ts +20 -0
  39. package/dist/hooks/index.js +103 -0
  40. package/dist/hooks/index.js.map +1 -0
  41. package/dist/hooks/index.mjs +65 -0
  42. package/dist/hooks/index.mjs.map +1 -0
  43. package/dist/index.d.mts +21 -0
  44. package/dist/index.d.ts +21 -0
  45. package/dist/index.js +459 -0
  46. package/dist/index.js.map +1 -0
  47. package/dist/index.mjs +401 -0
  48. package/dist/index.mjs.map +1 -0
  49. package/dist/prisma/index.d.mts +11 -0
  50. package/dist/prisma/index.d.ts +11 -0
  51. package/dist/prisma/index.js +46 -0
  52. package/dist/prisma/index.js.map +1 -0
  53. package/dist/prisma/index.mjs +20 -0
  54. package/dist/prisma/index.mjs.map +1 -0
  55. package/dist/providers/index.d.mts +37 -0
  56. package/dist/providers/index.d.ts +37 -0
  57. package/dist/providers/index.js +97 -0
  58. package/dist/providers/index.js.map +1 -0
  59. package/dist/providers/index.mjs +69 -0
  60. package/dist/providers/index.mjs.map +1 -0
  61. package/dist/sidebar-ttX_iZ40.d.mts +22 -0
  62. package/dist/sidebar-ttX_iZ40.d.ts +22 -0
  63. package/package.json +122 -0
  64. package/prisma/schema.prisma +106 -0
@@ -0,0 +1,696 @@
1
+ "use client";
2
+
3
+ // src/components/ui/avatar.tsx
4
+ import * as AvatarPrimitive from "@radix-ui/react-avatar";
5
+
6
+ // src/lib/utils.ts
7
+ import { clsx } from "clsx";
8
+ import { twMerge } from "tailwind-merge";
9
+ function cn(...inputs) {
10
+ return twMerge(clsx(inputs));
11
+ }
12
+
13
+ // src/components/ui/avatar.tsx
14
+ import { jsx } from "react/jsx-runtime";
15
+ function Avatar({
16
+ className,
17
+ ...props
18
+ }) {
19
+ return /* @__PURE__ */ jsx(
20
+ AvatarPrimitive.Root,
21
+ {
22
+ "data-slot": "avatar",
23
+ className: cn(
24
+ "relative flex size-8 shrink-0 overflow-hidden rounded-full",
25
+ className
26
+ ),
27
+ ...props
28
+ }
29
+ );
30
+ }
31
+ function AvatarImage({
32
+ className,
33
+ ...props
34
+ }) {
35
+ return /* @__PURE__ */ jsx(
36
+ AvatarPrimitive.Image,
37
+ {
38
+ "data-slot": "avatar-image",
39
+ className: cn("aspect-square size-full", className),
40
+ ...props
41
+ }
42
+ );
43
+ }
44
+ function AvatarFallback({
45
+ className,
46
+ ...props
47
+ }) {
48
+ return /* @__PURE__ */ jsx(
49
+ AvatarPrimitive.Fallback,
50
+ {
51
+ "data-slot": "avatar-fallback",
52
+ className: cn(
53
+ "bg-muted flex size-full items-center justify-center rounded-full",
54
+ className
55
+ ),
56
+ ...props
57
+ }
58
+ );
59
+ }
60
+
61
+ // src/components/ui/button.tsx
62
+ import { Slot } from "@radix-ui/react-slot";
63
+ import { cva } from "class-variance-authority";
64
+ import { jsx as jsx2 } from "react/jsx-runtime";
65
+ var buttonVariants = cva(
66
+ "inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-all disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4 shrink-0 [&_svg]:shrink-0 outline-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive",
67
+ {
68
+ variants: {
69
+ variant: {
70
+ default: "bg-primary text-primary-foreground hover:bg-primary/90",
71
+ destructive: "bg-destructive text-white hover:bg-destructive/90 focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40 dark:bg-destructive/60",
72
+ outline: "border bg-background shadow-xs hover:bg-accent hover:text-accent-foreground dark:bg-input/30 dark:border-input dark:hover:bg-input/50",
73
+ secondary: "bg-secondary text-secondary-foreground hover:bg-secondary/80",
74
+ ghost: "hover:bg-accent hover:text-accent-foreground dark:hover:bg-accent/50",
75
+ link: "text-primary underline-offset-4 hover:underline"
76
+ },
77
+ size: {
78
+ default: "h-9 px-4 py-2 has-[>svg]:px-3",
79
+ sm: "h-8 rounded-md gap-1.5 px-3 has-[>svg]:px-2.5",
80
+ lg: "h-10 rounded-md px-6 has-[>svg]:px-4",
81
+ icon: "size-9",
82
+ "icon-sm": "size-8",
83
+ "icon-lg": "size-10"
84
+ }
85
+ },
86
+ defaultVariants: {
87
+ variant: "default",
88
+ size: "default"
89
+ }
90
+ }
91
+ );
92
+ function Button({
93
+ className,
94
+ variant = "default",
95
+ size = "default",
96
+ asChild = false,
97
+ ...props
98
+ }) {
99
+ const Comp = asChild ? Slot : "button";
100
+ return /* @__PURE__ */ jsx2(
101
+ Comp,
102
+ {
103
+ "data-slot": "button",
104
+ "data-variant": variant,
105
+ "data-size": size,
106
+ className: cn(buttonVariants({ variant, size, className })),
107
+ ...props
108
+ }
109
+ );
110
+ }
111
+
112
+ // src/components/ui/dropdown-menu.tsx
113
+ import * as DropdownMenuPrimitive from "@radix-ui/react-dropdown-menu";
114
+ import { CheckIcon, ChevronRightIcon, CircleIcon } from "lucide-react";
115
+ import { jsx as jsx3, jsxs } from "react/jsx-runtime";
116
+ function DropdownMenu({
117
+ ...props
118
+ }) {
119
+ return /* @__PURE__ */ jsx3(DropdownMenuPrimitive.Root, { "data-slot": "dropdown-menu", ...props });
120
+ }
121
+ function DropdownMenuTrigger({
122
+ ...props
123
+ }) {
124
+ return /* @__PURE__ */ jsx3(
125
+ DropdownMenuPrimitive.Trigger,
126
+ {
127
+ "data-slot": "dropdown-menu-trigger",
128
+ ...props
129
+ }
130
+ );
131
+ }
132
+ function DropdownMenuContent({
133
+ className,
134
+ sideOffset = 4,
135
+ ...props
136
+ }) {
137
+ return /* @__PURE__ */ jsx3(DropdownMenuPrimitive.Portal, { children: /* @__PURE__ */ jsx3(
138
+ DropdownMenuPrimitive.Content,
139
+ {
140
+ "data-slot": "dropdown-menu-content",
141
+ sideOffset,
142
+ className: cn(
143
+ "bg-popover text-popover-foreground data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 z-50 max-h-(--radix-dropdown-menu-content-available-height) min-w-[8rem] origin-(--radix-dropdown-menu-content-transform-origin) overflow-x-hidden overflow-y-auto rounded-md border p-1 shadow-md",
144
+ className
145
+ ),
146
+ ...props
147
+ }
148
+ ) });
149
+ }
150
+ function DropdownMenuItem({
151
+ className,
152
+ inset,
153
+ variant = "default",
154
+ ...props
155
+ }) {
156
+ return /* @__PURE__ */ jsx3(
157
+ DropdownMenuPrimitive.Item,
158
+ {
159
+ "data-slot": "dropdown-menu-item",
160
+ "data-inset": inset,
161
+ "data-variant": variant,
162
+ className: cn(
163
+ "focus:bg-accent focus:text-accent-foreground data-[variant=destructive]:text-destructive data-[variant=destructive]:focus:bg-destructive/10 dark:data-[variant=destructive]:focus:bg-destructive/20 data-[variant=destructive]:focus:text-destructive data-[variant=destructive]:*:[svg]:!text-destructive [&_svg:not([class*='text-'])]:text-muted-foreground relative flex cursor-default items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-hidden select-none data-[disabled]:pointer-events-none data-[disabled]:opacity-50 data-[inset]:pl-8 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
164
+ className
165
+ ),
166
+ ...props
167
+ }
168
+ );
169
+ }
170
+ function DropdownMenuLabel({
171
+ className,
172
+ inset,
173
+ ...props
174
+ }) {
175
+ return /* @__PURE__ */ jsx3(
176
+ DropdownMenuPrimitive.Label,
177
+ {
178
+ "data-slot": "dropdown-menu-label",
179
+ "data-inset": inset,
180
+ className: cn(
181
+ "px-2 py-1.5 text-sm font-medium data-[inset]:pl-8",
182
+ className
183
+ ),
184
+ ...props
185
+ }
186
+ );
187
+ }
188
+ function DropdownMenuSeparator({
189
+ className,
190
+ ...props
191
+ }) {
192
+ return /* @__PURE__ */ jsx3(
193
+ DropdownMenuPrimitive.Separator,
194
+ {
195
+ "data-slot": "dropdown-menu-separator",
196
+ className: cn("bg-border -mx-1 my-1 h-px", className),
197
+ ...props
198
+ }
199
+ );
200
+ }
201
+
202
+ // src/components/ui/drawer.tsx
203
+ import { Drawer as DrawerPrimitive } from "vaul";
204
+ import { jsx as jsx4, jsxs as jsxs2 } from "react/jsx-runtime";
205
+ function Drawer({
206
+ ...props
207
+ }) {
208
+ return /* @__PURE__ */ jsx4(DrawerPrimitive.Root, { "data-slot": "drawer", ...props });
209
+ }
210
+ function DrawerTrigger({
211
+ ...props
212
+ }) {
213
+ return /* @__PURE__ */ jsx4(DrawerPrimitive.Trigger, { "data-slot": "drawer-trigger", ...props });
214
+ }
215
+ function DrawerPortal({
216
+ ...props
217
+ }) {
218
+ return /* @__PURE__ */ jsx4(DrawerPrimitive.Portal, { "data-slot": "drawer-portal", ...props });
219
+ }
220
+ function DrawerOverlay({
221
+ className,
222
+ ...props
223
+ }) {
224
+ return /* @__PURE__ */ jsx4(
225
+ DrawerPrimitive.Overlay,
226
+ {
227
+ "data-slot": "drawer-overlay",
228
+ className: cn(
229
+ "data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 fixed inset-0 z-50 bg-black/50",
230
+ className
231
+ ),
232
+ ...props
233
+ }
234
+ );
235
+ }
236
+ function DrawerContent({
237
+ className,
238
+ children,
239
+ ...props
240
+ }) {
241
+ return /* @__PURE__ */ jsxs2(DrawerPortal, { "data-slot": "drawer-portal", children: [
242
+ /* @__PURE__ */ jsx4(DrawerOverlay, {}),
243
+ /* @__PURE__ */ jsxs2(
244
+ DrawerPrimitive.Content,
245
+ {
246
+ "data-slot": "drawer-content",
247
+ className: cn(
248
+ "group/drawer-content bg-background fixed z-50 flex h-auto flex-col",
249
+ "data-[vaul-drawer-direction=top]:inset-x-0 data-[vaul-drawer-direction=top]:top-0 data-[vaul-drawer-direction=top]:mb-24 data-[vaul-drawer-direction=top]:max-h-[80vh] data-[vaul-drawer-direction=top]:rounded-b-lg data-[vaul-drawer-direction=top]:border-b",
250
+ "data-[vaul-drawer-direction=bottom]:inset-x-0 data-[vaul-drawer-direction=bottom]:bottom-0 data-[vaul-drawer-direction=bottom]:mt-24 data-[vaul-drawer-direction=bottom]:max-h-[80vh] data-[vaul-drawer-direction=bottom]:rounded-t-lg data-[vaul-drawer-direction=bottom]:border-t",
251
+ "data-[vaul-drawer-direction=right]:inset-y-0 data-[vaul-drawer-direction=right]:right-0 data-[vaul-drawer-direction=right]:w-3/4 data-[vaul-drawer-direction=right]:border-l data-[vaul-drawer-direction=right]:sm:max-w-sm",
252
+ "data-[vaul-drawer-direction=left]:inset-y-0 data-[vaul-drawer-direction=left]:left-0 data-[vaul-drawer-direction=left]:w-3/4 data-[vaul-drawer-direction=left]:border-r data-[vaul-drawer-direction=left]:sm:max-w-sm",
253
+ className
254
+ ),
255
+ ...props,
256
+ children: [
257
+ /* @__PURE__ */ jsx4("div", { className: "bg-muted mx-auto mt-4 hidden h-2 w-[100px] shrink-0 rounded-full group-data-[vaul-drawer-direction=bottom]/drawer-content:block" }),
258
+ children
259
+ ]
260
+ }
261
+ )
262
+ ] });
263
+ }
264
+ function DrawerHeader({ className, ...props }) {
265
+ return /* @__PURE__ */ jsx4(
266
+ "div",
267
+ {
268
+ "data-slot": "drawer-header",
269
+ className: cn(
270
+ "flex flex-col gap-0.5 p-4 group-data-[vaul-drawer-direction=bottom]/drawer-content:text-center group-data-[vaul-drawer-direction=top]/drawer-content:text-center md:gap-1.5 md:text-left",
271
+ className
272
+ ),
273
+ ...props
274
+ }
275
+ );
276
+ }
277
+ function DrawerTitle({
278
+ className,
279
+ ...props
280
+ }) {
281
+ return /* @__PURE__ */ jsx4(
282
+ DrawerPrimitive.Title,
283
+ {
284
+ "data-slot": "drawer-title",
285
+ className: cn("text-foreground font-semibold", className),
286
+ ...props
287
+ }
288
+ );
289
+ }
290
+
291
+ // src/components/auth/user-button.tsx
292
+ import { signOut, useSession } from "next-auth/react";
293
+ import { LogOut, Settings, User, CreditCard, Moon, Sun } from "lucide-react";
294
+ import Link from "next/link";
295
+
296
+ // src/hooks/use-mobile.ts
297
+ import * as React from "react";
298
+ var MOBILE_BREAKPOINT = 768;
299
+ function useIsMobile() {
300
+ const [isMobile, setIsMobile] = React.useState(void 0);
301
+ React.useEffect(() => {
302
+ const mql = window.matchMedia(`(max-width: ${MOBILE_BREAKPOINT - 1}px)`);
303
+ const onChange = () => {
304
+ setIsMobile(window.innerWidth < MOBILE_BREAKPOINT);
305
+ };
306
+ mql.addEventListener("change", onChange);
307
+ setIsMobile(window.innerWidth < MOBILE_BREAKPOINT);
308
+ return () => mql.removeEventListener("change", onChange);
309
+ }, []);
310
+ return !!isMobile;
311
+ }
312
+
313
+ // src/components/auth/user-button.tsx
314
+ import { useTheme } from "next-themes";
315
+ import { jsx as jsx5, jsxs as jsxs3 } from "react/jsx-runtime";
316
+ var defaultMenuItems = [
317
+ {
318
+ icon: User,
319
+ label: "Profile",
320
+ href: "/dashboard/settings/profile"
321
+ },
322
+ {
323
+ icon: CreditCard,
324
+ label: "Billing",
325
+ href: "/dashboard/settings/billing"
326
+ },
327
+ {
328
+ icon: Settings,
329
+ label: "Settings",
330
+ href: "/dashboard/settings"
331
+ }
332
+ ];
333
+ function UserButton({
334
+ menuItems = defaultMenuItems,
335
+ signInHref = "/login",
336
+ showThemeToggle = true
337
+ }) {
338
+ const { data: session } = useSession();
339
+ const isMobile = useIsMobile();
340
+ const { theme, setTheme } = useTheme();
341
+ if (!session?.user) {
342
+ return /* @__PURE__ */ jsx5(Button, { asChild: true, variant: "ghost", size: "sm", children: /* @__PURE__ */ jsx5(Link, { href: signInHref, children: "Sign in" }) });
343
+ }
344
+ const initials = session.user.name?.split(" ").map((n) => n[0]).join("").toUpperCase() || session.user.email?.[0]?.toUpperCase() || "U";
345
+ const toggleTheme = () => {
346
+ setTheme(theme === "dark" ? "light" : "dark");
347
+ };
348
+ const handleSignOut = () => {
349
+ signOut({ callbackUrl: "/" });
350
+ };
351
+ const AvatarButton = /* @__PURE__ */ jsx5(Button, { variant: "ghost", className: "relative h-8 w-8 rounded-full", children: /* @__PURE__ */ jsxs3(Avatar, { className: "h-8 w-8", children: [
352
+ /* @__PURE__ */ jsx5(
353
+ AvatarImage,
354
+ {
355
+ src: session.user.image || void 0,
356
+ alt: session.user.name || "User"
357
+ }
358
+ ),
359
+ /* @__PURE__ */ jsx5(AvatarFallback, { className: "bg-primary/10 text-primary", children: initials })
360
+ ] }) });
361
+ if (isMobile) {
362
+ return /* @__PURE__ */ jsxs3(Drawer, { children: [
363
+ /* @__PURE__ */ jsx5(DrawerTrigger, { asChild: true, children: AvatarButton }),
364
+ /* @__PURE__ */ jsxs3(DrawerContent, { children: [
365
+ /* @__PURE__ */ jsx5(DrawerHeader, { className: "text-left", children: /* @__PURE__ */ jsx5(DrawerTitle, { children: "Account" }) }),
366
+ /* @__PURE__ */ jsxs3("div", { className: "px-4 pb-8", children: [
367
+ /* @__PURE__ */ jsxs3("div", { className: "mb-6 flex items-center gap-3", children: [
368
+ /* @__PURE__ */ jsxs3(Avatar, { className: "h-12 w-12", children: [
369
+ /* @__PURE__ */ jsx5(
370
+ AvatarImage,
371
+ {
372
+ src: session.user.image || void 0,
373
+ alt: session.user.name || "User"
374
+ }
375
+ ),
376
+ /* @__PURE__ */ jsx5(AvatarFallback, { className: "bg-primary/10 text-primary text-lg", children: initials })
377
+ ] }),
378
+ /* @__PURE__ */ jsxs3("div", { children: [
379
+ /* @__PURE__ */ jsx5("p", { className: "font-medium", children: session.user.name || "User" }),
380
+ /* @__PURE__ */ jsx5("p", { className: "text-sm text-muted-foreground", children: session.user.email })
381
+ ] })
382
+ ] }),
383
+ /* @__PURE__ */ jsxs3("div", { className: "space-y-1", children: [
384
+ menuItems.map((item) => /* @__PURE__ */ jsx5(
385
+ Button,
386
+ {
387
+ variant: "ghost",
388
+ className: "w-full justify-start",
389
+ asChild: true,
390
+ children: /* @__PURE__ */ jsxs3(Link, { href: item.href, children: [
391
+ /* @__PURE__ */ jsx5(item.icon, { className: "mr-3 h-4 w-4" }),
392
+ item.label
393
+ ] })
394
+ },
395
+ item.label
396
+ )),
397
+ showThemeToggle && /* @__PURE__ */ jsxs3(
398
+ Button,
399
+ {
400
+ variant: "ghost",
401
+ className: "w-full justify-start",
402
+ onClick: toggleTheme,
403
+ children: [
404
+ theme === "dark" ? /* @__PURE__ */ jsx5(Sun, { className: "mr-3 h-4 w-4" }) : /* @__PURE__ */ jsx5(Moon, { className: "mr-3 h-4 w-4" }),
405
+ theme === "dark" ? "Light mode" : "Dark mode"
406
+ ]
407
+ }
408
+ ),
409
+ /* @__PURE__ */ jsx5("div", { className: "my-2 border-t" }),
410
+ /* @__PURE__ */ jsxs3(
411
+ Button,
412
+ {
413
+ variant: "ghost",
414
+ className: "w-full justify-start text-destructive hover:text-destructive",
415
+ onClick: handleSignOut,
416
+ children: [
417
+ /* @__PURE__ */ jsx5(LogOut, { className: "mr-3 h-4 w-4" }),
418
+ "Sign out"
419
+ ]
420
+ }
421
+ )
422
+ ] })
423
+ ] })
424
+ ] })
425
+ ] });
426
+ }
427
+ return /* @__PURE__ */ jsxs3(DropdownMenu, { children: [
428
+ /* @__PURE__ */ jsx5(DropdownMenuTrigger, { asChild: true, children: AvatarButton }),
429
+ /* @__PURE__ */ jsxs3(DropdownMenuContent, { className: "w-56", align: "end", forceMount: true, children: [
430
+ /* @__PURE__ */ jsx5(DropdownMenuLabel, { className: "font-normal", children: /* @__PURE__ */ jsxs3("div", { className: "flex flex-col space-y-1", children: [
431
+ /* @__PURE__ */ jsx5("p", { className: "text-sm font-medium leading-none", children: session.user.name || "User" }),
432
+ /* @__PURE__ */ jsx5("p", { className: "text-xs leading-none text-muted-foreground", children: session.user.email })
433
+ ] }) }),
434
+ /* @__PURE__ */ jsx5(DropdownMenuSeparator, {}),
435
+ menuItems.map((item) => /* @__PURE__ */ jsx5(DropdownMenuItem, { asChild: true, children: /* @__PURE__ */ jsxs3(Link, { href: item.href, children: [
436
+ /* @__PURE__ */ jsx5(item.icon, { className: "mr-2 h-4 w-4" }),
437
+ item.label
438
+ ] }) }, item.label)),
439
+ showThemeToggle && /* @__PURE__ */ jsxs3(DropdownMenuItem, { onClick: toggleTheme, children: [
440
+ theme === "dark" ? /* @__PURE__ */ jsx5(Sun, { className: "mr-2 h-4 w-4" }) : /* @__PURE__ */ jsx5(Moon, { className: "mr-2 h-4 w-4" }),
441
+ theme === "dark" ? "Light mode" : "Dark mode"
442
+ ] }),
443
+ /* @__PURE__ */ jsx5(DropdownMenuSeparator, {}),
444
+ /* @__PURE__ */ jsxs3(
445
+ DropdownMenuItem,
446
+ {
447
+ onClick: handleSignOut,
448
+ className: "text-destructive focus:text-destructive",
449
+ children: [
450
+ /* @__PURE__ */ jsx5(LogOut, { className: "mr-2 h-4 w-4" }),
451
+ "Sign out"
452
+ ]
453
+ }
454
+ )
455
+ ] })
456
+ ] });
457
+ }
458
+
459
+ // src/components/ui/input.tsx
460
+ import { jsx as jsx6 } from "react/jsx-runtime";
461
+ function Input({ className, type, ...props }) {
462
+ return /* @__PURE__ */ jsx6(
463
+ "input",
464
+ {
465
+ type,
466
+ "data-slot": "input",
467
+ className: cn(
468
+ "file:text-foreground placeholder:text-muted-foreground selection:bg-primary selection:text-primary-foreground dark:bg-input/30 border-input h-9 w-full min-w-0 rounded-md border bg-transparent px-3 py-1 text-base shadow-xs transition-[color,box-shadow] outline-none file:inline-flex file:h-7 file:border-0 file:bg-transparent file:text-sm file:font-medium disabled:pointer-events-none disabled:cursor-not-allowed disabled:opacity-50 md:text-sm",
469
+ "focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px]",
470
+ "aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive",
471
+ className
472
+ ),
473
+ ...props
474
+ }
475
+ );
476
+ }
477
+
478
+ // src/components/ui/label.tsx
479
+ import * as LabelPrimitive from "@radix-ui/react-label";
480
+ import { jsx as jsx7 } from "react/jsx-runtime";
481
+ function Label2({
482
+ className,
483
+ ...props
484
+ }) {
485
+ return /* @__PURE__ */ jsx7(
486
+ LabelPrimitive.Root,
487
+ {
488
+ "data-slot": "label",
489
+ className: cn(
490
+ "flex items-center gap-2 text-sm leading-none font-medium select-none group-data-[disabled=true]:pointer-events-none group-data-[disabled=true]:opacity-50 peer-disabled:cursor-not-allowed peer-disabled:opacity-50",
491
+ className
492
+ ),
493
+ ...props
494
+ }
495
+ );
496
+ }
497
+
498
+ // src/components/ui/separator.tsx
499
+ import * as SeparatorPrimitive from "@radix-ui/react-separator";
500
+ import { jsx as jsx8 } from "react/jsx-runtime";
501
+ function Separator2({
502
+ className,
503
+ orientation = "horizontal",
504
+ decorative = true,
505
+ ...props
506
+ }) {
507
+ return /* @__PURE__ */ jsx8(
508
+ SeparatorPrimitive.Root,
509
+ {
510
+ "data-slot": "separator",
511
+ decorative,
512
+ orientation,
513
+ className: cn(
514
+ "bg-border shrink-0 data-[orientation=horizontal]:h-px data-[orientation=horizontal]:w-full data-[orientation=vertical]:h-full data-[orientation=vertical]:w-px",
515
+ className
516
+ ),
517
+ ...props
518
+ }
519
+ );
520
+ }
521
+
522
+ // src/components/auth/auth-form.tsx
523
+ import { signIn } from "next-auth/react";
524
+ import { useState as useState2 } from "react";
525
+ import { Loader2 } from "lucide-react";
526
+ import Link2 from "next/link";
527
+ import { useSearchParams } from "next/navigation";
528
+ import { Fragment, jsx as jsx9, jsxs as jsxs4 } from "react/jsx-runtime";
529
+ function AuthForm({
530
+ mode,
531
+ providers = ["google", "email"],
532
+ signupHref = "/signup",
533
+ loginHref = "/login",
534
+ verifyRequestHref = "/verify-request",
535
+ defaultCallbackUrl = "/dashboard"
536
+ }) {
537
+ const [email, setEmail] = useState2("");
538
+ const [isLoading, setIsLoading] = useState2(false);
539
+ const [isGoogleLoading, setIsGoogleLoading] = useState2(false);
540
+ const [isGithubLoading, setIsGithubLoading] = useState2(false);
541
+ const [error, setError] = useState2("");
542
+ const searchParams = useSearchParams();
543
+ const callbackUrl = searchParams.get("callbackUrl") || defaultCallbackUrl;
544
+ const isLogin = mode === "login";
545
+ const showGoogle = providers.includes("google");
546
+ const showGithub = providers.includes("github");
547
+ const showEmail = providers.includes("email");
548
+ const handleEmailSubmit = async (e) => {
549
+ e.preventDefault();
550
+ setIsLoading(true);
551
+ setError("");
552
+ try {
553
+ const result = await signIn("resend", {
554
+ email,
555
+ callbackUrl,
556
+ redirect: false
557
+ });
558
+ if (result?.error) {
559
+ setError("Failed to send magic link. Please try again.");
560
+ } else {
561
+ window.location.href = verifyRequestHref;
562
+ }
563
+ } catch {
564
+ setError("Something went wrong");
565
+ } finally {
566
+ setIsLoading(false);
567
+ }
568
+ };
569
+ const handleGoogleSignIn = async () => {
570
+ setIsGoogleLoading(true);
571
+ try {
572
+ await signIn("google", {
573
+ callbackUrl
574
+ });
575
+ } catch (error2) {
576
+ console.error("Google sign in error:", error2);
577
+ } finally {
578
+ setIsGoogleLoading(false);
579
+ }
580
+ };
581
+ const handleGithubSignIn = async () => {
582
+ setIsGithubLoading(true);
583
+ try {
584
+ await signIn("github", {
585
+ callbackUrl
586
+ });
587
+ } catch (error2) {
588
+ console.error("GitHub sign in error:", error2);
589
+ } finally {
590
+ setIsGithubLoading(false);
591
+ }
592
+ };
593
+ return /* @__PURE__ */ jsxs4("div", { className: "space-y-6", children: [
594
+ /* @__PURE__ */ jsxs4("div", { className: "space-y-2 text-center", children: [
595
+ /* @__PURE__ */ jsx9("h1", { className: "text-2xl font-semibold tracking-tight", children: isLogin ? "Welcome back" : "Create an account" }),
596
+ /* @__PURE__ */ jsx9("p", { className: "text-sm text-muted-foreground", children: isLogin ? "Sign in to your account to continue" : "Enter your email to get started" })
597
+ ] }),
598
+ error && /* @__PURE__ */ jsx9("div", { className: "p-3 text-sm text-destructive bg-destructive/10 rounded-md", children: error }),
599
+ showGoogle && /* @__PURE__ */ jsxs4(
600
+ Button,
601
+ {
602
+ type: "button",
603
+ variant: "outline",
604
+ className: "w-full",
605
+ onClick: handleGoogleSignIn,
606
+ disabled: isGoogleLoading,
607
+ children: [
608
+ isGoogleLoading ? /* @__PURE__ */ jsx9(Loader2, { className: "mr-2 h-4 w-4 animate-spin" }) : /* @__PURE__ */ jsxs4("svg", { className: "mr-2 h-4 w-4", viewBox: "0 0 24 24", children: [
609
+ /* @__PURE__ */ jsx9(
610
+ "path",
611
+ {
612
+ d: "M22.56 12.25c0-.78-.07-1.53-.2-2.25H12v4.26h5.92c-.26 1.37-1.04 2.53-2.21 3.31v2.77h3.57c2.08-1.92 3.28-4.74 3.28-8.09z",
613
+ fill: "#4285F4"
614
+ }
615
+ ),
616
+ /* @__PURE__ */ jsx9(
617
+ "path",
618
+ {
619
+ d: "M12 23c2.97 0 5.46-.98 7.28-2.66l-3.57-2.77c-.98.66-2.23 1.06-3.71 1.06-2.86 0-5.29-1.93-6.16-4.53H2.18v2.84C3.99 20.53 7.7 23 12 23z",
620
+ fill: "#34A853"
621
+ }
622
+ ),
623
+ /* @__PURE__ */ jsx9(
624
+ "path",
625
+ {
626
+ d: "M5.84 14.09c-.22-.66-.35-1.36-.35-2.09s.13-1.43.35-2.09V7.07H2.18C1.43 8.55 1 10.22 1 12s.43 3.45 1.18 4.93l2.85-2.22.81-.62z",
627
+ fill: "#FBBC05"
628
+ }
629
+ ),
630
+ /* @__PURE__ */ jsx9(
631
+ "path",
632
+ {
633
+ d: "M12 5.38c1.62 0 3.06.56 4.21 1.64l3.15-3.15C17.45 2.09 14.97 1 12 1 7.7 1 3.99 3.47 2.18 7.07l3.66 2.84c.87-2.6 3.3-4.53 6.16-4.53z",
634
+ fill: "#EA4335"
635
+ }
636
+ )
637
+ ] }),
638
+ "Continue with Google"
639
+ ]
640
+ }
641
+ ),
642
+ showGithub && /* @__PURE__ */ jsxs4(
643
+ Button,
644
+ {
645
+ type: "button",
646
+ variant: "outline",
647
+ className: "w-full",
648
+ onClick: handleGithubSignIn,
649
+ disabled: isGithubLoading,
650
+ children: [
651
+ isGithubLoading ? /* @__PURE__ */ jsx9(Loader2, { className: "mr-2 h-4 w-4 animate-spin" }) : /* @__PURE__ */ jsx9("svg", { className: "mr-2 h-4 w-4", viewBox: "0 0 24 24", fill: "currentColor", children: /* @__PURE__ */ jsx9("path", { d: "M12 0c-6.626 0-12 5.373-12 12 0 5.302 3.438 9.8 8.207 11.387.599.111.793-.261.793-.577v-2.234c-3.338.726-4.033-1.416-4.033-1.416-.546-1.387-1.333-1.756-1.333-1.756-1.089-.745.083-.729.083-.729 1.205.084 1.839 1.237 1.839 1.237 1.07 1.834 2.807 1.304 3.492.997.107-.775.418-1.305.762-1.604-2.665-.305-5.467-1.334-5.467-5.931 0-1.311.469-2.381 1.236-3.221-.124-.303-.535-1.524.117-3.176 0 0 1.008-.322 3.301 1.23.957-.266 1.983-.399 3.003-.404 1.02.005 2.047.138 3.006.404 2.291-1.552 3.297-1.23 3.297-1.23.653 1.653.242 2.874.118 3.176.77.84 1.235 1.911 1.235 3.221 0 4.609-2.807 5.624-5.479 5.921.43.372.823 1.102.823 2.222v3.293c0 .319.192.694.801.576 4.765-1.589 8.199-6.086 8.199-11.386 0-6.627-5.373-12-12-12z" }) }),
652
+ "Continue with GitHub"
653
+ ]
654
+ }
655
+ ),
656
+ showEmail && (showGoogle || showGithub) && /* @__PURE__ */ jsxs4("div", { className: "relative", children: [
657
+ /* @__PURE__ */ jsx9("div", { className: "absolute inset-0 flex items-center", children: /* @__PURE__ */ jsx9(Separator2, { className: "w-full" }) }),
658
+ /* @__PURE__ */ jsx9("div", { className: "relative flex justify-center text-xs uppercase", children: /* @__PURE__ */ jsx9("span", { className: "bg-background px-2 text-muted-foreground", children: "Or continue with email" }) })
659
+ ] }),
660
+ showEmail && /* @__PURE__ */ jsxs4("form", { onSubmit: handleEmailSubmit, className: "space-y-4", children: [
661
+ /* @__PURE__ */ jsxs4("div", { className: "space-y-2", children: [
662
+ /* @__PURE__ */ jsx9(Label2, { htmlFor: "email", children: "Email" }),
663
+ /* @__PURE__ */ jsx9(
664
+ Input,
665
+ {
666
+ id: "email",
667
+ type: "email",
668
+ placeholder: "name@example.com",
669
+ value: email,
670
+ onChange: (e) => setEmail(e.target.value),
671
+ required: true,
672
+ disabled: isLoading
673
+ }
674
+ )
675
+ ] }),
676
+ /* @__PURE__ */ jsx9(Button, { type: "submit", className: "w-full", disabled: isLoading, children: isLoading ? /* @__PURE__ */ jsxs4(Fragment, { children: [
677
+ /* @__PURE__ */ jsx9(Loader2, { className: "mr-2 h-4 w-4 animate-spin" }),
678
+ "Sending magic link..."
679
+ ] }) : "Send magic link" })
680
+ ] }),
681
+ /* @__PURE__ */ jsx9("p", { className: "text-center text-sm text-muted-foreground", children: isLogin ? /* @__PURE__ */ jsxs4(Fragment, { children: [
682
+ "Don't have an account?",
683
+ " ",
684
+ /* @__PURE__ */ jsx9(Link2, { href: signupHref, className: "text-primary hover:underline", children: "Sign up" })
685
+ ] }) : /* @__PURE__ */ jsxs4(Fragment, { children: [
686
+ "Already have an account?",
687
+ " ",
688
+ /* @__PURE__ */ jsx9(Link2, { href: loginHref, className: "text-primary hover:underline", children: "Sign in" })
689
+ ] }) })
690
+ ] });
691
+ }
692
+ export {
693
+ AuthForm,
694
+ UserButton
695
+ };
696
+ //# sourceMappingURL=index.mjs.map