better-ts-stack 1.1.3 → 2.0.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 (162) hide show
  1. package/README.md +113 -82
  2. package/dist/args.d.ts +12 -0
  3. package/dist/args.d.ts.map +1 -0
  4. package/dist/args.js +90 -0
  5. package/dist/args.js.map +1 -0
  6. package/dist/builder/configGenerator.d.ts +1 -0
  7. package/dist/builder/configGenerator.d.ts.map +1 -1
  8. package/dist/builder/configGenerator.js +46 -6
  9. package/dist/builder/configGenerator.js.map +1 -1
  10. package/dist/builder/dependencyInstaller.d.ts.map +1 -1
  11. package/dist/builder/dependencyInstaller.js +8 -14
  12. package/dist/builder/dependencyInstaller.js.map +1 -1
  13. package/dist/builder/gitInitializer.d.ts.map +1 -1
  14. package/dist/builder/gitInitializer.js +8 -41
  15. package/dist/builder/gitInitializer.js.map +1 -1
  16. package/dist/builder/index.d.ts.map +1 -1
  17. package/dist/builder/index.js +27 -63
  18. package/dist/builder/index.js.map +1 -1
  19. package/dist/builder/moduleSelector.d.ts +1 -4
  20. package/dist/builder/moduleSelector.d.ts.map +1 -1
  21. package/dist/builder/moduleSelector.js +11 -15
  22. package/dist/builder/moduleSelector.js.map +1 -1
  23. package/dist/builder/templateContext.d.ts +2 -17
  24. package/dist/builder/templateContext.d.ts.map +1 -1
  25. package/dist/builder/templateContext.js +6 -5
  26. package/dist/builder/templateContext.js.map +1 -1
  27. package/dist/index.js +45 -20
  28. package/dist/index.js.map +1 -1
  29. package/dist/output/nextSteps.d.ts +1 -2
  30. package/dist/output/nextSteps.d.ts.map +1 -1
  31. package/dist/output/nextSteps.js +10 -19
  32. package/dist/output/nextSteps.js.map +1 -1
  33. package/dist/prompts/index.d.ts +3 -3
  34. package/dist/prompts/index.d.ts.map +1 -1
  35. package/dist/prompts/index.js +22 -58
  36. package/dist/prompts/index.js.map +1 -1
  37. package/dist/prompts/stack.d.ts +3 -2
  38. package/dist/prompts/stack.d.ts.map +1 -1
  39. package/dist/prompts/stack.js +81 -70
  40. package/dist/prompts/stack.js.map +1 -1
  41. package/dist/types/index.d.ts +20 -42
  42. package/dist/types/index.d.ts.map +1 -1
  43. package/dist/types/index.js +40 -38
  44. package/dist/types/index.js.map +1 -1
  45. package/dist/ui/index.d.ts +3 -0
  46. package/dist/ui/index.d.ts.map +1 -0
  47. package/dist/ui/index.js +19 -0
  48. package/dist/ui/index.js.map +1 -0
  49. package/dist/ui/messages.d.ts +9 -0
  50. package/dist/ui/messages.d.ts.map +1 -0
  51. package/dist/ui/messages.js +100 -0
  52. package/dist/ui/messages.js.map +1 -0
  53. package/dist/ui/prompts.d.ts +20 -0
  54. package/dist/ui/prompts.d.ts.map +1 -0
  55. package/dist/ui/prompts.js +71 -0
  56. package/dist/ui/prompts.js.map +1 -0
  57. package/dist/ui/theme.d.ts +9 -0
  58. package/dist/ui/theme.d.ts.map +1 -0
  59. package/dist/ui/theme.js +24 -0
  60. package/dist/ui/theme.js.map +1 -0
  61. package/dist/validators/index.d.ts +1 -0
  62. package/dist/validators/index.d.ts.map +1 -1
  63. package/dist/validators/index.js +6 -0
  64. package/dist/validators/index.js.map +1 -1
  65. package/package.json +70 -66
  66. package/templates/backend/express/config.json +44 -44
  67. package/templates/backend/express/eslint.config.mjs +40 -40
  68. package/templates/backend/express/gitignore +36 -36
  69. package/templates/backend/express/src/index.ts +76 -76
  70. package/templates/backend/express/src/routes/health.ts +12 -12
  71. package/templates/backend/express/tsconfig.json +25 -25
  72. package/templates/backend/nestjs/.prettierignore +52 -0
  73. package/templates/backend/nestjs/.prettierrc +16 -0
  74. package/templates/backend/nestjs/config.json +44 -0
  75. package/templates/backend/nestjs/eslint.config.mjs +40 -0
  76. package/templates/backend/nestjs/gitignore +36 -0
  77. package/templates/backend/nestjs/nest-cli.json +9 -0
  78. package/templates/backend/nestjs/src/app.module.ts.hbs +34 -0
  79. package/templates/backend/nestjs/src/health/health.controller.ts +12 -0
  80. package/templates/backend/nestjs/src/main.ts +23 -0
  81. package/templates/backend/nestjs/tsconfig.eslint.json +9 -0
  82. package/templates/backend/nestjs/tsconfig.json +25 -0
  83. package/templates/frontend/nextjs/app/layout.tsx +13 -13
  84. package/templates/frontend/nextjs/app/page.tsx.hbs +97 -97
  85. package/templates/frontend/nextjs/components/ui/button.tsx +51 -51
  86. package/templates/frontend/nextjs/components/ui/card.tsx +60 -60
  87. package/templates/frontend/nextjs/components/ui/field.tsx +67 -67
  88. package/templates/frontend/nextjs/components/ui/input.tsx +18 -18
  89. package/templates/frontend/nextjs/components.json +19 -19
  90. package/templates/frontend/nextjs/config.json +38 -38
  91. package/templates/frontend/nextjs/eslint.config.mjs +11 -11
  92. package/templates/frontend/nextjs/gitignore +42 -42
  93. package/templates/frontend/nextjs/lib/utils.ts +6 -6
  94. package/templates/frontend/nextjs/next.config.ts.hbs +12 -12
  95. package/templates/frontend/nextjs/proxy.ts.hbs +24 -24
  96. package/templates/frontend/nextjs/tsconfig.json +34 -34
  97. package/templates/frontend/tanstack/config.json +48 -48
  98. package/templates/frontend/tanstack/src/routeTree.gen.ts +68 -68
  99. package/templates/frontend/tanstack/src/styles/app.css +85 -85
  100. package/templates/modules/auth/express/config.json +27 -26
  101. package/templates/modules/auth/express/src/controllers/authController.ts.hbs +116 -130
  102. package/templates/modules/auth/express/src/middleware/requireAuth.ts.hbs +26 -26
  103. package/templates/modules/auth/express/src/services/userStore.ts.hbs +117 -117
  104. package/templates/modules/auth/nestjs/config.json +24 -0
  105. package/templates/modules/auth/nestjs/src/auth/auth.controller.ts +46 -0
  106. package/templates/modules/auth/nestjs/src/auth/auth.guard.ts +36 -0
  107. package/templates/modules/auth/nestjs/src/auth/auth.module.ts.hbs +22 -0
  108. package/templates/modules/auth/nestjs/src/auth/auth.schemas.ts +35 -0
  109. package/templates/modules/auth/nestjs/src/auth/auth.service.ts +70 -0
  110. package/templates/modules/auth/nestjs/src/auth/jwt.ts +27 -0
  111. package/templates/modules/auth/nestjs/src/auth/users.service.ts.hbs +144 -0
  112. package/templates/modules/auth/nestjs/src/auth/zod-validation.pipe.ts +16 -0
  113. package/templates/modules/auth/nextjs/app/dashboard/page.tsx +96 -96
  114. package/templates/modules/auth/nextjs/app/sign-in/page.tsx +35 -35
  115. package/templates/modules/auth/nextjs/app/sign-up/page.tsx +35 -35
  116. package/templates/modules/auth/nextjs/components/auth/sign-in-form.tsx +132 -132
  117. package/templates/modules/auth/nextjs/components/auth/sign-out-button.tsx +50 -50
  118. package/templates/modules/auth/nextjs/components/auth/sign-up-form.tsx +152 -152
  119. package/templates/modules/auth/nextjs/config.json +31 -31
  120. package/templates/modules/auth/nextjs/lib/auth-schema.ts +39 -39
  121. package/templates/modules/auth/nextjs/lib/auth.ts.hbs +37 -37
  122. package/templates/modules/auth/tanstack/src/lib/auth-functions.ts +4 -4
  123. package/templates/modules/auth/tanstack/src/routeTree.gen.ts +140 -140
  124. package/templates/modules/docker/.dockerignore +14 -14
  125. package/templates/modules/docker/Dockerfile.hbs +137 -130
  126. package/templates/modules/docker/config.json +19 -19
  127. package/templates/modules/docker/docker-compose.yml.hbs +9 -9
  128. package/templates/modules/drizzle/express/config.json +27 -27
  129. package/templates/modules/drizzle/express/drizzle.config.ts +13 -13
  130. package/templates/modules/drizzle/express/src/lib/db.ts +13 -13
  131. package/templates/modules/drizzle/express/src/lib/schema.ts +10 -10
  132. package/templates/modules/drizzle/nestjs/config.json +27 -0
  133. package/templates/modules/drizzle/nestjs/drizzle.config.ts +13 -0
  134. package/templates/modules/drizzle/nestjs/src/database/database.module.ts +30 -0
  135. package/templates/modules/drizzle/nestjs/src/database/schema.ts +10 -0
  136. package/templates/modules/drizzle/nextjs/config.json +29 -29
  137. package/templates/modules/drizzle/nextjs/lib/db.ts +13 -13
  138. package/templates/modules/drizzle/nextjs/lib/schema.ts.hbs +84 -84
  139. package/templates/modules/mongoose/express/config.json +16 -16
  140. package/templates/modules/mongoose/nestjs/config.json +16 -0
  141. package/templates/modules/mongoose/nestjs/src/users/user.schema.ts +21 -0
  142. package/templates/modules/mongoose/nextjs/config.json +16 -16
  143. package/templates/modules/mongoose/nextjs/lib/db.ts +36 -36
  144. package/templates/modules/mongoose/tanstack/config.json +17 -17
  145. package/templates/modules/mongoose/tanstack/src/lib/db.ts +37 -37
  146. package/templates/modules/prisma/express/config.json +28 -28
  147. package/templates/modules/prisma/express/prisma/schema.prisma +1 -1
  148. package/templates/modules/prisma/express/prisma.config.ts +12 -12
  149. package/templates/modules/prisma/express/src/lib/prisma.ts +24 -24
  150. package/templates/modules/prisma/nestjs/config.json +28 -0
  151. package/templates/modules/prisma/nestjs/prisma/schema.prisma +23 -0
  152. package/templates/modules/prisma/nestjs/prisma.config.ts +12 -0
  153. package/templates/modules/prisma/nestjs/src/prisma/prisma.module.ts +11 -0
  154. package/templates/modules/prisma/nestjs/src/prisma/prisma.service.ts +17 -0
  155. package/templates/modules/prisma/nextjs/config.json +29 -29
  156. package/templates/modules/prisma/nextjs/prisma/schema.prisma.hbs +91 -91
  157. package/templates/modules/prisma/tanstack/config.json +5 -5
  158. package/dist/intro.d.ts +0 -2
  159. package/dist/intro.d.ts.map +0 -1
  160. package/dist/intro.js +0 -27
  161. package/dist/intro.js.map +0 -1
  162. package/templates/backend/express/.eslintrc.js +0 -24
@@ -1,96 +1,96 @@
1
- import Link from "next/link";
2
- import { headers } from "next/headers";
3
- import { redirect } from "next/navigation";
4
- import { ArrowRight, ShieldCheck } from "lucide-react";
5
-
6
- import { SignOutButton } from "@/components/auth/sign-out-button";
7
- import { buttonVariants } from "@/components/ui/button";
8
- import {
9
- Card,
10
- CardContent,
11
- CardDescription,
12
- CardHeader,
13
- CardTitle,
14
- } from "@/components/ui/card";
15
- import { auth } from "@/lib/auth";
16
- import { cn } from "@/lib/utils";
17
-
18
- export default async function DashboardPage() {
19
- const session = await auth.api.getSession({
20
- headers: await headers(),
21
- });
22
-
23
- if (!session) {
24
- redirect("/sign-in");
25
- }
26
-
27
- return (
28
- <main className="min-h-screen bg-[linear-gradient(180deg,_#f8fafc_0%,_#eef2ff_100%)] px-6 py-16 sm:px-10">
29
- <div className="mx-auto max-w-5xl space-y-8">
30
- <header className="flex flex-col gap-6 rounded-[2rem] border border-white/70 bg-white/85 p-8 shadow-xl shadow-slate-200/60 backdrop-blur md:flex-row md:items-end md:justify-between">
31
- <div className="space-y-3">
32
- <span className="inline-flex items-center gap-2 rounded-full bg-emerald-50 px-3 py-1 text-xs font-medium uppercase tracking-[0.24em] text-emerald-700">
33
- <ShieldCheck className="size-4" />
34
- Authenticated session
35
- </span>
36
- <div className="space-y-1">
37
- <h1 className="text-3xl font-semibold tracking-tight text-slate-950">
38
- Welcome {session.user.name}
39
- </h1>
40
- <p className="text-sm text-slate-600">{session.user.email}</p>
41
- </div>
42
- </div>
43
- <SignOutButton />
44
- </header>
45
-
46
- <section className="grid gap-5 md:grid-cols-2">
47
- <Card>
48
- <CardHeader>
49
- <CardTitle>Protected route example</CardTitle>
50
- <CardDescription>
51
- This page verifies the session on the server before rendering any
52
- content.
53
- </CardDescription>
54
- </CardHeader>
55
- <CardContent className="space-y-4 text-sm text-slate-600">
56
- <p>
57
- The generated proxy only performs an optimistic cookie check. The
58
- authoritative guard happens here with{" "}
59
- <code className="font-mono text-slate-900">
60
- auth.api.getSession({"{"} headers: await headers() {"}"})
61
- </code>
62
- .
63
- </p>
64
- <p>
65
- That keeps route protection aligned with Better Auth&apos;s
66
- Next.js guidance while staying easy to extend.
67
- </p>
68
- </CardContent>
69
- </Card>
70
-
71
- <Card>
72
- <CardHeader>
73
- <CardTitle>Suggested next steps</CardTitle>
74
- <CardDescription>
75
- Keep the generated auth flow, then customize the app around your
76
- product.
77
- </CardDescription>
78
- </CardHeader>
79
- <CardContent className="space-y-3 text-sm text-slate-600">
80
- <p>Replace the demo dashboard with your first authenticated page.</p>
81
- <p>Extend the schema with profile or onboarding fields.</p>
82
- <p>Add provider-based auth or email verification later if needed.</p>
83
- <Link
84
- href="/"
85
- className={cn(buttonVariants({ variant: "outline" }), "w-fit gap-2")}
86
- >
87
- Back to home
88
- <ArrowRight className="size-4" />
89
- </Link>
90
- </CardContent>
91
- </Card>
92
- </section>
93
- </div>
94
- </main>
95
- );
96
- }
1
+ import Link from "next/link";
2
+ import { headers } from "next/headers";
3
+ import { redirect } from "next/navigation";
4
+ import { ArrowRight, ShieldCheck } from "lucide-react";
5
+
6
+ import { SignOutButton } from "@/components/auth/sign-out-button";
7
+ import { buttonVariants } from "@/components/ui/button";
8
+ import {
9
+ Card,
10
+ CardContent,
11
+ CardDescription,
12
+ CardHeader,
13
+ CardTitle,
14
+ } from "@/components/ui/card";
15
+ import { auth } from "@/lib/auth";
16
+ import { cn } from "@/lib/utils";
17
+
18
+ export default async function DashboardPage() {
19
+ const session = await auth.api.getSession({
20
+ headers: await headers(),
21
+ });
22
+
23
+ if (!session) {
24
+ redirect("/sign-in");
25
+ }
26
+
27
+ return (
28
+ <main className="min-h-screen bg-[linear-gradient(180deg,_#f8fafc_0%,_#eef2ff_100%)] px-6 py-16 sm:px-10">
29
+ <div className="mx-auto max-w-5xl space-y-8">
30
+ <header className="flex flex-col gap-6 rounded-[2rem] border border-white/70 bg-white/85 p-8 shadow-xl shadow-slate-200/60 backdrop-blur md:flex-row md:items-end md:justify-between">
31
+ <div className="space-y-3">
32
+ <span className="inline-flex items-center gap-2 rounded-full bg-emerald-50 px-3 py-1 text-xs font-medium uppercase tracking-[0.24em] text-emerald-700">
33
+ <ShieldCheck className="size-4" />
34
+ Authenticated session
35
+ </span>
36
+ <div className="space-y-1">
37
+ <h1 className="text-3xl font-semibold tracking-tight text-slate-950">
38
+ Welcome {session.user.name}
39
+ </h1>
40
+ <p className="text-sm text-slate-600">{session.user.email}</p>
41
+ </div>
42
+ </div>
43
+ <SignOutButton />
44
+ </header>
45
+
46
+ <section className="grid gap-5 md:grid-cols-2">
47
+ <Card>
48
+ <CardHeader>
49
+ <CardTitle>Protected route example</CardTitle>
50
+ <CardDescription>
51
+ This page verifies the session on the server before rendering any
52
+ content.
53
+ </CardDescription>
54
+ </CardHeader>
55
+ <CardContent className="space-y-4 text-sm text-slate-600">
56
+ <p>
57
+ The generated proxy only performs an optimistic cookie check. The
58
+ authoritative guard happens here with{" "}
59
+ <code className="font-mono text-slate-900">
60
+ auth.api.getSession({"{"} headers: await headers() {"}"})
61
+ </code>
62
+ .
63
+ </p>
64
+ <p>
65
+ That keeps route protection aligned with Better Auth&apos;s
66
+ Next.js guidance while staying easy to extend.
67
+ </p>
68
+ </CardContent>
69
+ </Card>
70
+
71
+ <Card>
72
+ <CardHeader>
73
+ <CardTitle>Suggested next steps</CardTitle>
74
+ <CardDescription>
75
+ Keep the generated auth flow, then customize the app around your
76
+ product.
77
+ </CardDescription>
78
+ </CardHeader>
79
+ <CardContent className="space-y-3 text-sm text-slate-600">
80
+ <p>Replace the demo dashboard with your first authenticated page.</p>
81
+ <p>Extend the schema with profile or onboarding fields.</p>
82
+ <p>Add provider-based auth or email verification later if needed.</p>
83
+ <Link
84
+ href="/"
85
+ className={cn(buttonVariants({ variant: "outline" }), "w-fit gap-2")}
86
+ >
87
+ Back to home
88
+ <ArrowRight className="size-4" />
89
+ </Link>
90
+ </CardContent>
91
+ </Card>
92
+ </section>
93
+ </div>
94
+ </main>
95
+ );
96
+ }
@@ -1,35 +1,35 @@
1
- import { headers } from "next/headers";
2
- import { redirect } from "next/navigation";
3
-
4
- import { SignInForm } from "@/components/auth/sign-in-form";
5
- import { auth } from "@/lib/auth";
6
-
7
- export default async function SignInPage() {
8
- const session = await auth.api.getSession({
9
- headers: await headers(),
10
- });
11
-
12
- if (session) {
13
- redirect("/dashboard");
14
- }
15
-
16
- return (
17
- <main className="min-h-screen bg-[radial-gradient(circle_at_top,_rgba(14,165,233,0.16),_transparent_30%),linear-gradient(180deg,_#f8fafc_0%,_#eef2ff_100%)] px-6 py-16 sm:px-10">
18
- <div className="mx-auto flex min-h-[calc(100vh-8rem)] max-w-5xl items-center gap-12 lg:grid lg:grid-cols-[0.9fr_1.1fr]">
19
- <section className="hidden space-y-5 lg:block">
20
- <span className="inline-flex rounded-full border border-sky-200 bg-white/80 px-3 py-1 text-xs font-medium uppercase tracking-[0.24em] text-sky-700">
21
- Better Auth
22
- </span>
23
- <h1 className="text-4xl font-semibold tracking-tight text-slate-950">
24
- Welcome back.
25
- </h1>
26
- <p className="max-w-md text-base leading-7 text-slate-600">
27
- This generated starter includes protected routes, session-aware page
28
- guards, and reusable auth form primitives you can extend from here.
29
- </p>
30
- </section>
31
- <SignInForm />
32
- </div>
33
- </main>
34
- );
35
- }
1
+ import { headers } from "next/headers";
2
+ import { redirect } from "next/navigation";
3
+
4
+ import { SignInForm } from "@/components/auth/sign-in-form";
5
+ import { auth } from "@/lib/auth";
6
+
7
+ export default async function SignInPage() {
8
+ const session = await auth.api.getSession({
9
+ headers: await headers(),
10
+ });
11
+
12
+ if (session) {
13
+ redirect("/dashboard");
14
+ }
15
+
16
+ return (
17
+ <main className="min-h-screen bg-[radial-gradient(circle_at_top,_rgba(14,165,233,0.16),_transparent_30%),linear-gradient(180deg,_#f8fafc_0%,_#eef2ff_100%)] px-6 py-16 sm:px-10">
18
+ <div className="mx-auto flex min-h-[calc(100vh-8rem)] max-w-5xl items-center gap-12 lg:grid lg:grid-cols-[0.9fr_1.1fr]">
19
+ <section className="hidden space-y-5 lg:block">
20
+ <span className="inline-flex rounded-full border border-sky-200 bg-white/80 px-3 py-1 text-xs font-medium uppercase tracking-[0.24em] text-sky-700">
21
+ Better Auth
22
+ </span>
23
+ <h1 className="text-4xl font-semibold tracking-tight text-slate-950">
24
+ Welcome back.
25
+ </h1>
26
+ <p className="max-w-md text-base leading-7 text-slate-600">
27
+ This generated starter includes protected routes, session-aware page
28
+ guards, and reusable auth form primitives you can extend from here.
29
+ </p>
30
+ </section>
31
+ <SignInForm />
32
+ </div>
33
+ </main>
34
+ );
35
+ }
@@ -1,35 +1,35 @@
1
- import { headers } from "next/headers";
2
- import { redirect } from "next/navigation";
3
-
4
- import { SignUpForm } from "@/components/auth/sign-up-form";
5
- import { auth } from "@/lib/auth";
6
-
7
- export default async function SignUpPage() {
8
- const session = await auth.api.getSession({
9
- headers: await headers(),
10
- });
11
-
12
- if (session) {
13
- redirect("/dashboard");
14
- }
15
-
16
- return (
17
- <main className="min-h-screen bg-[radial-gradient(circle_at_top,_rgba(56,189,248,0.18),_transparent_28%),linear-gradient(180deg,_#f8fafc_0%,_#ecfeff_100%)] px-6 py-16 sm:px-10">
18
- <div className="mx-auto flex min-h-[calc(100vh-8rem)] max-w-5xl items-center gap-12 lg:grid lg:grid-cols-[0.9fr_1.1fr]">
19
- <section className="hidden space-y-5 lg:block">
20
- <span className="inline-flex rounded-full border border-sky-200 bg-white/80 px-3 py-1 text-xs font-medium uppercase tracking-[0.24em] text-sky-700">
21
- Better Auth
22
- </span>
23
- <h1 className="text-4xl font-semibold tracking-tight text-slate-950">
24
- Create your account.
25
- </h1>
26
- <p className="max-w-md text-base leading-7 text-slate-600">
27
- Start with email and password today, then layer in profile data,
28
- onboarding, and provider-based auth when your product needs it.
29
- </p>
30
- </section>
31
- <SignUpForm />
32
- </div>
33
- </main>
34
- );
35
- }
1
+ import { headers } from "next/headers";
2
+ import { redirect } from "next/navigation";
3
+
4
+ import { SignUpForm } from "@/components/auth/sign-up-form";
5
+ import { auth } from "@/lib/auth";
6
+
7
+ export default async function SignUpPage() {
8
+ const session = await auth.api.getSession({
9
+ headers: await headers(),
10
+ });
11
+
12
+ if (session) {
13
+ redirect("/dashboard");
14
+ }
15
+
16
+ return (
17
+ <main className="min-h-screen bg-[radial-gradient(circle_at_top,_rgba(56,189,248,0.18),_transparent_28%),linear-gradient(180deg,_#f8fafc_0%,_#ecfeff_100%)] px-6 py-16 sm:px-10">
18
+ <div className="mx-auto flex min-h-[calc(100vh-8rem)] max-w-5xl items-center gap-12 lg:grid lg:grid-cols-[0.9fr_1.1fr]">
19
+ <section className="hidden space-y-5 lg:block">
20
+ <span className="inline-flex rounded-full border border-sky-200 bg-white/80 px-3 py-1 text-xs font-medium uppercase tracking-[0.24em] text-sky-700">
21
+ Better Auth
22
+ </span>
23
+ <h1 className="text-4xl font-semibold tracking-tight text-slate-950">
24
+ Create your account.
25
+ </h1>
26
+ <p className="max-w-md text-base leading-7 text-slate-600">
27
+ Start with email and password today, then layer in profile data,
28
+ onboarding, and provider-based auth when your product needs it.
29
+ </p>
30
+ </section>
31
+ <SignUpForm />
32
+ </div>
33
+ </main>
34
+ );
35
+ }
@@ -1,132 +1,132 @@
1
- "use client";
2
-
3
- import Link from "next/link";
4
- import { useRouter } from "next/navigation";
5
- import { useState, useTransition } from "react";
6
- import { zodResolver } from "@hookform/resolvers/zod";
7
- import { LoaderCircle } from "lucide-react";
8
- import { useForm } from "react-hook-form";
9
-
10
- import { authClient } from "@/lib/auth-client";
11
- import {
12
- getAuthErrorMessage,
13
- signInSchema,
14
- type SignInValues,
15
- } from "@/lib/auth-schema";
16
- import { Button } from "@/components/ui/button";
17
- import {
18
- Card,
19
- CardContent,
20
- CardDescription,
21
- CardFooter,
22
- CardHeader,
23
- CardTitle,
24
- } from "@/components/ui/card";
25
- import {
26
- Field,
27
- FieldDescription,
28
- FieldError,
29
- FieldGroup,
30
- FieldLabel,
31
- } from "@/components/ui/field";
32
- import { Input } from "@/components/ui/input";
33
-
34
- export function SignInForm() {
35
- const router = useRouter();
36
- const [isPending, startTransition] = useTransition();
37
- const [authError, setAuthError] = useState<string | null>(null);
38
- const form = useForm<SignInValues>({
39
- resolver: zodResolver(signInSchema),
40
- defaultValues: {
41
- email: "",
42
- password: "",
43
- },
44
- });
45
-
46
- const onSubmit = form.handleSubmit((values) => {
47
- setAuthError(null);
48
-
49
- startTransition(async () => {
50
- const result = await authClient.signIn.email({
51
- email: values.email,
52
- password: values.password,
53
- });
54
-
55
- if (result.error) {
56
- setAuthError(getAuthErrorMessage(result.error));
57
- return;
58
- }
59
-
60
- router.push("/dashboard");
61
- router.refresh();
62
- });
63
- });
64
-
65
- return (
66
- <Card className="border-white/80 bg-white/90 shadow-2xl shadow-slate-200/70 backdrop-blur">
67
- <CardHeader className="space-y-2">
68
- <CardTitle>Login to your account</CardTitle>
69
- <CardDescription>
70
- Sign in with your email and password to continue to your dashboard.
71
- </CardDescription>
72
- </CardHeader>
73
- <CardContent>
74
- <form className="space-y-6" onSubmit={onSubmit} noValidate>
75
- <FieldGroup>
76
- <Field data-invalid={Boolean(form.formState.errors.email)}>
77
- <FieldLabel htmlFor="email">Email</FieldLabel>
78
- <Input
79
- id="email"
80
- type="email"
81
- autoComplete="email"
82
- aria-invalid={Boolean(form.formState.errors.email)}
83
- placeholder="you@example.com"
84
- {...form.register("email")}
85
- />
86
- <FieldDescription>Use the email you registered with.</FieldDescription>
87
- <FieldError errors={[form.formState.errors.email]} />
88
- </Field>
89
-
90
- <Field data-invalid={Boolean(form.formState.errors.password)}>
91
- <FieldLabel htmlFor="password">Password</FieldLabel>
92
- <Input
93
- id="password"
94
- type="password"
95
- autoComplete="current-password"
96
- aria-invalid={Boolean(form.formState.errors.password)}
97
- placeholder="Enter your password"
98
- {...form.register("password")}
99
- />
100
- <FieldError errors={[form.formState.errors.password]} />
101
- </Field>
102
- </FieldGroup>
103
-
104
- {authError ? (
105
- <div className="rounded-2xl border border-rose-200 bg-rose-50 px-4 py-3 text-sm text-rose-700">
106
- {authError}
107
- </div>
108
- ) : null}
109
-
110
- <Button className="w-full" type="submit" disabled={isPending}>
111
- {isPending ? (
112
- <>
113
- <LoaderCircle className="mr-2 size-4 animate-spin" />
114
- Signing in
115
- </>
116
- ) : (
117
- "Login"
118
- )}
119
- </Button>
120
- </form>
121
- </CardContent>
122
- <CardFooter className="justify-center">
123
- <p className="text-sm text-slate-600">
124
- Need an account?{" "}
125
- <Link className="font-medium text-sky-700 hover:text-sky-800" href="/sign-up">
126
- Signup
127
- </Link>
128
- </p>
129
- </CardFooter>
130
- </Card>
131
- );
132
- }
1
+ "use client";
2
+
3
+ import Link from "next/link";
4
+ import { useRouter } from "next/navigation";
5
+ import { useState, useTransition } from "react";
6
+ import { zodResolver } from "@hookform/resolvers/zod";
7
+ import { LoaderCircle } from "lucide-react";
8
+ import { useForm } from "react-hook-form";
9
+
10
+ import { authClient } from "@/lib/auth-client";
11
+ import {
12
+ getAuthErrorMessage,
13
+ signInSchema,
14
+ type SignInValues,
15
+ } from "@/lib/auth-schema";
16
+ import { Button } from "@/components/ui/button";
17
+ import {
18
+ Card,
19
+ CardContent,
20
+ CardDescription,
21
+ CardFooter,
22
+ CardHeader,
23
+ CardTitle,
24
+ } from "@/components/ui/card";
25
+ import {
26
+ Field,
27
+ FieldDescription,
28
+ FieldError,
29
+ FieldGroup,
30
+ FieldLabel,
31
+ } from "@/components/ui/field";
32
+ import { Input } from "@/components/ui/input";
33
+
34
+ export function SignInForm() {
35
+ const router = useRouter();
36
+ const [isPending, startTransition] = useTransition();
37
+ const [authError, setAuthError] = useState<string | null>(null);
38
+ const form = useForm<SignInValues>({
39
+ resolver: zodResolver(signInSchema),
40
+ defaultValues: {
41
+ email: "",
42
+ password: "",
43
+ },
44
+ });
45
+
46
+ const onSubmit = form.handleSubmit((values) => {
47
+ setAuthError(null);
48
+
49
+ startTransition(async () => {
50
+ const result = await authClient.signIn.email({
51
+ email: values.email,
52
+ password: values.password,
53
+ });
54
+
55
+ if (result.error) {
56
+ setAuthError(getAuthErrorMessage(result.error));
57
+ return;
58
+ }
59
+
60
+ router.push("/dashboard");
61
+ router.refresh();
62
+ });
63
+ });
64
+
65
+ return (
66
+ <Card className="border-white/80 bg-white/90 shadow-2xl shadow-slate-200/70 backdrop-blur">
67
+ <CardHeader className="space-y-2">
68
+ <CardTitle>Login to your account</CardTitle>
69
+ <CardDescription>
70
+ Sign in with your email and password to continue to your dashboard.
71
+ </CardDescription>
72
+ </CardHeader>
73
+ <CardContent>
74
+ <form className="space-y-6" onSubmit={onSubmit} noValidate>
75
+ <FieldGroup>
76
+ <Field data-invalid={Boolean(form.formState.errors.email)}>
77
+ <FieldLabel htmlFor="email">Email</FieldLabel>
78
+ <Input
79
+ id="email"
80
+ type="email"
81
+ autoComplete="email"
82
+ aria-invalid={Boolean(form.formState.errors.email)}
83
+ placeholder="you@example.com"
84
+ {...form.register("email")}
85
+ />
86
+ <FieldDescription>Use the email you registered with.</FieldDescription>
87
+ <FieldError errors={[form.formState.errors.email]} />
88
+ </Field>
89
+
90
+ <Field data-invalid={Boolean(form.formState.errors.password)}>
91
+ <FieldLabel htmlFor="password">Password</FieldLabel>
92
+ <Input
93
+ id="password"
94
+ type="password"
95
+ autoComplete="current-password"
96
+ aria-invalid={Boolean(form.formState.errors.password)}
97
+ placeholder="Enter your password"
98
+ {...form.register("password")}
99
+ />
100
+ <FieldError errors={[form.formState.errors.password]} />
101
+ </Field>
102
+ </FieldGroup>
103
+
104
+ {authError ? (
105
+ <div className="rounded-2xl border border-rose-200 bg-rose-50 px-4 py-3 text-sm text-rose-700">
106
+ {authError}
107
+ </div>
108
+ ) : null}
109
+
110
+ <Button className="w-full" type="submit" disabled={isPending}>
111
+ {isPending ? (
112
+ <>
113
+ <LoaderCircle className="mr-2 size-4 animate-spin" />
114
+ Signing in
115
+ </>
116
+ ) : (
117
+ "Login"
118
+ )}
119
+ </Button>
120
+ </form>
121
+ </CardContent>
122
+ <CardFooter className="justify-center">
123
+ <p className="text-sm text-slate-600">
124
+ Need an account?{" "}
125
+ <Link className="font-medium text-sky-700 hover:text-sky-800" href="/sign-up">
126
+ Signup
127
+ </Link>
128
+ </p>
129
+ </CardFooter>
130
+ </Card>
131
+ );
132
+ }