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,108 +0,0 @@
1
- {
2
- "name": "@repo/packages-ui",
3
- "version": "1.0.0",
4
- "type": "module",
5
- "exports": {
6
- "./alert-dialog": "./src/alert-dialog.tsx",
7
- "./alert": "./src/alert.tsx",
8
- "./animated-theme-toggler": "./src/animated-theme-toggler.tsx",
9
- "./avatar": "./src/avatar.tsx",
10
- "./badge": "./src/badge.tsx",
11
- "./button": "./src/button.tsx",
12
- "./card": "./src/card.tsx",
13
- "./checkbox": "./src/checkbox.tsx",
14
- "./icons/brand-icons": "./src/icons/brand-icons.tsx",
15
- "./lib/utils": "./src/lib/utils.ts",
16
- "./data-table": "./src/data-table/data-table.tsx",
17
- "./data-table/data-table": "./src/data-table/data-table.tsx",
18
- "./data-table-column-header": "./src/data-table/data-table-column-header.tsx",
19
- "./data-table/data-table-column-header": "./src/data-table/data-table-column-header.tsx",
20
- "./data-table-pagination": "./src/data-table/data-table-pagination.tsx",
21
- "./data-table/data-table-pagination": "./src/data-table/data-table-pagination.tsx",
22
- "./data-table-toolbar": "./src/data-table/data-table-toolbar.tsx",
23
- "./data-table/data-table-toolbar": "./src/data-table/data-table-toolbar.tsx",
24
- "./data-table-view-options": "./src/data-table/data-table-view-options.tsx",
25
- "./data-table/data-table-view-options": "./src/data-table/data-table-view-options.tsx",
26
- "./dialog": "./src/dialog.tsx",
27
- "./dropdown-menu": "./src/dropdown-menu.tsx",
28
- "./empty-state": "./src/empty-state.tsx",
29
- "./file-upload-input": "./src/file-upload-input.tsx",
30
- "./form": "./src/form.tsx",
31
- "./input": "./src/input.tsx",
32
- "./label": "./src/label.tsx",
33
- "./password-input": "./src/password-input.tsx",
34
- "./popover": "./src/popover.tsx",
35
- "./radio-group": "./src/radio-group.tsx",
36
- "./scroll-area": "./src/scroll-area.tsx",
37
- "./select": "./src/select.tsx",
38
- "./separator": "./src/separator.tsx",
39
- "./sheet": "./src/sheet.tsx",
40
- "./sidebar": "./src/sidebar.tsx",
41
- "./skeleton": "./src/skeleton.tsx",
42
- "./skeleton-variants": "./src/skeleton-variants.tsx",
43
- "./slider": "./src/slider.tsx",
44
- "./sonner": "./src/sonner.tsx",
45
- "./spinner": "./src/spinner.tsx",
46
- "./switch": "./src/switch.tsx",
47
- "./table": "./src/table.tsx",
48
- "./tabs": "./src/tabs.tsx",
49
- "./textarea": "./src/textarea.tsx",
50
- "./tooltip": "./src/tooltip.tsx",
51
- "./user-avatar": "./src/user-avatar.tsx"
52
- },
53
- "scripts": {
54
- "typecheck": "tsc --noEmit",
55
- "lint": "eslint src",
56
- "lint:fix": "eslint src --fix",
57
- "test": "vitest run",
58
- "test:unit": "vitest run",
59
- "test:integration": "echo 'No integration tests for packages-ui'",
60
- "test:watch": "vitest",
61
- "test:coverage": "vitest run --coverage"
62
- },
63
- "peerDependencies": {
64
- "react": ">=19.0.0",
65
- "react-dom": ">=19.0.0"
66
- },
67
- "dependencies": {
68
- "@hookform/resolvers": "^5.2.2",
69
- "@radix-ui/react-alert-dialog": "^1.1.15",
70
- "@radix-ui/react-avatar": "^1.1.10",
71
- "@radix-ui/react-checkbox": "^1.3.3",
72
- "@radix-ui/react-dialog": "^1.1.15",
73
- "@radix-ui/react-dropdown-menu": "^2.1.16",
74
- "@radix-ui/react-label": "^2.1.7",
75
- "@radix-ui/react-popover": "^1.1.15",
76
- "@radix-ui/react-radio-group": "^1.3.8",
77
- "@radix-ui/react-scroll-area": "^1.2.10",
78
- "@radix-ui/react-select": "^2.2.6",
79
- "@radix-ui/react-separator": "^1.1.7",
80
- "@radix-ui/react-slider": "^1.3.6",
81
- "@radix-ui/react-slot": "^1.2.3",
82
- "@radix-ui/react-switch": "^1.2.6",
83
- "@radix-ui/react-tabs": "^1.1.13",
84
- "@radix-ui/react-tooltip": "^1.2.8",
85
- "@repo/packages-utils": "workspace:*",
86
- "@tanstack/react-table": "^8.21.3",
87
- "class-variance-authority": "^0.7.1",
88
- "clsx": "^2.1.1",
89
- "date-fns": "^4.1.0",
90
- "framer-motion": "^12.23.24",
91
- "lucide-react": "^0.545.0",
92
- "next-themes": "^0.4.6",
93
- "react-hook-form": "^7.65.0",
94
- "sonner": "^2.0.7",
95
- "tailwind-merge": "^3.3.1"
96
- },
97
- "devDependencies": {
98
- "@testing-library/jest-dom": "^6.1.5",
99
- "@testing-library/react": "^14.1.2",
100
- "@testing-library/user-event": "^14.5.1",
101
- "@types/node": "^24.7.1",
102
- "@types/react": "19.2.6",
103
- "@types/react-dom": "19.2.3",
104
- "@vitejs/plugin-react": "^5.0.4",
105
- "typescript": "^5.7.2",
106
- "vitest": "^3.2.4"
107
- }
108
- }
@@ -1,70 +0,0 @@
1
- import { render, screen } from '@testing-library/react';
2
- import userEvent from '@testing-library/user-event';
3
- import { describe, expect, it, vi } from 'vitest';
4
-
5
- import { Button } from '../button';
6
-
7
- describe('Button', () => {
8
- it('should render button with default variant', () => {
9
- render(<Button>Click me</Button>);
10
- const button = screen.getByRole('button', { name: /click me/i });
11
- expect(button).toBeInTheDocument();
12
- });
13
-
14
- it('should apply destructive variant classes', () => {
15
- render(<Button variant="destructive">Delete</Button>);
16
- const button = screen.getByRole('button', { name: /delete/i });
17
- expect(button).toHaveClass('bg-destructive');
18
- });
19
-
20
- it('should apply outline variant classes', () => {
21
- render(<Button variant="outline">Outline</Button>);
22
- const button = screen.getByRole('button', { name: /outline/i });
23
- expect(button).toHaveClass('border');
24
- });
25
-
26
- it('should apply size variants', () => {
27
- const { rerender } = render(<Button size="sm">Small</Button>);
28
- expect(screen.getByRole('button')).toHaveClass('h-8');
29
-
30
- rerender(<Button size="lg">Large</Button>);
31
- expect(screen.getByRole('button')).toHaveClass('h-10');
32
-
33
- rerender(<Button size="icon">Icon</Button>);
34
- expect(screen.getByRole('button')).toHaveClass('size-9');
35
- });
36
-
37
- it('should handle disabled state', () => {
38
- render(<Button disabled>Disabled</Button>);
39
- const button = screen.getByRole('button');
40
- expect(button).toBeDisabled();
41
- expect(button).toHaveClass('disabled:opacity-50');
42
- });
43
-
44
- it('should accept custom className', () => {
45
- render(<Button className="custom-class">Custom</Button>);
46
- const button = screen.getByRole('button');
47
- expect(button).toHaveClass('custom-class');
48
- });
49
-
50
- it('should handle onClick', async () => {
51
- const handleClick = vi.fn();
52
- render(<Button onClick={handleClick}>Click</Button>);
53
-
54
- const user = userEvent.setup();
55
- await user.click(screen.getByRole('button'));
56
- expect(handleClick).toHaveBeenCalledOnce();
57
- });
58
-
59
- it('should render as child when asChild is true', () => {
60
- render(
61
- <Button asChild>
62
- <a href="/test">Link Button</a>
63
- </Button>
64
- );
65
-
66
- const link = screen.getByRole('link');
67
- expect(link).toBeInTheDocument();
68
- expect(link).toHaveAttribute('href', '/test');
69
- });
70
- });
@@ -1,141 +0,0 @@
1
- 'use client';
2
-
3
- import * as AlertDialogPrimitive from '@radix-ui/react-alert-dialog';
4
- import * as React from 'react';
5
-
6
- import { buttonVariants } from './button';
7
- import { cn } from './lib/utils';
8
-
9
- const AlertDialog = AlertDialogPrimitive.Root;
10
-
11
- const AlertDialogTrigger = AlertDialogPrimitive.Trigger;
12
-
13
- const AlertDialogPortal = AlertDialogPrimitive.Portal;
14
-
15
- const AlertDialogOverlay = React.forwardRef<
16
- React.ElementRef<typeof AlertDialogPrimitive.Overlay>,
17
- React.ComponentPropsWithoutRef<typeof AlertDialogPrimitive.Overlay>
18
- >(({ className, ...props }, ref) => (
19
- <AlertDialogPrimitive.Overlay
20
- className={cn(
21
- 'data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 fixed inset-0 z-50 bg-black/80',
22
- className
23
- )}
24
- {...props}
25
- ref={ref}
26
- />
27
- ));
28
- AlertDialogOverlay.displayName = AlertDialogPrimitive.Overlay.displayName;
29
-
30
- const AlertDialogContent = React.forwardRef<
31
- React.ElementRef<typeof AlertDialogPrimitive.Content>,
32
- React.ComponentPropsWithoutRef<typeof AlertDialogPrimitive.Content>
33
- >(({ className, ...props }, ref) => (
34
- <AlertDialogPortal>
35
- <AlertDialogOverlay />
36
- <AlertDialogPrimitive.Content
37
- ref={ref}
38
- className={cn(
39
- 'data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[state=closed]:slide-out-to-left-1/2 data-[state=closed]:slide-out-to-top-[48%] data-[state=open]:slide-in-from-left-1/2 data-[state=open]:slide-in-from-top-[48%] bg-background fixed left-[50%] top-[50%] z-50 grid w-full max-w-lg translate-x-[-50%] translate-y-[-50%] gap-4 border p-6 shadow-lg duration-200 sm:rounded-lg',
40
- className
41
- )}
42
- {...props}
43
- />
44
- </AlertDialogPortal>
45
- ));
46
- AlertDialogContent.displayName = AlertDialogPrimitive.Content.displayName;
47
-
48
- const AlertDialogHeader = ({
49
- className,
50
- ...props
51
- }: React.HTMLAttributes<HTMLDivElement>) => (
52
- <div
53
- className={cn(
54
- 'flex flex-col space-y-2 text-center sm:text-left',
55
- className
56
- )}
57
- {...props}
58
- />
59
- );
60
- AlertDialogHeader.displayName = 'AlertDialogHeader';
61
-
62
- const AlertDialogFooter = ({
63
- className,
64
- ...props
65
- }: React.HTMLAttributes<HTMLDivElement>) => (
66
- <div
67
- className={cn(
68
- 'flex flex-col-reverse sm:flex-row sm:justify-end sm:space-x-2',
69
- className
70
- )}
71
- {...props}
72
- />
73
- );
74
- AlertDialogFooter.displayName = 'AlertDialogFooter';
75
-
76
- const AlertDialogTitle = React.forwardRef<
77
- React.ElementRef<typeof AlertDialogPrimitive.Title>,
78
- React.ComponentPropsWithoutRef<typeof AlertDialogPrimitive.Title>
79
- >(({ className, ...props }, ref) => (
80
- <AlertDialogPrimitive.Title
81
- ref={ref}
82
- className={cn('text-lg font-semibold', className)}
83
- {...props}
84
- />
85
- ));
86
- AlertDialogTitle.displayName = AlertDialogPrimitive.Title.displayName;
87
-
88
- const AlertDialogDescription = React.forwardRef<
89
- React.ElementRef<typeof AlertDialogPrimitive.Description>,
90
- React.ComponentPropsWithoutRef<typeof AlertDialogPrimitive.Description>
91
- >(({ className, ...props }, ref) => (
92
- <AlertDialogPrimitive.Description
93
- ref={ref}
94
- className={cn('text-muted-foreground text-sm', className)}
95
- {...props}
96
- />
97
- ));
98
- AlertDialogDescription.displayName =
99
- AlertDialogPrimitive.Description.displayName;
100
-
101
- const AlertDialogAction = React.forwardRef<
102
- React.ElementRef<typeof AlertDialogPrimitive.Action>,
103
- React.ComponentPropsWithoutRef<typeof AlertDialogPrimitive.Action>
104
- >(({ className, ...props }, ref) => (
105
- <AlertDialogPrimitive.Action
106
- ref={ref}
107
- className={cn(buttonVariants(), className)}
108
- {...props}
109
- />
110
- ));
111
- AlertDialogAction.displayName = AlertDialogPrimitive.Action.displayName;
112
-
113
- const AlertDialogCancel = React.forwardRef<
114
- React.ElementRef<typeof AlertDialogPrimitive.Cancel>,
115
- React.ComponentPropsWithoutRef<typeof AlertDialogPrimitive.Cancel>
116
- >(({ className, ...props }, ref) => (
117
- <AlertDialogPrimitive.Cancel
118
- ref={ref}
119
- className={cn(
120
- buttonVariants({ variant: 'outline' }),
121
- 'mt-2 sm:mt-0',
122
- className
123
- )}
124
- {...props}
125
- />
126
- ));
127
- AlertDialogCancel.displayName = AlertDialogPrimitive.Cancel.displayName;
128
-
129
- export {
130
- AlertDialog,
131
- AlertDialogAction,
132
- AlertDialogCancel,
133
- AlertDialogContent,
134
- AlertDialogDescription,
135
- AlertDialogFooter,
136
- AlertDialogHeader,
137
- AlertDialogOverlay,
138
- AlertDialogPortal,
139
- AlertDialogTitle,
140
- AlertDialogTrigger,
141
- };
@@ -1,66 +0,0 @@
1
- import { cva, type VariantProps } from 'class-variance-authority';
2
- import * as React from 'react';
3
-
4
- import { cn } from './lib/utils';
5
-
6
- const alertVariants = cva(
7
- 'relative w-full rounded-lg border px-4 py-3 text-sm grid has-[>svg]:grid-cols-[calc(var(--spacing)*4)_1fr] grid-cols-[0_1fr] has-[>svg]:gap-x-3 gap-y-0.5 items-start [&>svg]:size-4 [&>svg]:translate-y-0.5 [&>svg]:text-current',
8
- {
9
- variants: {
10
- variant: {
11
- default: 'bg-card text-card-foreground',
12
- destructive:
13
- 'text-destructive bg-card [&>svg]:text-current *:data-[slot=alert-description]:text-destructive/90',
14
- },
15
- },
16
- defaultVariants: {
17
- variant: 'default',
18
- },
19
- }
20
- );
21
-
22
- function Alert({
23
- className,
24
- variant,
25
- ...props
26
- }: React.ComponentProps<'div'> & VariantProps<typeof alertVariants>) {
27
- return (
28
- <div
29
- data-slot="alert"
30
- role="alert"
31
- className={cn(alertVariants({ variant }), className)}
32
- {...props}
33
- />
34
- );
35
- }
36
-
37
- function AlertTitle({ className, ...props }: React.ComponentProps<'div'>) {
38
- return (
39
- <div
40
- data-slot="alert-title"
41
- className={cn(
42
- 'col-start-2 line-clamp-1 min-h-4 font-medium tracking-tight',
43
- className
44
- )}
45
- {...props}
46
- />
47
- );
48
- }
49
-
50
- function AlertDescription({
51
- className,
52
- ...props
53
- }: React.ComponentProps<'div'>) {
54
- return (
55
- <div
56
- data-slot="alert-description"
57
- className={cn(
58
- 'text-muted-foreground col-start-2 grid justify-items-start gap-1 text-sm [&_p]:leading-relaxed',
59
- className
60
- )}
61
- {...props}
62
- />
63
- );
64
- }
65
-
66
- export { Alert, AlertDescription, AlertTitle };
@@ -1,167 +0,0 @@
1
- 'use client';
2
-
3
- import { AnimatePresence, motion } from 'framer-motion';
4
- import { Moon, Sun } from 'lucide-react';
5
- import { useTheme } from 'next-themes';
6
- import { useCallback, useEffect, useRef, useState } from 'react';
7
- import { flushSync } from 'react-dom';
8
-
9
- import { cn } from './lib/utils';
10
-
11
- interface AnimatedThemeTogglerProps {
12
- className?: string;
13
- duration?: number;
14
- }
15
-
16
- export const AnimatedThemeToggler = ({
17
- className,
18
- duration = 600,
19
- }: AnimatedThemeTogglerProps) => {
20
- const { theme, setTheme } = useTheme();
21
- const [mounted, setMounted] = useState(false);
22
- const [mousePosition, setMousePosition] = useState({ x: 0, y: 0 });
23
- const [isHovered, setIsHovered] = useState(false);
24
- const buttonRef = useRef<HTMLButtonElement>(null);
25
-
26
- useEffect(() => {
27
- setMounted(true);
28
- }, []);
29
-
30
- const handleMouseMove = useCallback(
31
- (e: React.MouseEvent<HTMLButtonElement>) => {
32
- if (!buttonRef.current) return;
33
-
34
- const rect = buttonRef.current.getBoundingClientRect();
35
- const centerX = rect.left + rect.width / 2;
36
- const centerY = rect.top + rect.height / 2;
37
-
38
- const deltaX = (e.clientX - centerX) / 10;
39
- const deltaY = (e.clientY - centerY) / 10;
40
-
41
- setMousePosition({ x: deltaX, y: deltaY });
42
- },
43
- []
44
- );
45
-
46
- const toggleTheme = useCallback(async () => {
47
- if (!buttonRef.current || !mounted) return;
48
-
49
- const isDark = theme === 'dark';
50
-
51
- if (!document.startViewTransition) {
52
- setTheme(isDark ? 'light' : 'dark');
53
- return;
54
- }
55
-
56
- const transition = document.startViewTransition(() => {
57
- flushSync(() => {
58
- setTheme(isDark ? 'light' : 'dark');
59
- });
60
- });
61
-
62
- await transition.ready;
63
-
64
- const clipPath = isDark
65
- ? ['inset(0 0 100% 0)', 'inset(0 0 0 0)']
66
- : ['inset(100% 0 0 0)', 'inset(0 0 0 0)'];
67
-
68
- document.documentElement.animate(
69
- {
70
- clipPath,
71
- },
72
- {
73
- duration,
74
- easing: 'ease-in-out',
75
- pseudoElement: '::view-transition-new(root)',
76
- }
77
- );
78
- }, [theme, setTheme, mounted, duration]);
79
-
80
- if (!mounted) {
81
- return (
82
- <button
83
- className={cn(
84
- 'relative cursor-pointer overflow-visible',
85
- 'bg-secondary hover:bg-secondary/80',
86
- 'border-border border',
87
- 'rounded-md p-2',
88
- 'transition-colors duration-200',
89
- className
90
- )}
91
- disabled
92
- >
93
- <div className="flex h-5 w-5 items-center justify-center" />
94
- <span className="sr-only">Toggle theme</span>
95
- </button>
96
- );
97
- }
98
-
99
- return (
100
- <motion.button
101
- ref={buttonRef}
102
- onClick={toggleTheme}
103
- onMouseMove={handleMouseMove}
104
- onMouseEnter={() => setIsHovered(true)}
105
- onMouseLeave={() => {
106
- setIsHovered(false);
107
- setMousePosition({ x: 0, y: 0 });
108
- }}
109
- className={cn(
110
- 'relative cursor-pointer overflow-visible',
111
- 'bg-secondary hover:bg-secondary/80',
112
- 'border-border border',
113
- 'rounded-md p-2',
114
- 'transition-colors duration-200',
115
- className
116
- )}
117
- animate={{
118
- x: isHovered ? mousePosition.x : 0,
119
- y: isHovered ? mousePosition.y : 0,
120
- }}
121
- transition={{
122
- type: 'spring',
123
- stiffness: 150,
124
- damping: 15,
125
- mass: 0.1,
126
- }}
127
- >
128
- <AnimatePresence mode="wait" initial={false}>
129
- {theme === 'dark' ? (
130
- <motion.div
131
- key="moon"
132
- initial={{ rotateY: -180, scale: 0 }}
133
- animate={{ rotateY: 0, scale: 1 }}
134
- exit={{ rotateY: 180, scale: 0 }}
135
- transition={{
136
- type: 'spring',
137
- stiffness: 260,
138
- damping: 20,
139
- }}
140
- className="flex items-center justify-center"
141
- style={{ transformStyle: 'preserve-3d' }}
142
- >
143
- <Moon className="h-5 w-5" />
144
- </motion.div>
145
- ) : (
146
- <motion.div
147
- key="sun"
148
- initial={{ rotateY: -180, scale: 0 }}
149
- animate={{ rotateY: 0, scale: 1 }}
150
- exit={{ rotateY: 180, scale: 0 }}
151
- transition={{
152
- type: 'spring',
153
- stiffness: 260,
154
- damping: 20,
155
- }}
156
- className="flex items-center justify-center"
157
- style={{ transformStyle: 'preserve-3d' }}
158
- >
159
- <Sun className="h-5 w-5" />
160
- </motion.div>
161
- )}
162
- </AnimatePresence>
163
-
164
- <span className="sr-only">Toggle theme</span>
165
- </motion.button>
166
- );
167
- };
@@ -1,53 +0,0 @@
1
- 'use client';
2
-
3
- import * as AvatarPrimitive from '@radix-ui/react-avatar';
4
- import * as React from 'react';
5
-
6
- import { cn } from './lib/utils';
7
-
8
- function Avatar({
9
- className,
10
- ...props
11
- }: React.ComponentProps<typeof AvatarPrimitive.Root>) {
12
- return (
13
- <AvatarPrimitive.Root
14
- data-slot="avatar"
15
- className={cn(
16
- 'relative flex size-8 shrink-0 overflow-hidden rounded-full',
17
- className
18
- )}
19
- {...props}
20
- />
21
- );
22
- }
23
-
24
- function AvatarImage({
25
- className,
26
- ...props
27
- }: React.ComponentProps<typeof AvatarPrimitive.Image>) {
28
- return (
29
- <AvatarPrimitive.Image
30
- data-slot="avatar-image"
31
- className={cn('aspect-square size-full', className)}
32
- {...props}
33
- />
34
- );
35
- }
36
-
37
- function AvatarFallback({
38
- className,
39
- ...props
40
- }: React.ComponentProps<typeof AvatarPrimitive.Fallback>) {
41
- return (
42
- <AvatarPrimitive.Fallback
43
- data-slot="avatar-fallback"
44
- className={cn(
45
- 'bg-muted flex size-full items-center justify-center rounded-full',
46
- className
47
- )}
48
- {...props}
49
- />
50
- );
51
- }
52
-
53
- export { Avatar, AvatarFallback, AvatarImage };
@@ -1,36 +0,0 @@
1
- import { cva, type VariantProps } from 'class-variance-authority';
2
- import * as React from 'react';
3
-
4
- import { cn } from './lib/utils';
5
-
6
- const badgeVariants = cva(
7
- 'inline-flex items-center rounded-full border px-2.5 py-0.5 text-xs font-semibold transition-colors focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2',
8
- {
9
- variants: {
10
- variant: {
11
- default:
12
- 'border-transparent bg-primary text-primary-foreground hover:bg-primary/80',
13
- secondary:
14
- 'border-transparent bg-secondary text-secondary-foreground hover:bg-secondary/80',
15
- destructive:
16
- 'border-transparent bg-destructive text-destructive-foreground hover:bg-destructive/80',
17
- outline: 'text-foreground',
18
- },
19
- },
20
- defaultVariants: {
21
- variant: 'default',
22
- },
23
- }
24
- );
25
-
26
- export interface BadgeProps
27
- extends React.HTMLAttributes<HTMLDivElement>,
28
- VariantProps<typeof badgeVariants> {}
29
-
30
- function Badge({ className, variant, ...props }: BadgeProps) {
31
- return (
32
- <div className={cn(badgeVariants({ variant }), className)} {...props} />
33
- );
34
- }
35
-
36
- export { Badge, badgeVariants };