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
@@ -0,0 +1,22 @@
1
+ {
2
+ "$schema": "https://ui.shadcn.com/schema.json",
3
+ "style": "base-maia",
4
+ "rsc": false,
5
+ "tsx": true,
6
+ "tailwind": {
7
+ "config": "",
8
+ "css": "",
9
+ "baseColor": "stone",
10
+ "cssVariables": true,
11
+ "prefix": ""
12
+ },
13
+ "iconLibrary": "hugeicons",
14
+ "rtl": true,
15
+ "aliases": {
16
+ "components": "@/components",
17
+ "utils": "@/lib/utils",
18
+ "ui": "@/components/ui",
19
+ "lib": "@/lib",
20
+ "hooks": "@/hooks"
21
+ }
22
+ }
@@ -0,0 +1,33 @@
1
+ {
2
+ "name": "@asas/mithaq",
3
+ "version": "0.0.0",
4
+ "private": true,
5
+ "type": "module",
6
+ "exports": {
7
+ ".": "./src/mithaq.ts",
8
+ "./auth": "./src/mithaq/auth/index.ts",
9
+ "./auth/backoffice-roles": "./src/mithaq/auth/backoffice-roles.ts",
10
+ "./auth/organizations": "./src/mithaq/auth/organizations.ts",
11
+ "./*": "./src/*"
12
+ },
13
+ "dependencies": {
14
+ "@better-auth/core": "1.7.4",
15
+ "@lexical/extension": "0.52.0",
16
+ "@lexical/link": "0.52.0",
17
+ "@lexical/list": "0.52.0",
18
+ "@lexical/rich-text": "0.52.0",
19
+ "aws4fetch": "1.0.20",
20
+ "better-auth": "1.7.4",
21
+ "file-type": "22.0.1",
22
+ "image-dimensions": "2.5.1",
23
+ "lexical": "0.52.0",
24
+ "mithaq": "*",
25
+ "mithaq-admin": "*"
26
+ },
27
+ "devDependencies": {
28
+ "@types/react": "^19.3.0",
29
+ "lucide-react": "^1.16.0",
30
+ "react": "^19.3.0",
31
+ "zod": "^4.6.5"
32
+ }
33
+ }
@@ -0,0 +1,32 @@
1
+ // Which mailer this project sends with. `mailers.ts` beside this file is copied from the
2
+ // `mithaq-mail` registry item and owned by the project; a project on another service writes its
3
+ // adapter there and names it here. The auth setup sends the password reset, the invitation and the
4
+ // email verification through it, and refuses an open sign-up in production while none is configured.
5
+ import { defaultResendBaseURL, resendMailer, type Mailer } from './mailers.ts'
6
+
7
+ export { logMailer, type Mail, type Mailer } from './mailers.ts'
8
+
9
+ /**
10
+ * Resend when both `RESEND_API_KEY` and `MAIL_FROM` are set, none when neither is. Half the settings
11
+ * is refused, so a typo never leaves a deploy sending nothing while it seems configured.
12
+ * `RESEND_BASE_URL`, Resend's own SDK variable, points it at another address that speaks its API.
13
+ */
14
+ export const mailerFromEnv = (env: Record<string, string | undefined>): Mailer | undefined => {
15
+ const { RESEND_API_KEY: apiKey, MAIL_FROM: from } = env
16
+ if (!apiKey && !from) return undefined
17
+ if (!apiKey) throw new Error('MAIL_FROM is set and RESEND_API_KEY is not. Set both, or neither.')
18
+ if (!from) throw new Error('RESEND_API_KEY is set and MAIL_FROM is not: set MAIL_FROM to an address at a domain your Resend account verified.')
19
+ const baseURL = env.RESEND_BASE_URL || defaultResendBaseURL
20
+ // The key and every letter cross to it, so in the clear only to this machine, such as a test's catcher.
21
+ const at = URL.canParse(baseURL) ? new URL(baseURL) : undefined
22
+ if (!at || (at.protocol !== 'https:' && !(at.protocol === 'http:' && ['localhost', '127.0.0.1', '[::1]'].includes(at.hostname)))) {
23
+ throw new Error(`RESEND_BASE_URL is ${JSON.stringify(baseURL)}: set an https address, or http on this machine.`)
24
+ }
25
+ return resendMailer({ apiKey, from, baseURL })
26
+ }
27
+
28
+ /**
29
+ * The mailer this deploy configured, or none; the auth setup logs mail in development without one.
30
+ * The auth setup asks it as it loads, so every load reads the environment it is started with.
31
+ */
32
+ export const configuredMailer = (): Mailer | undefined => mailerFromEnv(process.env)
@@ -0,0 +1,36 @@
1
+ /** One message, written twice: plain text for every client, HTML for those that draw it. */
2
+ export type Mail = { to: string; subject: string; text: string; html: string }
3
+
4
+ /** What sends this project's mail: one function, so an adapter for another service is a few lines. */
5
+ export type Mailer = { send(mail: Mail): Promise<void> }
6
+
7
+ /**
8
+ * Development only: the message goes to the server's log, links and all, and nothing leaves the
9
+ * machine. Never use it in production, where the log would hold every reset and sign-in link.
10
+ */
11
+ export const logMailer: Mailer = {
12
+ send: async ({ to, subject, text }) => {
13
+ // The log reads as an inbox, so nobody waits for a message that never left.
14
+ console.log(`[asas mail] Development: emails print here instead of being sent.\nTo ${to}: ${subject}\n${text}`)
15
+ },
16
+ }
17
+
18
+ /** Where Resend's API answers, as its own SDK names it. */
19
+ export const defaultResendBaseURL = 'https://api.resend.com'
20
+
21
+ /**
22
+ * Resend's HTTP API over `fetch`, so no SDK is installed. `from` is an address at a domain the Resend
23
+ * account verified; `baseURL` is another address that speaks Resend's API, such as a test's catcher.
24
+ * A refusal throws with the status alone: the address and the body stay out of the log.
25
+ */
26
+ export const resendMailer = ({ apiKey, from, baseURL }: { apiKey: string; from: string; baseURL: string }): Mailer => ({
27
+ send: async ({ to, subject, text, html }) => {
28
+ const response = await fetch(`${baseURL}/emails`, {
29
+ method: 'POST',
30
+ headers: { authorization: `Bearer ${apiKey}`, 'content-type': 'application/json' },
31
+ body: JSON.stringify({ from, to: [to], subject, text, html }),
32
+ })
33
+ if (!response.ok) throw new Error(`Resend refused a message: ${response.status} ${response.statusText}`)
34
+ },
35
+ })
36
+ // mithaq-admin 0.0.0 37f2bedf34183fb350814b0e9f4a1453fb4f5491d359794f443461f04bf81e9e
@@ -0,0 +1,111 @@
1
+ import type { AuditHandler } from 'mithaq'
2
+ import { same } from 'mithaq/admin'
3
+
4
+ /**
5
+ * The kayan this sink writes, which `./audit-log.ts` declares under the same
6
+ * name. A file of its own, apart from the kayan's, because the service target
7
+ * that holds this sink is built in `../mithaq.ts` and the kayan imports its
8
+ * `defineKayan` from there, so one file holding both would be a cycle.
9
+ */
10
+ export const auditLogName = 'auditLog'
11
+
12
+ /**
13
+ * One changed column: what it held before the write and what it holds after. A change one side of
14
+ * which was too long to keep whole is `truncated`: both sides are JSON text, the long one cut short.
15
+ */
16
+ export type Change = { from: unknown; to: unknown; truncated?: true }
17
+
18
+ /** How many characters of one side of a change an entry keeps: one large json value never swells the log. */
19
+ export const keptLength = 2000
20
+
21
+ const textOf = (value: unknown) => {
22
+ if (value === null) return null
23
+ // By code point, so the cut never leaves half of a surrogate pair, which Postgres refuses.
24
+ const points = [...JSON.stringify(value)]
25
+ return points.length > keptLength ? `${points.slice(0, keptLength).join('')}…` : points.join('')
26
+ }
27
+ // By code point, as `textOf` cuts, so a side is cut exactly when it is marked cut.
28
+ const tooLong = (value: unknown) => [...(JSON.stringify(value) ?? '')].length > keptLength
29
+
30
+ /**
31
+ * What the Activity panel draws as a dash: absent, `null`, an empty string or
32
+ * an empty list. A column that is one of these before and after changed
33
+ * nothing a reader could see.
34
+ */
35
+ export const empty = (value: unknown) => value === undefined || value === null || value === '' || (Array.isArray(value) && !value.length)
36
+
37
+ /**
38
+ * The columns whose value differs, each with its value on both sides and
39
+ * `null` for a side that held none. A column empty on both sides is no change,
40
+ * so a create lists only what it set and a delete only what the row held. A
41
+ * side longer than `keptLength` is kept as the start of its JSON.
42
+ */
43
+ export const changesOf = (before: Readonly<Record<string, unknown>>, after: Readonly<Record<string, unknown>>) => {
44
+ const changes: Record<string, Change> = {}
45
+ for (const key of new Set([...Object.keys(before), ...Object.keys(after)])) {
46
+ if (same(before[key], after[key]) || (empty(before[key]) && empty(after[key]))) continue
47
+ const [from, to] = [before[key] ?? null, after[key] ?? null]
48
+ // Both sides as JSON text where either is cut, so the panel reads a cut change one way.
49
+ changes[key] = tooLong(from) || tooLong(to) ? { from: textOf(from), to: textOf(to), truncated: true } : { from, to }
50
+ }
51
+ return changes
52
+ }
53
+
54
+ const rowOf = (value: unknown): Readonly<Record<string, unknown>> | undefined =>
55
+ value && typeof value === 'object' && !Array.isArray(value) && 'id' in value ? (value as Record<string, unknown>) : undefined
56
+
57
+ const stringOf = (value: unknown) => (typeof value === 'string' ? value : null)
58
+
59
+ /**
60
+ * The service target's audit sink: one `auditLog` row per row a write changed,
61
+ * written through the store and the transaction the write itself runs in, so
62
+ * the entry commits with the change or not at all. It goes to the store and
63
+ * not through an operation, because the log declares none that writes: this is
64
+ * the only thing that writes it.
65
+ *
66
+ * A bulk runs every row through the single-row write of its effect, which
67
+ * audits that row, and then audits itself with the page of rows as its
68
+ * result; that second call names no row and is passed over here. An update
69
+ * that moved nothing but the columns the runner bumps on every write declines
70
+ * its own row. A delete of a kayan with no soft-delete field removes the row,
71
+ * so its change is every column to nothing; a soft delete hands back the row it
72
+ * marked, and its change is the mark.
73
+ *
74
+ * The actor is the person the write ran as, and `impersonatedBy` the real one
75
+ * behind an impersonated session. `mithaq rbac assign` hands the sink no person
76
+ * but the command and the operating-system user, and the entry names both. The
77
+ * organization is the one the row belongs to, which is the active one inside a
78
+ * managed write and the one the log is read in; a shared kayan writes no entry.
79
+ */
80
+ export const auditSink: AuditHandler = async ({ effect, kayan, operation, dataBefore, dataAfter, store, tx, user }) => {
81
+ // A shared kayan writes no entry: the log is read in one organization alone,
82
+ // so an entry of none is one nobody reads, and the shared kayan are the
83
+ // provider's generated auth tables and local auth's accounts, whose rows hold
84
+ // tokens and hashes a log should never keep.
85
+ const owner = kayan.tenant
86
+ if (effect === 'read' || !store || kayan.name === auditLogName || !owner) return
87
+ const before = dataBefore ?? {}
88
+ const after = rowOf(dataAfter)
89
+ const record = after?.id ?? dataBefore?.id
90
+ if (record === undefined || record === null) return
91
+ const removed = effect === 'delete' && !kayan.softDelete
92
+ const changes = changesOf(before, removed ? {} : (after ?? {}))
93
+ if (effect === 'update' && Object.keys(changes).every((name) => kayan.fields[name]?.rules?.nowOnUpdate)) return
94
+ const actor = user && typeof user === 'object' ? (user as { id?: unknown; impersonatedBy?: unknown; command?: unknown; osUser?: unknown }) : undefined
95
+ await store.insert({ tx, kayan: auditLogName, data: {
96
+ createdAt: new Date(),
97
+ organization: stringOf(after?.[owner] ?? before[owner]),
98
+ kayan: kayan.name,
99
+ record: String(record),
100
+ // An update that took a draft to published is named a publish, as Payload's
101
+ // own history says it, so the Activity filters it apart from an edit.
102
+ operation: operation === 'update' && changes._status?.to === 'published' ? 'publish' : operation,
103
+ actor: stringOf(actor?.id) ?? (typeof actor?.command === 'string' ? `${actor.command} (${String(actor.osUser)})` : null),
104
+ impersonatedBy: stringOf(actor?.impersonatedBy),
105
+ // The object itself, a jsonb value. The log is never ordered by it, and
106
+ // searched by it only within the kayan whose columns its reader may be
107
+ // told of (`reachOf`), since a match in any other would tell a value its
108
+ // reader may not read.
109
+ changes,
110
+ } })
111
+ }
@@ -0,0 +1,108 @@
1
+ import type { SystemRoles } from 'mithaq'
2
+
3
+ /** One kayan read whole on the backoffice: its list, its count, one row, and each column named. */
4
+ const reads = (kayan: string, fields: readonly string[]) => [`${kayan}.find`, `${kayan}.count`, `${kayan}.findByID`, ...fields.map((field) => `${kayan}.fields.${field}`)]
5
+ const stamps = ['id', 'createdAt', 'updatedAt'] as const
6
+
7
+ /**
8
+ * The columns of a kayan the backoffice lists that no staff role reads, each
9
+ * one a decision written down rather than a name left out. No backoffice role
10
+ * reads one, `super_admin`'s `'*'` included: the backoffice's row closes each
11
+ * by name whatever keys the actor holds.
12
+ */
13
+ export const secret: readonly `${string}.fields.${string}`[] = [
14
+ // add-on: courses
15
+ // The code a live course's learners join with, a tenant's secret.
16
+ 'courses.fields.accessCode',
17
+ // end add-on: courses
18
+ ]
19
+
20
+ /**
21
+ * The kayan the backoffice lists that no staff role reads at all, a decision
22
+ * about the whole kayan rather than about its columns: no staff role holds a
23
+ * key of one, and only `super_admin`'s `'*'` lists it. `localUsers` is read
24
+ * and verified by a super admin alone, which the kayan's own access says, so a
25
+ * key here would grant support or explorer nothing, and its protected columns
26
+ * are closed to every reader by the provider.
27
+ */
28
+ export const superAdminOnly = ['localUsers'] as const
29
+
30
+ /** The people, the organizations and their members, which the provider owns. */
31
+ const people = [
32
+ ...reads('user', [...stamps, 'name', 'email', 'emailVerified', 'image', 'role', 'banned', 'banReason', 'banExpires']),
33
+ ...reads('organization', [...stamps, 'name', 'names', 'slug', 'logo', 'metadata']),
34
+ ...reads('member', [...stamps, 'organizationId', 'userId', 'role', 'lastSeenAt']),
35
+ ]
36
+
37
+ /** Each tenant's business, across every organization. */
38
+ const business = [
39
+ // add-on: courses
40
+ // Every column but the one `secret` names.
41
+ ...reads('courses', [...stamps, 'organization', 'title', 'summary', 'details', 'cover', 'fee', 'currency', 'level', 'notifications', 'delivery', 'audience', 'archived', 'topics', 'opensOn', 'archiveReason', 'tags', 'scheduledSessions', '_status', 'selfEnroll']),
42
+ // A course's versions, each cut to the columns above, so staff read a
43
+ // draft's history across every organization and restore none of it: no
44
+ // staff role but `super_admin`'s `'*'` holds `courses.restoreVersion`.
45
+ 'courses.findVersions',
46
+ 'courses.findVersionByID',
47
+ ...reads('sessions', [...stamps, 'organization', 'course', 'startsAt', 'capacity', 'venue', 'format', 'archived', 'details', 'enrollments', 'archiveReason']),
48
+ ...reads('enrollments', [...stamps, 'organization', 'session', 'student', 'deletedAt']),
49
+ // end add-on: courses
50
+ ...reads('tags', [...stamps, 'organization', 'name']),
51
+ ]
52
+
53
+ /** Who holds what inside each tenant: its role rows and its assignments. */
54
+ const grants: string[] = [
55
+ // add-on: roles
56
+ ...reads('roles', [...stamps, 'organization', 'name', 'label', 'permissions']),
57
+ ...reads('memberRoles', [...stamps, 'organization', 'user', 'role']),
58
+ // end add-on: roles
59
+ ]
60
+
61
+ /**
62
+ * The staff history: who impersonated whom, set whose backoffice role and banned whom. `super_admin`
63
+ * reads it by its `'*'` and `explorer` by these keys; never support, whose impersonations it records.
64
+ */
65
+ const history: string[] = [
66
+ // add-on: audit-log
67
+ ...reads('backofficeLog', ['id', 'createdAt', 'action', 'actor', 'actorEmail', 'target', 'targetEmail', 'previousRole', 'role', 'banReason']),
68
+ // end add-on: audit-log
69
+ ]
70
+
71
+ /**
72
+ * The backoffice roles: staff above every tenant, held on Better Auth's own
73
+ * `user.role` rather than on a tenant's assignment. The column may hold several,
74
+ * comma-separated, which is how the admin plugin reads it.
75
+ *
76
+ * Each holds catalog keys the way a tenant role does, and the access target
77
+ * holds them only in the backoffice app: in the tenant app an actor holds its
78
+ * membership's roles in its active organization and nothing of these. The
79
+ * admin plugin's own roles are built from the same lists below, so the routes
80
+ * that set a role, ban and impersonate refuse what the list does not name.
81
+ *
82
+ * `organization.rename` is this template's own statement, added beside these in
83
+ * `asas.ts`. Every read is a kayan's own keys: its list, its count and one row,
84
+ * and one key per column, since a backoffice role reads a column only where it
85
+ * holds that column's key. A project adds its own backoffice role, `billing` say,
86
+ * as one more entry here.
87
+ */
88
+ export const backofficeRoles = {
89
+ /** Every key, and so the only role that grants a backoffice role, `user.set-role`, and renames an organization, `organization.rename`. */
90
+ super_admin: {
91
+ label: { ar: 'مدير لوحة الإدارة الخلفية', en: 'Backoffice administrator' },
92
+ permissions: '*',
93
+ },
94
+ /**
95
+ * Reads the people, the organizations and each tenant's business, and
96
+ * impersonates a tenant's user. It bans no one, sets no role, renames no
97
+ * organization, and reads no tenant's roles: it sees those by impersonating.
98
+ */
99
+ support: {
100
+ label: { ar: 'الدعم', en: 'Support' },
101
+ permissions: ['screen.backoffice', 'user.impersonate', ...people, ...business],
102
+ },
103
+ /** Reads everything on the backoffice, each tenant's roles and the staff history included, and changes nothing. */
104
+ explorer: {
105
+ label: { ar: 'مستكشف', en: 'Explorer' },
106
+ permissions: ['screen.backoffice', ...people, ...business, ...grants, ...history],
107
+ },
108
+ } as const satisfies SystemRoles