create-blitzpack 0.1.0 → 0.1.2

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 +92 -94
  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,73 +0,0 @@
1
- 'use client';
2
-
3
- import type { User } from '@repo/packages-types/user';
4
- import {
5
- AlertDialog,
6
- AlertDialogAction,
7
- AlertDialogCancel,
8
- AlertDialogContent,
9
- AlertDialogDescription,
10
- AlertDialogFooter,
11
- AlertDialogHeader,
12
- AlertDialogTitle,
13
- AlertDialogTrigger,
14
- } from '@repo/packages-ui/alert-dialog';
15
- import { Button } from '@repo/packages-ui/button';
16
- import { Trash2 } from 'lucide-react';
17
- import * as React from 'react';
18
- import { toast } from 'sonner';
19
-
20
- import { useDeleteUser } from '@/hooks/api/use-users';
21
-
22
- interface UserDeleteDialogProps {
23
- user: User;
24
- }
25
-
26
- export function UserDeleteDialog({ user }: UserDeleteDialogProps) {
27
- const [open, setOpen] = React.useState(false);
28
- const { mutate: deleteUser, isPending } = useDeleteUser();
29
-
30
- const handleDelete = () => {
31
- deleteUser(user.id, {
32
- onSuccess: () => {
33
- toast.success('User deleted successfully');
34
- setOpen(false);
35
- },
36
- onError: (error) => {
37
- toast.error(error.message || 'Failed to delete user');
38
- },
39
- });
40
- };
41
-
42
- return (
43
- <AlertDialog open={open} onOpenChange={setOpen}>
44
- <AlertDialogTrigger asChild>
45
- <Button variant="ghost" size="sm" className="text-destructive">
46
- <Trash2 className="mr-2 h-4 w-4" />
47
- Delete
48
- </Button>
49
- </AlertDialogTrigger>
50
- <AlertDialogContent>
51
- <AlertDialogHeader>
52
- <AlertDialogTitle>Are you absolutely sure?</AlertDialogTitle>
53
- <AlertDialogDescription>
54
- This action cannot be undone. This will permanently delete the user{' '}
55
- <span className="font-semibold">{user.name}</span> (
56
- <span className="font-mono text-sm">{user.email}</span>) and remove
57
- all associated data from the system.
58
- </AlertDialogDescription>
59
- </AlertDialogHeader>
60
- <AlertDialogFooter>
61
- <AlertDialogCancel disabled={isPending}>Cancel</AlertDialogCancel>
62
- <AlertDialogAction
63
- onClick={handleDelete}
64
- disabled={isPending}
65
- className="bg-destructive text-destructive-foreground hover:bg-destructive/90"
66
- >
67
- {isPending ? 'Deleting...' : 'Delete User'}
68
- </AlertDialogAction>
69
- </AlertDialogFooter>
70
- </AlertDialogContent>
71
- </AlertDialog>
72
- );
73
- }
@@ -1,170 +0,0 @@
1
- 'use client';
2
-
3
- import { RoleSchema } from '@repo/packages-types/role';
4
- import type { UpdateUser, User } from '@repo/packages-types/user';
5
- import { Button } from '@repo/packages-ui/button';
6
- import {
7
- Dialog,
8
- DialogContent,
9
- DialogDescription,
10
- DialogFooter,
11
- DialogHeader,
12
- DialogTitle,
13
- DialogTrigger,
14
- } from '@repo/packages-ui/dialog';
15
- import { Input } from '@repo/packages-ui/input';
16
- import { Label } from '@repo/packages-ui/label';
17
- import {
18
- Select,
19
- SelectContent,
20
- SelectItem,
21
- SelectTrigger,
22
- SelectValue,
23
- } from '@repo/packages-ui/select';
24
- import { Pencil } from 'lucide-react';
25
- import * as React from 'react';
26
- import { toast } from 'sonner';
27
-
28
- import { useUpdateUser } from '@/hooks/api/use-users';
29
-
30
- interface UserEditDialogProps {
31
- user: User;
32
- }
33
-
34
- export function UserEditDialog({ user }: UserEditDialogProps) {
35
- const [open, setOpen] = React.useState(false);
36
- const [formData, setFormData] = React.useState<UpdateUser>({
37
- name: user.name,
38
- email: user.email,
39
- role: user.role,
40
- });
41
-
42
- const { mutate: updateUser, isPending } = useUpdateUser();
43
-
44
- const handleSubmit = (e: React.FormEvent) => {
45
- e.preventDefault();
46
-
47
- const changes: UpdateUser = {};
48
- if (formData.name !== user.name) changes.name = formData.name;
49
- if (formData.email !== user.email) changes.email = formData.email;
50
- if (formData.role !== user.role) changes.role = formData.role;
51
-
52
- if (Object.keys(changes).length === 0) {
53
- toast.info('No changes to save');
54
- return;
55
- }
56
-
57
- updateUser(
58
- { id: user.id, data: changes },
59
- {
60
- onSuccess: () => {
61
- toast.success('User updated successfully');
62
- setOpen(false);
63
- },
64
- onError: (error) => {
65
- toast.error(error.message || 'Failed to update user');
66
- },
67
- }
68
- );
69
- };
70
-
71
- React.useEffect(() => {
72
- if (open) {
73
- setFormData({
74
- name: user.name,
75
- email: user.email,
76
- role: user.role,
77
- });
78
- }
79
- }, [open, user]);
80
-
81
- return (
82
- <Dialog open={open} onOpenChange={setOpen}>
83
- <DialogTrigger asChild>
84
- <Button variant="ghost" size="sm">
85
- <Pencil className="mr-2 h-4 w-4" />
86
- Edit
87
- </Button>
88
- </DialogTrigger>
89
- <DialogContent className="sm:max-w-[500px]">
90
- <form onSubmit={handleSubmit}>
91
- <DialogHeader>
92
- <DialogTitle>Edit User</DialogTitle>
93
- <DialogDescription>
94
- Update user information and role assignment
95
- </DialogDescription>
96
- </DialogHeader>
97
-
98
- <div className="grid gap-4 py-4">
99
- <div className="grid gap-2">
100
- <Label htmlFor="name">Name</Label>
101
- <Input
102
- id="name"
103
- value={formData.name}
104
- onChange={(e) =>
105
- setFormData((prev) => ({ ...prev, name: e.target.value }))
106
- }
107
- placeholder="Enter user name"
108
- required
109
- />
110
- </div>
111
-
112
- <div className="grid gap-2">
113
- <Label htmlFor="email">Email</Label>
114
- <Input
115
- id="email"
116
- type="email"
117
- value={formData.email}
118
- onChange={(e) =>
119
- setFormData((prev) => ({ ...prev, email: e.target.value }))
120
- }
121
- placeholder="user@example.com"
122
- required
123
- />
124
- </div>
125
-
126
- <div className="grid gap-2">
127
- <Label htmlFor="role">Role</Label>
128
- <Select
129
- value={formData.role}
130
- onValueChange={(value) =>
131
- setFormData((prev) => ({
132
- ...prev,
133
- role: value as UpdateUser['role'],
134
- }))
135
- }
136
- >
137
- <SelectTrigger id="role">
138
- <SelectValue placeholder="Select role" />
139
- </SelectTrigger>
140
- <SelectContent>
141
- {RoleSchema.options.map((role) => (
142
- <SelectItem key={role} value={role}>
143
- {role === 'super_admin'
144
- ? 'Super Admin'
145
- : role.charAt(0).toUpperCase() + role.slice(1)}
146
- </SelectItem>
147
- ))}
148
- </SelectContent>
149
- </Select>
150
- </div>
151
- </div>
152
-
153
- <DialogFooter>
154
- <Button
155
- type="button"
156
- variant="outline"
157
- onClick={() => setOpen(false)}
158
- disabled={isPending}
159
- >
160
- Cancel
161
- </Button>
162
- <Button type="submit" disabled={isPending}>
163
- {isPending ? 'Saving...' : 'Save changes'}
164
- </Button>
165
- </DialogFooter>
166
- </form>
167
- </DialogContent>
168
- </Dialog>
169
- );
170
- }
@@ -1,285 +0,0 @@
1
- 'use client';
2
-
3
- import type { Role } from '@repo/packages-types/role';
4
- import { RoleSchema } from '@repo/packages-types/role';
5
- import type { User } from '@repo/packages-types/user';
6
- import { Badge } from '@repo/packages-ui/badge';
7
- import { Button } from '@repo/packages-ui/button';
8
- import { DataTable } from '@repo/packages-ui/data-table/data-table';
9
- import { DataTableColumnHeader } from '@repo/packages-ui/data-table/data-table-column-header';
10
- import {
11
- DropdownMenu,
12
- DropdownMenuCheckboxItem,
13
- DropdownMenuContent,
14
- DropdownMenuItem,
15
- DropdownMenuLabel,
16
- DropdownMenuSeparator,
17
- DropdownMenuTrigger,
18
- } from '@repo/packages-ui/dropdown-menu';
19
- import type {
20
- ColumnDef,
21
- ColumnFiltersState,
22
- PaginationState,
23
- SortingState,
24
- } from '@tanstack/react-table';
25
- import { format } from 'date-fns';
26
- import { Filter, MoreHorizontal, UserCheck } from 'lucide-react';
27
- import * as React from 'react';
28
-
29
- import { UserCreateDialog } from '@/components/admin/user-create-dialog';
30
- import { UserDeleteDialog } from '@/components/admin/user-delete-dialog';
31
- import { UserEditDialog } from '@/components/admin/user-edit-dialog';
32
- import { useFetchUsers } from '@/hooks/api/use-users';
33
-
34
- const getRoleBadgeVariant = (role: Role) => {
35
- switch (role) {
36
- case 'super_admin':
37
- return 'default';
38
- case 'admin':
39
- return 'secondary';
40
- default:
41
- return 'outline';
42
- }
43
- };
44
-
45
- const getRoleDisplay = (role: Role) => {
46
- switch (role) {
47
- case 'super_admin':
48
- return 'Super Admin';
49
- case 'admin':
50
- return 'Admin';
51
- default:
52
- return 'User';
53
- }
54
- };
55
-
56
- const columns: ColumnDef<User>[] = [
57
- {
58
- accessorKey: 'name',
59
- header: ({ column }) => (
60
- <DataTableColumnHeader column={column} title="Name" />
61
- ),
62
- cell: ({ row }) => (
63
- <div className="flex items-center gap-2">
64
- <div className="bg-primary/10 flex h-8 w-8 items-center justify-center rounded-full">
65
- <UserCheck className="text-primary h-4 w-4" />
66
- </div>
67
- <div className="font-medium">{row.getValue('name')}</div>
68
- </div>
69
- ),
70
- },
71
- {
72
- accessorKey: 'email',
73
- header: ({ column }) => (
74
- <DataTableColumnHeader column={column} title="Email" />
75
- ),
76
- cell: ({ row }) => (
77
- <div className="text-muted-foreground font-mono text-sm">
78
- {row.getValue('email')}
79
- </div>
80
- ),
81
- },
82
- {
83
- accessorKey: 'role',
84
- header: ({ column }) => (
85
- <DataTableColumnHeader column={column} title="Role" />
86
- ),
87
- cell: ({ row }) => {
88
- const role = row.getValue('role') as Role;
89
- return (
90
- <Badge variant={getRoleBadgeVariant(role)}>
91
- {getRoleDisplay(role)}
92
- </Badge>
93
- );
94
- },
95
- filterFn: (row, id, value) => {
96
- return value.includes(row.getValue(id));
97
- },
98
- },
99
- {
100
- accessorKey: 'createdAt',
101
- header: ({ column }) => (
102
- <DataTableColumnHeader column={column} title="Created" />
103
- ),
104
- cell: ({ row }) => {
105
- const date = row.getValue('createdAt') as string | Date;
106
- return (
107
- <div className="text-muted-foreground text-sm">
108
- {format(new Date(date), 'MMM dd, yyyy')}
109
- </div>
110
- );
111
- },
112
- },
113
- {
114
- id: 'actions',
115
- cell: ({ row }) => {
116
- const user = row.original;
117
-
118
- return (
119
- <div className="flex items-center justify-end gap-2">
120
- <UserEditDialog user={user} />
121
- <UserDeleteDialog user={user} />
122
- <DropdownMenu>
123
- <DropdownMenuTrigger asChild>
124
- <Button variant="ghost" className="h-8 w-8 p-0">
125
- <span className="sr-only">More options</span>
126
- <MoreHorizontal className="h-4 w-4" />
127
- </Button>
128
- </DropdownMenuTrigger>
129
- <DropdownMenuContent align="end">
130
- <DropdownMenuLabel>More Actions</DropdownMenuLabel>
131
- <DropdownMenuItem
132
- onClick={() => {
133
- navigator.clipboard.writeText(user.id);
134
- }}
135
- >
136
- Copy User ID
137
- </DropdownMenuItem>
138
- <DropdownMenuItem
139
- onClick={() => {
140
- navigator.clipboard.writeText(user.email);
141
- }}
142
- >
143
- Copy Email
144
- </DropdownMenuItem>
145
- </DropdownMenuContent>
146
- </DropdownMenu>
147
- </div>
148
- );
149
- },
150
- },
151
- ];
152
-
153
- export function UsersManagementTable() {
154
- const [pagination, setPagination] = React.useState<PaginationState>({
155
- pageIndex: 0,
156
- pageSize: 10,
157
- });
158
-
159
- const [sorting, setSorting] = React.useState<SortingState>([
160
- { id: 'createdAt', desc: true },
161
- ]);
162
-
163
- const [columnFilters, setColumnFilters] = React.useState<ColumnFiltersState>(
164
- []
165
- );
166
-
167
- const [selectedRoles, setSelectedRoles] = React.useState<Role[]>([]);
168
-
169
- const searchFilter = columnFilters.find((filter) => filter.id === 'name');
170
- const searchValue = searchFilter?.value as string | undefined;
171
-
172
- const queryParams: {
173
- page: number;
174
- limit: number;
175
- sortBy: 'name' | 'email' | 'createdAt';
176
- sortOrder: 'asc' | 'desc';
177
- search?: string;
178
- } = {
179
- page: pagination.pageIndex + 1,
180
- limit: pagination.pageSize,
181
- sortBy: (sorting[0]?.id as 'name' | 'email' | 'createdAt') || 'createdAt',
182
- sortOrder: sorting[0]?.desc ? 'desc' : 'asc',
183
- };
184
-
185
- if (searchValue) {
186
- queryParams.search = searchValue;
187
- }
188
-
189
- const {
190
- data: response,
191
- isLoading,
192
- isError,
193
- error,
194
- } = useFetchUsers(queryParams);
195
-
196
- if (isError) {
197
- return (
198
- <div className="border-destructive/50 bg-destructive/10 rounded-lg border p-4">
199
- <p className="text-destructive text-sm">
200
- Error loading users: {error?.message || 'Unknown error'}
201
- </p>
202
- </div>
203
- );
204
- }
205
-
206
- const allUsers = response?.data || [];
207
- const filteredUsers =
208
- selectedRoles.length > 0
209
- ? allUsers.filter((user) => selectedRoles.includes(user.role))
210
- : allUsers;
211
-
212
- const total = response?.pagination?.total || 0;
213
-
214
- const toggleRole = (role: Role) => {
215
- setSelectedRoles((prev) =>
216
- prev.includes(role) ? prev.filter((r) => r !== role) : [...prev, role]
217
- );
218
- };
219
-
220
- return (
221
- <div className="space-y-4">
222
- <div className="flex items-center justify-between gap-4">
223
- <div className="flex items-center gap-2">
224
- <DropdownMenu>
225
- <DropdownMenuTrigger asChild>
226
- <Button variant="outline" size="sm">
227
- <Filter className="mr-2 h-4 w-4" />
228
- Filter by Role
229
- {selectedRoles.length > 0 && (
230
- <Badge variant="secondary" className="ml-2">
231
- {selectedRoles.length}
232
- </Badge>
233
- )}
234
- </Button>
235
- </DropdownMenuTrigger>
236
- <DropdownMenuContent align="start" className="w-48">
237
- <DropdownMenuLabel>Filter by Role</DropdownMenuLabel>
238
- <DropdownMenuSeparator />
239
- {RoleSchema.options.map((role) => (
240
- <DropdownMenuCheckboxItem
241
- key={role}
242
- checked={selectedRoles.includes(role)}
243
- onCheckedChange={() => toggleRole(role)}
244
- >
245
- {getRoleDisplay(role)}
246
- </DropdownMenuCheckboxItem>
247
- ))}
248
- {selectedRoles.length > 0 && (
249
- <>
250
- <DropdownMenuSeparator />
251
- <DropdownMenuItem onClick={() => setSelectedRoles([])}>
252
- Clear filters
253
- </DropdownMenuItem>
254
- </>
255
- )}
256
- </DropdownMenuContent>
257
- </DropdownMenu>
258
-
259
- {selectedRoles.length > 0 && (
260
- <div className="text-muted-foreground text-sm">
261
- Showing {filteredUsers.length} of {allUsers.length} users
262
- </div>
263
- )}
264
- </div>
265
-
266
- <UserCreateDialog />
267
- </div>
268
-
269
- <DataTable
270
- columns={columns}
271
- data={filteredUsers}
272
- pagination={pagination}
273
- onPaginationChange={setPagination}
274
- sorting={sorting}
275
- onSortingChange={setSorting}
276
- columnFilters={columnFilters}
277
- onColumnFiltersChange={setColumnFilters}
278
- rowCount={total}
279
- isLoading={isLoading}
280
- searchPlaceholder="Search by name or email..."
281
- searchColumnId="name"
282
- />
283
- </div>
284
- );
285
- }
@@ -1,85 +0,0 @@
1
- 'use client';
2
-
3
- import { Alert, AlertDescription, AlertTitle } from '@repo/packages-ui/alert';
4
- import { Button } from '@repo/packages-ui/button';
5
- import { AlertCircle, Mail } from 'lucide-react';
6
- import { useState } from 'react';
7
- import { toast } from 'sonner';
8
-
9
- import { authClient } from '@/lib/auth';
10
-
11
- export function EmailVerificationBanner() {
12
- const { data: session } = authClient.useSession();
13
- const [isResending, setIsResending] = useState(false);
14
-
15
- if (!session?.user || session.user.emailVerified) {
16
- return null;
17
- }
18
-
19
- const handleResendEmail = async () => {
20
- setIsResending(true);
21
-
22
- try {
23
- const response = await fetch(
24
- `${process.env.NEXT_PUBLIC_API_URL}/verification/resend`,
25
- {
26
- method: 'POST',
27
- headers: {
28
- 'Content-Type': 'application/json',
29
- },
30
- credentials: 'include',
31
- body: JSON.stringify({}),
32
- }
33
- );
34
-
35
- if (!response.ok) {
36
- const error = await response.json();
37
- throw new Error(error.message || 'Failed to resend verification email');
38
- }
39
-
40
- toast.success('Verification email sent!', {
41
- description: 'Please check your inbox and spam folder.',
42
- });
43
- } catch (error) {
44
- toast.error('Failed to resend verification email', {
45
- description:
46
- error instanceof Error ? error.message : 'Please try again later',
47
- });
48
- } finally {
49
- setIsResending(false);
50
- }
51
- };
52
-
53
- return (
54
- <Alert className="border-amber-200 bg-amber-50">
55
- <AlertCircle className="h-4 w-4 text-amber-600" />
56
- <AlertTitle className="text-amber-900">
57
- Email Verification Required
58
- </AlertTitle>
59
- <AlertDescription className="text-amber-700">
60
- <div className="flex flex-col gap-2 sm:flex-row sm:items-center sm:justify-between">
61
- <span>
62
- Please verify your email address ({session.user.email}) to access
63
- all features.
64
- </span>
65
- <Button
66
- variant="outline"
67
- size="sm"
68
- onClick={handleResendEmail}
69
- disabled={isResending}
70
- className="border-amber-300 bg-white text-amber-900 hover:bg-amber-100"
71
- >
72
- {isResending ? (
73
- 'Sending...'
74
- ) : (
75
- <>
76
- <Mail className="mr-2 h-4 w-4" />
77
- Resend Email
78
- </>
79
- )}
80
- </Button>
81
- </div>
82
- </AlertDescription>
83
- </Alert>
84
- );
85
- }
@@ -1,40 +0,0 @@
1
- 'use client';
2
-
3
- import { Button } from '@repo/packages-ui/button';
4
- import { useState } from 'react';
5
-
6
- import { authClient } from '@/lib/auth';
7
-
8
- export function GithubButton() {
9
- const [isLoading, setIsLoading] = useState(false);
10
-
11
- const handleGithubSignIn = async () => {
12
- setIsLoading(true);
13
- await authClient.signIn.social({
14
- provider: 'github',
15
- callbackURL: `${window.location.origin}/dashboard`,
16
- });
17
- };
18
-
19
- return (
20
- <Button
21
- type="button"
22
- variant="outline"
23
- className="w-full"
24
- onClick={handleGithubSignIn}
25
- isLoading={isLoading}
26
- >
27
- {!isLoading && (
28
- <svg
29
- className="mr-2 h-5 w-5"
30
- viewBox="0 0 24 24"
31
- xmlns="http://www.w3.org/2000/svg"
32
- fill="currentColor"
33
- >
34
- <path d="M12 0c-6.626 0-12 5.373-12 12 0 5.302 3.438 9.8 8.207 11.387.599.111.793-.261.793-.577v-2.234c-3.338.726-4.033-1.416-4.033-1.416-.546-1.387-1.333-1.756-1.333-1.756-1.089-.745.083-.729.083-.729 1.205.084 1.839 1.237 1.839 1.237 1.07 1.834 2.807 1.304 3.492.997.107-.775.418-1.305.762-1.604-2.665-.305-5.467-1.334-5.467-5.931 0-1.311.469-2.381 1.236-3.221-.124-.303-.535-1.524.117-3.176 0 0 1.008-.322 3.301 1.23.957-.266 1.983-.399 3.003-.404 1.02.005 2.047.138 3.006.404 2.291-1.552 3.297-1.23 3.297-1.23.653 1.653.242 2.874.118 3.176.77.84 1.235 1.911 1.235 3.221 0 4.609-2.807 5.624-5.479 5.921.43.372.823 1.102.823 2.222v3.293c0 .319.192.694.801.576 4.765-1.589 8.199-6.086 8.199-11.386 0-6.627-5.373-12-12-12z" />
35
- </svg>
36
- )}
37
- Continue with GitHub
38
- </Button>
39
- );
40
- }