create-blitzpack 0.1.0 → 0.1.1

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 (259) hide show
  1. package/dist/index.js +35 -77
  2. package/package.json +5 -6
  3. package/template/.dockerignore +0 -59
  4. package/template/.github/workflows/ci.yml +0 -157
  5. package/template/.husky/pre-commit +0 -1
  6. package/template/.husky/pre-push +0 -1
  7. package/template/.lintstagedrc.cjs +0 -4
  8. package/template/.nvmrc +0 -1
  9. package/template/.prettierrc +0 -9
  10. package/template/.vscode/settings.json +0 -13
  11. package/template/CLAUDE.md +0 -175
  12. package/template/CONTRIBUTING.md +0 -32
  13. package/template/Dockerfile +0 -90
  14. package/template/GETTING_STARTED.md +0 -35
  15. package/template/LICENSE +0 -21
  16. package/template/README.md +0 -116
  17. package/template/apps/api/.dockerignore +0 -51
  18. package/template/apps/api/.env.local.example +0 -62
  19. package/template/apps/api/emails/account-deleted-email.tsx +0 -69
  20. package/template/apps/api/emails/components/email-layout.tsx +0 -154
  21. package/template/apps/api/emails/config.ts +0 -22
  22. package/template/apps/api/emails/password-changed-email.tsx +0 -88
  23. package/template/apps/api/emails/password-reset-email.tsx +0 -86
  24. package/template/apps/api/emails/verification-email.tsx +0 -85
  25. package/template/apps/api/emails/welcome-email.tsx +0 -70
  26. package/template/apps/api/package.json +0 -84
  27. package/template/apps/api/prisma/migrations/20251012111439_init/migration.sql +0 -13
  28. package/template/apps/api/prisma/migrations/20251018162629_add_better_auth_fields/migration.sql +0 -67
  29. package/template/apps/api/prisma/migrations/20251019142208_add_user_role_enum/migration.sql +0 -5
  30. package/template/apps/api/prisma/migrations/20251019182151_user_auth/migration.sql +0 -7
  31. package/template/apps/api/prisma/migrations/20251019211416_faster_session_lookup/migration.sql +0 -2
  32. package/template/apps/api/prisma/migrations/20251119124337_add_upload_model/migration.sql +0 -26
  33. package/template/apps/api/prisma/migrations/20251120071241_add_scope_to_account/migration.sql +0 -2
  34. package/template/apps/api/prisma/migrations/20251120072608_add_oauth_token_expiration_fields/migration.sql +0 -10
  35. package/template/apps/api/prisma/migrations/20251120144705_add_audit_logs/migration.sql +0 -29
  36. package/template/apps/api/prisma/migrations/20251127123614_remove_impersonated_by/migration.sql +0 -8
  37. package/template/apps/api/prisma/migrations/20251127125630_remove_audit_logs/migration.sql +0 -11
  38. package/template/apps/api/prisma/migrations/migration_lock.toml +0 -3
  39. package/template/apps/api/prisma/schema.prisma +0 -116
  40. package/template/apps/api/prisma/seed.ts +0 -159
  41. package/template/apps/api/prisma.config.ts +0 -14
  42. package/template/apps/api/src/app.ts +0 -377
  43. package/template/apps/api/src/common/logger.service.ts +0 -227
  44. package/template/apps/api/src/config/env.ts +0 -60
  45. package/template/apps/api/src/config/rate-limit.ts +0 -29
  46. package/template/apps/api/src/hooks/auth.ts +0 -122
  47. package/template/apps/api/src/plugins/auth.ts +0 -198
  48. package/template/apps/api/src/plugins/database.ts +0 -45
  49. package/template/apps/api/src/plugins/logger.ts +0 -33
  50. package/template/apps/api/src/plugins/multipart.ts +0 -16
  51. package/template/apps/api/src/plugins/scalar.ts +0 -20
  52. package/template/apps/api/src/plugins/schedule.ts +0 -52
  53. package/template/apps/api/src/plugins/services.ts +0 -66
  54. package/template/apps/api/src/plugins/swagger.ts +0 -56
  55. package/template/apps/api/src/routes/accounts.ts +0 -91
  56. package/template/apps/api/src/routes/admin-sessions.ts +0 -92
  57. package/template/apps/api/src/routes/metrics.ts +0 -71
  58. package/template/apps/api/src/routes/password.ts +0 -46
  59. package/template/apps/api/src/routes/sessions.ts +0 -53
  60. package/template/apps/api/src/routes/stats.ts +0 -38
  61. package/template/apps/api/src/routes/uploads-serve.ts +0 -27
  62. package/template/apps/api/src/routes/uploads.ts +0 -154
  63. package/template/apps/api/src/routes/users.ts +0 -114
  64. package/template/apps/api/src/routes/verification.ts +0 -90
  65. package/template/apps/api/src/server.ts +0 -34
  66. package/template/apps/api/src/services/accounts.service.ts +0 -125
  67. package/template/apps/api/src/services/authorization.service.ts +0 -162
  68. package/template/apps/api/src/services/email.service.ts +0 -170
  69. package/template/apps/api/src/services/file-storage.service.ts +0 -267
  70. package/template/apps/api/src/services/metrics.service.ts +0 -175
  71. package/template/apps/api/src/services/password.service.ts +0 -56
  72. package/template/apps/api/src/services/sessions.service.spec.ts +0 -134
  73. package/template/apps/api/src/services/sessions.service.ts +0 -276
  74. package/template/apps/api/src/services/stats.service.ts +0 -273
  75. package/template/apps/api/src/services/uploads.service.ts +0 -163
  76. package/template/apps/api/src/services/users.service.spec.ts +0 -249
  77. package/template/apps/api/src/services/users.service.ts +0 -198
  78. package/template/apps/api/src/utils/file-validation.ts +0 -108
  79. package/template/apps/api/start.sh +0 -33
  80. package/template/apps/api/test/helpers/fastify-app.ts +0 -24
  81. package/template/apps/api/test/helpers/mock-authorization.ts +0 -16
  82. package/template/apps/api/test/helpers/mock-logger.ts +0 -28
  83. package/template/apps/api/test/helpers/mock-prisma.ts +0 -30
  84. package/template/apps/api/test/helpers/test-db.ts +0 -125
  85. package/template/apps/api/test/integration/auth-flow.integration.spec.ts +0 -449
  86. package/template/apps/api/test/integration/password.integration.spec.ts +0 -427
  87. package/template/apps/api/test/integration/rate-limit.integration.spec.ts +0 -51
  88. package/template/apps/api/test/integration/sessions.integration.spec.ts +0 -445
  89. package/template/apps/api/test/integration/users.integration.spec.ts +0 -211
  90. package/template/apps/api/test/setup.ts +0 -31
  91. package/template/apps/api/tsconfig.json +0 -26
  92. package/template/apps/api/vitest.config.ts +0 -35
  93. package/template/apps/web/.env.local.example +0 -11
  94. package/template/apps/web/components.json +0 -24
  95. package/template/apps/web/next.config.ts +0 -22
  96. package/template/apps/web/package.json +0 -56
  97. package/template/apps/web/postcss.config.js +0 -5
  98. package/template/apps/web/public/apple-icon.png +0 -0
  99. package/template/apps/web/public/icon.png +0 -0
  100. package/template/apps/web/public/robots.txt +0 -3
  101. package/template/apps/web/src/app/(admin)/admin/layout.tsx +0 -222
  102. package/template/apps/web/src/app/(admin)/admin/page.tsx +0 -157
  103. package/template/apps/web/src/app/(admin)/admin/sessions/page.tsx +0 -18
  104. package/template/apps/web/src/app/(admin)/admin/users/page.tsx +0 -20
  105. package/template/apps/web/src/app/(auth)/forgot-password/page.tsx +0 -177
  106. package/template/apps/web/src/app/(auth)/login/page.tsx +0 -159
  107. package/template/apps/web/src/app/(auth)/reset-password/page.tsx +0 -245
  108. package/template/apps/web/src/app/(auth)/signup/page.tsx +0 -153
  109. package/template/apps/web/src/app/dashboard/change-password/page.tsx +0 -255
  110. package/template/apps/web/src/app/dashboard/page.tsx +0 -296
  111. package/template/apps/web/src/app/error.tsx +0 -32
  112. package/template/apps/web/src/app/examples/file-upload/page.tsx +0 -200
  113. package/template/apps/web/src/app/favicon.ico +0 -0
  114. package/template/apps/web/src/app/global-error.tsx +0 -96
  115. package/template/apps/web/src/app/globals.css +0 -22
  116. package/template/apps/web/src/app/icon.png +0 -0
  117. package/template/apps/web/src/app/layout.tsx +0 -34
  118. package/template/apps/web/src/app/not-found.tsx +0 -28
  119. package/template/apps/web/src/app/page.tsx +0 -192
  120. package/template/apps/web/src/components/admin/activity-feed.tsx +0 -101
  121. package/template/apps/web/src/components/admin/charts/auth-breakdown-chart.tsx +0 -114
  122. package/template/apps/web/src/components/admin/charts/chart-tooltip.tsx +0 -124
  123. package/template/apps/web/src/components/admin/charts/realtime-metrics-chart.tsx +0 -511
  124. package/template/apps/web/src/components/admin/charts/role-distribution-chart.tsx +0 -102
  125. package/template/apps/web/src/components/admin/charts/session-activity-chart.tsx +0 -90
  126. package/template/apps/web/src/components/admin/charts/user-growth-chart.tsx +0 -108
  127. package/template/apps/web/src/components/admin/health-indicator.tsx +0 -175
  128. package/template/apps/web/src/components/admin/refresh-control.tsx +0 -90
  129. package/template/apps/web/src/components/admin/session-revoke-all-dialog.tsx +0 -79
  130. package/template/apps/web/src/components/admin/session-revoke-dialog.tsx +0 -74
  131. package/template/apps/web/src/components/admin/sessions-management-table.tsx +0 -372
  132. package/template/apps/web/src/components/admin/stat-card.tsx +0 -137
  133. package/template/apps/web/src/components/admin/user-create-dialog.tsx +0 -152
  134. package/template/apps/web/src/components/admin/user-delete-dialog.tsx +0 -73
  135. package/template/apps/web/src/components/admin/user-edit-dialog.tsx +0 -170
  136. package/template/apps/web/src/components/admin/users-management-table.tsx +0 -285
  137. package/template/apps/web/src/components/auth/email-verification-banner.tsx +0 -85
  138. package/template/apps/web/src/components/auth/github-button.tsx +0 -40
  139. package/template/apps/web/src/components/auth/google-button.tsx +0 -54
  140. package/template/apps/web/src/components/auth/protected-route.tsx +0 -66
  141. package/template/apps/web/src/components/auth/redirect-if-authenticated.tsx +0 -31
  142. package/template/apps/web/src/components/auth/with-auth.tsx +0 -30
  143. package/template/apps/web/src/components/error/error-card.tsx +0 -47
  144. package/template/apps/web/src/components/error/forbidden.tsx +0 -25
  145. package/template/apps/web/src/components/landing/command-block.tsx +0 -64
  146. package/template/apps/web/src/components/landing/feature-card.tsx +0 -60
  147. package/template/apps/web/src/components/landing/included-feature-card.tsx +0 -63
  148. package/template/apps/web/src/components/landing/logo.tsx +0 -41
  149. package/template/apps/web/src/components/landing/tech-badge.tsx +0 -11
  150. package/template/apps/web/src/components/layout/auth-nav.tsx +0 -58
  151. package/template/apps/web/src/components/layout/footer.tsx +0 -3
  152. package/template/apps/web/src/config/landing-data.ts +0 -152
  153. package/template/apps/web/src/config/site.ts +0 -5
  154. package/template/apps/web/src/hooks/api/__tests__/use-users.test.tsx +0 -181
  155. package/template/apps/web/src/hooks/api/use-admin-sessions.ts +0 -75
  156. package/template/apps/web/src/hooks/api/use-admin-stats.ts +0 -33
  157. package/template/apps/web/src/hooks/api/use-sessions.ts +0 -52
  158. package/template/apps/web/src/hooks/api/use-uploads.ts +0 -156
  159. package/template/apps/web/src/hooks/api/use-users.ts +0 -149
  160. package/template/apps/web/src/hooks/use-mobile.ts +0 -21
  161. package/template/apps/web/src/hooks/use-realtime-metrics.ts +0 -120
  162. package/template/apps/web/src/lib/__tests__/utils.test.ts +0 -29
  163. package/template/apps/web/src/lib/api.ts +0 -151
  164. package/template/apps/web/src/lib/auth.ts +0 -13
  165. package/template/apps/web/src/lib/env.ts +0 -52
  166. package/template/apps/web/src/lib/form-utils.ts +0 -11
  167. package/template/apps/web/src/lib/utils.ts +0 -1
  168. package/template/apps/web/src/providers.tsx +0 -34
  169. package/template/apps/web/src/store/atoms.ts +0 -15
  170. package/template/apps/web/src/test/helpers/test-utils.tsx +0 -44
  171. package/template/apps/web/src/test/setup.ts +0 -8
  172. package/template/apps/web/tailwind.config.ts +0 -5
  173. package/template/apps/web/tsconfig.json +0 -26
  174. package/template/apps/web/vitest.config.ts +0 -32
  175. package/template/assets/logo-512.png +0 -0
  176. package/template/assets/logo.svg +0 -4
  177. package/template/docker-compose.prod.yml +0 -66
  178. package/template/docker-compose.yml +0 -36
  179. package/template/eslint.config.ts +0 -119
  180. package/template/package.json +0 -77
  181. package/template/packages/tailwind-config/package.json +0 -9
  182. package/template/packages/tailwind-config/theme.css +0 -179
  183. package/template/packages/types/package.json +0 -29
  184. package/template/packages/types/src/__tests__/schemas.test.ts +0 -255
  185. package/template/packages/types/src/api-response.ts +0 -53
  186. package/template/packages/types/src/health-check.ts +0 -11
  187. package/template/packages/types/src/pagination.ts +0 -41
  188. package/template/packages/types/src/role.ts +0 -5
  189. package/template/packages/types/src/session.ts +0 -48
  190. package/template/packages/types/src/stats.ts +0 -113
  191. package/template/packages/types/src/upload.ts +0 -51
  192. package/template/packages/types/src/user.ts +0 -36
  193. package/template/packages/types/tsconfig.json +0 -5
  194. package/template/packages/types/vitest.config.ts +0 -21
  195. package/template/packages/ui/components.json +0 -21
  196. package/template/packages/ui/package.json +0 -108
  197. package/template/packages/ui/src/__tests__/button.test.tsx +0 -70
  198. package/template/packages/ui/src/alert-dialog.tsx +0 -141
  199. package/template/packages/ui/src/alert.tsx +0 -66
  200. package/template/packages/ui/src/animated-theme-toggler.tsx +0 -167
  201. package/template/packages/ui/src/avatar.tsx +0 -53
  202. package/template/packages/ui/src/badge.tsx +0 -36
  203. package/template/packages/ui/src/button.tsx +0 -84
  204. package/template/packages/ui/src/card.tsx +0 -92
  205. package/template/packages/ui/src/checkbox.tsx +0 -32
  206. package/template/packages/ui/src/data-table/data-table-column-header.tsx +0 -68
  207. package/template/packages/ui/src/data-table/data-table-pagination.tsx +0 -99
  208. package/template/packages/ui/src/data-table/data-table-toolbar.tsx +0 -55
  209. package/template/packages/ui/src/data-table/data-table-view-options.tsx +0 -63
  210. package/template/packages/ui/src/data-table/data-table.tsx +0 -167
  211. package/template/packages/ui/src/dialog.tsx +0 -143
  212. package/template/packages/ui/src/dropdown-menu.tsx +0 -257
  213. package/template/packages/ui/src/empty-state.tsx +0 -52
  214. package/template/packages/ui/src/file-upload-input.tsx +0 -202
  215. package/template/packages/ui/src/form.tsx +0 -168
  216. package/template/packages/ui/src/hooks/use-mobile.ts +0 -19
  217. package/template/packages/ui/src/icons/brand-icons.tsx +0 -16
  218. package/template/packages/ui/src/input.tsx +0 -21
  219. package/template/packages/ui/src/label.tsx +0 -24
  220. package/template/packages/ui/src/lib/utils.ts +0 -6
  221. package/template/packages/ui/src/password-input.tsx +0 -102
  222. package/template/packages/ui/src/popover.tsx +0 -48
  223. package/template/packages/ui/src/radio-group.tsx +0 -45
  224. package/template/packages/ui/src/scroll-area.tsx +0 -58
  225. package/template/packages/ui/src/select.tsx +0 -187
  226. package/template/packages/ui/src/separator.tsx +0 -28
  227. package/template/packages/ui/src/sheet.tsx +0 -139
  228. package/template/packages/ui/src/sidebar.tsx +0 -726
  229. package/template/packages/ui/src/skeleton-variants.tsx +0 -87
  230. package/template/packages/ui/src/skeleton.tsx +0 -13
  231. package/template/packages/ui/src/slider.tsx +0 -63
  232. package/template/packages/ui/src/sonner.tsx +0 -25
  233. package/template/packages/ui/src/spinner.tsx +0 -16
  234. package/template/packages/ui/src/switch.tsx +0 -31
  235. package/template/packages/ui/src/table.tsx +0 -116
  236. package/template/packages/ui/src/tabs.tsx +0 -66
  237. package/template/packages/ui/src/textarea.tsx +0 -18
  238. package/template/packages/ui/src/tooltip.tsx +0 -61
  239. package/template/packages/ui/src/user-avatar.tsx +0 -97
  240. package/template/packages/ui/test-config.js +0 -3
  241. package/template/packages/ui/tsconfig.json +0 -12
  242. package/template/packages/ui/turbo.json +0 -18
  243. package/template/packages/ui/vitest.config.ts +0 -17
  244. package/template/packages/ui/vitest.setup.ts +0 -1
  245. package/template/packages/utils/package.json +0 -23
  246. package/template/packages/utils/src/__tests__/utils.test.ts +0 -223
  247. package/template/packages/utils/src/array.ts +0 -18
  248. package/template/packages/utils/src/async.ts +0 -3
  249. package/template/packages/utils/src/date.ts +0 -77
  250. package/template/packages/utils/src/errors.ts +0 -73
  251. package/template/packages/utils/src/number.ts +0 -11
  252. package/template/packages/utils/src/string.ts +0 -13
  253. package/template/packages/utils/tsconfig.json +0 -5
  254. package/template/packages/utils/vitest.config.ts +0 -21
  255. package/template/pnpm-workspace.yaml +0 -4
  256. package/template/tsconfig.base.json +0 -32
  257. package/template/turbo.json +0 -133
  258. package/template/vitest.shared.ts +0 -26
  259. package/template/vitest.workspace.ts +0 -9
@@ -1,153 +0,0 @@
1
- 'use client';
2
-
3
- import { zodResolver } from '@hookform/resolvers/zod';
4
- import { Button } from '@repo/packages-ui/button';
5
- import { Input } from '@repo/packages-ui/input';
6
- import { Label } from '@repo/packages-ui/label';
7
- import { PasswordInput } from '@repo/packages-ui/password-input';
8
- import Link from 'next/link';
9
- import { useRouter } from 'next/navigation';
10
- import React, { useState } from 'react';
11
- import { useForm } from 'react-hook-form';
12
- import { toast } from 'sonner';
13
- import { z } from 'zod';
14
-
15
- import { GithubButton } from '@/components/auth/github-button';
16
- import { GoogleButton } from '@/components/auth/google-button';
17
- import { RedirectIfAuthenticated } from '@/components/auth/redirect-if-authenticated';
18
- import { authClient } from '@/lib/auth';
19
-
20
- const signupSchema = z.object({
21
- name: z.string().min(2, 'Name must be at least 2 characters'),
22
- email: z.string().email('Invalid email address'),
23
- password: z.string().min(8, 'Password must be at least 8 characters'),
24
- });
25
-
26
- type SignupFormData = z.infer<typeof signupSchema>;
27
-
28
- export default function SignupPage() {
29
- const router = useRouter();
30
- const [isLoading, setIsLoading] = useState(false);
31
-
32
- const {
33
- register,
34
- handleSubmit,
35
- formState: { errors },
36
- } = useForm<SignupFormData>({
37
- resolver: zodResolver(signupSchema),
38
- });
39
-
40
- const onSubmit = async (data: SignupFormData) => {
41
- setIsLoading(true);
42
-
43
- try {
44
- const result = await authClient.signUp.email({
45
- email: data.email,
46
- password: data.password,
47
- name: data.name,
48
- });
49
-
50
- if (result.error) {
51
- toast.error(result.error.message || 'Failed to create account');
52
- return;
53
- }
54
-
55
- toast.success('Account created successfully!');
56
- router.push('/dashboard');
57
- } catch (error) {
58
- toast.error('An unexpected error occurred');
59
- console.error('Signup error:', error);
60
- } finally {
61
- setIsLoading(false);
62
- }
63
- };
64
-
65
- return (
66
- <RedirectIfAuthenticated>
67
- <div className="flex min-h-screen items-center justify-center p-4">
68
- <div className="bg-card w-full max-w-md rounded-lg border p-8">
69
- <div className="mb-6">
70
- <h1 className="text-2xl font-semibold">Create Account</h1>
71
- <p className="text-muted-foreground mt-2 text-sm">
72
- Enter your information to create a new account
73
- </p>
74
- </div>
75
-
76
- <div className="mb-6 space-y-3">
77
- <GoogleButton />
78
- <GithubButton />
79
- </div>
80
-
81
- <div className="relative mb-6">
82
- <div className="absolute inset-0 flex items-center">
83
- <span className="w-full border-t" />
84
- </div>
85
- <div className="relative flex justify-center text-xs uppercase">
86
- <span className="bg-card text-muted-foreground px-2">
87
- Or continue with email
88
- </span>
89
- </div>
90
- </div>
91
-
92
- <form onSubmit={handleSubmit(onSubmit)} className="space-y-4">
93
- <div className="space-y-2">
94
- <Label htmlFor="name">Name</Label>
95
- <Input
96
- id="name"
97
- type="text"
98
- placeholder="John Doe"
99
- {...register('name')}
100
- disabled={isLoading}
101
- />
102
- {errors.name && (
103
- <p className="text-destructive text-xs">
104
- {errors.name.message}
105
- </p>
106
- )}
107
- </div>
108
- <div className="space-y-2">
109
- <Label htmlFor="email">Email</Label>
110
- <Input
111
- id="email"
112
- type="email"
113
- placeholder="name@example.com"
114
- {...register('email')}
115
- disabled={isLoading}
116
- />
117
- {errors.email && (
118
- <p className="text-destructive text-xs">
119
- {errors.email.message}
120
- </p>
121
- )}
122
- </div>
123
- <div className="space-y-2">
124
- <Label htmlFor="password">Password</Label>
125
- <PasswordInput
126
- id="password"
127
- placeholder="Create a password (min. 8 characters)"
128
- {...register('password')}
129
- disabled={isLoading}
130
- />
131
- {errors.password && (
132
- <p className="text-destructive text-xs">
133
- {errors.password.message}
134
- </p>
135
- )}
136
- </div>
137
- <div className="flex flex-col gap-4 pt-2">
138
- <Button type="submit" className="w-full" isLoading={isLoading}>
139
- Create Account
140
- </Button>
141
- <p className="text-muted-foreground text-center text-sm">
142
- Already have an account?{' '}
143
- <Link href="/login" className="text-primary hover:underline">
144
- Sign in
145
- </Link>
146
- </p>
147
- </div>
148
- </form>
149
- </div>
150
- </div>
151
- </RedirectIfAuthenticated>
152
- );
153
- }
@@ -1,255 +0,0 @@
1
- 'use client';
2
-
3
- import { zodResolver } from '@hookform/resolvers/zod';
4
- import { Button } from '@repo/packages-ui/button';
5
- import { Checkbox } from '@repo/packages-ui/checkbox';
6
- import {
7
- Form,
8
- FormControl,
9
- FormDescription,
10
- FormField,
11
- FormItem,
12
- FormLabel,
13
- FormMessage,
14
- } from '@repo/packages-ui/form';
15
- import { PasswordInput } from '@repo/packages-ui/password-input';
16
- import { ArrowLeft, CheckCircle2, KeyRound, ShieldAlert } from 'lucide-react';
17
- import Link from 'next/link';
18
- import { useRouter } from 'next/navigation';
19
- import React, { useState } from 'react';
20
- import { useForm } from 'react-hook-form';
21
- import { toast } from 'sonner';
22
- import { z } from 'zod';
23
-
24
- import { ProtectedRoute } from '@/components/auth/protected-route';
25
- import { authClient } from '@/lib/auth';
26
-
27
- const changePasswordSchema = z
28
- .object({
29
- currentPassword: z.string().min(1, 'Current password is required'),
30
- newPassword: z.string().min(8, 'Password must be at least 8 characters'),
31
- confirmPassword: z.string(),
32
- revokeOtherSessions: z.boolean(),
33
- })
34
- .refine((data) => data.newPassword === data.confirmPassword, {
35
- message: 'Passwords do not match',
36
- path: ['confirmPassword'],
37
- })
38
- .refine((data) => data.currentPassword !== data.newPassword, {
39
- message: 'New password must be different from current password',
40
- path: ['newPassword'],
41
- });
42
-
43
- type ChangePasswordFormData = z.infer<typeof changePasswordSchema>;
44
-
45
- export default function ChangePasswordPage() {
46
- const router = useRouter();
47
- const [isLoading, setIsLoading] = useState(false);
48
- const [changeSuccess, setChangeSuccess] = useState(false);
49
-
50
- const form = useForm<ChangePasswordFormData>({
51
- resolver: zodResolver(changePasswordSchema),
52
- defaultValues: {
53
- currentPassword: '',
54
- newPassword: '',
55
- confirmPassword: '',
56
- revokeOtherSessions: true,
57
- },
58
- });
59
-
60
- const onSubmit = async (data: ChangePasswordFormData) => {
61
- setIsLoading(true);
62
-
63
- try {
64
- const result = await authClient.changePassword({
65
- currentPassword: data.currentPassword,
66
- newPassword: data.newPassword,
67
- revokeOtherSessions: data.revokeOtherSessions,
68
- });
69
-
70
- if (result.error) {
71
- toast.error(result.error.message || 'Failed to change password');
72
- return;
73
- }
74
-
75
- setChangeSuccess(true);
76
- form.reset();
77
- toast.success(
78
- data.revokeOtherSessions
79
- ? 'Password changed successfully! All other sessions have been signed out.'
80
- : 'Password changed successfully!'
81
- );
82
- } catch (error) {
83
- toast.error('An unexpected error occurred');
84
- console.error('Change password error:', error);
85
- } finally {
86
- setIsLoading(false);
87
- }
88
- };
89
-
90
- const hasErrors = Object.keys(form.formState.errors).length > 0;
91
-
92
- if (changeSuccess) {
93
- return (
94
- <ProtectedRoute redirectTo="/login">
95
- <div className="container mx-auto max-w-3xl space-y-6 p-8">
96
- <div className="border-border bg-card rounded-lg border p-8">
97
- <div className="mb-6 text-center">
98
- <div className="mx-auto mb-4 flex h-16 w-16 items-center justify-center rounded-full bg-green-500/10 text-green-600 dark:text-green-500">
99
- <CheckCircle2 className="h-8 w-8" />
100
- </div>
101
- <h1 className="text-2xl font-bold">
102
- Password Changed Successfully
103
- </h1>
104
- <p className="text-muted-foreground mt-2">
105
- Your password has been updated. You can continue using your
106
- account with the new password.
107
- </p>
108
- </div>
109
- <div className="flex gap-3">
110
- <Button
111
- className="flex-1"
112
- onClick={() => router.push('/dashboard')}
113
- >
114
- Back to Dashboard
115
- </Button>
116
- </div>
117
- </div>
118
- </div>
119
- </ProtectedRoute>
120
- );
121
- }
122
-
123
- return (
124
- <ProtectedRoute redirectTo="/login">
125
- <div className="container mx-auto max-w-3xl space-y-6 p-8">
126
- <div className="flex items-center gap-2">
127
- <Link
128
- href="/dashboard"
129
- className="text-muted-foreground hover:text-foreground flex items-center gap-1 text-sm transition-colors"
130
- >
131
- <ArrowLeft className="h-4 w-4" />
132
- Back to Dashboard
133
- </Link>
134
- </div>
135
-
136
- <div className="border-border bg-card rounded-lg border p-8">
137
- <div className="mb-6 flex items-center gap-3">
138
- <div className="bg-primary/10 text-primary rounded-full p-3">
139
- <KeyRound className="h-6 w-6" />
140
- </div>
141
- <div>
142
- <h1 className="text-2xl font-bold">Change Password</h1>
143
- <p className="text-muted-foreground">
144
- Update your password to keep your account secure
145
- </p>
146
- </div>
147
- </div>
148
-
149
- <Form {...form}>
150
- <form onSubmit={form.handleSubmit(onSubmit)} className="space-y-6">
151
- <FormField
152
- control={form.control}
153
- name="currentPassword"
154
- render={({ field }) => (
155
- <FormItem>
156
- <FormLabel>Current Password</FormLabel>
157
- <FormControl>
158
- <PasswordInput
159
- placeholder="Enter current password"
160
- disabled={isLoading}
161
- {...field}
162
- />
163
- </FormControl>
164
- <FormMessage />
165
- </FormItem>
166
- )}
167
- />
168
-
169
- <FormField
170
- control={form.control}
171
- name="newPassword"
172
- render={({ field }) => (
173
- <FormItem>
174
- <FormLabel>New Password</FormLabel>
175
- <FormControl>
176
- <PasswordInput
177
- placeholder="Enter new password"
178
- disabled={isLoading}
179
- {...field}
180
- />
181
- </FormControl>
182
- <FormMessage />
183
- </FormItem>
184
- )}
185
- />
186
-
187
- <FormField
188
- control={form.control}
189
- name="confirmPassword"
190
- render={({ field }) => (
191
- <FormItem>
192
- <FormLabel>Confirm New Password</FormLabel>
193
- <FormControl>
194
- <PasswordInput
195
- placeholder="Confirm new password"
196
- disabled={isLoading}
197
- {...field}
198
- />
199
- </FormControl>
200
- <FormMessage />
201
- </FormItem>
202
- )}
203
- />
204
-
205
- {hasErrors && (
206
- <div className="border-destructive/50 bg-destructive/5 rounded-lg border p-4">
207
- <div className="flex items-start gap-3">
208
- <ShieldAlert className="text-destructive mt-0.5 h-5 w-5 shrink-0" />
209
- <div>
210
- <p className="text-destructive text-sm font-medium">
211
- Password requirements:
212
- </p>
213
- <ul className="text-destructive/80 mt-1.5 space-y-1 text-sm">
214
- <li>• At least 8 characters long</li>
215
- <li>• Different from current password</li>
216
- <li>• Passwords must match</li>
217
- </ul>
218
- </div>
219
- </div>
220
- </div>
221
- )}
222
-
223
- <FormField
224
- control={form.control}
225
- name="revokeOtherSessions"
226
- render={({ field }) => (
227
- <FormItem className="border-border bg-muted/30 flex flex-row items-start space-x-3 space-y-0 rounded-lg border p-4">
228
- <FormControl>
229
- <Checkbox
230
- checked={field.value}
231
- onCheckedChange={field.onChange}
232
- disabled={isLoading}
233
- />
234
- </FormControl>
235
- <div className="space-y-1 leading-none">
236
- <FormLabel>Sign out all other devices</FormLabel>
237
- <FormDescription>
238
- Recommended for security. This will end all active
239
- sessions on other devices.
240
- </FormDescription>
241
- </div>
242
- </FormItem>
243
- )}
244
- />
245
-
246
- <Button type="submit" className="w-full" isLoading={isLoading}>
247
- Change Password
248
- </Button>
249
- </form>
250
- </Form>
251
- </div>
252
- </div>
253
- </ProtectedRoute>
254
- );
255
- }