create-mithaq 0.0.0-stage → 0.2.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 (396) hide show
  1. package/LICENSE +22 -0
  2. package/LICENSES/openstatus-time-picker.txt +21 -0
  3. package/LICENSES/payload.txt +22 -0
  4. package/LICENSES/ponytail.txt +21 -0
  5. package/index.js +521 -0
  6. package/package.json +19 -4
  7. package/template/apps/asas-backoffice/components.json +25 -0
  8. package/template/apps/asas-backoffice/gitignore +3 -0
  9. package/template/apps/asas-backoffice/package.json +57 -0
  10. package/template/apps/asas-backoffice/public/favicon.svg +1 -0
  11. package/template/apps/asas-backoffice/src/api.ts +147 -0
  12. package/template/apps/asas-backoffice/src/auth-frame.tsx +150 -0
  13. package/template/apps/asas-backoffice/src/components/ui/accordion.tsx +74 -0
  14. package/template/apps/asas-backoffice/src/components/ui/alert-dialog.tsx +185 -0
  15. package/template/apps/asas-backoffice/src/components/ui/avatar.tsx +106 -0
  16. package/template/apps/asas-backoffice/src/components/ui/badge.tsx +48 -0
  17. package/template/apps/asas-backoffice/src/components/ui/breadcrumb.tsx +124 -0
  18. package/template/apps/asas-backoffice/src/components/ui/button-group.tsx +87 -0
  19. package/template/apps/asas-backoffice/src/components/ui/button.tsx +55 -0
  20. package/template/apps/asas-backoffice/src/components/ui/calendar.tsx +223 -0
  21. package/template/apps/asas-backoffice/src/components/ui/card.tsx +102 -0
  22. package/template/apps/asas-backoffice/src/components/ui/chart.tsx +371 -0
  23. package/template/apps/asas-backoffice/src/components/ui/checkbox.tsx +28 -0
  24. package/template/apps/asas-backoffice/src/components/ui/collapsible.tsx +19 -0
  25. package/template/apps/asas-backoffice/src/components/ui/combobox.tsx +308 -0
  26. package/template/apps/asas-backoffice/src/components/ui/dialog.tsx +158 -0
  27. package/template/apps/asas-backoffice/src/components/ui/direction.tsx +6 -0
  28. package/template/apps/asas-backoffice/src/components/ui/directional-icon.tsx +20 -0
  29. package/template/apps/asas-backoffice/src/components/ui/dropdown-menu.tsx +267 -0
  30. package/template/apps/asas-backoffice/src/components/ui/field.tsx +236 -0
  31. package/template/apps/asas-backoffice/src/components/ui/input-group.tsx +155 -0
  32. package/template/apps/asas-backoffice/src/components/ui/input-otp.tsx +84 -0
  33. package/template/apps/asas-backoffice/src/components/ui/input.tsx +19 -0
  34. package/template/apps/asas-backoffice/src/components/ui/label.tsx +19 -0
  35. package/template/apps/asas-backoffice/src/components/ui/popover.tsx +90 -0
  36. package/template/apps/asas-backoffice/src/components/ui/radio-group.tsx +37 -0
  37. package/template/apps/asas-backoffice/src/components/ui/resizable.tsx +47 -0
  38. package/template/apps/asas-backoffice/src/components/ui/scroll-area.tsx +54 -0
  39. package/template/apps/asas-backoffice/src/components/ui/select.tsx +204 -0
  40. package/template/apps/asas-backoffice/src/components/ui/separator.tsx +24 -0
  41. package/template/apps/asas-backoffice/src/components/ui/sheet.tsx +139 -0
  42. package/template/apps/asas-backoffice/src/components/ui/sidebar.tsx +714 -0
  43. package/template/apps/asas-backoffice/src/components/ui/skeleton.tsx +13 -0
  44. package/template/apps/asas-backoffice/src/components/ui/slider.tsx +55 -0
  45. package/template/apps/asas-backoffice/src/components/ui/spinner.tsx +10 -0
  46. package/template/apps/asas-backoffice/src/components/ui/switch.tsx +29 -0
  47. package/template/apps/asas-backoffice/src/components/ui/table.tsx +115 -0
  48. package/template/apps/asas-backoffice/src/components/ui/tabs.tsx +79 -0
  49. package/template/apps/asas-backoffice/src/components/ui/textarea.tsx +17 -0
  50. package/template/apps/asas-backoffice/src/components/ui/toggle-group.tsx +89 -0
  51. package/template/apps/asas-backoffice/src/components/ui/toggle.tsx +44 -0
  52. package/template/apps/asas-backoffice/src/components/ui/tooltip.tsx +63 -0
  53. package/template/apps/asas-backoffice/src/components.ts +35 -0
  54. package/template/apps/asas-backoffice/src/copy.ts +1 -0
  55. package/template/apps/asas-backoffice/src/first-user.ts +14 -0
  56. package/template/apps/asas-backoffice/src/hooks/use-mobile.ts +19 -0
  57. package/template/apps/asas-backoffice/src/lib/utils.ts +1 -0
  58. package/template/apps/asas-backoffice/src/links.ts +38 -0
  59. package/template/apps/asas-backoffice/src/locale-switch.tsx +28 -0
  60. package/template/apps/asas-backoffice/src/no-organization.tsx +5 -0
  61. package/template/apps/asas-backoffice/src/redirect.ts +1 -0
  62. package/template/apps/asas-backoffice/src/request-headers.ts +11 -0
  63. package/template/apps/asas-backoffice/src/routeTree.gen.ts +206 -0
  64. package/template/apps/asas-backoffice/src/router.tsx +18 -0
  65. package/template/apps/asas-backoffice/src/routes/(screens)/backoffice/files.ts +10 -0
  66. package/template/apps/asas-backoffice/src/routes/(screens)/backoffice/screen.json +3971 -0
  67. package/template/apps/asas-backoffice/src/routes/(screens)/backoffice/types.ts +163 -0
  68. package/template/apps/asas-backoffice/src/routes/(screens)/backoffice.$.tsx +17 -0
  69. package/template/apps/asas-backoffice/src/routes/(screens)/backoffice.index.tsx +10 -0
  70. package/template/apps/asas-backoffice/src/routes/(screens)/backoffice.tsx +13 -0
  71. package/template/apps/asas-backoffice/src/routes/__root.tsx +95 -0
  72. package/template/apps/asas-backoffice/src/routes/api/$.ts +34 -0
  73. package/template/apps/asas-backoffice/src/routes/auth.create-first-user.tsx +113 -0
  74. package/template/apps/asas-backoffice/src/routes/auth.sign-in.tsx +77 -0
  75. package/template/apps/asas-backoffice/src/routes/unauthorized.tsx +134 -0
  76. package/template/apps/asas-backoffice/src/screen-form.tsx +36 -0
  77. package/template/apps/asas-backoffice/src/screen-list.tsx +140 -0
  78. package/template/apps/asas-backoffice/src/screen-names.ts +10 -0
  79. package/template/apps/asas-backoffice/src/screens.tsx +888 -0
  80. package/template/apps/asas-backoffice/src/server.ts +30 -0
  81. package/template/apps/asas-backoffice/src/slide.ts +22 -0
  82. package/template/apps/asas-backoffice/src/start.ts +42 -0
  83. package/template/apps/asas-backoffice/src/styles.css +202 -0
  84. package/template/apps/asas-backoffice/src/theme.ts +2 -0
  85. package/template/apps/asas-backoffice/tsconfig.json +19 -0
  86. package/template/apps/asas-backoffice/vite.config.ts +32 -0
  87. package/template/apps/asas-tanstack/components.json +25 -0
  88. package/template/apps/asas-tanstack/gitignore +3 -0
  89. package/template/apps/asas-tanstack/package.json +58 -0
  90. package/template/apps/asas-tanstack/public/favicon.svg +1 -0
  91. package/template/apps/asas-tanstack/src/api.ts +147 -0
  92. package/template/apps/asas-tanstack/src/auth-frame.tsx +150 -0
  93. package/template/apps/asas-tanstack/src/choose-password.tsx +150 -0
  94. package/template/apps/asas-tanstack/src/components/ui/accordion.tsx +10 -0
  95. package/template/apps/asas-tanstack/src/components/ui/alert-dialog.tsx +173 -0
  96. package/template/apps/asas-tanstack/src/components/ui/avatar.tsx +104 -0
  97. package/template/apps/asas-tanstack/src/components/ui/badge.tsx +48 -0
  98. package/template/apps/asas-tanstack/src/components/ui/breadcrumb.tsx +125 -0
  99. package/template/apps/asas-tanstack/src/components/ui/button-group.tsx +87 -0
  100. package/template/apps/asas-tanstack/src/components/ui/button.tsx +56 -0
  101. package/template/apps/asas-tanstack/src/components/ui/calendar.tsx +223 -0
  102. package/template/apps/asas-tanstack/src/components/ui/card.tsx +94 -0
  103. package/template/apps/asas-tanstack/src/components/ui/chart.tsx +369 -0
  104. package/template/apps/asas-tanstack/src/components/ui/checkbox.tsx +27 -0
  105. package/template/apps/asas-tanstack/src/components/ui/collapsible.tsx +19 -0
  106. package/template/apps/asas-tanstack/src/components/ui/combobox.tsx +300 -0
  107. package/template/apps/asas-tanstack/src/components/ui/dialog.tsx +149 -0
  108. package/template/apps/asas-tanstack/src/components/ui/direction.tsx +5 -0
  109. package/template/apps/asas-tanstack/src/components/ui/directional-icon.tsx +20 -0
  110. package/template/apps/asas-tanstack/src/components/ui/dropdown-menu.tsx +267 -0
  111. package/template/apps/asas-tanstack/src/components/ui/field.tsx +225 -0
  112. package/template/apps/asas-tanstack/src/components/ui/input-group.tsx +147 -0
  113. package/template/apps/asas-tanstack/src/components/ui/input-otp.tsx +20 -0
  114. package/template/apps/asas-tanstack/src/components/ui/input.tsx +20 -0
  115. package/template/apps/asas-tanstack/src/components/ui/label.tsx +18 -0
  116. package/template/apps/asas-tanstack/src/components/ui/popover.tsx +50 -0
  117. package/template/apps/asas-tanstack/src/components/ui/radio-group.tsx +10 -0
  118. package/template/apps/asas-tanstack/src/components/ui/resizable.tsx +47 -0
  119. package/template/apps/asas-tanstack/src/components/ui/scroll-area.tsx +52 -0
  120. package/template/apps/asas-tanstack/src/components/ui/select.tsx +190 -0
  121. package/template/apps/asas-tanstack/src/components/ui/separator.tsx +23 -0
  122. package/template/apps/asas-tanstack/src/components/ui/sheet.tsx +127 -0
  123. package/template/apps/asas-tanstack/src/components/ui/sidebar.tsx +716 -0
  124. package/template/apps/asas-tanstack/src/components/ui/skeleton.tsx +13 -0
  125. package/template/apps/asas-tanstack/src/components/ui/slider.tsx +13 -0
  126. package/template/apps/asas-tanstack/src/components/ui/spinner.tsx +11 -0
  127. package/template/apps/asas-tanstack/src/components/ui/switch.tsx +8 -0
  128. package/template/apps/asas-tanstack/src/components/ui/table.tsx +119 -0
  129. package/template/apps/asas-tanstack/src/components/ui/tabs.tsx +80 -0
  130. package/template/apps/asas-tanstack/src/components/ui/textarea.tsx +18 -0
  131. package/template/apps/asas-tanstack/src/components/ui/toggle-group.tsx +8 -0
  132. package/template/apps/asas-tanstack/src/components/ui/toggle.tsx +44 -0
  133. package/template/apps/asas-tanstack/src/components/ui/tooltip.tsx +68 -0
  134. package/template/apps/asas-tanstack/src/components.ts +35 -0
  135. package/template/apps/asas-tanstack/src/copy.ts +1207 -0
  136. package/template/apps/asas-tanstack/src/first-run.ts +16 -0
  137. package/template/apps/asas-tanstack/src/hooks/use-mobile.ts +19 -0
  138. package/template/apps/asas-tanstack/src/lib/utils.ts +6 -0
  139. package/template/apps/asas-tanstack/src/links.ts +38 -0
  140. package/template/apps/asas-tanstack/src/locale-switch.tsx +28 -0
  141. package/template/apps/asas-tanstack/src/no-organization.tsx +108 -0
  142. package/template/apps/asas-tanstack/src/quiet-abort.ts +25 -0
  143. package/template/apps/asas-tanstack/src/redirect.ts +39 -0
  144. package/template/apps/asas-tanstack/src/request-headers.ts +11 -0
  145. package/template/apps/asas-tanstack/src/routeTree.gen.ts +424 -0
  146. package/template/apps/asas-tanstack/src/router.tsx +18 -0
  147. package/template/apps/asas-tanstack/src/routes/(screens)/dashboard/files.ts +11 -0
  148. package/template/apps/asas-tanstack/src/routes/(screens)/dashboard/screen.json +4242 -0
  149. package/template/apps/asas-tanstack/src/routes/(screens)/dashboard/types.ts +160 -0
  150. package/template/apps/asas-tanstack/src/routes/(screens)/dashboard.$.tsx +17 -0
  151. package/template/apps/asas-tanstack/src/routes/(screens)/dashboard.index.tsx +10 -0
  152. package/template/apps/asas-tanstack/src/routes/(screens)/dashboard.tsx +13 -0
  153. package/template/apps/asas-tanstack/src/routes/(screens)/portal/files.ts +9 -0
  154. package/template/apps/asas-tanstack/src/routes/(screens)/portal/screen.json +1564 -0
  155. package/template/apps/asas-tanstack/src/routes/(screens)/portal/types.ts +94 -0
  156. package/template/apps/asas-tanstack/src/routes/(screens)/portal.$.tsx +17 -0
  157. package/template/apps/asas-tanstack/src/routes/(screens)/portal.index.tsx +10 -0
  158. package/template/apps/asas-tanstack/src/routes/(screens)/portal.tsx +13 -0
  159. package/template/apps/asas-tanstack/src/routes/__root.tsx +99 -0
  160. package/template/apps/asas-tanstack/src/routes/api/$.ts +11 -0
  161. package/template/apps/asas-tanstack/src/routes/auth.accept-invitation.tsx +108 -0
  162. package/template/apps/asas-tanstack/src/routes/auth.first-run.tsx +114 -0
  163. package/template/apps/asas-tanstack/src/routes/auth.forgot-password.tsx +94 -0
  164. package/template/apps/asas-tanstack/src/routes/auth.handoff.ts +39 -0
  165. package/template/apps/asas-tanstack/src/routes/auth.reset-password.tsx +16 -0
  166. package/template/apps/asas-tanstack/src/routes/auth.set-password.tsx +16 -0
  167. package/template/apps/asas-tanstack/src/routes/auth.sign-in.tsx +98 -0
  168. package/template/apps/asas-tanstack/src/routes/auth.sign-up.tsx +143 -0
  169. package/template/apps/asas-tanstack/src/routes/index.tsx +146 -0
  170. package/template/apps/asas-tanstack/src/routes/unauthorized.tsx +134 -0
  171. package/template/apps/asas-tanstack/src/screen-form.tsx +36 -0
  172. package/template/apps/asas-tanstack/src/screen-list.tsx +140 -0
  173. package/template/apps/asas-tanstack/src/screen-names.ts +10 -0
  174. package/template/apps/asas-tanstack/src/screens.tsx +888 -0
  175. package/template/apps/asas-tanstack/src/server.ts +26 -0
  176. package/template/apps/asas-tanstack/src/slide.ts +22 -0
  177. package/template/apps/asas-tanstack/src/start.ts +57 -0
  178. package/template/apps/asas-tanstack/src/styles.css +163 -0
  179. package/template/apps/asas-tanstack/src/theme.ts +5 -0
  180. package/template/apps/asas-tanstack/tsconfig.json +19 -0
  181. package/template/apps/asas-tanstack/vite.config.ts +34 -0
  182. package/template/package.json +16 -0
  183. package/template/templates/asas/.claude/skills/add-field/SKILL.md +27 -0
  184. package/template/templates/asas/.claude/skills/add-kayan/SKILL.md +26 -0
  185. package/template/templates/asas/.claude/skills/eject-admin-piece/SKILL.md +28 -0
  186. package/template/templates/asas/.claude/skills/upgrade-mithaq/SKILL.md +35 -0
  187. package/template/templates/asas/.env.example +33 -0
  188. package/template/templates/asas/AGENTS.md +101 -0
  189. package/template/templates/asas/CLAUDE.md +1 -0
  190. package/template/templates/asas/README.md +1056 -0
  191. package/template/templates/asas/mithaq.hints.json +25 -0
  192. package/template/templates/asas/package.json +53 -0
  193. package/template/templates/asas/src/asas.ts +118 -0
  194. package/template/templates/asas/src/backoffice-http.ts +117 -0
  195. package/template/templates/asas/src/dev-setup.ts +112 -0
  196. package/template/templates/asas/src/local-database.ts +22 -0
  197. package/template/templates/asas/src/messages/account.json +132 -0
  198. package/template/templates/asas/src/messages/auditLog.json +304 -0
  199. package/template/templates/asas/src/messages/backofficeLog.json +132 -0
  200. package/template/templates/asas/src/messages/courses.json +312 -0
  201. package/template/templates/asas/src/messages/enrollments.json +60 -0
  202. package/template/templates/asas/src/messages/invitation.json +82 -0
  203. package/template/templates/asas/src/messages/localUsers.json +48 -0
  204. package/template/templates/asas/src/messages/media.json +76 -0
  205. package/template/templates/asas/src/messages/member.json +62 -0
  206. package/template/templates/asas/src/messages/memberRoles.json +48 -0
  207. package/template/templates/asas/src/messages/mithaq.json +214 -0
  208. package/template/templates/asas/src/messages/organization.json +72 -0
  209. package/template/templates/asas/src/messages/organizationRole.json +62 -0
  210. package/template/templates/asas/src/messages/roles.json +72 -0
  211. package/template/templates/asas/src/messages/session.json +102 -0
  212. package/template/templates/asas/src/messages/sessions.json +136 -0
  213. package/template/templates/asas/src/messages/settings.json +64 -0
  214. package/template/templates/asas/src/messages/tags.json +48 -0
  215. package/template/templates/asas/src/messages/user.json +112 -0
  216. package/template/templates/asas/src/messages/verification.json +62 -0
  217. package/template/templates/asas/src/migrations/20260920080701_parched_tombstone/migration.sql +177 -0
  218. package/template/templates/asas/src/migrations/20260920080701_parched_tombstone/snapshot.json +2169 -0
  219. package/template/templates/asas/src/migrations/20260920230505_zippy_ben_urich/migration.sql +1 -0
  220. package/template/templates/asas/src/migrations/20260920230505_zippy_ben_urich/snapshot.json +2182 -0
  221. package/template/templates/asas/src/migrations/20260920231219_slim_retro_girl/migration.sql +20 -0
  222. package/template/templates/asas/src/migrations/20260920231219_slim_retro_girl/snapshot.json +2409 -0
  223. package/template/templates/asas/src/migrations/20260922082945_military_ironclad/migration.sql +15 -0
  224. package/template/templates/asas/src/migrations/20260922082945_military_ironclad/snapshot.json +2627 -0
  225. package/template/templates/asas/src/migrations/20260922112630_bitter_chat/migration.sql +5 -0
  226. package/template/templates/asas/src/migrations/20260922112630_bitter_chat/snapshot.json +2691 -0
  227. package/template/templates/asas/src/migrations/20260923235915_foamy_rachel_grey/migration.sql +8 -0
  228. package/template/templates/asas/src/migrations/20260923235915_foamy_rachel_grey/snapshot.json +2795 -0
  229. package/template/templates/asas/src/migrations/20260926145709_clean_the_initiative/migration.sql +3 -0
  230. package/template/templates/asas/src/migrations/20260926145709_clean_the_initiative/snapshot.json +2834 -0
  231. package/template/templates/asas/src/migrations/20260927212525_bent_prowler/migration.sql +9 -0
  232. package/template/templates/asas/src/migrations/20260927212525_bent_prowler/snapshot.json +2870 -0
  233. package/template/templates/asas/src/migrations/20260927223441_tranquil_bromley/migration.sql +1 -0
  234. package/template/templates/asas/src/migrations/20260927223441_tranquil_bromley/snapshot.json +2881 -0
  235. package/template/templates/asas/src/migrations/20260928030047_outgoing_wraith/migration.sql +15 -0
  236. package/template/templates/asas/src/migrations/20260928030047_outgoing_wraith/snapshot.json +3073 -0
  237. package/template/templates/asas/src/migrations/20260929024407_grey_morlun/migration.sql +21 -0
  238. package/template/templates/asas/src/migrations/20260929024407_grey_morlun/snapshot.json +3098 -0
  239. package/template/templates/asas/src/migrations/20260929130530_bored_jackal/migration.sql +21 -0
  240. package/template/templates/asas/src/migrations/20260929130530_bored_jackal/snapshot.json +3124 -0
  241. package/template/templates/asas/src/migrations/20260930102924_material_newton_destine/migration.sql +32 -0
  242. package/template/templates/asas/src/migrations/20260930102924_material_newton_destine/snapshot.json +3324 -0
  243. package/template/templates/asas/src/migrations/20261003022353_clumsy_taskmaster/migration.sql +2 -0
  244. package/template/templates/asas/src/migrations/20261003022353_clumsy_taskmaster/snapshot.json +3350 -0
  245. package/template/templates/asas/src/migrations/20261003143249_premium_red_shift/migration.sql +21 -0
  246. package/template/templates/asas/src/migrations/20261003143249_premium_red_shift/snapshot.json +3635 -0
  247. package/template/templates/asas/src/migrations/20261008215349_square_norrin_radd/migration.sql +8 -0
  248. package/template/templates/asas/src/migrations/20261008215349_square_norrin_radd/snapshot.json +3635 -0
  249. package/template/templates/asas/src/migrations/20261008230204_many_scarecrow/migration.sql +18 -0
  250. package/template/templates/asas/src/migrations/20261008230204_many_scarecrow/snapshot.json +3635 -0
  251. package/template/templates/asas/src/migrations/20261010005521_elite_giant_girl/migration.sql +18 -0
  252. package/template/templates/asas/src/migrations/20261010005521_elite_giant_girl/snapshot.json +3635 -0
  253. package/template/templates/asas/src/migrations/20261010120822_needy_thunderbolt/migration.sql +11 -0
  254. package/template/templates/asas/src/migrations/20261010120822_needy_thunderbolt/snapshot.json +3667 -0
  255. package/template/templates/asas/src/migrations/20261012000752_organization_ownership/migration.sql +48 -0
  256. package/template/templates/asas/src/migrations/20261012000752_organization_ownership/snapshot.json +3891 -0
  257. package/template/templates/asas/src/migrations/20261014221355_tags_restrict/migration.sql +1 -0
  258. package/template/templates/asas/src/migrations/20261014221355_tags_restrict/snapshot.json +3881 -0
  259. package/template/templates/asas/src/migrations/20261014222218_one_seat_per_session/migration.sql +1 -0
  260. package/template/templates/asas/src/migrations/20261014222218_one_seat_per_session/snapshot.json +3894 -0
  261. package/template/templates/asas/src/migrations/20261014223522_session_archive_reason/migration.sql +1 -0
  262. package/template/templates/asas/src/migrations/20261014223522_session_archive_reason/snapshot.json +3907 -0
  263. package/template/templates/asas/src/migrations/20261014225728_course_archive_by_operation/migration.sql +1 -0
  264. package/template/templates/asas/src/migrations/20261014225728_course_archive_by_operation/snapshot.json +3907 -0
  265. package/template/templates/asas/src/migrations/20261015042444_organization_names/migration.sql +1 -0
  266. package/template/templates/asas/src/migrations/20261015042444_organization_names/snapshot.json +3920 -0
  267. package/template/templates/asas/src/migrations/20261015053212_member_last_seen/migration.sql +1 -0
  268. package/template/templates/asas/src/migrations/20261015053212_member_last_seen/snapshot.json +3933 -0
  269. package/template/templates/asas/src/migrations/20261015093203_activity_and_staff_history/migration.sql +15 -0
  270. package/template/templates/asas/src/migrations/20261015093203_activity_and_staff_history/snapshot.json +4113 -0
  271. package/template/templates/asas/src/migrations/20261015095752_portal/migration.sql +2 -0
  272. package/template/templates/asas/src/migrations/20261015095752_portal/snapshot.json +4113 -0
  273. package/template/templates/asas/src/migrations/20261015135529_better_auth_indexes/migration.sql +9 -0
  274. package/template/templates/asas/src/migrations/20261015135529_better_auth_indexes/snapshot.json +4302 -0
  275. package/template/templates/asas/src/migrations/20261015162438_audit_changes_json/migration.sql +27 -0
  276. package/template/templates/asas/src/migrations/20261015162438_audit_changes_json/snapshot.json +4289 -0
  277. package/template/templates/asas/src/migrations/drizzle.config.ts +11 -0
  278. package/template/templates/asas/src/migrations/schema.ts +30 -0
  279. package/template/templates/asas/src/mithaq.ts +4 -0
  280. package/template/templates/asas/src/organizations.ts +3 -0
  281. package/template/templates/asas/src/seed.ts +427 -0
  282. package/template/templates/asas/src/ui-backoffice/home.tsx +63 -0
  283. package/template/templates/asas/src/ui-backoffice/organization-actions.tsx +121 -0
  284. package/template/templates/asas/src/ui-backoffice/record-page.tsx +30 -0
  285. package/template/templates/asas/src/ui-backoffice/role-cells.tsx +59 -0
  286. package/template/templates/asas/src/ui-backoffice/user-actions.tsx +249 -0
  287. package/template/templates/asas/src/ui-dashboard/dashboard.tsx +300 -0
  288. package/template/templates/asas/src/ui-dashboard/home.tsx +73 -0
  289. package/template/templates/asas/src/ui-dashboard/members-card.tsx +147 -0
  290. package/template/templates/asas/src/ui-dashboard/members.tsx +829 -0
  291. package/template/templates/asas/src/ui-dashboard/theme-provider.tsx +10 -0
  292. package/template/templates/asas/src/ui-dashboard/versions.tsx +446 -0
  293. package/template/templates/asas/src/ui-portal/courses-home.tsx +96 -0
  294. package/template/templates/asas/src/ui-portal/home.tsx +73 -0
  295. package/template/templates/asas/src/ui-portal/page.tsx +11 -0
  296. package/template/templates/features/audit-log/audit-log/audit-log.ts +284 -0
  297. package/template/templates/features/audit-log/backoffice-log/backoffice-log.ts +67 -0
  298. package/template/templates/features/audit-log/index.ts +4 -0
  299. package/template/templates/features/audit-log/package.json +13 -0
  300. package/template/templates/features/auth/index.ts +11 -0
  301. package/template/templates/features/auth/local-users/local-users.ts +143 -0
  302. package/template/templates/features/auth/package.json +13 -0
  303. package/template/templates/features/auth/user/account.access.ts +8 -0
  304. package/template/templates/features/auth/user/account.ts +31 -0
  305. package/template/templates/features/auth/user/index.ts +18 -0
  306. package/template/templates/features/auth/user/invitation.access.ts +4 -0
  307. package/template/templates/features/auth/user/invitation.ts +27 -0
  308. package/template/templates/features/auth/user/member.access.ts +10 -0
  309. package/template/templates/features/auth/user/member.screens.ts +37 -0
  310. package/template/templates/features/auth/user/member.ts +26 -0
  311. package/template/templates/features/auth/user/organization.access.ts +20 -0
  312. package/template/templates/features/auth/user/organization.screens.ts +37 -0
  313. package/template/templates/features/auth/user/organization.ts +25 -0
  314. package/template/templates/features/auth/user/organizationRole.access.ts +8 -0
  315. package/template/templates/features/auth/user/organizationRole.ts +25 -0
  316. package/template/templates/features/auth/user/session.access.ts +8 -0
  317. package/template/templates/features/auth/user/session.ts +28 -0
  318. package/template/templates/features/auth/user/user.access.ts +30 -0
  319. package/template/templates/features/auth/user/user.screens.ts +45 -0
  320. package/template/templates/features/auth/user/user.ts +31 -0
  321. package/template/templates/features/auth/user/verification.access.ts +8 -0
  322. package/template/templates/features/auth/user/verification.ts +23 -0
  323. package/template/templates/features/courses/courses/courses.ts +601 -0
  324. package/template/templates/features/courses/courses/cover-reads.ts +33 -0
  325. package/template/templates/features/courses/courses/ui/archived-badge.tsx +9 -0
  326. package/template/templates/features/courses/courses/ui/fee-cell.tsx +19 -0
  327. package/template/templates/features/courses/courses/ui/no-courses.tsx +13 -0
  328. package/template/templates/features/courses/courses/ui/status-cell.tsx +33 -0
  329. package/template/templates/features/courses/courses/ui/unpublish-button.tsx +15 -0
  330. package/template/templates/features/courses/domain/archive.ts +39 -0
  331. package/template/templates/features/courses/domain/on-offer.ts +6 -0
  332. package/template/templates/features/courses/enrollments/enrollments.ts +309 -0
  333. package/template/templates/features/courses/enrollments/ui/session-cell.tsx +75 -0
  334. package/template/templates/features/courses/enrollments/ui/session-labels.tsx +36 -0
  335. package/template/templates/features/courses/index.ts +8 -0
  336. package/template/templates/features/courses/package.json +24 -0
  337. package/template/templates/features/courses/portal/ui/catalogue.tsx +86 -0
  338. package/template/templates/features/courses/portal/ui/course.tsx +176 -0
  339. package/template/templates/features/courses/portal/ui/enrollments.tsx +170 -0
  340. package/template/templates/features/courses/portal/ui/parts.tsx +95 -0
  341. package/template/templates/features/courses/portal/ui/reads.ts +76 -0
  342. package/template/templates/features/courses/portal/ui/rules.ts +87 -0
  343. package/template/templates/features/courses/portal/ui/shell.tsx +194 -0
  344. package/template/templates/features/courses/sessions/seats.ts +42 -0
  345. package/template/templates/features/courses/sessions/sessions.ts +186 -0
  346. package/template/templates/features/media/index.ts +10 -0
  347. package/template/templates/features/media/media/media.ts +131 -0
  348. package/template/templates/features/media/media/ui/preview.tsx +143 -0
  349. package/template/templates/features/media/package.json +19 -0
  350. package/template/templates/features/roles/index.ts +4 -0
  351. package/template/templates/features/roles/member-roles/member-roles.ts +207 -0
  352. package/template/templates/features/roles/package.json +21 -0
  353. package/template/templates/features/roles/roles/roles.ts +184 -0
  354. package/template/templates/features/roles/roles/ui/role-form.tsx +267 -0
  355. package/template/templates/features/roles/roles/ui/roles.tsx +190 -0
  356. package/template/templates/features/settings/index.ts +3 -0
  357. package/template/templates/features/settings/package.json +13 -0
  358. package/template/templates/features/settings/settings/settings.ts +64 -0
  359. package/template/templates/features/tags/index.ts +3 -0
  360. package/template/templates/features/tags/package.json +14 -0
  361. package/template/templates/features/tags/tags/tags.ts +80 -0
  362. package/template/templates/mithaq/components.json +22 -0
  363. package/template/templates/mithaq/package.json +33 -0
  364. package/template/templates/mithaq/src/mail/index.ts +32 -0
  365. package/template/templates/mithaq/src/mail/mailers.ts +36 -0
  366. package/template/templates/mithaq/src/mithaq/audit-sink.ts +111 -0
  367. package/template/templates/mithaq/src/mithaq/auth/backoffice-roles.ts +108 -0
  368. package/template/templates/mithaq/src/mithaq/auth/index.ts +1859 -0
  369. package/template/templates/mithaq/src/mithaq/auth/organizations.ts +80 -0
  370. package/template/templates/mithaq/src/mithaq/pages.ts +83 -0
  371. package/template/templates/mithaq/src/mithaq/rich-text-nodes.ts +19 -0
  372. package/template/templates/mithaq/src/mithaq.ts +593 -0
  373. package/template/templates/mithaq/src/roles/default-member-roles.ts +29 -0
  374. package/template/templates/mithaq/src/roles/grants.ts +47 -0
  375. package/template/templates/mithaq/src/roles/member-access.ts +11 -0
  376. package/template/templates/mithaq/src/roles/statements.ts +13 -0
  377. package/template/templates/mithaq/src/roles/system-roles.ts +16 -0
  378. package/template/templates/mithaq/src/roles/tenant-access.ts +260 -0
  379. package/template/templates/mithaq/src/roles/tenant-roles.ts +321 -0
  380. package/template/templates/mithaq/src/steps/expected-updated-at.ts +41 -0
  381. package/template/templates/mithaq/src/storage/defaults.ts +156 -0
  382. package/template/templates/mithaq/src/storage/index.ts +97 -0
  383. package/template/templates/mithaq/src/storage/local.ts +143 -0
  384. package/template/templates/mithaq/src/storage/r2.ts +5 -0
  385. package/template/templates/mithaq/src/storage/s3.ts +91 -0
  386. package/template/templates/mithaq/src/storage/uploadthing.ts +59 -0
  387. package/template/templates/mithaq/src/ui/activity-page.tsx +33 -0
  388. package/template/templates/mithaq/src/ui/activity.tsx +527 -0
  389. package/template/templates/mithaq/src/ui/app-shell.tsx +268 -0
  390. package/template/templates/mithaq/src/ui/kpi-card.tsx +25 -0
  391. package/template/templates/mithaq/src/ui/record-page.tsx +50 -0
  392. package/template/templates/mithaq/src/ui/rich-text-nodes.tsx +33 -0
  393. package/template/templates/mithaq/src/ui/screen-nav.tsx +28 -0
  394. package/template/templates/mithaq/src/ui/table-page.tsx +9 -0
  395. package/template/templates/mithaq/src/ui/top-shell.tsx +60 -0
  396. package/README.md +0 -3
package/package.json CHANGED
@@ -1,6 +1,21 @@
1
1
  {
2
2
  "name": "create-mithaq",
3
- "version": "0.0.0-stage",
4
- "stub": true,
5
- "description": "Temporary package placeholder for staged publishing"
6
- }
3
+ "version": "0.2.0",
4
+ "license": "MIT",
5
+ "type": "module",
6
+ "bin": {
7
+ "create-mithaq": "index.js"
8
+ },
9
+ "engines": {
10
+ "node": ">=24"
11
+ },
12
+ "repository": {
13
+ "type": "git",
14
+ "url": "git+https://github.com/drhomet/mithaq.git",
15
+ "directory": "packages/create-mithaq"
16
+ },
17
+ "homepage": "https://github.com/drhomet/mithaq#readme",
18
+ "bugs": {
19
+ "url": "https://github.com/drhomet/mithaq/issues"
20
+ }
21
+ }
@@ -0,0 +1,25 @@
1
+ {
2
+ "$schema": "https://ui.shadcn.com/schema.json",
3
+ "style": "base-sera",
4
+ "rsc": false,
5
+ "tsx": true,
6
+ "tailwind": {
7
+ "config": "",
8
+ "css": "src/styles.css",
9
+ "baseColor": "mauve",
10
+ "cssVariables": true,
11
+ "prefix": ""
12
+ },
13
+ "iconLibrary": "lucide",
14
+ "rtl": true,
15
+ "aliases": {
16
+ "components": "@/components",
17
+ "utils": "@/lib/utils",
18
+ "ui": "@/components/ui",
19
+ "lib": "@/lib",
20
+ "hooks": "@/hooks"
21
+ },
22
+ "menuColor": "default",
23
+ "menuAccent": "subtle",
24
+ "registries": {}
25
+ }
@@ -0,0 +1,3 @@
1
+ .tanstack
2
+ dist
3
+ node_modules
@@ -0,0 +1,57 @@
1
+ {
2
+ "name": "asas-backoffice",
3
+ "version": "0.0.0",
4
+ "private": true,
5
+ "type": "module",
6
+ "scripts": {
7
+ "build": "(cd ../../templates/asas && mithaq lock --check) && vite build",
8
+ "dev": "(cd ../../templates/asas && mithaq lock --check) && vite dev --port 5174",
9
+ "preview": "(cd ../../templates/asas && mithaq lock --check) && vite preview --port 4174"
10
+ },
11
+ "dependencies": {
12
+ "@base-ui/react": "^1.5.0",
13
+ "@fontsource-variable/figtree": "^5.3.0",
14
+ "@fontsource-variable/noto-sans": "^5.3.0",
15
+ "@fontsource/ibm-plex-sans-arabic": "^5.2.8",
16
+ "@hugeicons/core-free-icons": "4.3.5",
17
+ "@hugeicons/react": "^1.1.6",
18
+ "@lexical/extension": "0.52.0",
19
+ "@lexical/link": "0.52.0",
20
+ "@lexical/list": "0.52.0",
21
+ "@lexical/react": "0.52.0",
22
+ "@lexical/rich-text": "0.52.0",
23
+ "@lexical/selection": "0.52.0",
24
+ "@lexical/utils": "0.52.0",
25
+ "@tanstack/react-form": "^1.33.5",
26
+ "@tanstack/react-query": "^5.104.0",
27
+ "@tanstack/react-router": "^1.170.29",
28
+ "@tanstack/react-start": "^1.168.56",
29
+ "asas": "*",
30
+ "class-variance-authority": "^0.7.1",
31
+ "cn": "^0.2.6",
32
+ "date-fns": "^4.4.0",
33
+ "input-otp": "^1.5.0",
34
+ "lexical": "0.52.0",
35
+ "lucide-react": "^1.16.0",
36
+ "mithaq": "*",
37
+ "mithaq-admin": "*",
38
+ "react": "^19.3.0",
39
+ "react-day-picker": "^10.0.1",
40
+ "react-dom": "^19.3.0",
41
+ "react-is": "^19.3.0",
42
+ "react-resizable-panels": "^4.14.1",
43
+ "recharts": "3.8.0",
44
+ "tw-animate-css": "^1.4.0"
45
+ },
46
+ "devDependencies": {
47
+ "@tailwindcss/vite": "^4.3.0",
48
+ "@tanstack/start-client-core": "^1.170.1",
49
+ "@types/react": "^19.3.0",
50
+ "@types/react-dom": "^19.3.0",
51
+ "@vitejs/plugin-react": "^6.0.2",
52
+ "shadcn": "4.18.0",
53
+ "tailwindcss": "^4.3.0",
54
+ "typescript": "^7.0.2",
55
+ "vite": "^8.0.13"
56
+ }
57
+ }
@@ -0,0 +1 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32"><rect width="32" height="32" fill="#5b21b6"/><path d="M16 5l9 4v6c0 6-4 10-9 12-5-2-9-6-9-12V9z" fill="none" stroke="#f5f3ff" stroke-width="2.5" stroke-linejoin="round"/><path d="M12 16l3 3 5-6" fill="none" stroke="#f5f3ff" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"/></svg>
@@ -0,0 +1,147 @@
1
+ import { afterCommitHeader, type ClientKayan, type FetchClient } from 'mithaq/admin'
2
+
3
+ /** What a write may ask beside its input on a kayan that keeps drafts: a draft save, and an autosave. The fetch plug reads them off the query string only. */
4
+ export type DraftOptions = { readonly draft?: boolean; readonly autosave?: boolean }
5
+
6
+ const queryOf = ({ draft, autosave }: DraftOptions = {}) => (draft ? `?draft=true${autosave ? '&autosave=true' : ''}` : '')
7
+
8
+ const call = async ({ kayan, operation, input, ...options }: { kayan: string; operation: string; input: Record<string, unknown> } & DraftOptions) => {
9
+ const response = await fetch(`/api/${encodeURIComponent(kayan)}/${encodeURIComponent(operation)}${queryOf(options)}`, {
10
+ method: 'POST',
11
+ headers: { 'content-type': 'application/json' },
12
+ body: JSON.stringify(input),
13
+ })
14
+ const answer = await response.json().catch(() => null)
15
+ if (!response.ok) {
16
+ const error = new Error(typeof answer?.message === 'string' ? answer.message : `HTTP ${response.status}`)
17
+ Object.assign(error, { status: response.status, code: answer?.code, data: answer?.data })
18
+ throw error
19
+ }
20
+ // A failed `afterCommit` raises the screens' banner and a committed write
21
+ // clears it. Only the server knows an operation's effect, so the plug sets
22
+ // the header on writes and never on reads.
23
+ const said = response.headers.get(afterCommitHeader)
24
+ if (typeof window !== 'undefined' && said) {
25
+ window.dispatchEvent(new CustomEvent('mithaq:after-commit', { detail: said === 'failed' }))
26
+ }
27
+ return answer
28
+ }
29
+
30
+ // Restated, not imported from the `.tsx` file that declares them: a `.ts` file
31
+ // must not need the JSX compiler.
32
+ type FoundPage = { docs: readonly Record<string, unknown>[]; hasNextPage: boolean; nextPage: number | null }
33
+ type FindApi = Readonly<
34
+ Record<
35
+ string,
36
+ | {
37
+ find: (input: Record<string, unknown>) => Promise<FoundPage>
38
+ create?: (input: Record<string, unknown>) => Promise<unknown>
39
+ delete?: (input: Record<string, unknown>) => Promise<unknown>
40
+ update?: (input: Record<string, unknown>) => Promise<unknown>
41
+ }
42
+ | undefined
43
+ >
44
+ >
45
+
46
+ /**
47
+ * Every operation of every kayan, keyed by name strings. `arm[kayan][operation](input)`
48
+ * posts to `POST /api/<kayan>/<operation>`; the proxy never answers `undefined`,
49
+ * and a name no kayan has is the server's not-found.
50
+ */
51
+ export type Arm = Readonly<Record<string, Readonly<Record<string, (input: Record<string, unknown>, options?: DraftOptions) => Promise<unknown>>>>>
52
+ export const arm = new Proxy(Object.create(null) as Arm, {
53
+ get: (_target, kayan: string) => new Proxy(Object.create(null) as Record<string, (input: Record<string, unknown>, options?: DraftOptions) => Promise<unknown>>, {
54
+ get: (_operations, operation: string) => (input: Record<string, unknown>, options?: DraftOptions) => call({ kayan, operation, input, ...options }),
55
+ }),
56
+ })
57
+
58
+ /**
59
+ * Whether an operation's input names a key: probes the schema with a value no
60
+ * browser-written schema accepts and looks for an issue at that key. A schema
61
+ * that answers asynchronously, or throws, reads as naming nothing.
62
+ */
63
+ export const takesKey = (input: { '~standard': { validate: (value: unknown) => unknown } }, key: string) => {
64
+ let answer: { issues?: readonly { path?: readonly unknown[] }[] }
65
+ try {
66
+ answer = input['~standard'].validate({ [key]: Symbol(key) }) as typeof answer
67
+ } catch {
68
+ return false
69
+ }
70
+ if (answer instanceof Promise) {
71
+ answer.catch(() => {})
72
+ return false
73
+ }
74
+ return answer.issues?.some(({ path }) => {
75
+ const segment = path?.[0]
76
+ return String(typeof segment === 'object' && segment ? (segment as { key: unknown }).key : segment) === key
77
+ }) ?? false
78
+ }
79
+
80
+ /** What a screen's `client` is over an arm; the draft words ride along with the input. */
81
+ export const clientOf = (through: Arm): FetchClient => ({ kayan, operation, input, draft, autosave }) => through[kayan]![operation]!(input, { draft, autosave })
82
+
83
+ /**
84
+ * What the server's `client` is: each operation in process, as `actor`. Only the
85
+ * two draft words are read off the caller's options and the actor goes last, so
86
+ * a loader cannot run as someone else.
87
+ */
88
+ export const actingAs = (
89
+ through: Readonly<Record<string, { operations: Record<string, (input: object, options: { actor?: unknown } & DraftOptions) => Promise<unknown>> }>>,
90
+ actor: unknown,
91
+ ): FetchClient => ({ kayan, operation, input, draft, autosave }) => through[kayan]!.operations[operation]!(input, { draft, autosave, actor })
92
+
93
+ /**
94
+ * What a relationship control and cell read a second kayan through. Only the
95
+ * kayans the screen's loader already narrowed to what this actor may read are
96
+ * in the record, so a picker cannot page any other.
97
+ */
98
+ export const readsOf = (kayans: readonly ClientKayan[]): FindApi =>
99
+ Object.fromEntries(
100
+ kayans.map((kayan) => [
101
+ kayan.name,
102
+ {
103
+ find: (input: Record<string, unknown>) => arm[kayan.name]!.find!(input) as Promise<FoundPage>,
104
+ },
105
+ ]),
106
+ )
107
+
108
+ type FieldReads = {
109
+ readonly collections?: Readonly<
110
+ Record<string, { readonly fields?: Readonly<Record<string, Readonly<Record<string, boolean>> | undefined>> } | undefined>
111
+ >
112
+ }
113
+
114
+ /**
115
+ * One kayan of the projection with every field this actor may not read taken
116
+ * off it. A loader's result is serialized into the document, so an unreadable
117
+ * field's name, label or rule would otherwise reach the viewer.
118
+ */
119
+ export const readable = (one: ClientKayan, report: FieldReads): ClientKayan => ({
120
+ ...one,
121
+ fields: one.fields.filter((field) => report.collections?.[one.name]?.fields?.[field.name]?.read === true),
122
+ })
123
+
124
+ /**
125
+ * Whether a thrown refusal is the caller's. Those carry an HTTP status; a store
126
+ * that is down or a hook bug carries none and must be rethrown, not reported
127
+ * as a missing row.
128
+ */
129
+ export const caused = (thrown: unknown): boolean =>
130
+ typeof (thrown as { status?: unknown } | null | undefined)?.status === 'number'
131
+
132
+ /**
133
+ * One POST at the auth arm (Better Auth's router under `/api/auth/user`) with the session cookie.
134
+ * Returns the answer instead of throwing: a refusal is a sentence for the viewer; no reply is status 0.
135
+ */
136
+ export const authArm = async (path: string, body: unknown = {}) => {
137
+ const response = await fetch(`/api/auth/user/${path}`, {
138
+ method: 'POST',
139
+ headers: { 'content-type': 'application/json' },
140
+ body: JSON.stringify(body),
141
+ }).catch(() => null)
142
+ if (!response) return { ok: false, status: 0, message: undefined, body: null }
143
+ const answered = (await response.json().catch(() => null)) as ({ message?: string } & Record<string, unknown>) | null
144
+ // The status too, because a rate limit's refusal carries no code to read.
145
+ return { ok: response.ok, status: response.status, message: answered?.message, body: answered }
146
+ }
147
+ // mithaq-admin 2d3ab739c92c6c2b397041b76bf4b23b2902b7a2 94db2fc49ad4391c0d1c2183cb41001dd08d62017c76af86cc5f611488c22c68
@@ -0,0 +1,150 @@
1
+ import { useState, type ComponentProps, type ReactNode } from 'react'
2
+ import { redirect, useRouteContext } from '@tanstack/react-router'
3
+ import { createServerFn } from '@tanstack/react-start'
4
+ import { Eye, EyeOff } from 'lucide-react'
5
+ import { Button } from '@/components/ui/button'
6
+ import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card'
7
+ import { Input } from '@/components/ui/input'
8
+ import { InputGroup, InputGroupAddon, InputGroupButton, InputGroupInput } from '@/components/ui/input-group'
9
+ import { authArm } from './api.ts'
10
+ import { FieldShell, describedBy } from 'mithaq-admin/field-shell.tsx'
11
+ import { refusalKey } from './copy.ts'
12
+ import { LocaleSwitch } from './locale-switch.tsx'
13
+ import { landing } from './redirect.ts'
14
+ import { useCopy } from './routes/__root.tsx'
15
+
16
+ /** The one search key the auth pages read, off the raw search. */
17
+ export const redirectSearch = (search: Record<string, unknown>): { redirect?: string } =>
18
+ typeof search.redirect === 'string' ? { redirect: search.redirect } : {}
19
+
20
+ /** Whether a read of the actor names someone. A read that throws, or an actor with no email, names no one. */
21
+ export const signedInAs = async (actor: Promise<unknown>) => Boolean(((await actor.catch(() => null)) as { email?: unknown } | null)?.email)
22
+
23
+ /**
24
+ * Whether this request carries a session, the screens it opens, the first of them as `home`, which a link back names,
25
+ * and the provider's password bounds. No auth page sends a referrer, since the reset page's address carries its token.
26
+ */
27
+ export const authGate = createServerFn({ method: 'GET' }).handler(async () => {
28
+ // Imported inside the server half so no auth page ships the screen host to the browser.
29
+ const [{ getRequest, setResponseHeader }, { asas }, { loadEnterable }] = await Promise.all([import('@tanstack/react-start/server'), import('asas'), import('./screens.tsx')])
30
+ setResponseHeader('referrer-policy', 'no-referrer')
31
+ type Provider = { name: string; server: { $context: Promise<{ password: { config: { minPasswordLength: number; maxPasswordLength: number } } }> } }
32
+ const provider = (asas.auth.strategies as unknown as Provider[]).find((one) => one.name === 'user')!
33
+ const [signedIn, { password }] = await Promise.all([signedInAs(asas.auth.actor(getRequest().headers)), provider.server.$context])
34
+ const entered = signedIn ? ((await loadEnterable())?.screens ?? []) : []
35
+ const home: { path: string; name?: string } = entered[0] ?? { path: '/unauthorized' }
36
+ return { signedIn, home, screens: entered, password: { min: password.config.minPasswordLength, max: password.config.maxPasswordLength } }
37
+ })
38
+
39
+ /**
40
+ * Sign-in's and sign-up's `beforeLoad`. A signed-in person is sent where a
41
+ * successful sign-in sends them; everyone else gets the password bounds in the
42
+ * route context.
43
+ */
44
+ export const gate = async ({ search }: { search: { redirect?: string } }) => {
45
+ const { signedIn, screens, password } = await authGate()
46
+ if (signedIn) throw redirect({ href: landing({ asked: search.redirect, screens }) })
47
+ return { password }
48
+ }
49
+
50
+ /** What a link to `home` says: the screen it opens, or, where it opens none, the action of seeing the account's status. */
51
+ export const useHomeLabel = () => {
52
+ const { t } = useCopy()
53
+ return (home: { name?: string }, sentence: 'auth.backTo' | 'marketing.openScreen') =>
54
+ home.name === undefined
55
+ ? t('auth.accountStatus', 'See your account status')
56
+ : t(sentence, sentence === 'auth.backTo' ? 'Back to {screen}' : 'Open {screen}')!.replace('{screen}', () => t(`screens.${home.name}`, home.name)!)
57
+ }
58
+
59
+ /** What the forgot and reset pages draw for someone already signed in: the way on, and a sign-out. */
60
+ export function AlreadySignedIn({ redirect: asked, home, screens }: { redirect?: string; home: { path: string; name?: string }; screens: readonly { name: string; path: string }[] }) {
61
+ const { t } = useCopy()
62
+ const label = useHomeLabel()
63
+ const to = landing({ asked, screens })
64
+ return (
65
+ <AuthFrame title={t('auth.alreadySignedIn', 'You are already signed in')}>
66
+ <div className="grid gap-4">
67
+ {/* An anchor: the path is the visitor's own, not a route the router knows. */}
68
+ <a className="text-primary text-center text-sm underline underline-offset-4" href={to}>
69
+ {to === home.path ? label(home, 'auth.backTo') : t('auth.continue', 'Continue')}
70
+ </a>
71
+ <Button type="button" variant="outline" onClick={() => void authArm('sign-out').then(() => window.location.reload())}>
72
+ {t('admin.signOut', 'Sign out')}
73
+ </Button>
74
+ </div>
75
+ </AuthFrame>
76
+ )
77
+ }
78
+
79
+ /** The sentence for a refusal of the provider: this app's own words for the status or code, then `fallback`. Never the provider's `message`, which is English in any locale. */
80
+ export const useRefusal = () => {
81
+ const { t } = useCopy()
82
+ return (answered: { status: number; body: Record<string, unknown> | null }, fallback: string | undefined) => {
83
+ const key = refusalKey(answered.status, answered.body?.code)
84
+ return (key && t(key)) || fallback || ''
85
+ }
86
+ }
87
+
88
+ /** Whether the browser's own `type="email"` refuses this text. */
89
+ export const notAnEmail = (value: string) => Object.assign(document.createElement('input'), { type: 'email', value }).validity.typeMismatch
90
+
91
+ /** The frame every auth page is drawn in: the app's name linking home, the language switch, and the page's card. */
92
+ export function AuthFrame({ title, description, children }: { title: string | undefined; description?: ReactNode; children: ReactNode }) {
93
+ const { t, locale } = useCopy()
94
+ const { app } = useRouteContext({ from: '__root__' })
95
+ return (
96
+ <div className="flex min-h-screen flex-col">
97
+ <header className="mx-auto flex h-16 w-full max-w-6xl items-center justify-between gap-2 px-4 sm:px-6">
98
+ {/* An anchor: `/` is not a router route in an app whose start entry answers its home. */}
99
+ <a className="text-lg font-semibold" href="/">
100
+ {t(app, 'Asas')}
101
+ </a>
102
+ <LocaleSwitch locale={locale} />
103
+ </header>
104
+ <main className="flex flex-1 items-center justify-center px-4 pb-16">
105
+ <Card className="w-full max-w-sm">
106
+ <CardHeader>
107
+ <CardTitle><h1>{title}</h1></CardTitle>
108
+ {description ? <CardDescription>{description}</CardDescription> : null}
109
+ </CardHeader>
110
+ {/* Descriptions follow the text direction; the shared primitive aligns them left. */}
111
+ <CardContent className="[&_[data-slot=field-description]]:text-start">{children}</CardContent>
112
+ </Card>
113
+ </main>
114
+ </div>
115
+ )
116
+ }
117
+
118
+ function PasswordInput(props: ComponentProps<'input'>) {
119
+ const { t } = useCopy()
120
+ const [shown, setShown] = useState(false)
121
+ return (
122
+ // Focus moving to the box's own toggle has not left the box.
123
+ <InputGroup onBlur={(event) => event.currentTarget.contains(event.relatedTarget) && event.stopPropagation()}>
124
+ <InputGroupInput {...props} dir="ltr" type={shown ? 'text' : 'password'} />
125
+ <InputGroupAddon align="inline-end">
126
+ <InputGroupButton aria-label={t('auth.showPassword', 'Show password')} aria-pressed={shown} size="icon-xs" onClick={() => setShown((was) => !was)}>
127
+ {shown ? <EyeOff /> : <Eye />}
128
+ </InputGroupButton>
129
+ </InputGroupAddon>
130
+ </InputGroup>
131
+ )
132
+ }
133
+
134
+ /** One labelled box, which says what is wrong with it once it has been left. An email or password reads left to right in any locale. */
135
+ export function AuthBox({
136
+ id,
137
+ label,
138
+ error,
139
+ description,
140
+ onLeave,
141
+ ...props
142
+ }: { id: string; label: string | undefined; error?: string; description?: string; onLeave: () => void } & ComponentProps<'input'>) {
143
+ const control = { 'aria-describedby': describedBy({ id, description, error }), 'aria-invalid': error ? true : undefined, id, name: id, required: true, ...props }
144
+ return (
145
+ <FieldShell id={id} label={label ?? id} description={description} error={error} onBlur={onLeave}>
146
+ {props.type === 'password' ? <PasswordInput {...control} /> : <Input dir={props.type === 'email' ? 'ltr' : 'auto'} {...control} />}
147
+ </FieldShell>
148
+ )
149
+ }
150
+ // mithaq-admin 0.0.0 e498423cf4a61adb8cf6358d043d31ae92d853de4c6939a9c0fe138c848b7211
@@ -0,0 +1,74 @@
1
+ import { Accordion as AccordionPrimitive } from "@base-ui/react/accordion"
2
+ import { cn } from "cn"
3
+ import { ChevronDownIcon, ChevronUpIcon } from "lucide-react"
4
+
5
+ function Accordion({ className, ...props }: AccordionPrimitive.Root.Props) {
6
+ return (
7
+ <AccordionPrimitive.Root
8
+ data-slot="accordion"
9
+ className={cn("flex w-full flex-col", className)}
10
+ {...props}
11
+ />
12
+ )
13
+ }
14
+
15
+ function AccordionItem({ className, ...props }: AccordionPrimitive.Item.Props) {
16
+ return (
17
+ <AccordionPrimitive.Item
18
+ data-slot="accordion-item"
19
+ className={cn("not-last:border-b", className)}
20
+ {...props}
21
+ />
22
+ )
23
+ }
24
+
25
+ function AccordionTrigger({
26
+ className,
27
+ children,
28
+ ...props
29
+ }: AccordionPrimitive.Trigger.Props) {
30
+ return (
31
+ <AccordionPrimitive.Header className="flex">
32
+ <AccordionPrimitive.Trigger
33
+ data-slot="accordion-trigger"
34
+ className={cn(
35
+ "group/accordion-trigger relative flex flex-1 items-start justify-between gap-6 rounded-none border border-transparent py-4 text-start text-sm font-semibold transition-all outline-none hover:underline focus-visible:border-ring focus-visible:ring-2 focus-visible:ring-ring/30 aria-disabled:pointer-events-none aria-disabled:opacity-50 **:data-[slot=accordion-trigger-icon]:ms-auto **:data-[slot=accordion-trigger-icon]:size-3.5 **:data-[slot=accordion-trigger-icon]:text-muted-foreground",
36
+ className
37
+ )}
38
+ {...props}
39
+ >
40
+ {children}
41
+ <ChevronDownIcon data-slot="accordion-trigger-icon" className="pointer-events-none shrink-0 group-aria-expanded/accordion-trigger:hidden" />
42
+ <ChevronUpIcon data-slot="accordion-trigger-icon" className="pointer-events-none hidden shrink-0 group-aria-expanded/accordion-trigger:inline" />
43
+ </AccordionPrimitive.Trigger>
44
+ </AccordionPrimitive.Header>
45
+ )
46
+ }
47
+
48
+ function AccordionContent({
49
+ className,
50
+ children,
51
+ ...props
52
+ }: AccordionPrimitive.Panel.Props) {
53
+ return (
54
+ <AccordionPrimitive.Panel
55
+ data-slot="accordion-content"
56
+ className="overflow-hidden text-sm data-open:animate-accordion-down data-closed:animate-accordion-up"
57
+ {...props}
58
+ >
59
+ <div
60
+ className={cn(
61
+ "h-(--accordion-panel-height) pt-0 pb-4 data-ending-style:h-0 data-starting-style:h-0 [&_a]:underline [&_a]:underline-offset-3 [&_a]:hover:text-foreground [&_p:not(:last-child)]:mb-4",
62
+ className
63
+ )}
64
+ >
65
+ {children}
66
+ </div>
67
+ </AccordionPrimitive.Panel>
68
+ )
69
+ }
70
+
71
+ const AccordionHeader = AccordionPrimitive.Header
72
+ const AccordionPanel = AccordionPrimitive.Panel
73
+
74
+ export { Accordion, AccordionItem, AccordionTrigger, AccordionContent, AccordionHeader, AccordionPanel }
@@ -0,0 +1,185 @@
1
+ import * as React from "react"
2
+ import { AlertDialog as AlertDialogPrimitive } from "@base-ui/react/alert-dialog"
3
+ import { cn } from "cn"
4
+
5
+ import { Button } from "@/components/ui/button"
6
+
7
+ function AlertDialog({ ...props }: AlertDialogPrimitive.Root.Props) {
8
+ return <AlertDialogPrimitive.Root data-slot="alert-dialog" {...props} />
9
+ }
10
+
11
+ function AlertDialogTrigger({ ...props }: AlertDialogPrimitive.Trigger.Props) {
12
+ return (
13
+ <AlertDialogPrimitive.Trigger data-slot="alert-dialog-trigger" {...props} />
14
+ )
15
+ }
16
+
17
+ function AlertDialogPortal({ ...props }: AlertDialogPrimitive.Portal.Props) {
18
+ return (
19
+ <AlertDialogPrimitive.Portal data-slot="alert-dialog-portal" {...props} />
20
+ )
21
+ }
22
+
23
+ function AlertDialogOverlay({
24
+ className,
25
+ ...props
26
+ }: AlertDialogPrimitive.Backdrop.Props) {
27
+ return (
28
+ <AlertDialogPrimitive.Backdrop
29
+ data-slot="alert-dialog-overlay"
30
+ className={cn(
31
+ "fixed inset-0 isolate z-50 bg-black/20 duration-100 supports-backdrop-filter:backdrop-blur-sm data-open:animate-in data-open:fade-in-0 data-closed:animate-out data-closed:fade-out-0",
32
+ className
33
+ )}
34
+ {...props}
35
+ />
36
+ )
37
+ }
38
+
39
+ function AlertDialogContent({
40
+ className,
41
+ size = "default",
42
+ ...props
43
+ }: AlertDialogPrimitive.Popup.Props & {
44
+ size?: "default" | "sm"
45
+ }) {
46
+ return (
47
+ <AlertDialogPortal>
48
+ <AlertDialogOverlay />
49
+ <AlertDialogPrimitive.Popup
50
+ data-slot="alert-dialog-content"
51
+ data-size={size}
52
+ className={cn(
53
+ "group/alert-dialog-content fixed top-1/2 start-1/2 z-50 grid w-full -translate-x-1/2 rtl:translate-x-1/2 -translate-y-1/2 gap-6 rounded-none bg-popover p-6 text-popover-foreground shadow-md ring-1 ring-foreground/10 duration-100 outline-none data-[size=default]:max-w-xs data-[size=sm]:max-w-xs data-[size=default]:sm:max-w-md data-open:animate-in data-open:fade-in-0 data-open:zoom-in-95 data-closed:animate-out data-closed:fade-out-0 data-closed:zoom-out-95",
54
+ className
55
+ )}
56
+ {...props}
57
+ />
58
+ </AlertDialogPortal>
59
+ )
60
+ }
61
+
62
+ function AlertDialogHeader({
63
+ className,
64
+ ...props
65
+ }: React.ComponentProps<"div">) {
66
+ return (
67
+ <div
68
+ data-slot="alert-dialog-header"
69
+ className={cn(
70
+ "grid grid-rows-[auto_1fr] place-items-center gap-2 text-center has-data-[slot=alert-dialog-media]:grid-rows-[auto_auto_1fr] has-data-[slot=alert-dialog-media]:gap-x-6 sm:group-data-[size=default]/alert-dialog-content:place-items-start sm:group-data-[size=default]/alert-dialog-content:text-start sm:group-data-[size=default]/alert-dialog-content:has-data-[slot=alert-dialog-media]:grid-rows-[auto_1fr]",
71
+ className
72
+ )}
73
+ {...props}
74
+ />
75
+ )
76
+ }
77
+
78
+ function AlertDialogFooter({
79
+ className,
80
+ ...props
81
+ }: React.ComponentProps<"div">) {
82
+ return (
83
+ <div
84
+ data-slot="alert-dialog-footer"
85
+ className={cn(
86
+ "flex flex-col-reverse gap-2 group-data-[size=sm]/alert-dialog-content:grid group-data-[size=sm]/alert-dialog-content:grid-cols-2 sm:flex-row sm:justify-end",
87
+ className
88
+ )}
89
+ {...props}
90
+ />
91
+ )
92
+ }
93
+
94
+ function AlertDialogMedia({
95
+ className,
96
+ ...props
97
+ }: React.ComponentProps<"div">) {
98
+ return (
99
+ <div
100
+ data-slot="alert-dialog-media"
101
+ className={cn(
102
+ "mb-2 inline-flex size-16 items-center justify-center rounded-none bg-muted sm:group-data-[size=default]/alert-dialog-content:row-span-2 *:[svg:not([class*='size-'])]:size-8",
103
+ className
104
+ )}
105
+ {...props}
106
+ />
107
+ )
108
+ }
109
+
110
+ function AlertDialogTitle({
111
+ className,
112
+ ...props
113
+ }: React.ComponentProps<typeof AlertDialogPrimitive.Title>) {
114
+ return (
115
+ <AlertDialogPrimitive.Title
116
+ data-slot="alert-dialog-title"
117
+ className={cn(
118
+ "text-lg font-semibold tracking-wider uppercase sm:group-data-[size=default]/alert-dialog-content:group-has-data-[slot=alert-dialog-media]/alert-dialog-content:col-start-2",
119
+ className
120
+ )}
121
+ {...props}
122
+ />
123
+ )
124
+ }
125
+
126
+ function AlertDialogDescription({
127
+ className,
128
+ ...props
129
+ }: React.ComponentProps<typeof AlertDialogPrimitive.Description>) {
130
+ return (
131
+ <AlertDialogPrimitive.Description
132
+ data-slot="alert-dialog-description"
133
+ className={cn(
134
+ "mt-0.5 text-sm leading-relaxed text-balance text-muted-foreground md:text-pretty *:[a]:underline *:[a]:underline-offset-3 *:[a]:hover:text-foreground",
135
+ className
136
+ )}
137
+ {...props}
138
+ />
139
+ )
140
+ }
141
+
142
+ function AlertDialogAction({
143
+ className,
144
+ ...props
145
+ }: React.ComponentProps<typeof Button>) {
146
+ return (
147
+ <Button
148
+ data-slot="alert-dialog-action"
149
+ className={cn(className)}
150
+ {...props}
151
+ />
152
+ )
153
+ }
154
+
155
+ function AlertDialogCancel({
156
+ className,
157
+ variant = "outline",
158
+ size = "default",
159
+ ...props
160
+ }: AlertDialogPrimitive.Close.Props &
161
+ Pick<React.ComponentProps<typeof Button>, "variant" | "size">) {
162
+ return (
163
+ <AlertDialogPrimitive.Close
164
+ data-slot="alert-dialog-cancel"
165
+ className={cn(className)}
166
+ render={<Button variant={variant} size={size} />}
167
+ {...props}
168
+ />
169
+ )
170
+ }
171
+
172
+ export {
173
+ AlertDialog,
174
+ AlertDialogAction,
175
+ AlertDialogCancel,
176
+ AlertDialogContent,
177
+ AlertDialogDescription,
178
+ AlertDialogFooter,
179
+ AlertDialogHeader,
180
+ AlertDialogMedia,
181
+ AlertDialogOverlay,
182
+ AlertDialogPortal,
183
+ AlertDialogTitle,
184
+ AlertDialogTrigger,
185
+ }