@aurostack/stacks 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (601) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +209 -0
  3. package/cli/src/extract.mjs +165 -0
  4. package/cli/src/fsx.mjs +197 -0
  5. package/cli/src/generate.mjs +235 -0
  6. package/cli/src/hooks.mjs +55 -0
  7. package/cli/src/manifest.mjs +163 -0
  8. package/cli/src/strip.mjs +208 -0
  9. package/cli/src/tokens.mjs +102 -0
  10. package/cli/stack.mjs +672 -0
  11. package/package.json +37 -0
  12. package/templates/nest-api/files/.bin/db.ts +123 -0
  13. package/templates/nest-api/files/.bin/dc.ts +74 -0
  14. package/templates/nest-api/files/.bin/helpers.ts +49 -0
  15. package/templates/nest-api/files/.bin/secrets.ts +68 -0
  16. package/templates/nest-api/files/.bin/start.ts +47 -0
  17. package/templates/nest-api/files/.dockerignore +7 -0
  18. package/templates/nest-api/files/.env.example +91 -0
  19. package/templates/nest-api/files/.env.test.example +75 -0
  20. package/templates/nest-api/files/.forgejo/workflows/build-and-push.yml +41 -0
  21. package/templates/nest-api/files/.forgejo/workflows/test-e2e.yml +83 -0
  22. package/templates/nest-api/files/.forgejo/workflows/test.yml +53 -0
  23. package/templates/nest-api/files/.npmrc.example +1 -0
  24. package/templates/nest-api/files/.nvmrc +1 -0
  25. package/templates/nest-api/files/.prettierrc +8 -0
  26. package/templates/nest-api/files/.swcrc +15 -0
  27. package/templates/nest-api/files/.watchmanconfig +3 -0
  28. package/templates/nest-api/files/.yarnrc.yml +1 -0
  29. package/templates/nest-api/files/Dockerfile +40 -0
  30. package/templates/nest-api/files/__mocks__/activation-twofa.js +7 -0
  31. package/templates/nest-api/files/__mocks__/activation.js +7 -0
  32. package/templates/nest-api/files/__mocks__/forgot-password.js +7 -0
  33. package/templates/nest-api/files/__mocks__/main-layout.js +7 -0
  34. package/templates/nest-api/files/__mocks__/password-changed.js +7 -0
  35. package/templates/nest-api/files/__mocks__/reset-password.js +7 -0
  36. package/templates/nest-api/files/__mocks__/set-password.js +7 -0
  37. package/templates/nest-api/files/__mocks__/welcome.js +7 -0
  38. package/templates/nest-api/files/_gitignore +62 -0
  39. package/templates/nest-api/files/compose.test.yml +33 -0
  40. package/templates/nest-api/files/compose.yml +39 -0
  41. package/templates/nest-api/files/emails/activation-twofa.tsx +58 -0
  42. package/templates/nest-api/files/emails/activation.tsx +36 -0
  43. package/templates/nest-api/files/emails/forgot-password.tsx +29 -0
  44. package/templates/nest-api/files/emails/main-layout.tsx +134 -0
  45. package/templates/nest-api/files/emails/password-changed.tsx +24 -0
  46. package/templates/nest-api/files/emails/reset-password.tsx +24 -0
  47. package/templates/nest-api/files/emails/set-password.tsx +25 -0
  48. package/templates/nest-api/files/emails/welcome.tsx +39 -0
  49. package/templates/nest-api/files/eslint.config.mjs +28 -0
  50. package/templates/nest-api/files/global.d.ts +5 -0
  51. package/templates/nest-api/files/nest-cli.json +8 -0
  52. package/templates/nest-api/files/package.json +150 -0
  53. package/templates/nest-api/files/prisma/schema/auth.prisma +75 -0
  54. package/templates/nest-api/files/prisma/schema/base.prisma +17 -0
  55. package/templates/nest-api/files/prisma/schema/enum.prisma +17 -0
  56. package/templates/nest-api/files/prisma/schema/notification.prisma +14 -0
  57. package/templates/nest-api/files/prisma.config.ts +13 -0
  58. package/templates/nest-api/files/public/.gitkeep +0 -0
  59. package/templates/nest-api/files/seeders/index.ts +27 -0
  60. package/templates/nest-api/files/seeders/util.ts +88 -0
  61. package/templates/nest-api/files/src/__tests__/app.controller.spec.ts +29 -0
  62. package/templates/nest-api/files/src/app.controller.ts +40 -0
  63. package/templates/nest-api/files/src/app.module.ts +268 -0
  64. package/templates/nest-api/files/src/app.resolver.ts +32 -0
  65. package/templates/nest-api/files/src/app.setup.ts +178 -0
  66. package/templates/nest-api/files/src/common/__tests__/auth.service.spec.ts +31 -0
  67. package/templates/nest-api/files/src/common/__tests__/cache.service.spec.ts +214 -0
  68. package/templates/nest-api/files/src/common/__tests__/config.service.spec.ts +56 -0
  69. package/templates/nest-api/files/src/common/__tests__/generator.service.spec.ts +114 -0
  70. package/templates/nest-api/files/src/common/__tests__/health.controller.spec.ts +72 -0
  71. package/templates/nest-api/files/src/common/__tests__/helpers.spec.ts +124 -0
  72. package/templates/nest-api/files/src/common/__tests__/mail.service.spec.ts +75 -0
  73. package/templates/nest-api/files/src/common/__tests__/prisma.indicator.spec.ts +39 -0
  74. package/templates/nest-api/files/src/common/__tests__/prisma.service.spec.ts +15 -0
  75. package/templates/nest-api/files/src/common/__tests__/records.spec.ts +87 -0
  76. package/templates/nest-api/files/src/common/__tests__/schemas.spec.ts +142 -0
  77. package/templates/nest-api/files/src/common/__tests__/throttler-storage.service.spec.ts +80 -0
  78. package/templates/nest-api/files/src/common/common.module.ts +72 -0
  79. package/templates/nest-api/files/src/common/constants/index.ts +2 -0
  80. package/templates/nest-api/files/src/common/constants/options.ts +52 -0
  81. package/templates/nest-api/files/src/common/constants/pino.ts +24 -0
  82. package/templates/nest-api/files/src/common/controllers/health.controller.ts +54 -0
  83. package/templates/nest-api/files/src/common/controllers/index.ts +2 -0
  84. package/templates/nest-api/files/src/common/controllers/metrics.controller.ts +21 -0
  85. package/templates/nest-api/files/src/common/decorators/index.ts +2 -0
  86. package/templates/nest-api/files/src/common/decorators/misc.decorator.ts +49 -0
  87. package/templates/nest-api/files/src/common/decorators/multi-part.decorator.ts +6 -0
  88. package/templates/nest-api/files/src/common/dto/filters.dto.ts +25 -0
  89. package/templates/nest-api/files/src/common/dto/index.ts +1 -0
  90. package/templates/nest-api/files/src/common/entity/index.ts +2 -0
  91. package/templates/nest-api/files/src/common/entity/ok.entity.ts +11 -0
  92. package/templates/nest-api/files/src/common/entity/pagination.entity.ts +24 -0
  93. package/templates/nest-api/files/src/common/exceptions/index.ts +1 -0
  94. package/templates/nest-api/files/src/common/exceptions/prisma.exception.ts +57 -0
  95. package/templates/nest-api/files/src/common/guards/feature-flag.guard.ts +51 -0
  96. package/templates/nest-api/files/src/common/guards/index.ts +1 -0
  97. package/templates/nest-api/files/src/common/interceptors/index.ts +1 -0
  98. package/templates/nest-api/files/src/common/interceptors/metrics.interceptor.ts +37 -0
  99. package/templates/nest-api/files/src/common/misc/helpers.ts +26 -0
  100. package/templates/nest-api/files/src/common/misc/index.ts +3 -0
  101. package/templates/nest-api/files/src/common/misc/origin-builder.ts +56 -0
  102. package/templates/nest-api/files/src/common/misc/prisma.indicator.ts +24 -0
  103. package/templates/nest-api/files/src/common/modules/index.ts +3 -0
  104. package/templates/nest-api/files/src/common/modules/logger.module.ts +104 -0
  105. package/templates/nest-api/files/src/common/modules/metrics.module.ts +28 -0
  106. package/templates/nest-api/files/src/common/modules/queue.module.ts +23 -0
  107. package/templates/nest-api/files/src/common/processors/index.ts +1 -0
  108. package/templates/nest-api/files/src/common/processors/mail/index.ts +47 -0
  109. package/templates/nest-api/files/src/common/processors/mail/process.ts +225 -0
  110. package/templates/nest-api/files/src/common/schemas/fields.ts +76 -0
  111. package/templates/nest-api/files/src/common/schemas/index.ts +1 -0
  112. package/templates/nest-api/files/src/common/services/auth-rate-limit.storage.ts +79 -0
  113. package/templates/nest-api/files/src/common/services/auth.service.ts +16 -0
  114. package/templates/nest-api/files/src/common/services/cache.service.ts +118 -0
  115. package/templates/nest-api/files/src/common/services/config.service.ts +388 -0
  116. package/templates/nest-api/files/src/common/services/feature-flag.service.ts +117 -0
  117. package/templates/nest-api/files/src/common/services/generator.service.ts +54 -0
  118. package/templates/nest-api/files/src/common/services/index.ts +10 -0
  119. package/templates/nest-api/files/src/common/services/logger.service.ts +14 -0
  120. package/templates/nest-api/files/src/common/services/mail.service.ts +44 -0
  121. package/templates/nest-api/files/src/common/services/prisma.service.ts +106 -0
  122. package/templates/nest-api/files/src/common/services/throttler-storage.service.ts +112 -0
  123. package/templates/nest-api/files/src/common/types/email.ts +55 -0
  124. package/templates/nest-api/files/src/common/types/index.ts +3 -0
  125. package/templates/nest-api/files/src/common/types/misc.ts +20 -0
  126. package/templates/nest-api/files/src/common/types/prisma.ts +18 -0
  127. package/templates/nest-api/files/src/common/utils/enum.ts +39 -0
  128. package/templates/nest-api/files/src/common/utils/helpers.ts +139 -0
  129. package/templates/nest-api/files/src/common/utils/index.ts +5 -0
  130. package/templates/nest-api/files/src/common/utils/ip.ts +22 -0
  131. package/templates/nest-api/files/src/common/utils/records.ts +66 -0
  132. package/templates/nest-api/files/src/common/utils/redis.keys.ts +31 -0
  133. package/templates/nest-api/files/src/lib/__tests__/access.spec.ts +33 -0
  134. package/templates/nest-api/files/src/lib/access.ts +114 -0
  135. package/templates/nest-api/files/src/lib/auth.ts +76 -0
  136. package/templates/nest-api/files/src/lib/rate-limit.ts +20 -0
  137. package/templates/nest-api/files/src/main.ts +28 -0
  138. package/templates/nest-api/files/src/media/__tests__/media.service.spec.ts +60 -0
  139. package/templates/nest-api/files/src/media/__tests__/s3.service.spec.ts +159 -0
  140. package/templates/nest-api/files/src/media/entity/file.entity.ts +7 -0
  141. package/templates/nest-api/files/src/media/entity/index.ts +1 -0
  142. package/templates/nest-api/files/src/media/media.module.ts +20 -0
  143. package/templates/nest-api/files/src/media/processors/index.ts +44 -0
  144. package/templates/nest-api/files/src/media/services/index.ts +2 -0
  145. package/templates/nest-api/files/src/media/services/media.service.ts +29 -0
  146. package/templates/nest-api/files/src/media/services/s3.service.ts +168 -0
  147. package/templates/nest-api/files/src/notifications/controllers/index.ts +1 -0
  148. package/templates/nest-api/files/src/notifications/controllers/notifications.controller.ts +72 -0
  149. package/templates/nest-api/files/src/notifications/dto/index.ts +1 -0
  150. package/templates/nest-api/files/src/notifications/dto/notification.dto.ts +11 -0
  151. package/templates/nest-api/files/src/notifications/entity/index.ts +1 -0
  152. package/templates/nest-api/files/src/notifications/entity/notification.entity.ts +37 -0
  153. package/templates/nest-api/files/src/notifications/notifications.module.ts +11 -0
  154. package/templates/nest-api/files/src/notifications/services/index.ts +1 -0
  155. package/templates/nest-api/files/src/notifications/services/notifications.service.ts +98 -0
  156. package/templates/nest-api/files/src/realtime/__tests__/events.gateway.spec.ts +88 -0
  157. package/templates/nest-api/files/src/realtime/__tests__/rooms.spec.ts +29 -0
  158. package/templates/nest-api/files/src/realtime/__tests__/ws-permissions.guard.spec.ts +54 -0
  159. package/templates/nest-api/files/src/realtime/adapters/redis-io.adapter.ts +57 -0
  160. package/templates/nest-api/files/src/realtime/constants/index.ts +68 -0
  161. package/templates/nest-api/files/src/realtime/decorators/index.ts +1 -0
  162. package/templates/nest-api/files/src/realtime/decorators/ws-permissions.decorator.ts +14 -0
  163. package/templates/nest-api/files/src/realtime/gateways/events.gateway.ts +187 -0
  164. package/templates/nest-api/files/src/realtime/guards/index.ts +2 -0
  165. package/templates/nest-api/files/src/realtime/guards/ws-auth.guard.ts +20 -0
  166. package/templates/nest-api/files/src/realtime/guards/ws-permissions.guard.ts +35 -0
  167. package/templates/nest-api/files/src/realtime/misc/session-from-socket.ts +54 -0
  168. package/templates/nest-api/files/src/realtime/realtime.module.ts +26 -0
  169. package/templates/nest-api/files/src/realtime/services/presence.service.ts +56 -0
  170. package/templates/nest-api/files/src/realtime/services/realtime.service.ts +72 -0
  171. package/templates/nest-api/files/src/users/__tests__/current-user.controller.spec.ts +77 -0
  172. package/templates/nest-api/files/src/users/__tests__/current-user.service.spec.ts +159 -0
  173. package/templates/nest-api/files/src/users/__tests__/user.entity.spec.ts +175 -0
  174. package/templates/nest-api/files/src/users/__tests__/users.controller.spec.ts +89 -0
  175. package/templates/nest-api/files/src/users/__tests__/users.service.spec.ts +398 -0
  176. package/templates/nest-api/files/src/users/controllers/current-user.controller.ts +82 -0
  177. package/templates/nest-api/files/src/users/controllers/index.ts +2 -0
  178. package/templates/nest-api/files/src/users/controllers/users.controller.ts +40 -0
  179. package/templates/nest-api/files/src/users/decorators/index.ts +1 -0
  180. package/templates/nest-api/files/src/users/decorators/permissions.decorator.ts +24 -0
  181. package/templates/nest-api/files/src/users/dto/avatar.dto.ts +13 -0
  182. package/templates/nest-api/files/src/users/dto/filters.dto.ts +8 -0
  183. package/templates/nest-api/files/src/users/dto/index.ts +2 -0
  184. package/templates/nest-api/files/src/users/entity/index.ts +1 -0
  185. package/templates/nest-api/files/src/users/entity/user.entity.ts +53 -0
  186. package/templates/nest-api/files/src/users/services/current-user.service.ts +46 -0
  187. package/templates/nest-api/files/src/users/services/index.ts +2 -0
  188. package/templates/nest-api/files/src/users/services/users.service.ts +88 -0
  189. package/templates/nest-api/files/src/users/users.module.ts +10 -0
  190. package/templates/nest-api/files/test/app.spec.ts +102 -0
  191. package/templates/nest-api/files/test/auth.spec.ts +108 -0
  192. package/templates/nest-api/files/test/current-user.spec.ts +154 -0
  193. package/templates/nest-api/files/test/factory/app.ts +172 -0
  194. package/templates/nest-api/files/test/factory/constants.ts +22 -0
  195. package/templates/nest-api/files/test/factory/dto.ts +27 -0
  196. package/templates/nest-api/files/test/factory/entity.ts +70 -0
  197. package/templates/nest-api/files/test/factory/gen.ts +75 -0
  198. package/templates/nest-api/files/test/factory/util.ts +21 -0
  199. package/templates/nest-api/files/test/notifications.spec.ts +176 -0
  200. package/templates/nest-api/files/test/rate-limit.spec.ts +180 -0
  201. package/templates/nest-api/files/test/setup.ts +1 -0
  202. package/templates/nest-api/files/test/users.spec.ts +437 -0
  203. package/templates/nest-api/files/tsconfig.build.json +13 -0
  204. package/templates/nest-api/files/tsconfig.json +35 -0
  205. package/templates/nest-api/files/vitest.config.ts +53 -0
  206. package/templates/nest-api/template.json +452 -0
  207. package/templates/node-worker/files/.bin/db.ts +123 -0
  208. package/templates/node-worker/files/.bin/helpers.ts +49 -0
  209. package/templates/node-worker/files/.bin/secrets.ts +68 -0
  210. package/templates/node-worker/files/.dockerignore +5 -0
  211. package/templates/node-worker/files/.env.example +21 -0
  212. package/templates/node-worker/files/.forgejo/workflows/build-and-push.yml +41 -0
  213. package/templates/node-worker/files/.nvmrc +1 -0
  214. package/templates/node-worker/files/.prettierignore +4 -0
  215. package/templates/node-worker/files/.prettierrc +8 -0
  216. package/templates/node-worker/files/.yarnrc.yml +5 -0
  217. package/templates/node-worker/files/Dockerfile +63 -0
  218. package/templates/node-worker/files/Dockerfile.dev +59 -0
  219. package/templates/node-worker/files/_gitignore +48 -0
  220. package/templates/node-worker/files/ecosystem.config.cjs +10 -0
  221. package/templates/node-worker/files/eslint.config.js +37 -0
  222. package/templates/node-worker/files/package.json +67 -0
  223. package/templates/node-worker/files/prisma/_gitignore +1 -0
  224. package/templates/node-worker/files/prisma/schema/base.prisma +36 -0
  225. package/templates/node-worker/files/prisma.config.ts +13 -0
  226. package/templates/node-worker/files/src/config.ts +55 -0
  227. package/templates/node-worker/files/src/db.ts +118 -0
  228. package/templates/node-worker/files/src/index.ts +7 -0
  229. package/templates/node-worker/files/src/logger.ts +45 -0
  230. package/templates/node-worker/files/src/types.ts +23 -0
  231. package/templates/node-worker/files/src/utils/browser.ts +16 -0
  232. package/templates/node-worker/files/src/utils/date.ts +68 -0
  233. package/templates/node-worker/files/src/utils/helpers.ts +17 -0
  234. package/templates/node-worker/files/src/utils/number.ts +47 -0
  235. package/templates/node-worker/files/src/utils/worker.ts +29 -0
  236. package/templates/node-worker/files/src/workers/constants.ts +6 -0
  237. package/templates/node-worker/files/src/workers/example/index.ts +27 -0
  238. package/templates/node-worker/files/src/workers/index.ts +1 -0
  239. package/templates/node-worker/files/tsconfig.json +30 -0
  240. package/templates/node-worker/template.json +135 -0
  241. package/templates/py-worker/files/.dockerignore +176 -0
  242. package/templates/py-worker/files/.env.example +20 -0
  243. package/templates/py-worker/files/.forgejo/workflows/build-amd-push.yml +39 -0
  244. package/templates/py-worker/files/Dockerfile +10 -0
  245. package/templates/py-worker/files/README.md +3 -0
  246. package/templates/py-worker/files/_gitignore +176 -0
  247. package/templates/py-worker/files/config.py +89 -0
  248. package/templates/py-worker/files/models/decorators.py +42 -0
  249. package/templates/py-worker/files/models/enums.py +32 -0
  250. package/templates/py-worker/files/models/mixins.py +61 -0
  251. package/templates/py-worker/files/requirements.txt +18 -0
  252. package/templates/py-worker/files/utils/db.py +67 -0
  253. package/templates/py-worker/files/utils/logger.py +97 -0
  254. package/templates/py-worker/files/utils/misc.py +45 -0
  255. package/templates/py-worker/files/utils/worker.py +80 -0
  256. package/templates/py-worker/files/worker.py +41 -0
  257. package/templates/py-worker/files/workers/hello/process.py +22 -0
  258. package/templates/py-worker/template.json +87 -0
  259. package/templates/react-app/README.md +78 -0
  260. package/templates/react-app/derive.sh +128 -0
  261. package/templates/react-app/files/.bin/openapi.js +32 -0
  262. package/templates/react-app/files/.bin/secrets.js +47 -0
  263. package/templates/react-app/files/.dockerignore +16 -0
  264. package/templates/react-app/files/.editorconfig +12 -0
  265. package/templates/react-app/files/.env.example +34 -0
  266. package/templates/react-app/files/.forgejo/workflows/build-and-push.yml +55 -0
  267. package/templates/react-app/files/.forgejo/workflows/ci.yml +39 -0
  268. package/templates/react-app/files/.husky/pre-commit +1 -0
  269. package/templates/react-app/files/.nvmrc +1 -0
  270. package/templates/react-app/files/.prettierignore +6 -0
  271. package/templates/react-app/files/.prettierrc +9 -0
  272. package/templates/react-app/files/.yarnrc.yml +2 -0
  273. package/templates/react-app/files/Dockerfile +52 -0
  274. package/templates/react-app/files/_gitignore +33 -0
  275. package/templates/react-app/files/build.sh +53 -0
  276. package/templates/react-app/files/docker/nginx.conf +134 -0
  277. package/templates/react-app/files/eslint.config.mjs +75 -0
  278. package/templates/react-app/files/index.html +62 -0
  279. package/templates/react-app/files/openapi-ts.config.ts +22 -0
  280. package/templates/react-app/files/package.json +80 -0
  281. package/templates/react-app/files/src/app/nav.tsx +32 -0
  282. package/templates/react-app/files/src/app/providers.tsx +39 -0
  283. package/templates/react-app/files/src/app/router.tsx +142 -0
  284. package/templates/react-app/files/src/app/store.ts +20 -0
  285. package/templates/react-app/files/src/features/auth/components/auth-card.tsx +23 -0
  286. package/templates/react-app/files/src/features/auth/components/checkbox-field.tsx +32 -0
  287. package/templates/react-app/files/src/features/auth/components/google-button.tsx +41 -0
  288. package/templates/react-app/files/src/features/auth/components/or-divider.tsx +9 -0
  289. package/templates/react-app/files/src/features/auth/components/password-field.tsx +70 -0
  290. package/templates/react-app/files/src/features/auth/components/submit-button.tsx +16 -0
  291. package/templates/react-app/files/src/features/auth/lib/auth-actions.ts +68 -0
  292. package/templates/react-app/files/src/features/auth/lib/password-strength.ts +20 -0
  293. package/templates/react-app/files/src/features/auth/lib/redirect.ts +6 -0
  294. package/templates/react-app/files/src/features/auth/lib/schemas.ts +37 -0
  295. package/templates/react-app/files/src/features/seo/routes.ts +65 -0
  296. package/templates/react-app/files/src/lib/env.ts +28 -0
  297. package/templates/react-app/files/src/lib/paths.ts +58 -0
  298. package/templates/react-app/files/src/main.tsx +19 -0
  299. package/templates/react-app/files/src/routes/admin/home.tsx +24 -0
  300. package/templates/react-app/files/src/routes/auth/forgot-password.tsx +93 -0
  301. package/templates/react-app/files/src/routes/auth/login.tsx +87 -0
  302. package/templates/react-app/files/src/routes/auth/reset-password.tsx +83 -0
  303. package/templates/react-app/files/src/routes/auth/signup.tsx +117 -0
  304. package/templates/react-app/files/src/routes/auth/verified.tsx +31 -0
  305. package/templates/react-app/files/src/routes/auth/verify-email.tsx +97 -0
  306. package/templates/react-app/files/src/routes/home.tsx +17 -0
  307. package/templates/react-app/files/src/routes/legal/privacy.tsx +26 -0
  308. package/templates/react-app/files/src/routes/legal/terms.tsx +28 -0
  309. package/templates/react-app/files/src/routes/marketing/home.tsx +18 -0
  310. package/templates/react-app/files/src/routes/settings.tsx +10 -0
  311. package/templates/react-app/files/src/shared/api/base-api.ts +24 -0
  312. package/templates/react-app/files/src/shared/api/base-query.ts +29 -0
  313. package/templates/react-app/files/src/shared/api/config.ts +21 -0
  314. package/templates/react-app/files/src/shared/api/index.ts +15 -0
  315. package/templates/react-app/files/src/shared/api/realtime-events.ts +34 -0
  316. package/templates/react-app/files/src/shared/api/socket.ts +41 -0
  317. package/templates/react-app/files/src/shared/api/store.ts +39 -0
  318. package/templates/react-app/files/src/shared/api/upload-query.ts +115 -0
  319. package/templates/react-app/files/src/shared/auth/client.ts +27 -0
  320. package/templates/react-app/files/src/shared/auth/config.ts +28 -0
  321. package/templates/react-app/files/src/shared/auth/guards.tsx +75 -0
  322. package/templates/react-app/files/src/shared/auth/hooks.ts +13 -0
  323. package/templates/react-app/files/src/shared/auth/index.ts +20 -0
  324. package/templates/react-app/files/src/shared/auth/logout.ts +23 -0
  325. package/templates/react-app/files/src/shared/auth/provider.tsx +52 -0
  326. package/templates/react-app/files/src/shared/auth/redirect.ts +65 -0
  327. package/templates/react-app/files/src/shared/auth/slice.ts +54 -0
  328. package/templates/react-app/files/src/shared/hooks/index.ts +6 -0
  329. package/templates/react-app/files/src/shared/layouts/app-shell/app-shell.tsx +225 -0
  330. package/templates/react-app/files/src/shared/layouts/app-shell/user-menu.tsx +74 -0
  331. package/templates/react-app/files/src/shared/layouts/auth-layout.tsx +137 -0
  332. package/templates/react-app/files/src/shared/layouts/error-boundary.tsx +38 -0
  333. package/templates/react-app/files/src/shared/layouts/index.ts +23 -0
  334. package/templates/react-app/files/src/shared/layouts/marketing/marketing-layout.tsx +29 -0
  335. package/templates/react-app/files/src/shared/layouts/marketing/section-heading.tsx +38 -0
  336. package/templates/react-app/files/src/shared/layouts/marketing/section.tsx +30 -0
  337. package/templates/react-app/files/src/shared/layouts/states/app-shell-skeleton.tsx +47 -0
  338. package/templates/react-app/files/src/shared/layouts/states/empty.tsx +22 -0
  339. package/templates/react-app/files/src/shared/layouts/states/forbidden.tsx +14 -0
  340. package/templates/react-app/files/src/shared/layouts/states/loading.tsx +11 -0
  341. package/templates/react-app/files/src/shared/layouts/states/not-found.tsx +21 -0
  342. package/templates/react-app/files/src/shared/layouts/states/page-skeleton.tsx +39 -0
  343. package/templates/react-app/files/src/shared/layouts/states/route-fallback.tsx +26 -0
  344. package/templates/react-app/files/src/shared/layouts/states/state-screen.tsx +22 -0
  345. package/templates/react-app/files/src/shared/types/README.md +28 -0
  346. package/templates/react-app/files/src/shared/types/api/index.ts +9 -0
  347. package/templates/react-app/files/src/shared/types/api/zod.gen.ts +5 -0
  348. package/templates/react-app/files/src/shared/types/index.ts +4 -0
  349. package/templates/react-app/files/src/shared/ui/components/brand/logo.tsx +51 -0
  350. package/templates/react-app/files/src/shared/ui/components/brand/social-icons.tsx +37 -0
  351. package/templates/react-app/files/src/shared/ui/components/form/form.tsx +120 -0
  352. package/templates/react-app/files/src/shared/ui/components/ui/alert.tsx +25 -0
  353. package/templates/react-app/files/src/shared/ui/components/ui/button.tsx +43 -0
  354. package/templates/react-app/files/src/shared/ui/components/ui/calendar.tsx +78 -0
  355. package/templates/react-app/files/src/shared/ui/components/ui/card.tsx +44 -0
  356. package/templates/react-app/files/src/shared/ui/components/ui/checkbox.tsx +16 -0
  357. package/templates/react-app/files/src/shared/ui/components/ui/date-picker.tsx +76 -0
  358. package/templates/react-app/files/src/shared/ui/components/ui/input.tsx +22 -0
  359. package/templates/react-app/files/src/shared/ui/components/ui/label.tsx +19 -0
  360. package/templates/react-app/files/src/shared/ui/components/ui/modal.tsx +94 -0
  361. package/templates/react-app/files/src/shared/ui/components/ui/popover.tsx +31 -0
  362. package/templates/react-app/files/src/shared/ui/components/ui/progress.tsx +33 -0
  363. package/templates/react-app/files/src/shared/ui/components/ui/search-input.tsx +69 -0
  364. package/templates/react-app/files/src/shared/ui/components/ui/select.tsx +80 -0
  365. package/templates/react-app/files/src/shared/ui/components/ui/skeleton.tsx +12 -0
  366. package/templates/react-app/files/src/shared/ui/components/ui/sonner.tsx +23 -0
  367. package/templates/react-app/files/src/shared/ui/components/ui/spinner.tsx +8 -0
  368. package/templates/react-app/files/src/shared/ui/components/ui/textarea.tsx +22 -0
  369. package/templates/react-app/files/src/shared/ui/index.ts +45 -0
  370. package/templates/react-app/files/src/shared/ui/lib/utils.ts +7 -0
  371. package/templates/react-app/files/src/shared/ui/lib/zod-setup.ts +28 -0
  372. package/templates/react-app/files/src/shared/ui/meta/brand.ts +18 -0
  373. package/templates/react-app/files/src/shared/ui/meta/meta-context.ts +30 -0
  374. package/templates/react-app/files/src/shared/ui/meta/meta-provider.tsx +24 -0
  375. package/templates/react-app/files/src/shared/ui/meta/page-meta.tsx +69 -0
  376. package/templates/react-app/files/src/shared/ui/meta/use-meta-config.ts +11 -0
  377. package/templates/react-app/files/src/shared/ui/styles/globals.css +285 -0
  378. package/templates/react-app/files/src/shared/ui/styles/theme.css +48 -0
  379. package/templates/react-app/files/src/shared/ui/theme/init-script.ts +23 -0
  380. package/templates/react-app/files/src/shared/ui/theme/theme-colors.ts +14 -0
  381. package/templates/react-app/files/src/shared/ui/theme/theme-context.ts +60 -0
  382. package/templates/react-app/files/src/shared/ui/theme/theme-provider.tsx +101 -0
  383. package/templates/react-app/files/src/shared/ui/theme/theme-toggle.tsx +40 -0
  384. package/templates/react-app/files/src/shared/ui/theme/use-theme.ts +11 -0
  385. package/templates/react-app/files/src/vite-env.d.ts +13 -0
  386. package/templates/react-app/files/tsconfig.json +26 -0
  387. package/templates/react-app/files/tsconfig.node.json +25 -0
  388. package/templates/react-app/files/vite.config.ts +344 -0
  389. package/templates/react-app/overrides/src/routes/auth/forgot-password.tsx +93 -0
  390. package/templates/react-app/overrides/src/routes/auth/login.tsx +87 -0
  391. package/templates/react-app/overrides/src/routes/auth/signup.tsx +117 -0
  392. package/templates/react-app/overrides/src/routes/auth/verified.tsx +31 -0
  393. package/templates/react-app/overrides/src/routes/auth/verify-email.tsx +97 -0
  394. package/templates/react-app/overrides/src/shared/auth/config.ts +28 -0
  395. package/templates/react-app/overrides/src/shared/auth/guards.tsx +75 -0
  396. package/templates/react-app/overrides/src/shared/auth/index.ts +20 -0
  397. package/templates/react-app/overrides/src/shared/auth/redirect.ts +65 -0
  398. package/templates/react-app/overrides/src/shared/hooks/index.ts +6 -0
  399. package/templates/react-app/overrides/src/shared/types/README.md +28 -0
  400. package/templates/react-app/template.json +222 -0
  401. package/templates/react-monorepo/files/.dockerignore +19 -0
  402. package/templates/react-monorepo/files/.editorconfig +12 -0
  403. package/templates/react-monorepo/files/.forgejo/workflows/build-and-push.yml +98 -0
  404. package/templates/react-monorepo/files/.forgejo/workflows/ci.yml +35 -0
  405. package/templates/react-monorepo/files/.husky/pre-commit +1 -0
  406. package/templates/react-monorepo/files/.nvmrc +1 -0
  407. package/templates/react-monorepo/files/.prettierignore +6 -0
  408. package/templates/react-monorepo/files/.prettierrc +9 -0
  409. package/templates/react-monorepo/files/.yarnrc.yml +2 -0
  410. package/templates/react-monorepo/files/Dockerfile +90 -0
  411. package/templates/react-monorepo/files/_gitignore +35 -0
  412. package/templates/react-monorepo/files/apps/admin/.bin/secrets.js +57 -0
  413. package/templates/react-monorepo/files/apps/admin/.env.example +16 -0
  414. package/templates/react-monorepo/files/apps/admin/eslint.config.mjs +3 -0
  415. package/templates/react-monorepo/files/apps/admin/index.html +57 -0
  416. package/templates/react-monorepo/files/apps/admin/package.json +33 -0
  417. package/templates/react-monorepo/files/apps/admin/src/App.tsx +3 -0
  418. package/templates/react-monorepo/files/apps/admin/src/app/nav.tsx +14 -0
  419. package/templates/react-monorepo/files/apps/admin/src/app/providers.tsx +42 -0
  420. package/templates/react-monorepo/files/apps/admin/src/app/router.tsx +50 -0
  421. package/templates/react-monorepo/files/apps/admin/src/app/store.ts +15 -0
  422. package/templates/react-monorepo/files/apps/admin/src/lib/env.ts +12 -0
  423. package/templates/react-monorepo/files/apps/admin/src/main.tsx +14 -0
  424. package/templates/react-monorepo/files/apps/admin/src/routes/home.tsx +17 -0
  425. package/templates/react-monorepo/files/apps/admin/src/routes/settings.tsx +10 -0
  426. package/templates/react-monorepo/files/apps/admin/src/vite-env.d.ts +14 -0
  427. package/templates/react-monorepo/files/apps/admin/tsconfig.json +10 -0
  428. package/templates/react-monorepo/files/apps/admin/vite.config.ts +13 -0
  429. package/templates/react-monorepo/files/apps/auth/.bin/secrets.js +57 -0
  430. package/templates/react-monorepo/files/apps/auth/.env.example +16 -0
  431. package/templates/react-monorepo/files/apps/auth/eslint.config.mjs +3 -0
  432. package/templates/react-monorepo/files/apps/auth/index.html +57 -0
  433. package/templates/react-monorepo/files/apps/auth/package.json +32 -0
  434. package/templates/react-monorepo/files/apps/auth/src/App.tsx +3 -0
  435. package/templates/react-monorepo/files/apps/auth/src/app/providers.tsx +42 -0
  436. package/templates/react-monorepo/files/apps/auth/src/app/router.tsx +19 -0
  437. package/templates/react-monorepo/files/apps/auth/src/app/store.ts +15 -0
  438. package/templates/react-monorepo/files/apps/auth/src/components/auth-card.tsx +23 -0
  439. package/templates/react-monorepo/files/apps/auth/src/components/checkbox-field.tsx +32 -0
  440. package/templates/react-monorepo/files/apps/auth/src/components/google-button.tsx +41 -0
  441. package/templates/react-monorepo/files/apps/auth/src/components/or-divider.tsx +9 -0
  442. package/templates/react-monorepo/files/apps/auth/src/components/password-field.tsx +70 -0
  443. package/templates/react-monorepo/files/apps/auth/src/components/submit-button.tsx +16 -0
  444. package/templates/react-monorepo/files/apps/auth/src/lib/auth-actions.ts +68 -0
  445. package/templates/react-monorepo/files/apps/auth/src/lib/env.ts +8 -0
  446. package/templates/react-monorepo/files/apps/auth/src/lib/password-strength.ts +20 -0
  447. package/templates/react-monorepo/files/apps/auth/src/lib/redirect.ts +6 -0
  448. package/templates/react-monorepo/files/apps/auth/src/lib/schemas.ts +37 -0
  449. package/templates/react-monorepo/files/apps/auth/src/main.tsx +14 -0
  450. package/templates/react-monorepo/files/apps/auth/src/routes/forgot-password.tsx +91 -0
  451. package/templates/react-monorepo/files/apps/auth/src/routes/login.tsx +82 -0
  452. package/templates/react-monorepo/files/apps/auth/src/routes/reset-password.tsx +83 -0
  453. package/templates/react-monorepo/files/apps/auth/src/routes/signup.tsx +117 -0
  454. package/templates/react-monorepo/files/apps/auth/src/routes/verified.tsx +30 -0
  455. package/templates/react-monorepo/files/apps/auth/src/routes/verify-email.tsx +96 -0
  456. package/templates/react-monorepo/files/apps/auth/src/vite-env.d.ts +14 -0
  457. package/templates/react-monorepo/files/apps/auth/tsconfig.json +10 -0
  458. package/templates/react-monorepo/files/apps/auth/vite.config.ts +13 -0
  459. package/templates/react-monorepo/files/apps/client/.bin/secrets.js +57 -0
  460. package/templates/react-monorepo/files/apps/client/.env.example +16 -0
  461. package/templates/react-monorepo/files/apps/client/eslint.config.mjs +3 -0
  462. package/templates/react-monorepo/files/apps/client/index.html +57 -0
  463. package/templates/react-monorepo/files/apps/client/package.json +35 -0
  464. package/templates/react-monorepo/files/apps/client/src/App.tsx +3 -0
  465. package/templates/react-monorepo/files/apps/client/src/app/nav.tsx +14 -0
  466. package/templates/react-monorepo/files/apps/client/src/app/providers.tsx +42 -0
  467. package/templates/react-monorepo/files/apps/client/src/app/router.tsx +50 -0
  468. package/templates/react-monorepo/files/apps/client/src/app/store.ts +15 -0
  469. package/templates/react-monorepo/files/apps/client/src/lib/env.ts +12 -0
  470. package/templates/react-monorepo/files/apps/client/src/main.tsx +19 -0
  471. package/templates/react-monorepo/files/apps/client/src/routes/home.tsx +17 -0
  472. package/templates/react-monorepo/files/apps/client/src/routes/settings.tsx +10 -0
  473. package/templates/react-monorepo/files/apps/client/src/vite-env.d.ts +15 -0
  474. package/templates/react-monorepo/files/apps/client/tsconfig.json +10 -0
  475. package/templates/react-monorepo/files/apps/client/vite.config.ts +120 -0
  476. package/templates/react-monorepo/files/apps/landing/.bin/secrets.js +57 -0
  477. package/templates/react-monorepo/files/apps/landing/.env.example +20 -0
  478. package/templates/react-monorepo/files/apps/landing/eslint.config.mjs +3 -0
  479. package/templates/react-monorepo/files/apps/landing/index.html +51 -0
  480. package/templates/react-monorepo/files/apps/landing/package.json +33 -0
  481. package/templates/react-monorepo/files/apps/landing/src/App.tsx +3 -0
  482. package/templates/react-monorepo/files/apps/landing/src/app/providers.tsx +42 -0
  483. package/templates/react-monorepo/files/apps/landing/src/app/router.tsx +21 -0
  484. package/templates/react-monorepo/files/apps/landing/src/app/store.ts +18 -0
  485. package/templates/react-monorepo/files/apps/landing/src/features/seo/routes.ts +65 -0
  486. package/templates/react-monorepo/files/apps/landing/src/lib/env.ts +10 -0
  487. package/templates/react-monorepo/files/apps/landing/src/main.tsx +14 -0
  488. package/templates/react-monorepo/files/apps/landing/src/routes/home.tsx +18 -0
  489. package/templates/react-monorepo/files/apps/landing/src/vite-env.d.ts +15 -0
  490. package/templates/react-monorepo/files/apps/landing/tsconfig.json +10 -0
  491. package/templates/react-monorepo/files/apps/landing/vite.config.ts +169 -0
  492. package/templates/react-monorepo/files/build.sh +61 -0
  493. package/templates/react-monorepo/files/docker/nginx.conf +136 -0
  494. package/templates/react-monorepo/files/package.json +37 -0
  495. package/templates/react-monorepo/files/packages/.gitkeep +0 -0
  496. package/templates/react-monorepo/files/packages/api/eslint.config.mjs +3 -0
  497. package/templates/react-monorepo/files/packages/api/package.json +30 -0
  498. package/templates/react-monorepo/files/packages/api/src/base-api.ts +24 -0
  499. package/templates/react-monorepo/files/packages/api/src/base-query.ts +29 -0
  500. package/templates/react-monorepo/files/packages/api/src/config.ts +21 -0
  501. package/templates/react-monorepo/files/packages/api/src/index.ts +15 -0
  502. package/templates/react-monorepo/files/packages/api/src/realtime-events.ts +34 -0
  503. package/templates/react-monorepo/files/packages/api/src/socket.ts +41 -0
  504. package/templates/react-monorepo/files/packages/api/src/store.ts +39 -0
  505. package/templates/react-monorepo/files/packages/api/src/upload-query.ts +115 -0
  506. package/templates/react-monorepo/files/packages/api/tsconfig.json +4 -0
  507. package/templates/react-monorepo/files/packages/auth/eslint.config.mjs +3 -0
  508. package/templates/react-monorepo/files/packages/auth/package.json +33 -0
  509. package/templates/react-monorepo/files/packages/auth/src/client.ts +27 -0
  510. package/templates/react-monorepo/files/packages/auth/src/config.ts +22 -0
  511. package/templates/react-monorepo/files/packages/auth/src/guards.tsx +61 -0
  512. package/templates/react-monorepo/files/packages/auth/src/hooks.ts +13 -0
  513. package/templates/react-monorepo/files/packages/auth/src/index.ts +20 -0
  514. package/templates/react-monorepo/files/packages/auth/src/logout.ts +23 -0
  515. package/templates/react-monorepo/files/packages/auth/src/provider.tsx +52 -0
  516. package/templates/react-monorepo/files/packages/auth/src/redirect.ts +37 -0
  517. package/templates/react-monorepo/files/packages/auth/src/slice.ts +54 -0
  518. package/templates/react-monorepo/files/packages/auth/tsconfig.json +4 -0
  519. package/templates/react-monorepo/files/packages/config/README.md +47 -0
  520. package/templates/react-monorepo/files/packages/config/eslint/base.mjs +83 -0
  521. package/templates/react-monorepo/files/packages/config/package.json +37 -0
  522. package/templates/react-monorepo/files/packages/config/tailwind/theme.css +48 -0
  523. package/templates/react-monorepo/files/packages/config/tsconfig/base.json +22 -0
  524. package/templates/react-monorepo/files/packages/config/tsconfig/node.json +9 -0
  525. package/templates/react-monorepo/files/packages/config/tsconfig/react.json +9 -0
  526. package/templates/react-monorepo/files/packages/config/vite/index.d.ts +18 -0
  527. package/templates/react-monorepo/files/packages/config/vite/index.mjs +129 -0
  528. package/templates/react-monorepo/files/packages/hooks/eslint.config.mjs +3 -0
  529. package/templates/react-monorepo/files/packages/hooks/package.json +16 -0
  530. package/templates/react-monorepo/files/packages/hooks/src/index.ts +3 -0
  531. package/templates/react-monorepo/files/packages/hooks/tsconfig.json +4 -0
  532. package/templates/react-monorepo/files/packages/layouts/eslint.config.mjs +3 -0
  533. package/templates/react-monorepo/files/packages/layouts/package.json +30 -0
  534. package/templates/react-monorepo/files/packages/layouts/src/app-shell/app-shell.tsx +225 -0
  535. package/templates/react-monorepo/files/packages/layouts/src/app-shell/user-menu.tsx +74 -0
  536. package/templates/react-monorepo/files/packages/layouts/src/auth-layout.tsx +137 -0
  537. package/templates/react-monorepo/files/packages/layouts/src/error-boundary.tsx +38 -0
  538. package/templates/react-monorepo/files/packages/layouts/src/index.ts +23 -0
  539. package/templates/react-monorepo/files/packages/layouts/src/marketing/marketing-layout.tsx +29 -0
  540. package/templates/react-monorepo/files/packages/layouts/src/marketing/section-heading.tsx +38 -0
  541. package/templates/react-monorepo/files/packages/layouts/src/marketing/section.tsx +30 -0
  542. package/templates/react-monorepo/files/packages/layouts/src/states/app-shell-skeleton.tsx +47 -0
  543. package/templates/react-monorepo/files/packages/layouts/src/states/empty.tsx +22 -0
  544. package/templates/react-monorepo/files/packages/layouts/src/states/forbidden.tsx +14 -0
  545. package/templates/react-monorepo/files/packages/layouts/src/states/loading.tsx +11 -0
  546. package/templates/react-monorepo/files/packages/layouts/src/states/not-found.tsx +21 -0
  547. package/templates/react-monorepo/files/packages/layouts/src/states/page-skeleton.tsx +39 -0
  548. package/templates/react-monorepo/files/packages/layouts/src/states/route-fallback.tsx +26 -0
  549. package/templates/react-monorepo/files/packages/layouts/src/states/state-screen.tsx +22 -0
  550. package/templates/react-monorepo/files/packages/layouts/tsconfig.json +4 -0
  551. package/templates/react-monorepo/files/packages/types/.bin/openapi.js +32 -0
  552. package/templates/react-monorepo/files/packages/types/.bin/secrets.js +57 -0
  553. package/templates/react-monorepo/files/packages/types/README.md +28 -0
  554. package/templates/react-monorepo/files/packages/types/eslint.config.mjs +3 -0
  555. package/templates/react-monorepo/files/packages/types/openapi-ts.config.ts +22 -0
  556. package/templates/react-monorepo/files/packages/types/package.json +29 -0
  557. package/templates/react-monorepo/files/packages/types/src/index.ts +4 -0
  558. package/templates/react-monorepo/files/packages/types/src/types/api/index.ts +9 -0
  559. package/templates/react-monorepo/files/packages/types/src/types/api/zod.gen.ts +5 -0
  560. package/templates/react-monorepo/files/packages/types/tsconfig.json +7 -0
  561. package/templates/react-monorepo/files/packages/ui/components.json +21 -0
  562. package/templates/react-monorepo/files/packages/ui/eslint.config.mjs +13 -0
  563. package/templates/react-monorepo/files/packages/ui/package.json +44 -0
  564. package/templates/react-monorepo/files/packages/ui/src/components/brand/logo.tsx +51 -0
  565. package/templates/react-monorepo/files/packages/ui/src/components/brand/social-icons.tsx +37 -0
  566. package/templates/react-monorepo/files/packages/ui/src/components/form/form.tsx +120 -0
  567. package/templates/react-monorepo/files/packages/ui/src/components/ui/alert.tsx +25 -0
  568. package/templates/react-monorepo/files/packages/ui/src/components/ui/button.tsx +43 -0
  569. package/templates/react-monorepo/files/packages/ui/src/components/ui/calendar.tsx +78 -0
  570. package/templates/react-monorepo/files/packages/ui/src/components/ui/card.tsx +44 -0
  571. package/templates/react-monorepo/files/packages/ui/src/components/ui/checkbox.tsx +16 -0
  572. package/templates/react-monorepo/files/packages/ui/src/components/ui/date-picker.tsx +76 -0
  573. package/templates/react-monorepo/files/packages/ui/src/components/ui/input.tsx +22 -0
  574. package/templates/react-monorepo/files/packages/ui/src/components/ui/label.tsx +19 -0
  575. package/templates/react-monorepo/files/packages/ui/src/components/ui/modal.tsx +94 -0
  576. package/templates/react-monorepo/files/packages/ui/src/components/ui/popover.tsx +31 -0
  577. package/templates/react-monorepo/files/packages/ui/src/components/ui/progress.tsx +33 -0
  578. package/templates/react-monorepo/files/packages/ui/src/components/ui/search-input.tsx +69 -0
  579. package/templates/react-monorepo/files/packages/ui/src/components/ui/select.tsx +80 -0
  580. package/templates/react-monorepo/files/packages/ui/src/components/ui/skeleton.tsx +12 -0
  581. package/templates/react-monorepo/files/packages/ui/src/components/ui/sonner.tsx +23 -0
  582. package/templates/react-monorepo/files/packages/ui/src/components/ui/spinner.tsx +8 -0
  583. package/templates/react-monorepo/files/packages/ui/src/components/ui/textarea.tsx +22 -0
  584. package/templates/react-monorepo/files/packages/ui/src/index.ts +45 -0
  585. package/templates/react-monorepo/files/packages/ui/src/lib/utils.ts +7 -0
  586. package/templates/react-monorepo/files/packages/ui/src/lib/zod-setup.ts +28 -0
  587. package/templates/react-monorepo/files/packages/ui/src/meta/brand.ts +18 -0
  588. package/templates/react-monorepo/files/packages/ui/src/meta/meta-context.ts +30 -0
  589. package/templates/react-monorepo/files/packages/ui/src/meta/meta-provider.tsx +24 -0
  590. package/templates/react-monorepo/files/packages/ui/src/meta/page-meta.tsx +69 -0
  591. package/templates/react-monorepo/files/packages/ui/src/meta/use-meta-config.ts +11 -0
  592. package/templates/react-monorepo/files/packages/ui/src/styles/globals.css +285 -0
  593. package/templates/react-monorepo/files/packages/ui/src/theme/init-script.ts +23 -0
  594. package/templates/react-monorepo/files/packages/ui/src/theme/theme-colors.ts +14 -0
  595. package/templates/react-monorepo/files/packages/ui/src/theme/theme-context.ts +60 -0
  596. package/templates/react-monorepo/files/packages/ui/src/theme/theme-provider.tsx +101 -0
  597. package/templates/react-monorepo/files/packages/ui/src/theme/theme-toggle.tsx +40 -0
  598. package/templates/react-monorepo/files/packages/ui/src/theme/use-theme.ts +11 -0
  599. package/templates/react-monorepo/files/packages/ui/tsconfig.json +9 -0
  600. package/templates/react-monorepo/files/turbo.json +24 -0
  601. package/templates/react-monorepo/template.json +230 -0
@@ -0,0 +1,51 @@
1
+ import { cn } from '../../lib/utils';
2
+
3
+ /** Acme Corp "in" monogram mark (fills with currentColor). */
4
+ export function LogoMark({ width = 18, className }: { width?: number; className?: string }) {
5
+ return (
6
+ <svg width={width} height={(width * 319) / 368} viewBox="0 0 368 319" aria-hidden="true" className={className}>
7
+ <g transform="translate(0,319) scale(0.1,-0.1)" fill="currentColor" stroke="none">
8
+ <path d="M20 2865 l0 -305 410 0 410 0 0 305 0 305 -410 0 -410 0 0 -305z" />
9
+ <path d="M20 1021 c0 -649 3 -1002 10 -1006 5 -3 376 -3 824 1 l814 7 5 166 c5 187 13 221 92 370 127 238 337 445 560 551 147 70 337 115 460 108 l60 -3 2 -590 c2 -324 3 -593 3 -597 0 -5 182 -8 405 -8 l405 0 0 1000 0 1000 -475 0 c-602 0 -716 -12 -980 -101 -439 -148 -809 -447 -1070 -864 -145 -232 -235 -474 -282 -760 -14 -87 -19 260 -14 1018 l4 707 -411 0 -412 0 0 -999z" />
10
+ </g>
11
+ </svg>
12
+ );
13
+ }
14
+
15
+ const DISC_SIZE = {
16
+ md: 'size-9 rounded-xl',
17
+ sm: 'size-8 rounded-[10px]'
18
+ } as const;
19
+
20
+ /**
21
+ * The brand mark on its green disc (no wordmark) — the reusable app icon.
22
+ * Ink-on-green is the approved primary treatment from the logo guidelines
23
+ * (the earlier yellow-on-ink disc is the superseded legacy mark). The mark is
24
+ * always ink #0e0f0c on Nerd Green, independent of light/dark theme.
25
+ */
26
+ export function LogoDisc({ size = 'md', className }: { size?: 'sm' | 'md'; className?: string }) {
27
+ return (
28
+ <span
29
+ className={cn('flex flex-none items-center justify-center bg-primary text-[#0e0f0c]', DISC_SIZE[size], className)}
30
+ >
31
+ <LogoMark width={size === 'md' ? 18 : 16} />
32
+ </span>
33
+ );
34
+ }
35
+
36
+ /** Full brand lockup: the mark on its disc plus the "Acme Corp" wordmark. */
37
+ export function Logo({ size = 'md', className }: { size?: 'sm' | 'md'; className?: string }) {
38
+ return (
39
+ <span className={cn('inline-flex items-center gap-2.5', className)}>
40
+ <LogoDisc size={size} />
41
+ <span
42
+ className={cn(
43
+ 'font-display font-extrabold leading-none tracking-tight text-foreground',
44
+ size === 'md' ? 'text-[15px]' : 'text-sm'
45
+ )}
46
+ >
47
+ Acme Corp
48
+ </span>
49
+ </span>
50
+ );
51
+ }
@@ -0,0 +1,37 @@
1
+ import type { ReactNode } from 'react';
2
+ import { Link2 } from 'lucide-react';
3
+
4
+ /**
5
+ * Brand marks for the platforms in the `socials` table.
6
+ *
7
+ * The backend stores an Iconify key (`bxl:twitter-x`), but pulling in Iconify
8
+ * would add a CDN fetch at runtime and break offline in the PWA, and lucide
9
+ * dropped brand icons entirely. So these are drawn locally and keyed on the
10
+ * platform `name`, which is unique in the table and stable.
11
+ */
12
+ const MARKS: Record<string, string> = {
13
+ linkedin:
14
+ 'M20.45 20.45h-3.56v-5.57c0-1.33-.03-3.04-1.85-3.04-1.85 0-2.14 1.45-2.14 2.94v5.67H9.35V9h3.41v1.56h.05c.48-.9 1.63-1.85 3.36-1.85 3.6 0 4.27 2.37 4.27 5.45v6.29zM5.34 7.43a2.06 2.06 0 1 1 0-4.13 2.06 2.06 0 0 1 0 4.13zM7.12 20.45H3.55V9h3.57v11.45zM22.22 0H1.77C.79 0 0 .77 0 1.72v20.56C0 23.23.79 24 1.77 24h20.45c.98 0 1.78-.77 1.78-1.72V1.72C24 .77 23.2 0 22.22 0z',
15
+ x: 'M18.24 2.25h3.31l-7.23 8.26 8.5 11.24h-6.66l-5.21-6.82-5.97 6.82H1.67l7.73-8.84L1.25 2.25h6.83l4.71 6.23 5.45-6.23zm-1.16 17.52h1.83L7.08 4.13H5.11l11.97 15.64z',
16
+ facebook:
17
+ 'M24 12.07C24 5.4 18.63 0 12 0S0 5.4 0 12.07C0 18.1 4.39 23.1 10.13 24v-8.44H7.08v-3.49h3.05V9.41c0-3.02 1.79-4.69 4.53-4.69 1.31 0 2.68.24 2.68.24v2.97h-1.51c-1.49 0-1.96.93-1.96 1.89v2.25h3.33l-.53 3.49h-2.8V24C19.61 23.1 24 18.1 24 12.07z',
18
+ instagram:
19
+ 'M12 2.16c3.2 0 3.58.01 4.85.07 1.17.05 1.8.25 2.23.41.56.22.96.48 1.38.9.42.42.68.82.9 1.38.16.42.36 1.06.41 2.23.06 1.27.07 1.65.07 4.85s-.01 3.58-.07 4.85c-.05 1.17-.25 1.8-.41 2.23-.22.56-.48.96-.9 1.38-.42.42-.82.68-1.38.9-.42.16-1.06.36-2.23.41-1.27.06-1.65.07-4.85.07s-3.58-.01-4.85-.07c-1.17-.05-1.8-.25-2.23-.41-.56-.22-.96-.48-1.38-.9-.42-.42-.68-.82-.9-1.38-.16-.42-.36-1.06-.41-2.23-.06-1.27-.07-1.65-.07-4.85s.01-3.58.07-4.85c.05-1.17.25-1.8.41-2.23.22-.56.48-.96.9-1.38.42-.42.82-.68 1.38-.9.42-.16 1.06-.36 2.23-.41 1.27-.06 1.65-.07 4.85-.07M12 0C8.74 0 8.33.01 7.05.07 5.78.13 4.9.33 4.14.63c-.79.3-1.46.72-2.13 1.38C1.35 2.68.93 3.35.63 4.14.33 4.9.13 5.78.07 7.05.01 8.33 0 8.74 0 12s.01 3.67.07 4.95c.06 1.27.26 2.15.56 2.91.3.79.72 1.46 1.38 2.13.67.66 1.34 1.08 2.13 1.38.76.3 1.64.5 2.91.56C8.33 23.99 8.74 24 12 24s3.67-.01 4.95-.07c1.27-.06 2.15-.26 2.91-.56.79-.3 1.46-.72 2.13-1.38.66-.67 1.08-1.34 1.38-2.13.3-.76.5-1.64.56-2.91.06-1.28.07-1.69.07-4.95s-.01-3.67-.07-4.95c-.06-1.27-.26-2.15-.56-2.91-.3-.79-.72-1.46-1.38-2.13C21.32 1.35 20.65.93 19.86.63c-.76-.3-1.64-.5-2.91-.56C15.67.01 15.26 0 12 0zm0 5.84a6.16 6.16 0 1 0 0 12.32 6.16 6.16 0 0 0 0-12.32zM12 16a4 4 0 1 1 0-8 4 4 0 0 1 0 8zm7.85-10.4a1.44 1.44 0 1 1-2.88 0 1.44 1.44 0 0 1 2.88 0z',
20
+ youtube:
21
+ 'M23.5 6.19a3.02 3.02 0 0 0-2.12-2.14C19.5 3.55 12 3.55 12 3.55s-7.5 0-9.38.5A3.02 3.02 0 0 0 .5 6.19C0 8.08 0 12 0 12s0 3.92.5 5.81a3.02 3.02 0 0 0 2.12 2.14c1.88.5 9.38.5 9.38.5s7.5 0 9.38-.5a3.02 3.02 0 0 0 2.12-2.14C24 15.92 24 12 24 12s0-3.92-.5-5.81zM9.55 15.57V8.43L15.82 12l-6.27 3.57z',
22
+ tiktok:
23
+ 'M12.53.02C13.84 0 15.14.01 16.44 0c.08 1.53.63 3.09 1.75 4.17 1.12 1.11 2.7 1.62 4.24 1.79v4.03c-1.44-.05-2.89-.35-4.2-.97-.57-.26-1.1-.59-1.62-.93-.01 2.92.01 5.84-.02 8.75-.08 1.4-.54 2.79-1.35 3.94-1.31 1.92-3.58 3.17-5.91 3.21-1.43.08-2.86-.31-4.08-1.03-2.02-1.19-3.44-3.37-3.65-5.71-.02-.5-.03-1-.01-1.49.18-1.9 1.12-3.72 2.58-4.96 1.66-1.44 3.98-2.13 6.15-1.72.02 1.48-.04 2.96-.04 4.44-.99-.32-2.15-.23-3.02.37-.63.41-1.11 1.04-1.36 1.75-.21.51-.15 1.07-.14 1.61.24 1.64 1.82 3.02 3.5 2.87 1.12-.01 2.19-.66 2.77-1.61.19-.33.4-.67.41-1.06.1-1.79.06-3.57.07-5.36.01-4.03-.01-8.05.02-12.07z',
24
+ whatsapp:
25
+ 'M17.47 14.38c-.3-.15-1.76-.87-2.03-.97-.27-.1-.47-.15-.67.15-.2.3-.77.97-.94 1.17-.17.2-.35.22-.65.07-.3-.15-1.26-.46-2.4-1.48-.89-.79-1.49-1.77-1.66-2.07-.17-.3-.02-.46.13-.61.13-.13.3-.35.45-.52.15-.17.2-.3.3-.5.1-.2.05-.37-.02-.52-.07-.15-.67-1.61-.92-2.21-.24-.58-.49-.5-.67-.51h-.57c-.2 0-.52.07-.79.37-.27.3-1.04 1.02-1.04 2.48s1.06 2.88 1.21 3.08c.15.2 2.1 3.2 5.08 4.49.71.31 1.26.49 1.69.63.71.23 1.36.19 1.87.12.57-.09 1.76-.72 2.01-1.41.25-.7.25-1.29.17-1.41-.07-.12-.27-.2-.57-.35zM12.05 21.8h-.01a9.87 9.87 0 0 1-5.03-1.38l-.36-.21-3.74.98 1-3.65-.24-.37a9.86 9.86 0 0 1-1.51-5.26c0-5.45 4.44-9.88 9.9-9.88 2.64 0 5.13 1.03 7 2.9a9.82 9.82 0 0 1 2.9 6.99c0 5.45-4.44 9.88-9.91 9.88zM20.52 3.45A11.81 11.81 0 0 0 12.05 0C5.5 0 .17 5.33.17 11.88c0 2.09.55 4.14 1.59 5.94L.07 24l6.33-1.66a11.86 11.86 0 0 0 5.65 1.44h.01c6.55 0 11.88-5.33 11.88-11.88 0-3.17-1.24-6.15-3.48-8.39z'
26
+ };
27
+
28
+ /** Look up a mark by platform name; unknown platforms get a generic link glyph. */
29
+ export function socialMark(name: string): ReactNode {
30
+ const path = MARKS[name.trim().toLowerCase()];
31
+ if (!path) return <Link2 className="size-4" aria-hidden="true" />;
32
+ return (
33
+ <svg viewBox="0 0 24 24" fill="currentColor" aria-hidden="true" className="size-4">
34
+ <path d={path} />
35
+ </svg>
36
+ );
37
+ }
@@ -0,0 +1,120 @@
1
+ import * as React from 'react';
2
+ import { useField } from 'formik';
3
+ import { Input } from '../ui/input';
4
+ import { Label } from '../ui/label';
5
+ import { Textarea } from '../ui/textarea';
6
+ import { DatePicker } from '../ui/date-picker';
7
+ import { cn } from '../../lib/utils';
8
+
9
+ /** Inline field error message (used by the fields below, also usable standalone). */
10
+ function FormError({ className, children }: { className?: string; children: React.ReactNode }) {
11
+ return (
12
+ <p role="alert" className={cn('text-sm text-negative', className)}>
13
+ {children}
14
+ </p>
15
+ );
16
+ }
17
+
18
+ /** Label + control wrapper shared by the form fields. */
19
+ function Field({
20
+ id,
21
+ label,
22
+ hint,
23
+ error,
24
+ className,
25
+ children
26
+ }: {
27
+ id: string;
28
+ label?: string;
29
+ hint?: string;
30
+ error?: string;
31
+ className?: string;
32
+ children: React.ReactNode;
33
+ }) {
34
+ return (
35
+ <div className={cn('flex flex-col gap-1.5', className)}>
36
+ {label ? <Label htmlFor={id}>{label}</Label> : null}
37
+ {children}
38
+ {error ? <FormError>{error}</FormError> : hint ? <p className="text-sm text-mute">{hint}</p> : null}
39
+ </div>
40
+ );
41
+ }
42
+
43
+ type FormFieldProps = Omit<React.ComponentProps<typeof Input>, 'name'> & {
44
+ name: string;
45
+ label?: string;
46
+ hint?: string;
47
+ };
48
+
49
+ /**
50
+ * Formik-bound labelled input with error display. Must render inside a <Formik>.
51
+ * The form primitives wrap shadcn parts so a future RHF swap stays contained here.
52
+ */
53
+ function FormField({ name, label, hint, className, id, ...props }: FormFieldProps) {
54
+ const [field, meta] = useField(name);
55
+ const fieldId = id ?? name;
56
+ const showError = meta.touched && Boolean(meta.error);
57
+
58
+ return (
59
+ <Field id={fieldId} label={label} hint={hint} error={showError ? meta.error : undefined} className={className}>
60
+ <Input id={fieldId} aria-invalid={showError || undefined} {...field} {...props} />
61
+ </Field>
62
+ );
63
+ }
64
+
65
+ type FormTextareaProps = Omit<React.ComponentProps<typeof Textarea>, 'name'> & {
66
+ name: string;
67
+ label?: string;
68
+ hint?: string;
69
+ };
70
+
71
+ /** Formik-bound multiline text field. */
72
+ function FormTextarea({ name, label, hint, className, id, ...props }: FormTextareaProps) {
73
+ const [field, meta] = useField(name);
74
+ const fieldId = id ?? name;
75
+ const showError = meta.touched && Boolean(meta.error);
76
+
77
+ return (
78
+ <Field id={fieldId} label={label} hint={hint} error={showError ? meta.error : undefined} className={className}>
79
+ <Textarea id={fieldId} aria-invalid={showError || undefined} {...field} {...props} />
80
+ </Field>
81
+ );
82
+ }
83
+
84
+ type FormDateFieldProps = {
85
+ name: string;
86
+ label?: string;
87
+ hint?: string;
88
+ className?: string;
89
+ id?: string;
90
+ placeholder?: string;
91
+ };
92
+
93
+ /**
94
+ * Formik-bound date field: an input-styled trigger opening a Popover calendar.
95
+ * Stores `yyyy-MM-dd`.
96
+ *
97
+ * The composition lives in `DatePicker` so the Formik-less apps (`admin`,
98
+ * `landing`) get the same control without a second copy to keep in sync.
99
+ */
100
+ function FormDateField({ name, label, hint, className, id, placeholder = 'Pick a date' }: FormDateFieldProps) {
101
+ const [field, meta, helpers] = useField<string>(name);
102
+ const fieldId = id ?? name;
103
+ const showError = meta.touched && Boolean(meta.error);
104
+
105
+ return (
106
+ <Field id={fieldId} label={label} hint={hint} error={showError ? meta.error : undefined} className={className}>
107
+ <DatePicker
108
+ id={fieldId}
109
+ value={field.value}
110
+ placeholder={placeholder}
111
+ invalid={showError}
112
+ onChange={(value) => void helpers.setValue(value)}
113
+ onCommit={() => void helpers.setTouched(true)}
114
+ />
115
+ </Field>
116
+ );
117
+ }
118
+
119
+ export { FormField, FormTextarea, FormDateField, FormError };
120
+ export type { FormFieldProps, FormTextareaProps, FormDateFieldProps };
@@ -0,0 +1,25 @@
1
+ import * as React from 'react';
2
+ import { cva, type VariantProps } from 'class-variance-authority';
3
+ import { cn } from '../../lib/utils';
4
+
5
+ const alertVariants = cva('flex items-start gap-2.5 rounded-xl border px-3.5 py-2.5 text-sm font-medium', {
6
+ variants: {
7
+ variant: {
8
+ default: 'border-border bg-secondary text-foreground',
9
+ destructive: 'border-negative/30 bg-negative/10 text-negative',
10
+ success: 'border-primary/40 bg-accent text-accent-foreground'
11
+ }
12
+ },
13
+ defaultVariants: {
14
+ variant: 'default'
15
+ }
16
+ });
17
+
18
+ type AlertProps = React.ComponentProps<'div'> & VariantProps<typeof alertVariants>;
19
+
20
+ function Alert({ className, variant, role = 'alert', ...props }: AlertProps) {
21
+ return <div role={role} className={cn(alertVariants({ variant }), className)} {...props} />;
22
+ }
23
+
24
+ export { Alert, alertVariants };
25
+ export type { AlertProps };
@@ -0,0 +1,43 @@
1
+ import * as React from 'react';
2
+ import { Slot } from '@radix-ui/react-slot';
3
+ import { cva, type VariantProps } from 'class-variance-authority';
4
+ import { cn } from '../../lib/utils';
5
+
6
+ const buttonVariants = cva(
7
+ 'inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-full font-semibold transition-colors duration-150 outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-background active:translate-y-px disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0',
8
+ {
9
+ variants: {
10
+ variant: {
11
+ primary: 'bg-primary text-primary-foreground hover:bg-primary-active',
12
+ secondary: 'bg-secondary text-secondary-foreground hover:bg-muted',
13
+ outline: 'border border-foreground bg-transparent text-foreground hover:bg-secondary',
14
+ ghost: 'bg-transparent text-foreground hover:bg-secondary',
15
+ destructive: 'bg-destructive text-destructive-foreground hover:opacity-90',
16
+ link: 'text-ink-deep underline-offset-4 hover:underline'
17
+ },
18
+ size: {
19
+ sm: 'h-9 px-4 text-sm',
20
+ md: 'h-11 px-5 text-base',
21
+ lg: 'h-12 px-6 text-base',
22
+ icon: 'size-11 rounded-full'
23
+ }
24
+ },
25
+ defaultVariants: {
26
+ variant: 'primary',
27
+ size: 'md'
28
+ }
29
+ }
30
+ );
31
+
32
+ type ButtonProps = React.ComponentProps<'button'> &
33
+ VariantProps<typeof buttonVariants> & {
34
+ asChild?: boolean;
35
+ };
36
+
37
+ function Button({ className, variant, size, asChild = false, ...props }: ButtonProps) {
38
+ const Comp = asChild ? Slot : 'button';
39
+ return <Comp className={cn(buttonVariants({ variant, size }), className)} {...props} />;
40
+ }
41
+
42
+ export { Button, buttonVariants };
43
+ export type { ButtonProps };
@@ -0,0 +1,78 @@
1
+ import * as React from 'react';
2
+ import { ChevronDown, ChevronLeft, ChevronRight, ChevronUp } from 'lucide-react';
3
+ import { DayPicker } from 'react-day-picker';
4
+ import { cn } from '../../lib/utils';
5
+
6
+ /**
7
+ * Calendar (react-day-picker v9) styled to the Wise design — self-contained, no external CSS.
8
+ * Defaults to month + year dropdown navigation (spanning ~60 years back) so users can jump to
9
+ * old dates without clicking through months. Override captionLayout / startMonth / endMonth as needed.
10
+ */
11
+ function Calendar({
12
+ className,
13
+ classNames,
14
+ showOutsideDays = true,
15
+ captionLayout = 'dropdown',
16
+ startMonth,
17
+ endMonth,
18
+ ...props
19
+ }: React.ComponentProps<typeof DayPicker>) {
20
+ const currentYear = new Date().getFullYear();
21
+ const start = startMonth ?? new Date(currentYear - 60, 0);
22
+ const end = endMonth ?? new Date(currentYear + 1, 11);
23
+
24
+ return (
25
+ <DayPicker
26
+ showOutsideDays={showOutsideDays}
27
+ captionLayout={captionLayout}
28
+ startMonth={start}
29
+ endMonth={end}
30
+ className={cn('p-1', className)}
31
+ classNames={{
32
+ root: 'w-fit',
33
+ months: 'relative flex flex-col gap-4',
34
+ month: 'flex flex-col gap-3',
35
+ month_caption: 'flex h-8 items-center justify-center',
36
+ caption_label:
37
+ 'flex items-center gap-1 px-1 text-sm font-semibold text-foreground [&>svg]:size-3.5 [&>svg]:text-mute',
38
+ dropdowns: 'flex items-center justify-center gap-1.5',
39
+ dropdown_root:
40
+ 'relative inline-flex items-center rounded-lg border border-input focus-within:border-foreground focus-within:ring-2 focus-within:ring-ring hover:bg-secondary',
41
+ dropdown: 'absolute inset-0 cursor-pointer opacity-0',
42
+ nav: 'absolute inset-x-0 top-0 flex items-center justify-between',
43
+ button_previous:
44
+ 'inline-flex size-8 items-center justify-center rounded-full text-mute outline-none hover:bg-secondary hover:text-foreground disabled:opacity-40',
45
+ button_next:
46
+ 'inline-flex size-8 items-center justify-center rounded-full text-mute outline-none hover:bg-secondary hover:text-foreground disabled:opacity-40',
47
+ month_grid: 'w-full border-collapse',
48
+ weekdays: 'flex',
49
+ weekday: 'w-9 text-[11px] font-semibold text-mute',
50
+ week: 'mt-1 flex w-full',
51
+ day: 'size-9 p-0 text-center',
52
+ day_button:
53
+ 'flex size-9 items-center justify-center rounded-full text-sm font-normal text-foreground outline-none hover:bg-secondary focus-visible:ring-2 focus-visible:ring-ring aria-selected:bg-foreground aria-selected:text-background aria-selected:hover:bg-foreground',
54
+ today: 'font-bold text-ink-deep',
55
+ outside: 'text-mute/50',
56
+ disabled: 'text-mute/40 opacity-50',
57
+ hidden: 'invisible',
58
+ ...classNames
59
+ }}
60
+ components={{
61
+ Chevron: ({ orientation, className: chevronClassName }) => {
62
+ const Icon =
63
+ orientation === 'left'
64
+ ? ChevronLeft
65
+ : orientation === 'right'
66
+ ? ChevronRight
67
+ : orientation === 'up'
68
+ ? ChevronUp
69
+ : ChevronDown;
70
+ return <Icon className={cn('size-4', chevronClassName)} />;
71
+ }
72
+ }}
73
+ {...props}
74
+ />
75
+ );
76
+ }
77
+
78
+ export { Calendar };
@@ -0,0 +1,44 @@
1
+ import * as React from 'react';
2
+ import { cn } from '../../lib/utils';
3
+
4
+ /**
5
+ * Wise card anatomy: white surface, 24px radius, 24px padding, NO shadow.
6
+ * Elevation comes from surface contrast (white card on sage), not shadows.
7
+ */
8
+ function Card({ className, ...props }: React.ComponentProps<'div'>) {
9
+ return (
10
+ <div
11
+ data-slot="card"
12
+ className={cn('flex flex-col gap-6 rounded-3xl bg-card p-6 text-card-foreground', className)}
13
+ {...props}
14
+ />
15
+ );
16
+ }
17
+
18
+ function CardHeader({ className, ...props }: React.ComponentProps<'div'>) {
19
+ return <div data-slot="card-header" className={cn('flex flex-col gap-1.5', className)} {...props} />;
20
+ }
21
+
22
+ function CardTitle({ className, ...props }: React.ComponentProps<'div'>) {
23
+ return (
24
+ <div
25
+ data-slot="card-title"
26
+ className={cn('font-display text-2xl font-semibold tracking-tight', className)}
27
+ {...props}
28
+ />
29
+ );
30
+ }
31
+
32
+ function CardDescription({ className, ...props }: React.ComponentProps<'div'>) {
33
+ return <div data-slot="card-description" className={cn('text-sm text-mute', className)} {...props} />;
34
+ }
35
+
36
+ function CardContent({ className, ...props }: React.ComponentProps<'div'>) {
37
+ return <div data-slot="card-content" className={cn('flex flex-col gap-4', className)} {...props} />;
38
+ }
39
+
40
+ function CardFooter({ className, ...props }: React.ComponentProps<'div'>) {
41
+ return <div data-slot="card-footer" className={cn('flex items-center gap-3', className)} {...props} />;
42
+ }
43
+
44
+ export { Card, CardHeader, CardTitle, CardDescription, CardContent, CardFooter };
@@ -0,0 +1,16 @@
1
+ import * as React from 'react';
2
+ import { cn } from '../../lib/utils';
3
+
4
+ /** Native checkbox tinted with the brand accent (matches the Wise auth design). */
5
+ function Checkbox({ className, ...props }: React.ComponentProps<'input'>) {
6
+ return (
7
+ <input
8
+ type="checkbox"
9
+ data-slot="checkbox"
10
+ className={cn('size-4 shrink-0 cursor-pointer rounded accent-primary', className)}
11
+ {...props}
12
+ />
13
+ );
14
+ }
15
+
16
+ export { Checkbox };
@@ -0,0 +1,76 @@
1
+ import * as React from 'react';
2
+ import { CalendarIcon } from 'lucide-react';
3
+ import { format, isValid, parseISO } from 'date-fns';
4
+ import { Popover, PopoverContent, PopoverTrigger } from './popover';
5
+ import { cn } from '../../lib/utils';
6
+
7
+ // Calendar (react-day-picker) is heavy — load it only when a date field opens.
8
+ const LazyCalendar = React.lazy(() => import('./calendar').then((module) => ({ default: module.Calendar })));
9
+
10
+ type DatePickerProps = {
11
+ /** `yyyy-MM-dd`, or '' for empty — the same wire shape as a native `<input type="date">`. */
12
+ value: string;
13
+ onChange: (value: string) => void;
14
+ id?: string;
15
+ placeholder?: string;
16
+ invalid?: boolean;
17
+ /** Called after a date is picked — `FormDateField` uses it to mark the field touched. */
18
+ onCommit?: () => void;
19
+ };
20
+
21
+ /**
22
+ * Controlled date field: an input-styled trigger opening a Popover calendar.
23
+ *
24
+ * Unbound to any form library, so it works in `admin` and `landing` (neither
25
+ * has Formik). `FormDateField` wraps this for the Formik apps — keep the
26
+ * composition here only, so the two can't drift.
27
+ */
28
+ function DatePicker({ value, onChange, id, placeholder = 'Pick a date', invalid, onCommit }: DatePickerProps) {
29
+ const [open, setOpen] = React.useState(false);
30
+
31
+ const parsed = value ? parseISO(value) : undefined;
32
+ const selected = parsed && isValid(parsed) ? parsed : undefined;
33
+
34
+ return (
35
+ <Popover open={open} onOpenChange={setOpen}>
36
+ <PopoverTrigger asChild>
37
+ <button
38
+ type="button"
39
+ id={id}
40
+ aria-invalid={invalid || undefined}
41
+ className={cn(
42
+ 'flex h-11 w-full items-center justify-between gap-2 rounded-xl border border-input bg-card px-3.5 text-sm outline-none transition-colors',
43
+ 'focus-visible:border-foreground focus-visible:ring-2 focus-visible:ring-ring',
44
+ 'aria-invalid:border-negative'
45
+ )}
46
+ >
47
+ {selected ? (
48
+ <span className="text-foreground">{format(selected, 'd MMM yyyy')}</span>
49
+ ) : (
50
+ <span className="text-mute">{placeholder}</span>
51
+ )}
52
+ <CalendarIcon className="size-4 shrink-0 text-mute" />
53
+ </button>
54
+ </PopoverTrigger>
55
+ <PopoverContent align="start">
56
+ <React.Suspense
57
+ fallback={<div className="flex h-72 w-64 items-center justify-center text-sm text-mute">Loading…</div>}
58
+ >
59
+ <LazyCalendar
60
+ mode="single"
61
+ autoFocus
62
+ selected={selected}
63
+ onSelect={(date) => {
64
+ onChange(date ? format(date, 'yyyy-MM-dd') : '');
65
+ onCommit?.();
66
+ setOpen(false);
67
+ }}
68
+ />
69
+ </React.Suspense>
70
+ </PopoverContent>
71
+ </Popover>
72
+ );
73
+ }
74
+
75
+ export { DatePicker };
76
+ export type { DatePickerProps };
@@ -0,0 +1,22 @@
1
+ import * as React from 'react';
2
+ import { cn } from '../../lib/utils';
3
+
4
+ function Input({ className, type, ...props }: React.ComponentProps<'input'>) {
5
+ return (
6
+ <input
7
+ type={type}
8
+ data-slot="input"
9
+ className={cn(
10
+ 'flex h-11 w-full min-w-0 rounded-xl border border-input bg-card px-3.5 py-2 text-base text-foreground transition-colors',
11
+ 'placeholder:text-mute selection:bg-primary selection:text-primary-foreground',
12
+ 'outline-none focus-visible:border-foreground focus-visible:ring-2 focus-visible:ring-ring',
13
+ 'aria-invalid:border-negative aria-invalid:focus-visible:ring-negative/30',
14
+ 'disabled:cursor-not-allowed disabled:opacity-50',
15
+ className
16
+ )}
17
+ {...props}
18
+ />
19
+ );
20
+ }
21
+
22
+ export { Input };
@@ -0,0 +1,19 @@
1
+ import * as React from 'react';
2
+ import * as LabelPrimitive from '@radix-ui/react-label';
3
+ import { cn } from '../../lib/utils';
4
+
5
+ function Label({ className, ...props }: React.ComponentProps<typeof LabelPrimitive.Root>) {
6
+ return (
7
+ <LabelPrimitive.Root
8
+ data-slot="label"
9
+ className={cn(
10
+ 'text-sm font-semibold leading-none text-foreground',
11
+ 'peer-disabled:cursor-not-allowed peer-disabled:opacity-70',
12
+ className
13
+ )}
14
+ {...props}
15
+ />
16
+ );
17
+ }
18
+
19
+ export { Label };
@@ -0,0 +1,94 @@
1
+ import { useEffect, useRef, useState, type ReactNode } from 'react';
2
+ import { createPortal } from 'react-dom';
3
+ import { cn } from '../../lib/utils';
4
+
5
+ // Open modals, oldest first — so Escape only closes the topmost when nested.
6
+ const modalStack: symbol[] = [];
7
+
8
+ type ModalProps = {
9
+ /** Called on overlay click, Escape, and by the dialog's own close controls. */
10
+ onClose: () => void;
11
+ children: ReactNode;
12
+ /** Accessible label for the dialog. */
13
+ label?: string;
14
+ /** Extra classes for the panel — e.g. a wider `sm:max-w-lg`. */
15
+ className?: string;
16
+ /** Show the mobile drag-handle grabber (default true). */
17
+ handle?: boolean;
18
+ /** Allow closing via overlay click / Escape (default true). */
19
+ dismissible?: boolean;
20
+ /** ARIA role — use "alertdialog" for destructive confirms. */
21
+ role?: 'dialog' | 'alertdialog';
22
+ };
23
+
24
+ /**
25
+ * App modal: a **bottom sheet on mobile** (slides up, rounded top, safe-area
26
+ * aware) and a **centered card on ≥sm**. Rendered in a portal on `document.body`;
27
+ * closes on overlay click + Escape, locks background scroll while open, and caps
28
+ * its height with internal scroll. Enter animation only (unmounts instantly on
29
+ * close, matching the conditional-render pattern callers use).
30
+ */
31
+ export function Modal({
32
+ onClose,
33
+ children,
34
+ label,
35
+ className,
36
+ handle = true,
37
+ dismissible = true,
38
+ role = 'dialog'
39
+ }: ModalProps) {
40
+ const [shown, setShown] = useState(false);
41
+ const panelRef = useRef<HTMLDivElement>(null);
42
+
43
+ // Animate in on mount.
44
+ useEffect(() => {
45
+ const raf = requestAnimationFrame(() => setShown(true));
46
+ return () => cancelAnimationFrame(raf);
47
+ }, []);
48
+
49
+ // Escape to close (topmost only) + lock background scroll while open.
50
+ useEffect(() => {
51
+ const id = Symbol('modal');
52
+ modalStack.push(id);
53
+ const onKey = (event: KeyboardEvent) => {
54
+ if (event.key === 'Escape' && dismissible && modalStack[modalStack.length - 1] === id) onClose();
55
+ };
56
+ document.addEventListener('keydown', onKey);
57
+ const previousOverflow = document.body.style.overflow;
58
+ document.body.style.overflow = 'hidden';
59
+ return () => {
60
+ document.removeEventListener('keydown', onKey);
61
+ const index = modalStack.indexOf(id);
62
+ if (index !== -1) modalStack.splice(index, 1);
63
+ // Restore the overflow value this modal saw (keeps the lock while a parent stays open).
64
+ document.body.style.overflow = previousOverflow;
65
+ };
66
+ }, [onClose, dismissible]);
67
+
68
+ return createPortal(
69
+ <div className="fixed inset-0 z-60 flex items-end justify-center sm:items-center sm:p-4">
70
+ <div
71
+ aria-hidden="true"
72
+ onClick={dismissible ? onClose : undefined}
73
+ className={cn('fixed inset-0 bg-scrim transition-opacity duration-200', shown ? 'opacity-100' : 'opacity-0')}
74
+ />
75
+ <div
76
+ ref={panelRef}
77
+ role={role}
78
+ aria-modal="true"
79
+ aria-label={label}
80
+ className={cn(
81
+ 'relative flex max-h-[92dvh] w-full flex-col overflow-y-auto bg-card p-6',
82
+ 'rounded-t-3xl pb-[calc(1.5rem+env(safe-area-inset-bottom))] sm:max-w-md sm:rounded-3xl sm:pb-6',
83
+ 'transition duration-200 ease-out',
84
+ shown ? 'translate-y-0 opacity-100 sm:scale-100' : 'translate-y-full opacity-0 sm:translate-y-0 sm:scale-95',
85
+ className
86
+ )}
87
+ >
88
+ {handle ? <div className="mx-auto mb-3 h-1 w-9 shrink-0 rounded-full bg-border sm:hidden" /> : null}
89
+ {children}
90
+ </div>
91
+ </div>,
92
+ document.body
93
+ );
94
+ }
@@ -0,0 +1,31 @@
1
+ import * as React from 'react';
2
+ import * as PopoverPrimitive from '@radix-ui/react-popover';
3
+ import { cn } from '../../lib/utils';
4
+
5
+ const Popover = PopoverPrimitive.Root;
6
+ const PopoverTrigger = PopoverPrimitive.Trigger;
7
+ const PopoverAnchor = PopoverPrimitive.Anchor;
8
+
9
+ function PopoverContent({
10
+ className,
11
+ align = 'start',
12
+ sideOffset = 6,
13
+ ...props
14
+ }: React.ComponentProps<typeof PopoverPrimitive.Content>) {
15
+ return (
16
+ <PopoverPrimitive.Portal>
17
+ <PopoverPrimitive.Content
18
+ data-slot="popover-content"
19
+ align={align}
20
+ sideOffset={sideOffset}
21
+ className={cn(
22
+ 'z-80 w-auto rounded-2xl border border-border bg-card p-3 text-foreground shadow-[0_12px_32px_rgba(14,15,12,0.18)] outline-none',
23
+ className
24
+ )}
25
+ {...props}
26
+ />
27
+ </PopoverPrimitive.Portal>
28
+ );
29
+ }
30
+
31
+ export { Popover, PopoverTrigger, PopoverContent, PopoverAnchor };