create-mithaq 0.0.0-stage → 0.1.1

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 (357) hide show
  1. package/LICENSE +22 -0
  2. package/LICENSES/payload.txt +22 -0
  3. package/LICENSES/ponytail.txt +21 -0
  4. package/index.js +127 -0
  5. package/package.json +19 -4
  6. package/template/apps/asas-platform/components.json +25 -0
  7. package/template/apps/asas-platform/gitignore +3 -0
  8. package/template/apps/asas-platform/package.json +55 -0
  9. package/template/apps/asas-platform/public/favicon.svg +1 -0
  10. package/template/apps/asas-platform/src/api.ts +147 -0
  11. package/template/apps/asas-platform/src/auth-frame.tsx +150 -0
  12. package/template/apps/asas-platform/src/components/mithaq/cells.tsx +112 -0
  13. package/template/apps/asas-platform/src/components/mithaq/controls.tsx +650 -0
  14. package/template/apps/asas-platform/src/components/mithaq/field-shell.tsx +53 -0
  15. package/template/apps/asas-platform/src/components/mithaq/filter-operators.ts +108 -0
  16. package/template/apps/asas-platform/src/components/mithaq/form.tsx +1467 -0
  17. package/template/apps/asas-platform/src/components/mithaq/list-columns.tsx +108 -0
  18. package/template/apps/asas-platform/src/components/mithaq/list-filters.tsx +368 -0
  19. package/template/apps/asas-platform/src/components/mithaq/list-pagination.tsx +153 -0
  20. package/template/apps/asas-platform/src/components/mithaq/list-query.ts +432 -0
  21. package/template/apps/asas-platform/src/components/mithaq/list-selection.tsx +74 -0
  22. package/template/apps/asas-platform/src/components/mithaq/list.tsx +865 -0
  23. package/template/apps/asas-platform/src/components/mithaq/nav.tsx +871 -0
  24. package/template/apps/asas-platform/src/components/mithaq/nodes.tsx +1024 -0
  25. package/template/apps/asas-platform/src/components/mithaq/relationship-cell.tsx +127 -0
  26. package/template/apps/asas-platform/src/components/mithaq/relationship-control.tsx +400 -0
  27. package/template/apps/asas-platform/src/components/mithaq/rich-text-nodes.ts +6 -0
  28. package/template/apps/asas-platform/src/components/mithaq/rich-text.tsx +1083 -0
  29. package/template/apps/asas-platform/src/components/mithaq/upload.tsx +686 -0
  30. package/template/apps/asas-platform/src/components/ui/accordion.tsx +74 -0
  31. package/template/apps/asas-platform/src/components/ui/alert-dialog.tsx +185 -0
  32. package/template/apps/asas-platform/src/components/ui/avatar.tsx +106 -0
  33. package/template/apps/asas-platform/src/components/ui/badge.tsx +48 -0
  34. package/template/apps/asas-platform/src/components/ui/breadcrumb.tsx +124 -0
  35. package/template/apps/asas-platform/src/components/ui/button-group.tsx +87 -0
  36. package/template/apps/asas-platform/src/components/ui/button.tsx +55 -0
  37. package/template/apps/asas-platform/src/components/ui/calendar.tsx +223 -0
  38. package/template/apps/asas-platform/src/components/ui/card.tsx +102 -0
  39. package/template/apps/asas-platform/src/components/ui/chart.tsx +371 -0
  40. package/template/apps/asas-platform/src/components/ui/checkbox.tsx +28 -0
  41. package/template/apps/asas-platform/src/components/ui/collapsible.tsx +19 -0
  42. package/template/apps/asas-platform/src/components/ui/combobox.tsx +308 -0
  43. package/template/apps/asas-platform/src/components/ui/dialog.tsx +158 -0
  44. package/template/apps/asas-platform/src/components/ui/direction.tsx +6 -0
  45. package/template/apps/asas-platform/src/components/ui/directional-icon.tsx +20 -0
  46. package/template/apps/asas-platform/src/components/ui/drawer.tsx +216 -0
  47. package/template/apps/asas-platform/src/components/ui/dropdown-menu.tsx +267 -0
  48. package/template/apps/asas-platform/src/components/ui/field.tsx +236 -0
  49. package/template/apps/asas-platform/src/components/ui/input-group.tsx +155 -0
  50. package/template/apps/asas-platform/src/components/ui/input-otp.tsx +84 -0
  51. package/template/apps/asas-platform/src/components/ui/input.tsx +19 -0
  52. package/template/apps/asas-platform/src/components/ui/label.tsx +19 -0
  53. package/template/apps/asas-platform/src/components/ui/popover.tsx +90 -0
  54. package/template/apps/asas-platform/src/components/ui/radio-group.tsx +37 -0
  55. package/template/apps/asas-platform/src/components/ui/resizable.tsx +47 -0
  56. package/template/apps/asas-platform/src/components/ui/scroll-area.tsx +54 -0
  57. package/template/apps/asas-platform/src/components/ui/select.tsx +204 -0
  58. package/template/apps/asas-platform/src/components/ui/separator.tsx +24 -0
  59. package/template/apps/asas-platform/src/components/ui/sheet.tsx +139 -0
  60. package/template/apps/asas-platform/src/components/ui/sidebar.tsx +714 -0
  61. package/template/apps/asas-platform/src/components/ui/skeleton.tsx +13 -0
  62. package/template/apps/asas-platform/src/components/ui/slider.tsx +55 -0
  63. package/template/apps/asas-platform/src/components/ui/spinner.tsx +10 -0
  64. package/template/apps/asas-platform/src/components/ui/switch.tsx +29 -0
  65. package/template/apps/asas-platform/src/components/ui/table.tsx +115 -0
  66. package/template/apps/asas-platform/src/components/ui/tabs.tsx +79 -0
  67. package/template/apps/asas-platform/src/components/ui/textarea.tsx +17 -0
  68. package/template/apps/asas-platform/src/components/ui/toggle-group.tsx +89 -0
  69. package/template/apps/asas-platform/src/components/ui/toggle.tsx +44 -0
  70. package/template/apps/asas-platform/src/components/ui/tooltip.tsx +63 -0
  71. package/template/apps/asas-platform/src/copy.ts +1 -0
  72. package/template/apps/asas-platform/src/hooks/use-mobile.ts +19 -0
  73. package/template/apps/asas-platform/src/lib/utils.ts +1 -0
  74. package/template/apps/asas-platform/src/links.ts +38 -0
  75. package/template/apps/asas-platform/src/locale-switch.tsx +26 -0
  76. package/template/apps/asas-platform/src/redirect.ts +1 -0
  77. package/template/apps/asas-platform/src/request-headers.ts +11 -0
  78. package/template/apps/asas-platform/src/routeTree.gen.ts +181 -0
  79. package/template/apps/asas-platform/src/router.tsx +7 -0
  80. package/template/apps/asas-platform/src/routes/(screens)/platform/files.ts +4 -0
  81. package/template/apps/asas-platform/src/routes/(screens)/platform/screen.json +3609 -0
  82. package/template/apps/asas-platform/src/routes/(screens)/platform/types.ts +145 -0
  83. package/template/apps/asas-platform/src/routes/(screens)/platform.$.tsx +17 -0
  84. package/template/apps/asas-platform/src/routes/(screens)/platform.index.tsx +10 -0
  85. package/template/apps/asas-platform/src/routes/(screens)/platform.tsx +13 -0
  86. package/template/apps/asas-platform/src/routes/__root.tsx +95 -0
  87. package/template/apps/asas-platform/src/routes/api/$.ts +30 -0
  88. package/template/apps/asas-platform/src/routes/auth.sign-in.tsx +72 -0
  89. package/template/apps/asas-platform/src/routes/unauthorized.tsx +122 -0
  90. package/template/apps/asas-platform/src/screen-form.tsx +34 -0
  91. package/template/apps/asas-platform/src/screen-list.tsx +139 -0
  92. package/template/apps/asas-platform/src/screen-names.ts +10 -0
  93. package/template/apps/asas-platform/src/screens.tsx +868 -0
  94. package/template/apps/asas-platform/src/server.ts +20 -0
  95. package/template/apps/asas-platform/src/slide.ts +22 -0
  96. package/template/apps/asas-platform/src/start.ts +42 -0
  97. package/template/apps/asas-platform/src/styles.css +200 -0
  98. package/template/apps/asas-platform/src/theme.ts +2 -0
  99. package/template/apps/asas-platform/tsconfig.json +19 -0
  100. package/template/apps/asas-platform/vite.config.ts +26 -0
  101. package/template/apps/asas-tanstack/components.json +25 -0
  102. package/template/apps/asas-tanstack/gitignore +3 -0
  103. package/template/apps/asas-tanstack/package.json +56 -0
  104. package/template/apps/asas-tanstack/public/favicon.svg +1 -0
  105. package/template/apps/asas-tanstack/src/api.ts +147 -0
  106. package/template/apps/asas-tanstack/src/auth-frame.tsx +150 -0
  107. package/template/apps/asas-tanstack/src/components/mithaq/cells.tsx +112 -0
  108. package/template/apps/asas-tanstack/src/components/mithaq/controls.tsx +650 -0
  109. package/template/apps/asas-tanstack/src/components/mithaq/field-shell.tsx +53 -0
  110. package/template/apps/asas-tanstack/src/components/mithaq/filter-operators.ts +108 -0
  111. package/template/apps/asas-tanstack/src/components/mithaq/form.tsx +1467 -0
  112. package/template/apps/asas-tanstack/src/components/mithaq/list-columns.tsx +108 -0
  113. package/template/apps/asas-tanstack/src/components/mithaq/list-filters.tsx +368 -0
  114. package/template/apps/asas-tanstack/src/components/mithaq/list-pagination.tsx +153 -0
  115. package/template/apps/asas-tanstack/src/components/mithaq/list-query.ts +432 -0
  116. package/template/apps/asas-tanstack/src/components/mithaq/list-selection.tsx +74 -0
  117. package/template/apps/asas-tanstack/src/components/mithaq/list.tsx +865 -0
  118. package/template/apps/asas-tanstack/src/components/mithaq/nav.tsx +871 -0
  119. package/template/apps/asas-tanstack/src/components/mithaq/nodes.tsx +1024 -0
  120. package/template/apps/asas-tanstack/src/components/mithaq/relationship-cell.tsx +127 -0
  121. package/template/apps/asas-tanstack/src/components/mithaq/relationship-control.tsx +400 -0
  122. package/template/apps/asas-tanstack/src/components/mithaq/rich-text-nodes.ts +6 -0
  123. package/template/apps/asas-tanstack/src/components/mithaq/rich-text.tsx +1083 -0
  124. package/template/apps/asas-tanstack/src/components/mithaq/upload.tsx +686 -0
  125. package/template/apps/asas-tanstack/src/components/ui/accordion.tsx +10 -0
  126. package/template/apps/asas-tanstack/src/components/ui/alert-dialog.tsx +173 -0
  127. package/template/apps/asas-tanstack/src/components/ui/avatar.tsx +104 -0
  128. package/template/apps/asas-tanstack/src/components/ui/badge.tsx +48 -0
  129. package/template/apps/asas-tanstack/src/components/ui/breadcrumb.tsx +125 -0
  130. package/template/apps/asas-tanstack/src/components/ui/button-group.tsx +87 -0
  131. package/template/apps/asas-tanstack/src/components/ui/button.tsx +56 -0
  132. package/template/apps/asas-tanstack/src/components/ui/calendar.tsx +223 -0
  133. package/template/apps/asas-tanstack/src/components/ui/card.tsx +94 -0
  134. package/template/apps/asas-tanstack/src/components/ui/chart.tsx +369 -0
  135. package/template/apps/asas-tanstack/src/components/ui/checkbox.tsx +27 -0
  136. package/template/apps/asas-tanstack/src/components/ui/collapsible.tsx +19 -0
  137. package/template/apps/asas-tanstack/src/components/ui/combobox.tsx +300 -0
  138. package/template/apps/asas-tanstack/src/components/ui/dialog.tsx +149 -0
  139. package/template/apps/asas-tanstack/src/components/ui/direction.tsx +5 -0
  140. package/template/apps/asas-tanstack/src/components/ui/directional-icon.tsx +20 -0
  141. package/template/apps/asas-tanstack/src/components/ui/drawer.tsx +215 -0
  142. package/template/apps/asas-tanstack/src/components/ui/dropdown-menu.tsx +267 -0
  143. package/template/apps/asas-tanstack/src/components/ui/field.tsx +225 -0
  144. package/template/apps/asas-tanstack/src/components/ui/input-group.tsx +147 -0
  145. package/template/apps/asas-tanstack/src/components/ui/input-otp.tsx +20 -0
  146. package/template/apps/asas-tanstack/src/components/ui/input.tsx +20 -0
  147. package/template/apps/asas-tanstack/src/components/ui/label.tsx +18 -0
  148. package/template/apps/asas-tanstack/src/components/ui/popover.tsx +50 -0
  149. package/template/apps/asas-tanstack/src/components/ui/radio-group.tsx +10 -0
  150. package/template/apps/asas-tanstack/src/components/ui/resizable.tsx +47 -0
  151. package/template/apps/asas-tanstack/src/components/ui/scroll-area.tsx +52 -0
  152. package/template/apps/asas-tanstack/src/components/ui/select.tsx +190 -0
  153. package/template/apps/asas-tanstack/src/components/ui/separator.tsx +23 -0
  154. package/template/apps/asas-tanstack/src/components/ui/sheet.tsx +127 -0
  155. package/template/apps/asas-tanstack/src/components/ui/sidebar.tsx +716 -0
  156. package/template/apps/asas-tanstack/src/components/ui/skeleton.tsx +13 -0
  157. package/template/apps/asas-tanstack/src/components/ui/slider.tsx +13 -0
  158. package/template/apps/asas-tanstack/src/components/ui/spinner.tsx +11 -0
  159. package/template/apps/asas-tanstack/src/components/ui/switch.tsx +8 -0
  160. package/template/apps/asas-tanstack/src/components/ui/table.tsx +119 -0
  161. package/template/apps/asas-tanstack/src/components/ui/tabs.tsx +80 -0
  162. package/template/apps/asas-tanstack/src/components/ui/textarea.tsx +18 -0
  163. package/template/apps/asas-tanstack/src/components/ui/toggle-group.tsx +8 -0
  164. package/template/apps/asas-tanstack/src/components/ui/toggle.tsx +44 -0
  165. package/template/apps/asas-tanstack/src/components/ui/tooltip.tsx +68 -0
  166. package/template/apps/asas-tanstack/src/copy.ts +870 -0
  167. package/template/apps/asas-tanstack/src/hooks/use-mobile.ts +19 -0
  168. package/template/apps/asas-tanstack/src/lib/utils.ts +6 -0
  169. package/template/apps/asas-tanstack/src/links.ts +38 -0
  170. package/template/apps/asas-tanstack/src/locale-switch.tsx +26 -0
  171. package/template/apps/asas-tanstack/src/quiet-abort.ts +25 -0
  172. package/template/apps/asas-tanstack/src/redirect.ts +20 -0
  173. package/template/apps/asas-tanstack/src/request-headers.ts +11 -0
  174. package/template/apps/asas-tanstack/src/routeTree.gen.ts +382 -0
  175. package/template/apps/asas-tanstack/src/router.tsx +7 -0
  176. package/template/apps/asas-tanstack/src/routes/(screens)/dashboard/files.ts +4 -0
  177. package/template/apps/asas-tanstack/src/routes/(screens)/dashboard/screen.json +3883 -0
  178. package/template/apps/asas-tanstack/src/routes/(screens)/dashboard/types.ts +158 -0
  179. package/template/apps/asas-tanstack/src/routes/(screens)/dashboard.$.tsx +17 -0
  180. package/template/apps/asas-tanstack/src/routes/(screens)/dashboard.index.tsx +10 -0
  181. package/template/apps/asas-tanstack/src/routes/(screens)/dashboard.tsx +13 -0
  182. package/template/apps/asas-tanstack/src/routes/(screens)/portal/files.ts +4 -0
  183. package/template/apps/asas-tanstack/src/routes/(screens)/portal/screen.json +1800 -0
  184. package/template/apps/asas-tanstack/src/routes/(screens)/portal/types.ts +92 -0
  185. package/template/apps/asas-tanstack/src/routes/(screens)/portal.$.tsx +17 -0
  186. package/template/apps/asas-tanstack/src/routes/(screens)/portal.index.tsx +10 -0
  187. package/template/apps/asas-tanstack/src/routes/(screens)/portal.tsx +13 -0
  188. package/template/apps/asas-tanstack/src/routes/__root.tsx +99 -0
  189. package/template/apps/asas-tanstack/src/routes/api/$.ts +11 -0
  190. package/template/apps/asas-tanstack/src/routes/auth.accept-invitation.tsx +101 -0
  191. package/template/apps/asas-tanstack/src/routes/auth.forgot-password.tsx +94 -0
  192. package/template/apps/asas-tanstack/src/routes/auth.handoff.ts +39 -0
  193. package/template/apps/asas-tanstack/src/routes/auth.reset-password.tsx +109 -0
  194. package/template/apps/asas-tanstack/src/routes/auth.sign-in.tsx +86 -0
  195. package/template/apps/asas-tanstack/src/routes/auth.sign-up.tsx +122 -0
  196. package/template/apps/asas-tanstack/src/routes/index.tsx +142 -0
  197. package/template/apps/asas-tanstack/src/routes/unauthorized.tsx +122 -0
  198. package/template/apps/asas-tanstack/src/screen-form.tsx +34 -0
  199. package/template/apps/asas-tanstack/src/screen-list.tsx +139 -0
  200. package/template/apps/asas-tanstack/src/screen-names.ts +10 -0
  201. package/template/apps/asas-tanstack/src/screens.tsx +868 -0
  202. package/template/apps/asas-tanstack/src/server.ts +16 -0
  203. package/template/apps/asas-tanstack/src/slide.ts +22 -0
  204. package/template/apps/asas-tanstack/src/start.ts +57 -0
  205. package/template/apps/asas-tanstack/src/styles.css +159 -0
  206. package/template/apps/asas-tanstack/src/theme.ts +5 -0
  207. package/template/apps/asas-tanstack/tsconfig.json +19 -0
  208. package/template/apps/asas-tanstack/vite.config.ts +28 -0
  209. package/template/package.json +16 -0
  210. package/template/templates/asas/.env.example +16 -0
  211. package/template/templates/asas/components.json +22 -0
  212. package/template/templates/asas/mithaq.hints.json +25 -0
  213. package/template/templates/asas/package.json +38 -0
  214. package/template/templates/asas/src/asas.ts +93 -0
  215. package/template/templates/asas/src/audit-log/audit-log.ts +187 -0
  216. package/template/templates/asas/src/audit-log/sink.ts +89 -0
  217. package/template/templates/asas/src/audit-log/ui/activity.tsx +243 -0
  218. package/template/templates/asas/src/courses/courses.ts +612 -0
  219. package/template/templates/asas/src/courses/ui/archive-button.tsx +25 -0
  220. package/template/templates/asas/src/courses/ui/fee-cell.tsx +19 -0
  221. package/template/templates/asas/src/courses/ui/no-courses-on-offer.tsx +11 -0
  222. package/template/templates/asas/src/courses/ui/no-courses.tsx +13 -0
  223. package/template/templates/asas/src/courses/ui/on-offer.ts +6 -0
  224. package/template/templates/asas/src/courses/ui/status-cell.tsx +31 -0
  225. package/template/templates/asas/src/enrollments/enrollments.ts +137 -0
  226. package/template/templates/asas/src/enrollments/ui/session-cell.tsx +65 -0
  227. package/template/templates/asas/src/kayan/auth/user/account.access.ts +8 -0
  228. package/template/templates/asas/src/kayan/auth/user/account.ts +31 -0
  229. package/template/templates/asas/src/kayan/auth/user/index.ts +18 -0
  230. package/template/templates/asas/src/kayan/auth/user/invitation.access.ts +4 -0
  231. package/template/templates/asas/src/kayan/auth/user/invitation.ts +27 -0
  232. package/template/templates/asas/src/kayan/auth/user/member.access.ts +10 -0
  233. package/template/templates/asas/src/kayan/auth/user/member.screens.ts +37 -0
  234. package/template/templates/asas/src/kayan/auth/user/member.ts +25 -0
  235. package/template/templates/asas/src/kayan/auth/user/organization.access.ts +20 -0
  236. package/template/templates/asas/src/kayan/auth/user/organization.screens.ts +37 -0
  237. package/template/templates/asas/src/kayan/auth/user/organization.ts +24 -0
  238. package/template/templates/asas/src/kayan/auth/user/organizationRole.access.ts +8 -0
  239. package/template/templates/asas/src/kayan/auth/user/organizationRole.ts +25 -0
  240. package/template/templates/asas/src/kayan/auth/user/session.access.ts +8 -0
  241. package/template/templates/asas/src/kayan/auth/user/session.ts +28 -0
  242. package/template/templates/asas/src/kayan/auth/user/user.access.ts +29 -0
  243. package/template/templates/asas/src/kayan/auth/user/user.screens.ts +45 -0
  244. package/template/templates/asas/src/kayan/auth/user/user.ts +31 -0
  245. package/template/templates/asas/src/kayan/auth/user/verification.access.ts +8 -0
  246. package/template/templates/asas/src/kayan/auth/user/verification.ts +23 -0
  247. package/template/templates/asas/src/local-users/local-users.ts +143 -0
  248. package/template/templates/asas/src/media/media.ts +137 -0
  249. package/template/templates/asas/src/media/ui/preview.tsx +143 -0
  250. package/template/templates/asas/src/member-roles/member-roles.ts +207 -0
  251. package/template/templates/asas/src/messages/account.json +106 -0
  252. package/template/templates/asas/src/messages/auditLog.json +186 -0
  253. package/template/templates/asas/src/messages/courses.json +270 -0
  254. package/template/templates/asas/src/messages/enrollments.json +42 -0
  255. package/template/templates/asas/src/messages/invitation.json +66 -0
  256. package/template/templates/asas/src/messages/localUsers.json +42 -0
  257. package/template/templates/asas/src/messages/media.json +66 -0
  258. package/template/templates/asas/src/messages/member.json +42 -0
  259. package/template/templates/asas/src/messages/memberRoles.json +42 -0
  260. package/template/templates/asas/src/messages/mithaq.json +170 -0
  261. package/template/templates/asas/src/messages/organization.json +50 -0
  262. package/template/templates/asas/src/messages/organizationRole.json +50 -0
  263. package/template/templates/asas/src/messages/roles.json +58 -0
  264. package/template/templates/asas/src/messages/session.json +82 -0
  265. package/template/templates/asas/src/messages/sessions.json +106 -0
  266. package/template/templates/asas/src/messages/settings.json +66 -0
  267. package/template/templates/asas/src/messages/tags.json +38 -0
  268. package/template/templates/asas/src/messages/user.json +90 -0
  269. package/template/templates/asas/src/messages/verification.json +50 -0
  270. package/template/templates/asas/src/migrations/20260920080701_parched_tombstone/migration.sql +177 -0
  271. package/template/templates/asas/src/migrations/20260920080701_parched_tombstone/snapshot.json +2169 -0
  272. package/template/templates/asas/src/migrations/20260920230505_zippy_ben_urich/migration.sql +1 -0
  273. package/template/templates/asas/src/migrations/20260920230505_zippy_ben_urich/snapshot.json +2182 -0
  274. package/template/templates/asas/src/migrations/20260920231219_slim_retro_girl/migration.sql +20 -0
  275. package/template/templates/asas/src/migrations/20260920231219_slim_retro_girl/snapshot.json +2409 -0
  276. package/template/templates/asas/src/migrations/20260922082945_military_ironclad/migration.sql +15 -0
  277. package/template/templates/asas/src/migrations/20260922082945_military_ironclad/snapshot.json +2627 -0
  278. package/template/templates/asas/src/migrations/20260922112630_bitter_chat/migration.sql +5 -0
  279. package/template/templates/asas/src/migrations/20260922112630_bitter_chat/snapshot.json +2691 -0
  280. package/template/templates/asas/src/migrations/20260923235915_foamy_rachel_grey/migration.sql +8 -0
  281. package/template/templates/asas/src/migrations/20260923235915_foamy_rachel_grey/snapshot.json +2795 -0
  282. package/template/templates/asas/src/migrations/20260926145709_clean_the_initiative/migration.sql +3 -0
  283. package/template/templates/asas/src/migrations/20260926145709_clean_the_initiative/snapshot.json +2834 -0
  284. package/template/templates/asas/src/migrations/20260927212525_bent_prowler/migration.sql +9 -0
  285. package/template/templates/asas/src/migrations/20260927212525_bent_prowler/snapshot.json +2870 -0
  286. package/template/templates/asas/src/migrations/20260927223441_tranquil_bromley/migration.sql +1 -0
  287. package/template/templates/asas/src/migrations/20260927223441_tranquil_bromley/snapshot.json +2881 -0
  288. package/template/templates/asas/src/migrations/20260928030047_outgoing_wraith/migration.sql +15 -0
  289. package/template/templates/asas/src/migrations/20260928030047_outgoing_wraith/snapshot.json +3073 -0
  290. package/template/templates/asas/src/migrations/20260929024407_grey_morlun/migration.sql +21 -0
  291. package/template/templates/asas/src/migrations/20260929024407_grey_morlun/snapshot.json +3098 -0
  292. package/template/templates/asas/src/migrations/20260929130530_bored_jackal/migration.sql +21 -0
  293. package/template/templates/asas/src/migrations/20260929130530_bored_jackal/snapshot.json +3124 -0
  294. package/template/templates/asas/src/migrations/20260930102924_material_newton_destine/migration.sql +32 -0
  295. package/template/templates/asas/src/migrations/20260930102924_material_newton_destine/snapshot.json +3324 -0
  296. package/template/templates/asas/src/migrations/20261003022353_clumsy_taskmaster/migration.sql +2 -0
  297. package/template/templates/asas/src/migrations/20261003022353_clumsy_taskmaster/snapshot.json +3350 -0
  298. package/template/templates/asas/src/migrations/20261003143249_premium_red_shift/migration.sql +21 -0
  299. package/template/templates/asas/src/migrations/20261003143249_premium_red_shift/snapshot.json +3635 -0
  300. package/template/templates/asas/src/migrations/20261008215349_square_norrin_radd/migration.sql +8 -0
  301. package/template/templates/asas/src/migrations/20261008215349_square_norrin_radd/snapshot.json +3635 -0
  302. package/template/templates/asas/src/migrations/20261008230204_many_scarecrow/migration.sql +18 -0
  303. package/template/templates/asas/src/migrations/20261008230204_many_scarecrow/snapshot.json +3635 -0
  304. package/template/templates/asas/src/migrations/20261010005521_elite_giant_girl/migration.sql +18 -0
  305. package/template/templates/asas/src/migrations/20261010005521_elite_giant_girl/snapshot.json +3635 -0
  306. package/template/templates/asas/src/migrations/20261010120822_needy_thunderbolt/migration.sql +11 -0
  307. package/template/templates/asas/src/migrations/20261010120822_needy_thunderbolt/snapshot.json +3667 -0
  308. package/template/templates/asas/src/migrations/20261012000752_organization_ownership/migration.sql +48 -0
  309. package/template/templates/asas/src/migrations/20261012000752_organization_ownership/snapshot.json +3891 -0
  310. package/template/templates/asas/src/migrations/drizzle.config.ts +11 -0
  311. package/template/templates/asas/src/migrations/schema.ts +29 -0
  312. package/template/templates/asas/src/mithaq.ts +406 -0
  313. package/template/templates/asas/src/pages.ts +74 -0
  314. package/template/templates/asas/src/platform-http.ts +96 -0
  315. package/template/templates/asas/src/rich-text-nodes.ts +11 -0
  316. package/template/templates/asas/src/roles/grants.ts +47 -0
  317. package/template/templates/asas/src/roles/roles.ts +184 -0
  318. package/template/templates/asas/src/roles/ui/role-form.tsx +226 -0
  319. package/template/templates/asas/src/roles/ui/roles.tsx +166 -0
  320. package/template/templates/asas/src/roles.ts +16 -0
  321. package/template/templates/asas/src/seed.ts +325 -0
  322. package/template/templates/asas/src/sessions/sessions.ts +155 -0
  323. package/template/templates/asas/src/settings/settings.ts +69 -0
  324. package/template/templates/asas/src/statements.ts +11 -0
  325. package/template/templates/asas/src/steps/expected-updated-at.ts +41 -0
  326. package/template/templates/asas/src/storage/defaults.ts +156 -0
  327. package/template/templates/asas/src/storage/index.ts +95 -0
  328. package/template/templates/asas/src/storage/local.ts +143 -0
  329. package/template/templates/asas/src/storage/r2.ts +5 -0
  330. package/template/templates/asas/src/storage/s3.ts +91 -0
  331. package/template/templates/asas/src/storage/uploadthing.ts +59 -0
  332. package/template/templates/asas/src/tags/tags.ts +80 -0
  333. package/template/templates/asas/src/tenant-access.ts +221 -0
  334. package/template/templates/asas/src/tenant-roles.ts +262 -0
  335. package/template/templates/asas/src/ui/app-shell.tsx +260 -0
  336. package/template/templates/asas/src/ui/kpi-card.tsx +25 -0
  337. package/template/templates/asas/src/ui/record-page.tsx +50 -0
  338. package/template/templates/asas/src/ui/rich-text-nodes.tsx +15 -0
  339. package/template/templates/asas/src/ui/screen-nav.tsx +28 -0
  340. package/template/templates/asas/src/ui/session-labels.tsx +36 -0
  341. package/template/templates/asas/src/ui/table-page.tsx +9 -0
  342. package/template/templates/asas/src/ui/top-shell.tsx +60 -0
  343. package/template/templates/asas/src/ui-dashboard/dashboard.tsx +294 -0
  344. package/template/templates/asas/src/ui-dashboard/members.tsx +630 -0
  345. package/template/templates/asas/src/ui-dashboard/theme-provider.tsx +10 -0
  346. package/template/templates/asas/src/ui-dashboard/versions.tsx +432 -0
  347. package/template/templates/asas/src/ui-platform/home.tsx +63 -0
  348. package/template/templates/asas/src/ui-platform/organization-actions.tsx +114 -0
  349. package/template/templates/asas/src/ui-platform/record-page.tsx +30 -0
  350. package/template/templates/asas/src/ui-platform/role-cells.tsx +52 -0
  351. package/template/templates/asas/src/ui-platform/user-actions.tsx +251 -0
  352. package/template/templates/asas/src/ui-portal/home.tsx +77 -0
  353. package/template/templates/asas/src/ui-portal/page.tsx +11 -0
  354. package/template/templates/auth/package.json +14 -0
  355. package/template/templates/auth/src/index.ts +690 -0
  356. package/template/templates/auth/src/platform-roles.ts +87 -0
  357. package/README.md +0 -3
@@ -0,0 +1,13 @@
1
+ // What the courses list draws when it holds no row, and what the board page
2
+ // draws by path: the same file under a registered name and an inline path.
3
+ import type { EmptyProps } from 'mithaq/admin'
4
+ import { useKayan, useLocale } from 'mithaq/admin/react'
5
+
6
+ export function NoCourses({ page }: EmptyProps) {
7
+ const { t } = useLocale()
8
+ return (
9
+ <p data-empty={`${useKayan().name}${page.path}`} className="text-muted-foreground text-sm">
10
+ {t('courses.none', 'No courses yet.')}
11
+ </p>
12
+ )
13
+ }
@@ -0,0 +1,6 @@
1
+ // The courses the portal offers: every course not archived. The portal lists
2
+ // these alone, as its screen's `baseFilter` for the kayan, and its home counts
3
+ // these alone. Plain data in a file of its own, so the page that counts reads it
4
+ // without the kayan's declaration. A course with no `archived` value is not
5
+ // archived either.
6
+ export const onOffer = { or: [{ archived: { exists: false } }, { archived: { equals: false } }] }
@@ -0,0 +1,31 @@
1
+ // The list reads each row's latest draft, so a published course whose newest version is a draft
2
+ // and a never-published course both arrive as `draft`. The plain read finds only courses with a
3
+ // published copy, which tells the two apart.
4
+ import { use } from 'react'
5
+ import { useQuery } from '@tanstack/react-query'
6
+ import type { CellProps, Row } from 'mithaq/admin'
7
+ import { ListContext, useClient, useLocale, useScreen } from 'mithaq/admin/react'
8
+ import { Badge } from '@/components/ui/badge'
9
+ import { noValue } from '@/components/mithaq/controls.tsx'
10
+ import { coursesOf } from '../../enrollments/ui/session-cell.tsx'
11
+
12
+ const pills = {
13
+ draft: 'bg-muted text-muted-foreground',
14
+ published: 'bg-emerald-500/15 text-emerald-700 dark:text-emerald-400',
15
+ changed: 'bg-amber-500/15 text-amber-700 dark:text-amber-400',
16
+ }
17
+ const words = { draft: 'Draft', published: 'Published', changed: 'Changed' }
18
+
19
+ export function StatusCell({ value, row }: CellProps) {
20
+ const client = useClient()
21
+ const { t } = useLocale()
22
+ const { maxLimit } = useScreen()
23
+ // Sorted, so the same rows in another order are the same query.
24
+ const ids = [...new Set((use(ListContext)?.page.docs ?? [row]).flatMap((one: Row) => (one._status === 'draft' ? [String(one.id)] : [])))].sort()
25
+ const { data: published, isError } = useQuery({ queryKey: ['status-cell', ids], queryFn: () => coursesOf({ client, ids, maxLimit }), enabled: ids.length > 0, staleTime: 30_000 })
26
+ if (value !== 'draft' && value !== 'published') return noValue
27
+ // Loading until the read answers, and a draft where it failed, which is what the row itself holds.
28
+ if (value === 'draft' && !published && !isError) return t('admin.loading', 'Loading…')
29
+ const status = value === 'draft' && published?.has(String(row.id)) ? 'changed' : value
30
+ return <Badge data-status={status} className={pills[status]}>{t(`version.${status}`, words[status])}</Badge>
31
+ }
@@ -0,0 +1,137 @@
1
+ import { defaultFields, defaultOperations, folderOf } from 'mithaq'
2
+ import { defineKayan, organization } from '../mithaq.ts'
3
+ import { readsPast, user as actor } from '../tenant-access.ts'
4
+ import { activity, appPage, danger, platformList, platformRecord, topPage } from '../pages.ts'
5
+ import { user } from '../kayan/auth/user/user.ts'
6
+ import { sessions } from '../sessions/sessions.ts'
7
+
8
+ const ui = folderOf(new URL('./ui/', import.meta.url))
9
+ const sessionCell = ui('session-cell.tsx#SessionCell')
10
+ const enrollment = [{ section: 'enrollment', label: { en: 'Enrollment', ar: 'التسجيل' }, fields: ['session', 'student'] }] as const
11
+
12
+ /**
13
+ * The seats an actor reads, edits and deletes, inside its active organization:
14
+ * every seat of its organization for an actor holding
15
+ * `enrollments.others` and for platform staff, its own seats alone for anyone
16
+ * else. That is the student, whose enrollment keys would otherwise reach every
17
+ * other student's seat, and a custom role a tenant writes without the key.
18
+ * Another's seat answers 404, as a missing row does.
19
+ *
20
+ * The id is read off the actor as an own key, and an actor carrying none reaches
21
+ * no seat. Every read answers from it, a session's join of its enrollments
22
+ * included.
23
+ */
24
+ const ownSeats = ({ user }: { user: unknown }) => {
25
+ if (readsPast(user, 'enrollments.others')) return true
26
+ const actor = user && typeof user === 'object' ? (user as Record<string, unknown>) : undefined
27
+ const id = actor && Object.hasOwn(actor, 'id') ? actor.id : undefined
28
+ return typeof id === 'string' && id ? { student: { equals: id } } : false
29
+ }
30
+
31
+ export const enrollments = defineKayan({
32
+ name: 'enrollments',
33
+ access: { read: ownSeats, update: ownSeats, delete: ownSeats },
34
+ fields: {
35
+ ...defaultFields,
36
+ organization,
37
+ // Read as the session's course and its start on every screen, where a
38
+ // session alone is titled by its start.
39
+ //
40
+ // Both are required, so no write makes a seat for nobody or in nothing, and
41
+ // a session or a person that holds one is not deleted under it: a required
42
+ // relationship restricts.
43
+ session: {
44
+ kind: 'relationship', label: { en: 'Session', ar: 'الحصة' }, relationTo: () => sessions,
45
+ rules: { required: true },
46
+ targets: { dashboard: { cell: sessionCell }, portal: { cell: sessionCell }, platform: { cell: sessionCell } },
47
+ },
48
+ // A create that names no student enrolls the person making it: the runner's
49
+ // defaults step fills it from the actor, and a field with a default is one a
50
+ // create may leave out, at the HTTP boundary too. A role without the column's
51
+ // key is refused the filled value, so today only the instructor's and the
52
+ // administrator's API create fills it; the student reads its own seats and
53
+ // creates none.
54
+ student: {
55
+ kind: 'relationship', label: { en: 'Student', ar: 'الطالب' }, relationTo: () => user,
56
+ rules: { required: true, defaultValue: actor.id },
57
+ },
58
+ /**
59
+ * The field the language names as its soft-delete mark, so a student who
60
+ * leaves a session is a row this kayan keeps rather than one it removes: an
61
+ * enrollment is the record that the student was there. It is the one kayan
62
+ * of this template that takes the mark. Nothing points at an enrollment, so
63
+ * no child outlives it, and nothing refers to it through a `restrict`
64
+ * foreign key that a delete would have to release. Nobody supplies the
65
+ * mark: the runner writes it at the delete.
66
+ *
67
+ * `session` and `student` are required, so each restricts, and a session or
68
+ * a person a marked enrollment still names is not deleted under it.
69
+ */
70
+ deletedAt: {
71
+ kind: 'datetime',
72
+ label: { en: 'Deleted at', ar: 'تاريخ الحذف' },
73
+ rules: { providedBy: { create: 'never', update: 'never' } },
74
+ },
75
+ },
76
+ targets: {
77
+ // Every organization's rows on the platform, read-only, and each row's
78
+ // record, which only reads. A record is titled by its student, a person the
79
+ // platform reads, where a session is titled only by its start.
80
+ platform: {
81
+ useAsTitle: 'student',
82
+ pages: {
83
+ ...platformList('/enrollments', { columns: ['session', 'student', 'organization', 'createdAt'], filters: ['organization', 'session', 'student'], sort: '-createdAt' }),
84
+ ...platformRecord({
85
+ path: '/enrollments/:id',
86
+ main: [{ section: 'enrollment', label: { en: 'Enrollment', ar: 'التسجيل' }, fields: ['session', 'student', 'deletedAt'] }],
87
+ }),
88
+ },
89
+ },
90
+ service: { operations: defaultOperations },
91
+ // Both screens list enrollments; the portal's create opens as a drawer over
92
+ // its list. Titled by the student on the dashboard, as on the platform, so a
93
+ // tab and a heading tell one seat from another; on the portal, where the
94
+ // student is most often the viewer, by the session.
95
+ dashboard: {
96
+ useAsTitle: 'student',
97
+ description: {
98
+ en: 'Which student holds a seat in which session.',
99
+ ar: 'أي طالب يشغل مقعدا في أي حصة.',
100
+ },
101
+ pages: {
102
+ // The same table every kayan's list is. Searched by the student's name,
103
+ // the one column of a person a tenant reads, and filtered by session,
104
+ // student and when the student enrolled, as the course list is.
105
+ list: { path: '/enrollments', page: appPage([{ layout: 'table', main: [{ list: {
106
+ columns: ['session', 'student', 'createdAt'],
107
+ filters: ['session', 'student', 'createdAt'],
108
+ search: ['student.name'],
109
+ } }] }]) },
110
+ create: { path: '/enrollments/new', page: appPage([{ layout: 'record', main: [{ form: enrollment }] }]) },
111
+ // The editor layout, and the one timestamp worth reading here, named and
112
+ // read-only: when the student enrolled, beside the organization it
113
+ // belongs to, read by its name.
114
+ edit: {
115
+ path: '/enrollments/:id',
116
+ page: appPage([{
117
+ layout: 'record',
118
+ main: [{ form: enrollment }],
119
+ side: [{ section: 'record', label: { en: 'Record', ar: 'السجل' }, fields: [{ field: 'organization', readOnly: true }, { field: 'createdAt', readOnly: true }] }, activity],
120
+ danger: danger(),
121
+ }]),
122
+ },
123
+ },
124
+ },
125
+ portal: {
126
+ useAsTitle: 'session',
127
+ pages: {
128
+ list: { path: '/enrollments', page: topPage([{ layout: 'page', main: [{ list: { columns: ['session', 'createdAt'] } }] }]) },
129
+ // The student too, as the dashboard draws it, so an instructor picks
130
+ // whom it enrolls. A role without `enrollments.fields.student` is not
131
+ // projected the field and is drawn the session alone.
132
+ create: { path: '/enrollments/new', mode: 'drawer', page: topPage([{ layout: 'page', main: [{ form: ['session', 'student'] }] }]) },
133
+ edit: { path: '/enrollments/:id', page: topPage([{ layout: 'page', main: [{ form: ['session'] }] }]) },
134
+ },
135
+ },
136
+ },
137
+ })
@@ -0,0 +1,65 @@
1
+ // What an enrollment's session column draws: the session's course by its title, then when it starts, which tells two sessions of one course apart.
2
+ // A session is titled by its start and a title is one field, so the course is a second read.
3
+ //
4
+ // The list's own `useRelationshipTitles` keeps only each session's title, the start as text, and not the course it points at, so this cell reads them again:
5
+ // one read of every session the page's rows point at and one of their courses, shared by every cell of the page through the query client rather than two per cell,
6
+ // and read again once it is as old as the list's own pages. A read that failed is not kept, so the next one tries again.
7
+ // Each read is the viewer's own, so a session or a course the viewer may not read is left out, and its cell keeps what it can say.
8
+ import { use } from 'react'
9
+ import { useQuery } from '@tanstack/react-query'
10
+ import type { CellProps, FetchClient, Row } from 'mithaq/admin'
11
+ import { ListContext, useClient, useLocale, useScreen } from 'mithaq/admin/react'
12
+ import { formatDate, noValue, refusalKeyOf, type FoundPage } from '@/components/mithaq/controls.tsx'
13
+ import { findByIds, nameOfRow, titleFieldOf } from '@/components/mithaq/relationship-control.tsx'
14
+
15
+ /** A refusal is the viewer's access answering, so it reads as no rows; anything else is a failure, and throws for the query to try again. */
16
+ const unread = (thrown: unknown): never[] => {
17
+ if (refusalKeyOf(thrown)) return []
18
+ throw thrown
19
+ }
20
+
21
+ const of = (client: FetchClient, kayan: string) => ({ find: (input: Record<string, unknown>) => client({ kayan, operation: 'find', input }) as Promise<FoundPage> })
22
+
23
+ /** Some courses by id, as the viewer reads them, which the session picker's label reads too. */
24
+ export const coursesOf = async ({ client, ids, maxLimit }: { client: FetchClient; ids: readonly string[]; maxLimit: number }): Promise<ReadonlyMap<string, Row>> => {
25
+ const courses = ids.length ? await findByIds({ reads: of(client, 'courses'), ids, maxLimit }).catch(unread) : []
26
+ return new Map(courses.map((one) => [String(one.id), one]))
27
+ }
28
+
29
+ const sessionsOf = async ({ client, ids, maxLimit }: { client: FetchClient; ids: readonly string[]; maxLimit: number }): Promise<ReadonlyMap<string, Row>> => {
30
+ const sessions = await findByIds({ reads: of(client, 'sessions'), ids, maxLimit }).catch(unread)
31
+ const courseIds = [...new Set(sessions.flatMap((one) => (typeof one.course === 'string' ? [one.course] : [])))]
32
+ const course = await coursesOf({ client, ids: courseIds, maxLimit })
33
+ return new Map(sessions.map((one) => [String(one.id), { ...one, course: course.get(String(one.course)) }]))
34
+ }
35
+
36
+ export function SessionCell({ field, value, row }: CellProps) {
37
+ const client = useClient()
38
+ const { locale, t } = useLocale()
39
+ const { kayan: { courses }, maxLimit } = useScreen()
40
+ // A list's page, or the one row a record page draws.
41
+ // Sorted, so the same rows in another order are the same query.
42
+ const ids = [...new Set((use(ListContext)?.page.docs ?? [row]).flatMap((one: Row) => (typeof one[field.name] === 'string' ? [one[field.name] as string] : [])))].sort()
43
+ const { data: sessions, isError } = useQuery({ queryKey: ['session-cell', ids], queryFn: () => sessionsOf({ client, ids, maxLimit }), staleTime: 30_000 })
44
+ if (typeof value !== 'string') return noValue
45
+ const session = sessions?.get(value)
46
+ const when = formatDate({ value: session?.startsAt, locale })
47
+ const course = session?.course ? nameOfRow({ row: session.course as Row, title: titleFieldOf(courses), locale }) : undefined
48
+ // Never the id the column holds, as the relationship cell draws none:
49
+ // loading until the read answers, the server's render among it, a dash
50
+ // where it failed, and a row the viewer cannot see for a session it may
51
+ // not read or one of which it may read neither the course nor the start.
52
+ if (!when && !course) {
53
+ if (sessions) return t('admin.cannotSee', 'You can\'t see this')
54
+ return isError ? '—' : t('admin.loading', 'Loading…')
55
+ }
56
+ // Two lines, the course over its start, so a narrow column wraps each
57
+ // whole rather than one into the other.
58
+ return (
59
+ <span className="grid">
60
+ {course ? <span className="font-medium">{course}</span> : null}
61
+ {/* One unbroken run, so a narrow column breaks between the course and the date and never inside the date. */}
62
+ {when ? <span className="text-muted-foreground text-xs"><bdi className="whitespace-nowrap">{when}</bdi></span> : null}
63
+ </span>
64
+ )
65
+ }
@@ -0,0 +1,8 @@
1
+ // This project's access for the generated `account` kayan, which `mithaq generate
2
+ // auth` imports into `./account.ts` because this file sits beside it.
3
+ //
4
+ // Better Auth alone writes and reads this table, through its own adapter, so
5
+ // every managed operation on it is refused to every actor. Written out rather
6
+ // than left to the administrator's default, which a super admin passes in the
7
+ // platform app.
8
+ export { providerOnly as access } from '../../../tenant-access.ts'
@@ -0,0 +1,31 @@
1
+ // Written by `mithaq generate auth` from the Better Auth options generate.providers names. Do not edit.
2
+ // This kayan's access and what each screen draws of it are the project's:
3
+ // `account.access.ts` exporting `access` and `account.screens.ts` exporting
4
+ // `screens`, beside this file, are imported here on the next run. A kayan
5
+ // built under this one's name elsewhere is refused by every relationship to it.
6
+ import { defaultFields, defaultOperations } from 'mithaq'
7
+ import { defineKayan } from '../../../mithaq.ts'
8
+ import { user } from './user.ts'
9
+ import { access } from './account.access.ts'
10
+
11
+ export const account = defineKayan({
12
+ name: 'account',
13
+ targets: { service: { operations: defaultOperations } },
14
+ access,
15
+ fields: {
16
+ ...defaultFields,
17
+ id: { kind: 'text' },
18
+ accountId: { kind: 'text', rules: { required: true } },
19
+ providerId: { kind: 'text', rules: { required: true } },
20
+ userId: { kind: 'relationship', relationTo: () => user, dbName: 'user_id', rules: { required: true, onDelete: 'cascade' } },
21
+ accessToken: { kind: 'text' },
22
+ refreshToken: { kind: 'text' },
23
+ idToken: { kind: 'text' },
24
+ accessTokenExpiresAt: { kind: 'datetime' },
25
+ refreshTokenExpiresAt: { kind: 'datetime' },
26
+ scope: { kind: 'text' },
27
+ password: { kind: 'text' },
28
+ createdAt: { kind: 'datetime', rules: { required: true } },
29
+ updatedAt: { kind: 'datetime', rules: { required: true, nowOnUpdate: true, providedBy: { update: 'application' } } },
30
+ },
31
+ })
@@ -0,0 +1,18 @@
1
+ // Written by `mithaq generate auth` from the Better Auth options generate.providers names. Do not edit.
2
+ // One key per plugin that brought tables of its own, and `core` for the tables
3
+ // Better Auth ships without one, so a project spreads these lists into
4
+ // `buildAsas` instead of naming every file. A plugin that leaves takes its key
5
+ // with it, and `mithaq generate auth --check` reports the drift.
6
+ import { account } from './account.ts'
7
+ import { invitation } from './invitation.ts'
8
+ import { member } from './member.ts'
9
+ import { organization } from './organization.ts'
10
+ import { organizationRole } from './organizationRole.ts'
11
+ import { session } from './session.ts'
12
+ import { user } from './user.ts'
13
+ import { verification } from './verification.ts'
14
+
15
+ export const betterAuth = {
16
+ core: [user, session, account, verification],
17
+ organization: [organization, organizationRole, member, invitation],
18
+ } as const
@@ -0,0 +1,4 @@
1
+ // Better Auth alone writes and reads this table, through its own adapter, so every managed
2
+ // operation on it is refused to every actor. Written out rather than left to the administrator's
3
+ // default, which a super admin passes.
4
+ export { providerOnly as access } from '../../../tenant-access.ts'
@@ -0,0 +1,27 @@
1
+ // Written by `mithaq generate auth` from the Better Auth options generate.providers names. Do not edit.
2
+ // This kayan's access and what each screen draws of it are the project's:
3
+ // `invitation.access.ts` exporting `access` and `invitation.screens.ts` exporting
4
+ // `screens`, beside this file, are imported here on the next run. A kayan
5
+ // built under this one's name elsewhere is refused by every relationship to it.
6
+ import { defaultFields, defaultOperations } from 'mithaq'
7
+ import { defineKayan } from '../../../mithaq.ts'
8
+ import { organization } from './organization.ts'
9
+ import { user } from './user.ts'
10
+ import { access } from './invitation.access.ts'
11
+
12
+ export const invitation = defineKayan({
13
+ name: 'invitation',
14
+ targets: { service: { operations: defaultOperations } },
15
+ access,
16
+ fields: {
17
+ ...defaultFields,
18
+ id: { kind: 'text' },
19
+ organizationId: { kind: 'relationship', relationTo: () => organization, dbName: 'organization_id', rules: { required: true, onDelete: 'cascade' } },
20
+ email: { kind: 'text', rules: { required: true } },
21
+ role: { kind: 'text' },
22
+ status: { kind: 'text', rules: { required: true } },
23
+ expiresAt: { kind: 'datetime', rules: { required: true } },
24
+ createdAt: { kind: 'datetime', rules: { required: true } },
25
+ inviterId: { kind: 'relationship', relationTo: () => user, dbName: 'inviter_id', rules: { required: true, onDelete: 'cascade' } },
26
+ },
27
+ })
@@ -0,0 +1,10 @@
1
+ // This project's access for the generated `member` kayan, which `mithaq
2
+ // generate auth` imports into `./member.ts` because this file sits beside it. A
3
+ // platform role reads every organization's members in the platform app, by the
4
+ // keys it holds, and no one writes through it. There is no `'*'` row, so a
5
+ // tenant's actor takes no row here and reads nothing: a tenant lists its
6
+ // members through the provider's own `list-members`.
7
+ import { rows } from '../../../mithaq.ts'
8
+ import { platformReads } from '../../../tenant-access.ts'
9
+
10
+ export const access = rows(platformReads)
@@ -0,0 +1,37 @@
1
+ // What the platform draws of the generated `member` kayan, which `mithaq
2
+ // generate auth` imports into `./member.ts` because this file sits beside it:
3
+ // every organization's members, each by the person's and the organization's name,
4
+ // filtered to one organization from that organization's row.
5
+ import { platformUi } from '../../../mithaq.ts'
6
+ import { appPage } from '../../../pages.ts'
7
+
8
+ /** The provider's governance role by the project's word for it, never its slug. */
9
+ const role = { field: 'role', cell: platformUi('role-cells.tsx#MembershipRole') } as const
10
+
11
+ export const screens = {
12
+ platform: {
13
+ // A membership's record is titled by its person.
14
+ useAsTitle: 'userId',
15
+ pages: {
16
+ list: {
17
+ path: '/members',
18
+ page: appPage([{ layout: 'table', main: [{ list: {
19
+ columns: ['userId', 'organizationId', role, 'createdAt'],
20
+ filters: ['organizationId', 'userId', 'role', 'createdAt'],
21
+ // A membership is looked for by its person.
22
+ search: ['userId.name', 'userId.email'],
23
+ sort: '-createdAt',
24
+ } }] }]),
25
+ },
26
+ // A membership's record, which a row opens and which only reads.
27
+ view: {
28
+ path: '/members/:id',
29
+ page: appPage([{
30
+ layout: 'record',
31
+ main: [{ section: 'membership', label: { en: 'Membership', ar: 'العضوية' }, fields: ['userId', 'organizationId', role] }],
32
+ side: [{ section: 'record', label: { en: 'Record', ar: 'السجل' }, fields: ['createdAt', 'updatedAt'] }],
33
+ }]),
34
+ },
35
+ },
36
+ },
37
+ } as const
@@ -0,0 +1,25 @@
1
+ // Written by `mithaq generate auth` from the Better Auth options generate.providers names. Do not edit.
2
+ // This kayan's access and what each screen draws of it are the project's:
3
+ // `member.access.ts` exporting `access` and `member.screens.ts` exporting
4
+ // `screens`, beside this file, are imported here on the next run. A kayan
5
+ // built under this one's name elsewhere is refused by every relationship to it.
6
+ import { defaultFields, defaultOperations } from 'mithaq'
7
+ import { defineKayan } from '../../../mithaq.ts'
8
+ import { organization } from './organization.ts'
9
+ import { user } from './user.ts'
10
+ import { access } from './member.access.ts'
11
+ import { screens } from './member.screens.ts'
12
+
13
+ export const member = defineKayan({
14
+ name: 'member',
15
+ targets: { ...screens, service: { operations: defaultOperations } },
16
+ access,
17
+ fields: {
18
+ ...defaultFields,
19
+ id: { kind: 'text' },
20
+ organizationId: { kind: 'relationship', relationTo: () => organization, dbName: 'organization_id', rules: { required: true, onDelete: 'cascade' } },
21
+ userId: { kind: 'relationship', relationTo: () => user, dbName: 'user_id', rules: { required: true, onDelete: 'cascade' } },
22
+ role: { kind: 'text', rules: { required: true } },
23
+ createdAt: { kind: 'datetime', rules: { required: true } },
24
+ },
25
+ })
@@ -0,0 +1,20 @@
1
+ // This project's access for the generated `organization` kayan, which `mithaq
2
+ // generate auth` imports into `./organization.ts` because this file sits
3
+ // beside it. The table is written here, beside the kayan it is for, and never
4
+ // in a file every kayan shares.
5
+ //
6
+ // An organization is written through no managed operation: Better Auth writes
7
+ // it through its own adapter, and the platform renames one through its own route.
8
+ // In the tenant app a member reads its active organization, the one every tenant-owned
9
+ // row belongs to, and by its id and its name alone, so a tenant
10
+ // relationship shows a name on the dashboard. In the platform app a
11
+ // platform role reads every organization, each column where it holds that
12
+ // column's key. The id and the name are held by `namesInTenant` for the
13
+ // reason `./user.access.ts` gives.
14
+ import { rows } from '../../../mithaq.ts'
15
+ import { namesInTenant, platformReads, user } from '../../../tenant-access.ts'
16
+
17
+ export const access = rows(
18
+ { '*': { read: { id: { equals: user.activeOrganizationId } } }, ...platformReads },
19
+ { holds: namesInTenant('organization') },
20
+ )
@@ -0,0 +1,37 @@
1
+ // What each screen draws of the generated `organization` kayan, which `mithaq
2
+ // generate auth` imports into `./organization.ts` because this file sits beside
3
+ // it, spreading it into the kayan's `targets` after the generated titles of the
4
+ // screens it does not name, so the platform's block here is that screen's whole
5
+ // block, its title included, and the dashboard keeps the generated one. The
6
+ // platform lists every organization with the registry's list and renames it or
7
+ // opens its members from the slug column.
8
+ import { platformUi } from '../../../mithaq.ts'
9
+ import { appPage } from '../../../pages.ts'
10
+
11
+ export const screens = {
12
+ platform: {
13
+ useAsTitle: 'name',
14
+ pages: {
15
+ list: {
16
+ path: '/organizations',
17
+ page: appPage([{ layout: 'table', main: [{ list: {
18
+ columns: ['name', { field: 'slug', cell: platformUi('organization-actions.tsx#OrganizationActions') }, 'createdAt'],
19
+ filters: ['name', 'slug', 'createdAt'],
20
+ search: ['name', 'slug'],
21
+ sort: 'name',
22
+ } }] }]),
23
+ },
24
+ // An organization's record, which a row opens and which only reads; its
25
+ // rename and its members stay on the list's slug column.
26
+ view: {
27
+ path: '/organizations/:id',
28
+ page: appPage([{
29
+ layout: 'record',
30
+ main: [{ section: 'organization', label: { en: 'Organization', ar: 'المؤسسة' }, fields: ['name', 'slug', 'logo', 'metadata'] }],
31
+ // No id: the record is read by its name.
32
+ side: [{ section: 'record', label: { en: 'Record', ar: 'السجل' }, fields: ['createdAt', 'updatedAt'] }],
33
+ }]),
34
+ },
35
+ },
36
+ },
37
+ } as const
@@ -0,0 +1,24 @@
1
+ // Written by `mithaq generate auth` from the Better Auth options generate.providers names. Do not edit.
2
+ // This kayan's access and what each screen draws of it are the project's:
3
+ // `organization.access.ts` exporting `access` and `organization.screens.ts` exporting
4
+ // `screens`, beside this file, are imported here on the next run. A kayan
5
+ // built under this one's name elsewhere is refused by every relationship to it.
6
+ import { defaultFields, defaultOperations } from 'mithaq'
7
+ import { defineKayan } from '../../../mithaq.ts'
8
+ import { access } from './organization.access.ts'
9
+ import { screens } from './organization.screens.ts'
10
+
11
+ export const organization = defineKayan({
12
+ name: 'organization',
13
+ targets: { dashboard: { useAsTitle: 'name' }, ...screens, service: { operations: defaultOperations } },
14
+ access,
15
+ fields: {
16
+ ...defaultFields,
17
+ id: { kind: 'text' },
18
+ name: { kind: 'text', rules: { required: true } },
19
+ slug: { kind: 'text', rules: { required: true, unique: true } },
20
+ logo: { kind: 'text' },
21
+ createdAt: { kind: 'datetime', rules: { required: true } },
22
+ metadata: { kind: 'text' },
23
+ },
24
+ })
@@ -0,0 +1,8 @@
1
+ // This project's access for the generated `organizationRole` kayan, which `mithaq generate
2
+ // auth` imports into `./organizationRole.ts` because this file sits beside it.
3
+ //
4
+ // Better Auth alone writes and reads this table, through its own adapter,
5
+ // so every managed operation on it is refused to every actor. Written out
6
+ // rather than left to the administrator's default, which a super admin
7
+ // passes in the platform app.
8
+ export { providerOnly as access } from '../../../tenant-access.ts'
@@ -0,0 +1,25 @@
1
+ // Written by `mithaq generate auth` from the Better Auth options generate.providers names. Do not edit.
2
+ // This kayan's access and what each screen draws of it are the project's:
3
+ // `organizationRole.access.ts` exporting `access` and `organizationRole.screens.ts` exporting
4
+ // `screens`, beside this file, are imported here on the next run. A kayan
5
+ // built under this one's name elsewhere is refused by every relationship to it.
6
+ import { defaultFields, defaultOperations } from 'mithaq'
7
+ import { defineKayan } from '../../../mithaq.ts'
8
+ import { organization } from './organization.ts'
9
+ import { access } from './organizationRole.access.ts'
10
+
11
+ export const organizationRole = defineKayan({
12
+ name: 'organizationRole',
13
+ dbName: 'organization_role',
14
+ targets: { service: { operations: defaultOperations } },
15
+ access,
16
+ fields: {
17
+ ...defaultFields,
18
+ id: { kind: 'text' },
19
+ organizationId: { kind: 'relationship', relationTo: () => organization, dbName: 'organization_id', rules: { required: true, onDelete: 'cascade' } },
20
+ role: { kind: 'text', rules: { required: true } },
21
+ permission: { kind: 'text', rules: { required: true } },
22
+ createdAt: { kind: 'datetime', rules: { required: true } },
23
+ updatedAt: { kind: 'datetime', rules: { nowOnUpdate: true, providedBy: { update: 'application' } } },
24
+ },
25
+ })
@@ -0,0 +1,8 @@
1
+ // This project's access for the generated `session` kayan, which `mithaq generate
2
+ // auth` imports into `./session.ts` because this file sits beside it.
3
+ //
4
+ // Better Auth alone writes and reads this table, through its own adapter, so
5
+ // every managed operation on it is refused to every actor. Written out rather
6
+ // than left to the administrator's default, which a super admin passes in the
7
+ // platform app.
8
+ export { providerOnly as access } from '../../../tenant-access.ts'
@@ -0,0 +1,28 @@
1
+ // Written by `mithaq generate auth` from the Better Auth options generate.providers names. Do not edit.
2
+ // This kayan's access and what each screen draws of it are the project's:
3
+ // `session.access.ts` exporting `access` and `session.screens.ts` exporting
4
+ // `screens`, beside this file, are imported here on the next run. A kayan
5
+ // built under this one's name elsewhere is refused by every relationship to it.
6
+ import { defaultFields, defaultOperations } from 'mithaq'
7
+ import { defineKayan } from '../../../mithaq.ts'
8
+ import { user } from './user.ts'
9
+ import { access } from './session.access.ts'
10
+
11
+ export const session = defineKayan({
12
+ name: 'session',
13
+ targets: { service: { operations: defaultOperations } },
14
+ access,
15
+ fields: {
16
+ ...defaultFields,
17
+ id: { kind: 'text' },
18
+ expiresAt: { kind: 'datetime', rules: { required: true } },
19
+ token: { kind: 'text', rules: { required: true, unique: true } },
20
+ createdAt: { kind: 'datetime', rules: { required: true } },
21
+ updatedAt: { kind: 'datetime', rules: { required: true, nowOnUpdate: true, providedBy: { update: 'application' } } },
22
+ ipAddress: { kind: 'text' },
23
+ userAgent: { kind: 'text' },
24
+ userId: { kind: 'relationship', relationTo: () => user, dbName: 'user_id', rules: { required: true, onDelete: 'cascade' } },
25
+ impersonatedBy: { kind: 'text' },
26
+ activeOrganizationId: { kind: 'text' },
27
+ },
28
+ })
@@ -0,0 +1,29 @@
1
+ // This project's access for the generated `user` kayan, which `mithaq generate
2
+ // auth` imports into `./user.ts` because this file sits beside it. The table is
3
+ // written here, beside the kayan it is for, and never in a file every kayan
4
+ // shares.
5
+ //
6
+ // A person is written through no managed operation: Better Auth writes it
7
+ // through its own adapter. The two sides of the platform line read it
8
+ // differently, and neither row reaches the other's side. In the tenant app a
9
+ // member reads its active organization's members by their id and their name
10
+ // alone, so a relationship to a person shows a name in the dashboard's cell and
11
+ // picker, and no email, role or ban reaches a tenant. In the platform app a
12
+ // platform role reads every person, each column where it holds that column's key.
13
+ //
14
+ // The id and the name are held by `namesInTenant` rather than by a field list
15
+ // on the `'*'` row, because every actor with a role takes the `'*'` row, the
16
+ // platform's included, and a column one row's list leaves out is hidden
17
+ // whatever the other rows say.
18
+ //
19
+ // The `'*'` row reads every person, and `membersOf` narrows it to the active
20
+ // organization's members: a person carries no tenant column, and a row filter
21
+ // reaches one relationship hop from the kayan's own fields, which `user` has
22
+ // none of toward `member`.
23
+ import { rows, serverTargets } from '../../../mithaq.ts'
24
+ import { membersOf, namesInTenant, platformReads } from '../../../tenant-access.ts'
25
+
26
+ export const access = {
27
+ ...rows({ '*': { read: true }, ...platformReads }, { holds: namesInTenant('user') }),
28
+ read: membersOf(serverTargets.drizzle),
29
+ }