@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,225 @@
1
+ import { type ReactNode, useState } from 'react';
2
+ import { NavLink, Outlet } from 'react-router';
3
+ import { Menu, X } from 'lucide-react';
4
+ import { Logo, cn } from '@/shared/ui';
5
+ import { UserMenu, type ShellUser } from './user-menu';
6
+
7
+ export type NavItem = {
8
+ label: string;
9
+ to: string;
10
+ icon: ReactNode;
11
+ /** Match the route exactly (use for the index/dashboard route). */
12
+ end?: boolean;
13
+ };
14
+
15
+ /**
16
+ * Warm a route's chunk before the click, given its `to`. Wired to hover, keyboard
17
+ * focus and touch-start, so it covers pointer, keyboard and mobile — a phone has
18
+ * no hover, but `touchstart` still lands a few hundred ms before the click.
19
+ * Optional: apps without code-split routes just leave it off.
20
+ */
21
+ export type PreloadRoute = (to: string) => void;
22
+
23
+ export type ComingSoonItem = {
24
+ label: string;
25
+ icon: ReactNode;
26
+ };
27
+
28
+ type AppShellProps = {
29
+ nav: NavItem[];
30
+ user: ShellUser;
31
+ onSignOut: () => void;
32
+ /** Brand lockup at the top of the sidebar. Defaults to the Acme Corp mark. */
33
+ brand?: ReactNode;
34
+ /** Locked "Coming soon" nav entries shown below the main nav. */
35
+ comingSoon?: ComingSoonItem[];
36
+ /** Slot pinned to the bottom of the sidebar (e.g. the market ticker). */
37
+ sidebarFooter?: ReactNode;
38
+ /** Topbar search slot. */
39
+ search?: ReactNode;
40
+ /** Topbar action slot to the left of the user menu (e.g. the notification bell). */
41
+ topbarActions?: ReactNode;
42
+ /** Page content. Defaults to the router <Outlet /> when omitted. */
43
+ children?: ReactNode;
44
+ /** Warms a route's code-split chunk on nav hover/focus/touch. */
45
+ preloadRoute?: PreloadRoute;
46
+ };
47
+
48
+ function DefaultBrand() {
49
+ return (
50
+ <div className="px-2">
51
+ <Logo />
52
+ </div>
53
+ );
54
+ }
55
+
56
+ function Sidebar({
57
+ nav,
58
+ comingSoon,
59
+ sidebarFooter,
60
+ brand,
61
+ onNavigate,
62
+ preloadRoute
63
+ }: Pick<AppShellProps, 'nav' | 'comingSoon' | 'sidebarFooter' | 'brand' | 'preloadRoute'> & {
64
+ onNavigate?: () => void;
65
+ }) {
66
+ return (
67
+ <div className="flex h-full flex-col p-4">
68
+ <div className="py-2">{brand ?? <DefaultBrand />}</div>
69
+ {/*
70
+ * `min-h-0` is load-bearing: a flex child defaults to `min-height: auto`,
71
+ * which lets it grow past its flex-basis to fit its content instead of
72
+ * scrolling — on a short viewport that pushed `sidebarFooter` off the
73
+ * bottom of the fixed-height sidebar with no way to reach it. Scrollbar
74
+ * is hidden (still scrollable via wheel/touch/keyboard) to match the
75
+ * flat, chrome-free sidebar surface — nav is short enough that a visible
76
+ * track would only appear briefly during scroll anyway.
77
+ */}
78
+ <div className="flex min-h-0 flex-1 flex-col gap-1 overflow-y-auto [scrollbar-width:none] [&::-webkit-scrollbar]:hidden">
79
+ <nav className="flex flex-col gap-0.5">
80
+ {nav.map((item) => (
81
+ <NavLink
82
+ key={item.to}
83
+ to={item.to}
84
+ end={item.end}
85
+ onClick={onNavigate}
86
+ onPointerEnter={() => preloadRoute?.(item.to)}
87
+ onFocus={() => preloadRoute?.(item.to)}
88
+ onTouchStart={() => preloadRoute?.(item.to)}
89
+ className={({ isActive }) =>
90
+ cn(
91
+ 'flex items-center gap-3 rounded-xl px-3 py-2.5 text-sm font-semibold transition-colors [&_svg]:size-[18px]',
92
+ isActive ? 'bg-accent text-accent-foreground' : 'text-body hover:bg-secondary'
93
+ )
94
+ }
95
+ >
96
+ {item.icon}
97
+ {item.label}
98
+ </NavLink>
99
+ ))}
100
+ </nav>
101
+ {comingSoon && comingSoon.length > 0 ? (
102
+ <>
103
+ <div className="mx-2 my-3 h-px bg-border" />
104
+ <p className="px-3 pb-1 text-[10px] font-bold uppercase tracking-[0.09em] text-mute">Coming soon</p>
105
+ <div className="flex flex-col gap-0.5">
106
+ {comingSoon.map((item) => (
107
+ <div
108
+ key={item.label}
109
+ className="flex cursor-default items-center justify-between gap-2 rounded-xl px-3 py-2 text-sm font-semibold text-mute [&_svg]:size-4"
110
+ >
111
+ <span className="flex items-center gap-3">
112
+ {item.icon}
113
+ {item.label}
114
+ </span>
115
+ <span className="rounded-full bg-accent px-1.5 py-0.5 text-[8.5px] font-bold uppercase tracking-[0.08em] text-accent-foreground">
116
+ Soon
117
+ </span>
118
+ </div>
119
+ ))}
120
+ </div>
121
+ </>
122
+ ) : null}
123
+ </div>
124
+ {sidebarFooter ? <div className="pt-3">{sidebarFooter}</div> : null}
125
+ </div>
126
+ );
127
+ }
128
+
129
+ /**
130
+ * Authenticated app shell (shared by Client + Admin): fixed sidebar at desktop,
131
+ * a slide-in drawer on mobile, and a sticky topbar with search / actions / user
132
+ * menu. Presentational only — nav and slots are passed in, and sign-out is
133
+ * delegated via `onSignOut`, so it stays decoupled from the auth package.
134
+ */
135
+ export function AppShell({
136
+ nav,
137
+ user,
138
+ onSignOut,
139
+ brand,
140
+ comingSoon,
141
+ sidebarFooter,
142
+ search,
143
+ topbarActions,
144
+ children,
145
+ preloadRoute
146
+ }: AppShellProps) {
147
+ const [mobileOpen, setMobileOpen] = useState(false);
148
+
149
+ return (
150
+ <div className="min-h-screen bg-background">
151
+ {/* Desktop sidebar */}
152
+ <aside className="fixed inset-y-0 left-0 hidden w-64 border-r border-border bg-card lg:block">
153
+ <Sidebar
154
+ nav={nav}
155
+ comingSoon={comingSoon}
156
+ sidebarFooter={sidebarFooter}
157
+ brand={brand}
158
+ preloadRoute={preloadRoute}
159
+ />
160
+ </aside>
161
+
162
+ {/* Mobile drawer — always mounted so it animates on both enter and exit. */}
163
+ <div
164
+ className={cn('fixed inset-0 z-50 lg:hidden', !mobileOpen && 'pointer-events-none')}
165
+ aria-hidden={!mobileOpen}
166
+ inert={!mobileOpen}
167
+ >
168
+ <div
169
+ onClick={() => setMobileOpen(false)}
170
+ aria-hidden="true"
171
+ className={cn(
172
+ 'absolute inset-0 bg-scrim transition-opacity duration-300 ease-out motion-reduce:transition-none',
173
+ mobileOpen ? 'opacity-100' : 'opacity-0'
174
+ )}
175
+ />
176
+ <aside
177
+ className={cn(
178
+ 'absolute inset-y-0 left-0 w-64 border-r border-border bg-card',
179
+ 'transition-transform duration-300 ease-out motion-reduce:transition-none',
180
+ mobileOpen ? 'translate-x-0' : '-translate-x-full'
181
+ )}
182
+ >
183
+ <button
184
+ type="button"
185
+ onClick={() => setMobileOpen(false)}
186
+ aria-label="Close menu"
187
+ className="absolute right-3 top-4 flex size-8 items-center justify-center rounded-full text-mute hover:bg-secondary"
188
+ >
189
+ <X className="size-5" />
190
+ </button>
191
+ <Sidebar
192
+ nav={nav}
193
+ comingSoon={comingSoon}
194
+ sidebarFooter={sidebarFooter}
195
+ brand={brand}
196
+ preloadRoute={preloadRoute}
197
+ onNavigate={() => setMobileOpen(false)}
198
+ />
199
+ </aside>
200
+ </div>
201
+
202
+ {/* Main column */}
203
+ <div className="flex min-h-screen flex-col lg:pl-64">
204
+ <header className="sticky top-0 z-40 flex items-center gap-2.5 border-b border-border bg-card px-4 py-2.5">
205
+ <button
206
+ type="button"
207
+ onClick={() => setMobileOpen(true)}
208
+ aria-label="Open menu"
209
+ className="flex size-9 items-center justify-center rounded-full text-foreground hover:bg-secondary lg:hidden"
210
+ >
211
+ <Menu className="size-5" />
212
+ </button>
213
+ {search ?? <div className="flex-1" />}
214
+ <div className="ml-auto flex items-center gap-2">
215
+ {topbarActions}
216
+ <UserMenu user={user} onSignOut={onSignOut} />
217
+ </div>
218
+ </header>
219
+ <main className="flex-1">
220
+ <div className="mx-auto w-full max-w-6xl p-4 sm:p-6 lg:p-8">{children ?? <Outlet />}</div>
221
+ </main>
222
+ </div>
223
+ </div>
224
+ );
225
+ }
@@ -0,0 +1,74 @@
1
+ import { useEffect, useRef, useState } from 'react';
2
+ import { LogOut } from 'lucide-react';
3
+
4
+ export type ShellUser = {
5
+ name?: string | null;
6
+ email?: string | null;
7
+ image?: string | null;
8
+ };
9
+
10
+ function initials(user: ShellUser): string {
11
+ const name = (user.name ?? '').trim();
12
+ if (name) {
13
+ const parts = name.split(/\s+/);
14
+ const first = parts[0]?.[0] ?? '';
15
+ const second = parts[1]?.[0] ?? '';
16
+ return (first + second || first).toUpperCase();
17
+ }
18
+ const email = (user.email ?? '').trim();
19
+ return email ? email[0].toUpperCase() : '?';
20
+ }
21
+
22
+ /** Avatar button + dropdown (name/email + sign out). Sign-out is delegated via `onSignOut`. */
23
+ export function UserMenu({ user, onSignOut }: { user: ShellUser; onSignOut: () => void }) {
24
+ const [open, setOpen] = useState(false);
25
+ const ref = useRef<HTMLDivElement>(null);
26
+
27
+ useEffect(() => {
28
+ if (!open) return;
29
+ const onDocClick = (event: MouseEvent) => {
30
+ if (ref.current && !ref.current.contains(event.target as Node)) setOpen(false);
31
+ };
32
+ document.addEventListener('mousedown', onDocClick);
33
+ return () => document.removeEventListener('mousedown', onDocClick);
34
+ }, [open]);
35
+
36
+ return (
37
+ <div className="relative" ref={ref}>
38
+ <button
39
+ type="button"
40
+ onClick={() => setOpen((value) => !value)}
41
+ aria-haspopup="menu"
42
+ aria-expanded={open}
43
+ aria-label="Account menu"
44
+ className="flex size-9 items-center justify-center overflow-hidden rounded-full bg-primary text-sm font-bold text-primary-foreground outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-background"
45
+ >
46
+ {user.image ? <img src={user.image} alt="" className="size-9 rounded-full object-cover" /> : initials(user)}
47
+ </button>
48
+ {open ? (
49
+ <div
50
+ role="menu"
51
+ className="absolute right-0 top-11 z-50 w-56 rounded-2xl border border-border bg-card p-2 shadow-[0_12px_32px_rgba(14,15,12,0.18)]"
52
+ >
53
+ <div className="px-3 py-2">
54
+ <p className="truncate text-sm font-semibold text-foreground">{user.name ?? 'Your account'}</p>
55
+ {user.email ? <p className="truncate text-xs text-mute">{user.email}</p> : null}
56
+ </div>
57
+ <div className="my-1 h-px bg-border" />
58
+ <button
59
+ type="button"
60
+ role="menuitem"
61
+ onClick={() => {
62
+ setOpen(false);
63
+ onSignOut();
64
+ }}
65
+ className="flex w-full items-center gap-2.5 rounded-xl px-3 py-2 text-left text-sm font-semibold text-foreground transition-colors hover:bg-secondary"
66
+ >
67
+ <LogOut className="size-4" />
68
+ Sign out
69
+ </button>
70
+ </div>
71
+ ) : null}
72
+ </div>
73
+ );
74
+ }
@@ -0,0 +1,137 @@
1
+ import type { ReactNode } from 'react';
2
+ import { Logo, LogoMark, ThemeToggle } from '@/shared/ui';
3
+
4
+ /**
5
+ * Brand-panel watermark, per the design system's Watermark guideline: one large
6
+ * mark, offset, clear of all text, never tiled — "felt, not read".
7
+ *
8
+ * Inline SVG (`LogoMark` fills with `currentColor`), so it costs no request and
9
+ * inverts with the theme for free: ink on the sage panel, near-white on the ink
10
+ * one. The DS quotes 6–8% ink, but that assumes a light surface — the dark panel
11
+ * is far darker, so the same alpha reads ~45% stronger against it. Measured in
12
+ * CIELAB against each panel colour and matched: 0.07 light gives ΔE 5.4, 0.05
13
+ * dark gives ΔE 5.6. Don't "simplify" these to one value.
14
+ *
15
+ * `-z-10` under the panel's `isolate` puts it behind the copy while staying above
16
+ * the panel's own background; without `isolate` the negative z-index would sink
17
+ * below it and disappear.
18
+ */
19
+ function BrandWatermark() {
20
+ return (
21
+ <div className="pointer-events-none absolute inset-0 -z-10 overflow-hidden" aria-hidden="true">
22
+ {/*
23
+ * Size and offset are load-bearing, and every value here came from looking
24
+ * at it rather than reasoning about it:
25
+ * - 460px left only a corner fragment on-panel, so it read as stray grey
26
+ * rectangles rather than the monogram.
27
+ * - 300px read correctly at 1280 but the dot poked out past the teaser
28
+ * card's right edge; the guideline is explicit that it stays clear.
29
+ * - 240px clears the card at 1280 — but not at 1024, where the panel is
30
+ * only 512px wide. The card is 340px inside 48px padding, so it owns the
31
+ * left 388px and *any* legible mark overlaps it horizontally there. The
32
+ * only way to clear is vertically, hence the smaller `lg` size that fits
33
+ * in the ~155px below the card, going full size from `xl`.
34
+ *
35
+ * Sized in CSS, not the `width` prop, so it can be responsive — the classes
36
+ * override the SVG's width/height attributes and the viewBox keeps the ratio.
37
+ * Re-check both breakpoints if the card's width or the panel padding changes.
38
+ */}
39
+ <LogoMark
40
+ width={240}
41
+ className="absolute -bottom-4 -right-4 h-auto w-40 text-foreground opacity-[0.07] dark:opacity-[0.05] xl:w-60"
42
+ />
43
+ </div>
44
+ );
45
+ }
46
+
47
+ /**
48
+ * Product teaser — the brand panel's signature element. Shows a prospective user
49
+ * what the product does instead of decorating around them, and costs nothing:
50
+ * the sparkline is inline SVG, no image request.
51
+ *
52
+ * Replace the copy and the figure with your own; keep the shape. **The numbers
53
+ * are illustrative, not anyone's data**, which is why the visually-hidden
54
+ * "Example" prefix is here — without it a screen reader announces a fabricated
55
+ * figure as fact.
56
+ *
57
+ * Brand green on the line doesn't conflict with "green is for CTAs": `--primary`
58
+ * is the same hex in both themes, so the card needs no per-theme handling, and
59
+ * the surrounding surfaces do.
60
+ */
61
+ const SPARK = 'M0,44 C24,40 40,46 62,38 C86,29 102,34 124,27 C148,19 166,24 190,17 C214,11 236,14 256,9 L280,6';
62
+
63
+ function ProductTeaser() {
64
+ return (
65
+ <div className="mt-7 max-w-[340px] rounded-[20px] bg-card px-5 py-[18px]">
66
+ <div className="flex items-baseline justify-between gap-2.5">
67
+ <span className="text-[11px] font-semibold uppercase leading-[15px] tracking-[0.07em] text-mute">
68
+ <span className="sr-only">Example: </span>This month
69
+ </span>
70
+ <span className="text-[11.5px] font-semibold leading-4 text-positive">▲ +18.4%</span>
71
+ </div>
72
+ <div className="mt-1.5 text-[26px] font-extrabold leading-8 tracking-[-0.02em] tabular-nums text-foreground">
73
+ 42,830.16
74
+ </div>
75
+ <svg viewBox="0 0 280 56" className="mt-2.5 h-14 w-full" aria-hidden="true">
76
+ {/* Area first so the stroke sits on top of it. */}
77
+ <path d={`${SPARK} L280,56 L0,56 Z`} className="fill-primary opacity-12" />
78
+ <path d={SPARK} fill="none" strokeWidth="2.5" className="stroke-primary" />
79
+ </svg>
80
+ <p className="mt-1.5 text-[10.5px] leading-[15px] text-mute">Illustrative figures · replace with your own</p>
81
+ </div>
82
+ );
83
+ }
84
+
85
+ type AuthLayoutProps = {
86
+ children: ReactNode;
87
+ /** Optional brand-panel content (headline / illustration). */
88
+ aside?: ReactNode;
89
+ };
90
+
91
+ /**
92
+ * Split auth shell (Wise pattern): sage brand panel on the left at desktop,
93
+ * form panel on the right; stacked to a single centered column on mobile.
94
+ * No shadows — the form sits on the page/card surface via contrast.
95
+ */
96
+ export function AuthLayout({ children, aside }: AuthLayoutProps) {
97
+ return (
98
+ <div className="min-h-screen w-full bg-background lg:grid lg:grid-cols-2">
99
+ {/*
100
+ * `isolate` so the watermark's -z-10 stays above the panel background.
101
+ *
102
+ * `my-auto` on the content block rather than `justify-between` on the panel:
103
+ * the footer line used to anchor the bottom, so with it gone space-between
104
+ * would drop the headline to the floor.
105
+ */}
106
+ <aside className="relative isolate hidden flex-col gap-10 bg-brand-panel p-12 lg:flex">
107
+ <BrandWatermark />
108
+ <Logo />
109
+ <div className="my-auto max-w-md">
110
+ {aside ?? (
111
+ <>
112
+ <h1 className="font-display text-4xl font-black leading-[1.06] tracking-tight text-foreground">
113
+ One line on why you exist.
114
+ </h1>
115
+ <p className="mt-4 max-w-sm text-body">
116
+ A sentence of supporting copy, written for someone who has never heard of this product.
117
+ </p>
118
+ <ProductTeaser />
119
+ </>
120
+ )}
121
+ </div>
122
+ </aside>
123
+ {/*
124
+ * The auth app has no topbar, so the toggle rides the form panel. Placed
125
+ * here rather than per-route because every auth screen goes through this
126
+ * layout. `relative` on the panel keeps it inside the right-hand column at
127
+ * desktop instead of over the brand panel.
128
+ */}
129
+ <main className="relative flex min-h-screen items-center justify-center p-6">
130
+ <div className="absolute right-4 top-4">
131
+ <ThemeToggle />
132
+ </div>
133
+ <div className="w-full max-w-md">{children}</div>
134
+ </main>
135
+ </div>
136
+ );
137
+ }
@@ -0,0 +1,38 @@
1
+ import { Component, type ErrorInfo, type ReactNode } from 'react';
2
+ import { Button } from '@/shared/ui';
3
+ import { StateScreen } from './states/state-screen';
4
+
5
+ type ErrorBoundaryProps = {
6
+ children: ReactNode;
7
+ fallback?: ReactNode;
8
+ };
9
+
10
+ type ErrorBoundaryState = {
11
+ hasError: boolean;
12
+ };
13
+
14
+ /** Catches render errors in its subtree and shows a recoverable fallback. */
15
+ export class ErrorBoundary extends Component<ErrorBoundaryProps, ErrorBoundaryState> {
16
+ state: ErrorBoundaryState = { hasError: false };
17
+
18
+ static getDerivedStateFromError(): ErrorBoundaryState {
19
+ return { hasError: true };
20
+ }
21
+
22
+ componentDidCatch(error: Error, info: ErrorInfo): void {
23
+ console.error('ErrorBoundary caught an error', error, info);
24
+ }
25
+
26
+ render(): ReactNode {
27
+ if (this.state.hasError) {
28
+ return (
29
+ this.props.fallback ?? (
30
+ <StateScreen title="Something went wrong" description="An unexpected error occurred. Try reloading the page.">
31
+ <Button onClick={() => window.location.reload()}>Reload</Button>
32
+ </StateScreen>
33
+ )
34
+ );
35
+ }
36
+ return this.props.children;
37
+ }
38
+ }
@@ -0,0 +1,23 @@
1
+ // @/shared/layouts — shared layouts and state components.
2
+ export { AuthLayout } from './auth-layout';
3
+ export { Loading } from './states/loading';
4
+ export { AppShellSkeleton } from './states/app-shell-skeleton';
5
+ export { RouteFallback } from './states/route-fallback';
6
+ export { PageSkeleton } from './states/page-skeleton';
7
+ export { Forbidden } from './states/forbidden';
8
+ export { NotFound } from './states/not-found';
9
+ export { Empty } from './states/empty';
10
+ export { StateScreen } from './states/state-screen';
11
+ export { ErrorBoundary } from './error-boundary';
12
+
13
+ // App shell (Client + Admin)
14
+ export { AppShell } from './app-shell/app-shell';
15
+ export type { NavItem, ComingSoonItem, PreloadRoute } from './app-shell/app-shell';
16
+ export { UserMenu } from './app-shell/user-menu';
17
+ export type { ShellUser } from './app-shell/user-menu';
18
+
19
+ // Marketing (landing)
20
+ export { MarketingLayout } from './marketing/marketing-layout';
21
+ export { Section } from './marketing/section';
22
+ export type { SectionTone } from './marketing/section';
23
+ export { SectionHeading } from './marketing/section-heading';
@@ -0,0 +1,29 @@
1
+ import type { ReactNode } from 'react';
2
+ import { Outlet } from 'react-router';
3
+
4
+ type MarketingLayoutProps = {
5
+ /** Sticky header slot (brand, nav, auth CTAs). */
6
+ header?: ReactNode;
7
+ /** Footer slot (link columns, legal, newsletter). */
8
+ footer?: ReactNode;
9
+ /** Page content. Defaults to the router <Outlet /> when omitted. */
10
+ children?: ReactNode;
11
+ };
12
+
13
+ /**
14
+ * Shared shell for the public marketing site (landing) and its legal pages: a
15
+ * header slot, the routed page content, and a footer slot — defined once so
16
+ * nav and footer aren't rebuilt per route. Presentational only, like
17
+ * `AppShell`: no data fetching, no router coupling beyond the `Outlet`
18
+ * fallback, and no Suspense of its own — that's the consumer's job, same as
19
+ * `AppShell` leaves it to `ClientShell`.
20
+ */
21
+ export function MarketingLayout({ header, footer, children }: MarketingLayoutProps) {
22
+ return (
23
+ <div className="flex min-h-screen flex-col bg-background">
24
+ {header}
25
+ <main className="flex-1">{children ?? <Outlet />}</main>
26
+ {footer}
27
+ </div>
28
+ );
29
+ }
@@ -0,0 +1,38 @@
1
+ import { cn } from '@/shared/ui';
2
+
3
+ type SectionHeadingProps = {
4
+ eyebrow?: string;
5
+ title: string;
6
+ lede?: string;
7
+ /** `left` drops the centering, for a heading inside a two-column row rather than standing alone. */
8
+ align?: 'center' | 'left';
9
+ className?: string;
10
+ };
11
+
12
+ /**
13
+ * Eyebrow pill / h2 / lede — the heading stack the design repeats at the top
14
+ * of most sections. `bg-accent text-accent-foreground` rather than the fixed
15
+ * `primary-pale` + `ink-deep`: the accent pair flips together per theme, where a
16
+ * fixed pale badge goes muddy on dark.
17
+ */
18
+ export function SectionHeading({ eyebrow, title, lede, align = 'center', className }: SectionHeadingProps) {
19
+ return (
20
+ <div
21
+ className={cn(
22
+ 'flex flex-col gap-4',
23
+ align === 'center' ? 'items-center text-center' : 'items-start text-left',
24
+ className
25
+ )}
26
+ >
27
+ {eyebrow ? (
28
+ <span className="inline-flex rounded-full bg-accent px-3.5 py-1.5 text-xs font-bold uppercase tracking-wider text-accent-foreground">
29
+ {eyebrow}
30
+ </span>
31
+ ) : null}
32
+ <h2 className="max-w-3xl font-display text-3xl font-black tracking-tight text-foreground sm:text-4xl lg:text-5xl">
33
+ {title}
34
+ </h2>
35
+ {lede ? <p className="max-w-2xl text-lg text-body">{lede}</p> : null}
36
+ </div>
37
+ );
38
+ }
@@ -0,0 +1,30 @@
1
+ import type { ReactNode } from 'react';
2
+ import { cn } from '@/shared/ui';
3
+
4
+ export type SectionTone = 'page' | 'band';
5
+
6
+ type SectionProps = {
7
+ /** `page` (default) is the plain page background; `band` is the tinted alternate. */
8
+ tone?: SectionTone;
9
+ id?: string;
10
+ className?: string;
11
+ children?: ReactNode;
12
+ };
13
+
14
+ /**
15
+ * The design alternates the plain page background and a tinted band between
16
+ * sections. This owns that tone plus the shared 1200px container and vertical
17
+ * rhythm, so pages reach for a prop instead of one-off classes per section.
18
+ */
19
+ export function Section({ tone = 'page', id, className, children }: SectionProps) {
20
+ return (
21
+ <section
22
+ id={id}
23
+ // `scroll-mt` only matters for a section that's also an anchor
24
+ // target — otherwise the sticky header covers the heading on arrival.
25
+ className={cn(tone === 'band' ? 'bg-secondary' : 'bg-background', id && 'scroll-mt-20')}
26
+ >
27
+ <div className={cn('mx-auto max-w-[1200px] px-5 py-14 sm:py-16 lg:py-24', className)}>{children}</div>
28
+ </section>
29
+ );
30
+ }
@@ -0,0 +1,47 @@
1
+ import { Logo, Skeleton } from '@/shared/ui';
2
+ import { PageSkeleton } from './page-skeleton';
3
+
4
+ /**
5
+ * Full-page skeleton for the authenticated bootstrap (session resolving on reload).
6
+ * Renders the real app-shell chrome — sidebar frame + brand + topbar — so the frame
7
+ * appears instantly and only nav/content pulse, then hands off to the route's own
8
+ * skeletons once the shell mounts. Route-agnostic content (used by Client + Admin).
9
+ */
10
+ export function AppShellSkeleton() {
11
+ return (
12
+ <div className="min-h-screen bg-background">
13
+ {/* Desktop sidebar */}
14
+ <aside className="fixed inset-y-0 left-0 hidden w-64 border-r border-border bg-card lg:block">
15
+ <div className="flex h-full flex-col gap-1 p-4">
16
+ <div className="px-2 py-2">
17
+ <Logo />
18
+ </div>
19
+ <div className="mt-1 flex flex-col gap-1">
20
+ {Array.from({ length: 6 }).map((_, i) => (
21
+ <div key={i} className="flex items-center gap-3 px-3 py-2.5">
22
+ <Skeleton className="size-[18px] rounded-md" />
23
+ <Skeleton className="h-3.5 w-24" />
24
+ </div>
25
+ ))}
26
+ </div>
27
+ </div>
28
+ </aside>
29
+
30
+ {/* Main column */}
31
+ <div className="flex min-h-screen flex-col lg:pl-64">
32
+ <header className="sticky top-0 z-40 flex items-center gap-2.5 border-b border-border bg-card px-4 py-2.5">
33
+ <Skeleton className="size-9 rounded-full lg:hidden" />
34
+ <div className="flex-1" />
35
+ <Skeleton className="size-9 rounded-full" />
36
+ </header>
37
+ <main className="flex-1">
38
+ {/* Same content skeleton a route transition shows, so a reload and a
39
+ navigation look identical. */}
40
+ <div className="mx-auto w-full max-w-6xl p-4 sm:p-6 lg:p-8">
41
+ <PageSkeleton />
42
+ </div>
43
+ </main>
44
+ </div>
45
+ </div>
46
+ );
47
+ }
@@ -0,0 +1,22 @@
1
+ import type { ReactNode } from 'react';
2
+
3
+ type EmptyProps = {
4
+ title: string;
5
+ description?: string;
6
+ icon?: ReactNode;
7
+ action?: ReactNode;
8
+ };
9
+
10
+ /** Inline empty state for lists/panels (a card, not a full page). */
11
+ export function Empty({ title, description, icon, action }: EmptyProps) {
12
+ return (
13
+ <div className="flex flex-col items-center justify-center gap-3 rounded-3xl border border-border bg-card p-10 text-center">
14
+ {icon ? <div className="text-mute">{icon}</div> : null}
15
+ <div className="flex flex-col gap-1">
16
+ <p className="font-semibold text-foreground">{title}</p>
17
+ {description ? <p className="text-sm text-mute">{description}</p> : null}
18
+ </div>
19
+ {action}
20
+ </div>
21
+ );
22
+ }