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,177 +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 { Info, MailIcon, TriangleAlert } from 'lucide-react';
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 { RedirectIfAuthenticated } from '@/components/auth/redirect-if-authenticated';
16
- import { authClient } from '@/lib/auth';
17
-
18
- const forgotPasswordSchema = z.object({
19
- email: z.string().email('Invalid email address'),
20
- });
21
-
22
- type ForgotPasswordFormData = z.infer<typeof forgotPasswordSchema>;
23
-
24
- export default function ForgotPasswordPage() {
25
- const router = useRouter();
26
- const [isLoading, setIsLoading] = useState(false);
27
- const [emailSent, setEmailSent] = useState(false);
28
-
29
- const {
30
- register,
31
- handleSubmit,
32
- formState: { errors },
33
- getValues,
34
- } = useForm<ForgotPasswordFormData>({
35
- resolver: zodResolver(forgotPasswordSchema),
36
- defaultValues: {
37
- email: '',
38
- },
39
- });
40
-
41
- const onSubmit = async (data: ForgotPasswordFormData) => {
42
- setIsLoading(true);
43
-
44
- try {
45
- const result = await authClient.forgetPassword({
46
- email: data.email,
47
- redirectTo: '/reset-password',
48
- });
49
-
50
- if (result.error) {
51
- toast.error(result.error.message || 'Failed to send reset email');
52
- return;
53
- }
54
-
55
- setEmailSent(true);
56
- toast.success('Password reset email sent! Check your inbox.');
57
- } catch (error) {
58
- toast.error('An unexpected error occurred');
59
- console.error('Forgot password error:', error);
60
- } finally {
61
- setIsLoading(false);
62
- }
63
- };
64
-
65
- if (emailSent) {
66
- return (
67
- <RedirectIfAuthenticated>
68
- <div className="flex min-h-screen items-center justify-center p-4">
69
- <div className="bg-card w-full max-w-md rounded-lg border p-8">
70
- <div className="mb-6 text-center">
71
- <div className="bg-primary text-primary-foreground mx-auto mb-4 flex size-16 items-center justify-center rounded-full">
72
- <MailIcon className="size-8" />
73
- </div>
74
- <h1 className="text-2xl font-semibold">Check your email</h1>
75
- <p className="text-muted-foreground mt-2 text-sm">
76
- We sent a password reset link to
77
- </p>
78
- <p className="text-foreground mt-1 text-sm font-medium">
79
- {getValues('email')}
80
- </p>
81
- </div>
82
- <div className="mb-6 space-y-4 rounded-lg border p-4">
83
- <div className="flex gap-3">
84
- <div className="mt-0.5 flex-shrink-0">
85
- <Info className="size-4" />
86
- </div>
87
- <div className="space-y-1">
88
- <p className="text-foreground text-sm font-medium">
89
- Next steps
90
- </p>
91
- <p className="text-muted-foreground text-sm leading-relaxed">
92
- Click the link in the email to reset your password. The link
93
- will expire in 1 hour.
94
- </p>
95
- </div>
96
- </div>
97
- <div className="flex gap-3">
98
- <div className="mt-0.5 flex-shrink-0">
99
- <TriangleAlert className="size-4" />
100
- </div>
101
- <div className="space-y-1">
102
- <p className="text-foreground text-sm font-medium">
103
- Didn't receive it?
104
- </p>
105
- <p className="text-muted-foreground text-sm leading-relaxed">
106
- Check your spam folder or use the button below to send
107
- another email.
108
- </p>
109
- </div>
110
- </div>
111
- </div>
112
- <div className="flex flex-col gap-3">
113
- <Button
114
- variant="outline"
115
- className="w-full"
116
- onClick={() => setEmailSent(false)}
117
- >
118
- Send another email
119
- </Button>
120
- <Button
121
- variant="ghost"
122
- className="w-full"
123
- onClick={() => router.push('/login')}
124
- >
125
- Back to sign in
126
- </Button>
127
- </div>
128
- </div>
129
- </div>
130
- </RedirectIfAuthenticated>
131
- );
132
- }
133
-
134
- return (
135
- <RedirectIfAuthenticated>
136
- <div className="flex min-h-screen items-center justify-center p-4">
137
- <div className="bg-card w-full max-w-md rounded-lg border p-8">
138
- <div className="mb-6">
139
- <h1 className="text-2xl font-semibold">Reset your password</h1>
140
- <p className="text-muted-foreground mt-2 text-sm">
141
- Enter your email address and we'll send you a link to reset your
142
- password
143
- </p>
144
- </div>
145
- <form onSubmit={handleSubmit(onSubmit)} className="space-y-4">
146
- <div className="space-y-2">
147
- <Label htmlFor="email">Email</Label>
148
- <Input
149
- id="email"
150
- type="email"
151
- placeholder="name@example.com"
152
- {...register('email')}
153
- disabled={isLoading}
154
- />
155
- {errors.email && (
156
- <p className="text-destructive text-xs">
157
- {errors.email.message}
158
- </p>
159
- )}
160
- </div>
161
- <div className="flex flex-col gap-4 pt-2">
162
- <Button type="submit" className="w-full" isLoading={isLoading}>
163
- Send reset link
164
- </Button>
165
- <p className="text-muted-foreground text-center text-sm">
166
- Remember your password?{' '}
167
- <Link href="/login" className="text-primary hover:underline">
168
- Sign in
169
- </Link>
170
- </p>
171
- </div>
172
- </form>
173
- </div>
174
- </div>
175
- </RedirectIfAuthenticated>
176
- );
177
- }
@@ -1,159 +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 { Input } from '@repo/packages-ui/input';
7
- import { Label } from '@repo/packages-ui/label';
8
- import { PasswordInput } from '@repo/packages-ui/password-input';
9
- import Link from 'next/link';
10
- import { useRouter } from 'next/navigation';
11
- import React, { useState } from 'react';
12
- import { useForm } from 'react-hook-form';
13
- import { toast } from 'sonner';
14
- import { z } from 'zod';
15
-
16
- import { GithubButton } from '@/components/auth/github-button';
17
- import { GoogleButton } from '@/components/auth/google-button';
18
- import { RedirectIfAuthenticated } from '@/components/auth/redirect-if-authenticated';
19
- import { authClient } from '@/lib/auth';
20
-
21
- const loginSchema = z.object({
22
- email: z.string().email('Invalid email address'),
23
- password: z.string().min(8, 'Password must be at least 8 characters'),
24
- rememberMe: z.boolean().default(false),
25
- });
26
-
27
- export default function LoginPage() {
28
- const router = useRouter();
29
- const [isLoading, setIsLoading] = useState(false);
30
-
31
- const {
32
- register,
33
- handleSubmit,
34
- formState: { errors },
35
- } = useForm({
36
- resolver: zodResolver(loginSchema),
37
- defaultValues: {
38
- email: '',
39
- password: '',
40
- rememberMe: false,
41
- },
42
- });
43
-
44
- const onSubmit = async (data: z.infer<typeof loginSchema>) => {
45
- setIsLoading(true);
46
-
47
- try {
48
- const result = await authClient.signIn.email({
49
- email: data.email,
50
- password: data.password,
51
- rememberMe: data.rememberMe,
52
- });
53
-
54
- if (result.error) {
55
- toast.error(result.error.message || 'Failed to sign in');
56
- return;
57
- }
58
-
59
- toast.success('Welcome back!');
60
- router.push('/dashboard');
61
- } catch (error) {
62
- toast.error('An unexpected error occurred');
63
- console.error('Login error:', error);
64
- } finally {
65
- setIsLoading(false);
66
- }
67
- };
68
-
69
- return (
70
- <RedirectIfAuthenticated>
71
- <div className="flex min-h-screen items-center justify-center p-4">
72
- <div className="bg-card w-full max-w-md rounded-lg border p-8">
73
- <div className="mb-6">
74
- <h1 className="text-2xl font-semibold">Sign In</h1>
75
- <p className="text-muted-foreground mt-2 text-sm">
76
- Enter your email and password to access your account
77
- </p>
78
- </div>
79
-
80
- <div className="mb-6 space-y-3">
81
- <GoogleButton />
82
- <GithubButton />
83
- </div>
84
-
85
- <div className="relative mb-6">
86
- <div className="absolute inset-0 flex items-center">
87
- <span className="w-full border-t" />
88
- </div>
89
- <div className="relative flex justify-center text-xs uppercase">
90
- <span className="bg-card text-muted-foreground px-2">
91
- Or continue with email
92
- </span>
93
- </div>
94
- </div>
95
-
96
- <form onSubmit={handleSubmit(onSubmit)} className="space-y-4">
97
- <div className="space-y-2">
98
- <Label htmlFor="email">Email</Label>
99
- <Input
100
- id="email"
101
- type="email"
102
- placeholder="name@example.com"
103
- {...register('email')}
104
- disabled={isLoading}
105
- />
106
- {errors.email && (
107
- <p className="text-destructive text-xs">
108
- {errors.email.message}
109
- </p>
110
- )}
111
- </div>
112
- <div className="space-y-2">
113
- <Label htmlFor="password">Password</Label>
114
- <PasswordInput
115
- id="password"
116
- placeholder="Enter your password"
117
- {...register('password')}
118
- disabled={isLoading}
119
- />
120
- {errors.password && (
121
- <p className="text-destructive text-xs">
122
- {errors.password.message}
123
- </p>
124
- )}
125
- </div>
126
- <div className="flex items-center justify-between pt-2">
127
- <div className="flex items-center space-x-2">
128
- <Checkbox id="rememberMe" {...register('rememberMe')} />
129
- <Label
130
- htmlFor="rememberMe"
131
- className="text-sm font-normal leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70"
132
- >
133
- Remember me for 30 days
134
- </Label>
135
- </div>
136
- <Link
137
- href="/forgot-password"
138
- className="text-primary text-sm hover:underline"
139
- >
140
- Forgot password?
141
- </Link>
142
- </div>
143
- <div className="flex flex-col gap-4 pt-2">
144
- <Button type="submit" className="w-full" isLoading={isLoading}>
145
- Sign In
146
- </Button>
147
- <p className="text-muted-foreground text-center text-sm">
148
- Don&apos;t have an account?{' '}
149
- <Link href="/signup" className="text-primary hover:underline">
150
- Sign up
151
- </Link>
152
- </p>
153
- </div>
154
- </form>
155
- </div>
156
- </div>
157
- </RedirectIfAuthenticated>
158
- );
159
- }
@@ -1,245 +0,0 @@
1
- 'use client';
2
-
3
- import { zodResolver } from '@hookform/resolvers/zod';
4
- import { Button } from '@repo/packages-ui/button';
5
- import { Label } from '@repo/packages-ui/label';
6
- import { PasswordInput } from '@repo/packages-ui/password-input';
7
- import { ShieldAlert } from 'lucide-react';
8
- import Link from 'next/link';
9
- import { useRouter, useSearchParams } from 'next/navigation';
10
- import React, { Suspense, useState } from 'react';
11
- import { useForm } from 'react-hook-form';
12
- import { toast } from 'sonner';
13
- import { z } from 'zod';
14
-
15
- import { authClient } from '@/lib/auth';
16
-
17
- const resetPasswordSchema = z.object({
18
- password: z.string().min(8, 'Password must be at least 8 characters'),
19
- });
20
-
21
- type ResetPasswordFormData = z.infer<typeof resetPasswordSchema>;
22
-
23
- function ResetPasswordContent() {
24
- const router = useRouter();
25
- const searchParams = useSearchParams();
26
- const [isLoading, setIsLoading] = useState(false);
27
- const [resetSuccess, setResetSuccess] = useState(false);
28
-
29
- const token = searchParams.get('token');
30
- const error = searchParams.get('error');
31
-
32
- const {
33
- register,
34
- handleSubmit,
35
- formState: { errors },
36
- } = useForm<ResetPasswordFormData>({
37
- resolver: zodResolver(resetPasswordSchema),
38
- defaultValues: {
39
- password: '',
40
- },
41
- });
42
-
43
- const hasErrors = Object.keys(errors).length > 0;
44
-
45
- if (!token || error) {
46
- return (
47
- <div className="flex min-h-screen items-center justify-center p-4">
48
- <div className="bg-card w-full max-w-md rounded-lg border p-8">
49
- <div className="mb-6 text-center">
50
- <div className="bg-destructive/10 text-destructive mx-auto mb-4 flex h-14 w-14 items-center justify-center rounded-full">
51
- <svg
52
- xmlns="http://www.w3.org/2000/svg"
53
- fill="none"
54
- viewBox="0 0 24 24"
55
- strokeWidth={1.5}
56
- stroke="currentColor"
57
- className="h-7 w-7"
58
- >
59
- <path
60
- strokeLinecap="round"
61
- strokeLinejoin="round"
62
- d="M12 9v3.75m-9.303 3.376c-.866 1.5.217 3.374 1.948 3.374h14.71c1.73 0 2.813-1.874 1.948-3.374L13.949 3.378c-.866-1.5-3.032-1.5-3.898 0L2.697 16.126ZM12 15.75h.007v.008H12v-.008Z"
63
- />
64
- </svg>
65
- </div>
66
- <h1 className="text-2xl font-semibold">Invalid reset link</h1>
67
- <p className="text-muted-foreground mt-2 text-sm">
68
- This password reset link is invalid or has expired.
69
- </p>
70
- </div>
71
- <div className="bg-muted/50 mb-6 space-y-2 rounded-lg p-4">
72
- <p className="text-muted-foreground text-sm">
73
- Password reset links expire after 1 hour for security reasons.
74
- </p>
75
- <p className="text-muted-foreground text-sm">
76
- Please request a new password reset link to continue.
77
- </p>
78
- </div>
79
- <div className="flex flex-col gap-3">
80
- <Button
81
- className="w-full"
82
- onClick={() => router.push('/forgot-password')}
83
- >
84
- Request new reset link
85
- </Button>
86
- <Button
87
- variant="ghost"
88
- className="w-full"
89
- onClick={() => router.push('/login')}
90
- >
91
- Back to sign in
92
- </Button>
93
- </div>
94
- </div>
95
- </div>
96
- );
97
- }
98
-
99
- const onSubmit = async (data: ResetPasswordFormData) => {
100
- if (!token) {
101
- toast.error('Reset token is missing');
102
- return;
103
- }
104
-
105
- setIsLoading(true);
106
-
107
- try {
108
- const result = await authClient.resetPassword({
109
- newPassword: data.password,
110
- token,
111
- });
112
-
113
- if (result.error) {
114
- toast.error(result.error.message || 'Failed to reset password');
115
- return;
116
- }
117
-
118
- setResetSuccess(true);
119
- toast.success('Password reset successful!');
120
- } catch (error) {
121
- toast.error('An unexpected error occurred');
122
- console.error('Reset password error:', error);
123
- } finally {
124
- setIsLoading(false);
125
- }
126
- };
127
-
128
- if (resetSuccess) {
129
- return (
130
- <div className="flex min-h-screen items-center justify-center p-4">
131
- <div className="bg-card w-full max-w-md rounded-lg border p-8">
132
- <div className="mb-6 text-center">
133
- <div className="bg-primary/10 text-primary mx-auto mb-4 flex h-14 w-14 items-center justify-center rounded-full">
134
- <svg
135
- xmlns="http://www.w3.org/2000/svg"
136
- fill="none"
137
- viewBox="0 0 24 24"
138
- strokeWidth={1.5}
139
- stroke="currentColor"
140
- className="h-7 w-7"
141
- >
142
- <path
143
- strokeLinecap="round"
144
- strokeLinejoin="round"
145
- d="m4.5 12.75 6 6 9-13.5"
146
- />
147
- </svg>
148
- </div>
149
- <h1 className="text-2xl font-semibold">
150
- Password reset successful
151
- </h1>
152
- <p className="text-muted-foreground mt-2 text-sm">
153
- Your password has been successfully reset. You can now sign in
154
- with your new password.
155
- </p>
156
- </div>
157
- <Button className="w-full" onClick={() => router.push('/login')}>
158
- Continue to sign in
159
- </Button>
160
- </div>
161
- </div>
162
- );
163
- }
164
-
165
- return (
166
- <div className="flex min-h-screen items-center justify-center p-4">
167
- <div className="bg-card w-full max-w-md rounded-lg border p-8">
168
- <div className="mb-6">
169
- <h1 className="text-2xl font-semibold">Create new password</h1>
170
- <p className="text-muted-foreground mt-2 text-sm">
171
- Enter a new password for your account
172
- </p>
173
- </div>
174
- <form onSubmit={handleSubmit(onSubmit)} className="space-y-4">
175
- <div className="space-y-2">
176
- <Label htmlFor="password">New password</Label>
177
- <PasswordInput
178
- id="password"
179
- placeholder="Enter your new password"
180
- {...register('password')}
181
- disabled={isLoading}
182
- />
183
- {errors.password && (
184
- <p className="text-destructive text-xs">
185
- {errors.password.message}
186
- </p>
187
- )}
188
- </div>
189
-
190
- {hasErrors && (
191
- <div className="border-destructive/50 bg-destructive/5 rounded-lg border p-4">
192
- <div className="flex items-start gap-3">
193
- <ShieldAlert className="text-destructive mt-0.5 h-5 w-5 shrink-0" />
194
- <div>
195
- <p className="text-destructive text-sm font-medium">
196
- Password requirements:
197
- </p>
198
- <ul className="text-destructive/80 mt-1.5 space-y-1 text-sm">
199
- <li>• At least 8 characters long</li>
200
- </ul>
201
- </div>
202
- </div>
203
- </div>
204
- )}
205
-
206
- {!hasErrors && (
207
- <div className="bg-muted/50 rounded-lg p-4">
208
- <p className="text-muted-foreground text-sm font-medium">
209
- Password requirements:
210
- </p>
211
- <ul className="text-muted-foreground mt-2 space-y-1 text-sm">
212
- <li>• At least 8 characters long</li>
213
- </ul>
214
- </div>
215
- )}
216
- <div className="flex flex-col gap-4 pt-2">
217
- <Button type="submit" className="w-full" isLoading={isLoading}>
218
- Reset password
219
- </Button>
220
- <p className="text-muted-foreground text-center text-sm">
221
- Remember your password?{' '}
222
- <Link href="/login" className="text-primary hover:underline">
223
- Sign in
224
- </Link>
225
- </p>
226
- </div>
227
- </form>
228
- </div>
229
- </div>
230
- );
231
- }
232
-
233
- export default function ResetPasswordPage() {
234
- return (
235
- <Suspense
236
- fallback={
237
- <div className="flex min-h-screen items-center justify-center">
238
- <div className="text-muted-foreground">Loading...</div>
239
- </div>
240
- }
241
- >
242
- <ResetPasswordContent />
243
- </Suspense>
244
- );
245
- }