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.
- package/LICENSE +22 -0
- package/LICENSES/openstatus-time-picker.txt +21 -0
- package/LICENSES/payload.txt +22 -0
- package/LICENSES/ponytail.txt +21 -0
- package/index.js +521 -0
- package/package.json +19 -4
- package/template/apps/asas-backoffice/components.json +25 -0
- package/template/apps/asas-backoffice/gitignore +3 -0
- package/template/apps/asas-backoffice/package.json +57 -0
- package/template/apps/asas-backoffice/public/favicon.svg +1 -0
- package/template/apps/asas-backoffice/src/api.ts +147 -0
- package/template/apps/asas-backoffice/src/auth-frame.tsx +150 -0
- package/template/apps/asas-backoffice/src/components/ui/accordion.tsx +74 -0
- package/template/apps/asas-backoffice/src/components/ui/alert-dialog.tsx +185 -0
- package/template/apps/asas-backoffice/src/components/ui/avatar.tsx +106 -0
- package/template/apps/asas-backoffice/src/components/ui/badge.tsx +48 -0
- package/template/apps/asas-backoffice/src/components/ui/breadcrumb.tsx +124 -0
- package/template/apps/asas-backoffice/src/components/ui/button-group.tsx +87 -0
- package/template/apps/asas-backoffice/src/components/ui/button.tsx +55 -0
- package/template/apps/asas-backoffice/src/components/ui/calendar.tsx +223 -0
- package/template/apps/asas-backoffice/src/components/ui/card.tsx +102 -0
- package/template/apps/asas-backoffice/src/components/ui/chart.tsx +371 -0
- package/template/apps/asas-backoffice/src/components/ui/checkbox.tsx +28 -0
- package/template/apps/asas-backoffice/src/components/ui/collapsible.tsx +19 -0
- package/template/apps/asas-backoffice/src/components/ui/combobox.tsx +308 -0
- package/template/apps/asas-backoffice/src/components/ui/dialog.tsx +158 -0
- package/template/apps/asas-backoffice/src/components/ui/direction.tsx +6 -0
- package/template/apps/asas-backoffice/src/components/ui/directional-icon.tsx +20 -0
- package/template/apps/asas-backoffice/src/components/ui/dropdown-menu.tsx +267 -0
- package/template/apps/asas-backoffice/src/components/ui/field.tsx +236 -0
- package/template/apps/asas-backoffice/src/components/ui/input-group.tsx +155 -0
- package/template/apps/asas-backoffice/src/components/ui/input-otp.tsx +84 -0
- package/template/apps/asas-backoffice/src/components/ui/input.tsx +19 -0
- package/template/apps/asas-backoffice/src/components/ui/label.tsx +19 -0
- package/template/apps/asas-backoffice/src/components/ui/popover.tsx +90 -0
- package/template/apps/asas-backoffice/src/components/ui/radio-group.tsx +37 -0
- package/template/apps/asas-backoffice/src/components/ui/resizable.tsx +47 -0
- package/template/apps/asas-backoffice/src/components/ui/scroll-area.tsx +54 -0
- package/template/apps/asas-backoffice/src/components/ui/select.tsx +204 -0
- package/template/apps/asas-backoffice/src/components/ui/separator.tsx +24 -0
- package/template/apps/asas-backoffice/src/components/ui/sheet.tsx +139 -0
- package/template/apps/asas-backoffice/src/components/ui/sidebar.tsx +714 -0
- package/template/apps/asas-backoffice/src/components/ui/skeleton.tsx +13 -0
- package/template/apps/asas-backoffice/src/components/ui/slider.tsx +55 -0
- package/template/apps/asas-backoffice/src/components/ui/spinner.tsx +10 -0
- package/template/apps/asas-backoffice/src/components/ui/switch.tsx +29 -0
- package/template/apps/asas-backoffice/src/components/ui/table.tsx +115 -0
- package/template/apps/asas-backoffice/src/components/ui/tabs.tsx +79 -0
- package/template/apps/asas-backoffice/src/components/ui/textarea.tsx +17 -0
- package/template/apps/asas-backoffice/src/components/ui/toggle-group.tsx +89 -0
- package/template/apps/asas-backoffice/src/components/ui/toggle.tsx +44 -0
- package/template/apps/asas-backoffice/src/components/ui/tooltip.tsx +63 -0
- package/template/apps/asas-backoffice/src/components.ts +35 -0
- package/template/apps/asas-backoffice/src/copy.ts +1 -0
- package/template/apps/asas-backoffice/src/first-user.ts +14 -0
- package/template/apps/asas-backoffice/src/hooks/use-mobile.ts +19 -0
- package/template/apps/asas-backoffice/src/lib/utils.ts +1 -0
- package/template/apps/asas-backoffice/src/links.ts +38 -0
- package/template/apps/asas-backoffice/src/locale-switch.tsx +28 -0
- package/template/apps/asas-backoffice/src/no-organization.tsx +5 -0
- package/template/apps/asas-backoffice/src/redirect.ts +1 -0
- package/template/apps/asas-backoffice/src/request-headers.ts +11 -0
- package/template/apps/asas-backoffice/src/routeTree.gen.ts +206 -0
- package/template/apps/asas-backoffice/src/router.tsx +18 -0
- package/template/apps/asas-backoffice/src/routes/(screens)/backoffice/files.ts +10 -0
- package/template/apps/asas-backoffice/src/routes/(screens)/backoffice/screen.json +3971 -0
- package/template/apps/asas-backoffice/src/routes/(screens)/backoffice/types.ts +163 -0
- package/template/apps/asas-backoffice/src/routes/(screens)/backoffice.$.tsx +17 -0
- package/template/apps/asas-backoffice/src/routes/(screens)/backoffice.index.tsx +10 -0
- package/template/apps/asas-backoffice/src/routes/(screens)/backoffice.tsx +13 -0
- package/template/apps/asas-backoffice/src/routes/__root.tsx +95 -0
- package/template/apps/asas-backoffice/src/routes/api/$.ts +34 -0
- package/template/apps/asas-backoffice/src/routes/auth.create-first-user.tsx +113 -0
- package/template/apps/asas-backoffice/src/routes/auth.sign-in.tsx +77 -0
- package/template/apps/asas-backoffice/src/routes/unauthorized.tsx +134 -0
- package/template/apps/asas-backoffice/src/screen-form.tsx +36 -0
- package/template/apps/asas-backoffice/src/screen-list.tsx +140 -0
- package/template/apps/asas-backoffice/src/screen-names.ts +10 -0
- package/template/apps/asas-backoffice/src/screens.tsx +888 -0
- package/template/apps/asas-backoffice/src/server.ts +30 -0
- package/template/apps/asas-backoffice/src/slide.ts +22 -0
- package/template/apps/asas-backoffice/src/start.ts +42 -0
- package/template/apps/asas-backoffice/src/styles.css +202 -0
- package/template/apps/asas-backoffice/src/theme.ts +2 -0
- package/template/apps/asas-backoffice/tsconfig.json +19 -0
- package/template/apps/asas-backoffice/vite.config.ts +32 -0
- package/template/apps/asas-tanstack/components.json +25 -0
- package/template/apps/asas-tanstack/gitignore +3 -0
- package/template/apps/asas-tanstack/package.json +58 -0
- package/template/apps/asas-tanstack/public/favicon.svg +1 -0
- package/template/apps/asas-tanstack/src/api.ts +147 -0
- package/template/apps/asas-tanstack/src/auth-frame.tsx +150 -0
- package/template/apps/asas-tanstack/src/choose-password.tsx +150 -0
- package/template/apps/asas-tanstack/src/components/ui/accordion.tsx +10 -0
- package/template/apps/asas-tanstack/src/components/ui/alert-dialog.tsx +173 -0
- package/template/apps/asas-tanstack/src/components/ui/avatar.tsx +104 -0
- package/template/apps/asas-tanstack/src/components/ui/badge.tsx +48 -0
- package/template/apps/asas-tanstack/src/components/ui/breadcrumb.tsx +125 -0
- package/template/apps/asas-tanstack/src/components/ui/button-group.tsx +87 -0
- package/template/apps/asas-tanstack/src/components/ui/button.tsx +56 -0
- package/template/apps/asas-tanstack/src/components/ui/calendar.tsx +223 -0
- package/template/apps/asas-tanstack/src/components/ui/card.tsx +94 -0
- package/template/apps/asas-tanstack/src/components/ui/chart.tsx +369 -0
- package/template/apps/asas-tanstack/src/components/ui/checkbox.tsx +27 -0
- package/template/apps/asas-tanstack/src/components/ui/collapsible.tsx +19 -0
- package/template/apps/asas-tanstack/src/components/ui/combobox.tsx +300 -0
- package/template/apps/asas-tanstack/src/components/ui/dialog.tsx +149 -0
- package/template/apps/asas-tanstack/src/components/ui/direction.tsx +5 -0
- package/template/apps/asas-tanstack/src/components/ui/directional-icon.tsx +20 -0
- package/template/apps/asas-tanstack/src/components/ui/dropdown-menu.tsx +267 -0
- package/template/apps/asas-tanstack/src/components/ui/field.tsx +225 -0
- package/template/apps/asas-tanstack/src/components/ui/input-group.tsx +147 -0
- package/template/apps/asas-tanstack/src/components/ui/input-otp.tsx +20 -0
- package/template/apps/asas-tanstack/src/components/ui/input.tsx +20 -0
- package/template/apps/asas-tanstack/src/components/ui/label.tsx +18 -0
- package/template/apps/asas-tanstack/src/components/ui/popover.tsx +50 -0
- package/template/apps/asas-tanstack/src/components/ui/radio-group.tsx +10 -0
- package/template/apps/asas-tanstack/src/components/ui/resizable.tsx +47 -0
- package/template/apps/asas-tanstack/src/components/ui/scroll-area.tsx +52 -0
- package/template/apps/asas-tanstack/src/components/ui/select.tsx +190 -0
- package/template/apps/asas-tanstack/src/components/ui/separator.tsx +23 -0
- package/template/apps/asas-tanstack/src/components/ui/sheet.tsx +127 -0
- package/template/apps/asas-tanstack/src/components/ui/sidebar.tsx +716 -0
- package/template/apps/asas-tanstack/src/components/ui/skeleton.tsx +13 -0
- package/template/apps/asas-tanstack/src/components/ui/slider.tsx +13 -0
- package/template/apps/asas-tanstack/src/components/ui/spinner.tsx +11 -0
- package/template/apps/asas-tanstack/src/components/ui/switch.tsx +8 -0
- package/template/apps/asas-tanstack/src/components/ui/table.tsx +119 -0
- package/template/apps/asas-tanstack/src/components/ui/tabs.tsx +80 -0
- package/template/apps/asas-tanstack/src/components/ui/textarea.tsx +18 -0
- package/template/apps/asas-tanstack/src/components/ui/toggle-group.tsx +8 -0
- package/template/apps/asas-tanstack/src/components/ui/toggle.tsx +44 -0
- package/template/apps/asas-tanstack/src/components/ui/tooltip.tsx +68 -0
- package/template/apps/asas-tanstack/src/components.ts +35 -0
- package/template/apps/asas-tanstack/src/copy.ts +1207 -0
- package/template/apps/asas-tanstack/src/first-run.ts +16 -0
- package/template/apps/asas-tanstack/src/hooks/use-mobile.ts +19 -0
- package/template/apps/asas-tanstack/src/lib/utils.ts +6 -0
- package/template/apps/asas-tanstack/src/links.ts +38 -0
- package/template/apps/asas-tanstack/src/locale-switch.tsx +28 -0
- package/template/apps/asas-tanstack/src/no-organization.tsx +108 -0
- package/template/apps/asas-tanstack/src/quiet-abort.ts +25 -0
- package/template/apps/asas-tanstack/src/redirect.ts +39 -0
- package/template/apps/asas-tanstack/src/request-headers.ts +11 -0
- package/template/apps/asas-tanstack/src/routeTree.gen.ts +424 -0
- package/template/apps/asas-tanstack/src/router.tsx +18 -0
- package/template/apps/asas-tanstack/src/routes/(screens)/dashboard/files.ts +11 -0
- package/template/apps/asas-tanstack/src/routes/(screens)/dashboard/screen.json +4242 -0
- package/template/apps/asas-tanstack/src/routes/(screens)/dashboard/types.ts +160 -0
- package/template/apps/asas-tanstack/src/routes/(screens)/dashboard.$.tsx +17 -0
- package/template/apps/asas-tanstack/src/routes/(screens)/dashboard.index.tsx +10 -0
- package/template/apps/asas-tanstack/src/routes/(screens)/dashboard.tsx +13 -0
- package/template/apps/asas-tanstack/src/routes/(screens)/portal/files.ts +9 -0
- package/template/apps/asas-tanstack/src/routes/(screens)/portal/screen.json +1564 -0
- package/template/apps/asas-tanstack/src/routes/(screens)/portal/types.ts +94 -0
- package/template/apps/asas-tanstack/src/routes/(screens)/portal.$.tsx +17 -0
- package/template/apps/asas-tanstack/src/routes/(screens)/portal.index.tsx +10 -0
- package/template/apps/asas-tanstack/src/routes/(screens)/portal.tsx +13 -0
- package/template/apps/asas-tanstack/src/routes/__root.tsx +99 -0
- package/template/apps/asas-tanstack/src/routes/api/$.ts +11 -0
- package/template/apps/asas-tanstack/src/routes/auth.accept-invitation.tsx +108 -0
- package/template/apps/asas-tanstack/src/routes/auth.first-run.tsx +114 -0
- package/template/apps/asas-tanstack/src/routes/auth.forgot-password.tsx +94 -0
- package/template/apps/asas-tanstack/src/routes/auth.handoff.ts +39 -0
- package/template/apps/asas-tanstack/src/routes/auth.reset-password.tsx +16 -0
- package/template/apps/asas-tanstack/src/routes/auth.set-password.tsx +16 -0
- package/template/apps/asas-tanstack/src/routes/auth.sign-in.tsx +98 -0
- package/template/apps/asas-tanstack/src/routes/auth.sign-up.tsx +143 -0
- package/template/apps/asas-tanstack/src/routes/index.tsx +146 -0
- package/template/apps/asas-tanstack/src/routes/unauthorized.tsx +134 -0
- package/template/apps/asas-tanstack/src/screen-form.tsx +36 -0
- package/template/apps/asas-tanstack/src/screen-list.tsx +140 -0
- package/template/apps/asas-tanstack/src/screen-names.ts +10 -0
- package/template/apps/asas-tanstack/src/screens.tsx +888 -0
- package/template/apps/asas-tanstack/src/server.ts +26 -0
- package/template/apps/asas-tanstack/src/slide.ts +22 -0
- package/template/apps/asas-tanstack/src/start.ts +57 -0
- package/template/apps/asas-tanstack/src/styles.css +163 -0
- package/template/apps/asas-tanstack/src/theme.ts +5 -0
- package/template/apps/asas-tanstack/tsconfig.json +19 -0
- package/template/apps/asas-tanstack/vite.config.ts +34 -0
- package/template/package.json +16 -0
- package/template/templates/asas/.claude/skills/add-field/SKILL.md +27 -0
- package/template/templates/asas/.claude/skills/add-kayan/SKILL.md +26 -0
- package/template/templates/asas/.claude/skills/eject-admin-piece/SKILL.md +28 -0
- package/template/templates/asas/.claude/skills/upgrade-mithaq/SKILL.md +35 -0
- package/template/templates/asas/.env.example +33 -0
- package/template/templates/asas/AGENTS.md +101 -0
- package/template/templates/asas/CLAUDE.md +1 -0
- package/template/templates/asas/README.md +1056 -0
- package/template/templates/asas/mithaq.hints.json +25 -0
- package/template/templates/asas/package.json +53 -0
- package/template/templates/asas/src/asas.ts +118 -0
- package/template/templates/asas/src/backoffice-http.ts +117 -0
- package/template/templates/asas/src/dev-setup.ts +112 -0
- package/template/templates/asas/src/local-database.ts +22 -0
- package/template/templates/asas/src/messages/account.json +132 -0
- package/template/templates/asas/src/messages/auditLog.json +304 -0
- package/template/templates/asas/src/messages/backofficeLog.json +132 -0
- package/template/templates/asas/src/messages/courses.json +312 -0
- package/template/templates/asas/src/messages/enrollments.json +60 -0
- package/template/templates/asas/src/messages/invitation.json +82 -0
- package/template/templates/asas/src/messages/localUsers.json +48 -0
- package/template/templates/asas/src/messages/media.json +76 -0
- package/template/templates/asas/src/messages/member.json +62 -0
- package/template/templates/asas/src/messages/memberRoles.json +48 -0
- package/template/templates/asas/src/messages/mithaq.json +214 -0
- package/template/templates/asas/src/messages/organization.json +72 -0
- package/template/templates/asas/src/messages/organizationRole.json +62 -0
- package/template/templates/asas/src/messages/roles.json +72 -0
- package/template/templates/asas/src/messages/session.json +102 -0
- package/template/templates/asas/src/messages/sessions.json +136 -0
- package/template/templates/asas/src/messages/settings.json +64 -0
- package/template/templates/asas/src/messages/tags.json +48 -0
- package/template/templates/asas/src/messages/user.json +112 -0
- package/template/templates/asas/src/messages/verification.json +62 -0
- package/template/templates/asas/src/migrations/20260920080701_parched_tombstone/migration.sql +177 -0
- package/template/templates/asas/src/migrations/20260920080701_parched_tombstone/snapshot.json +2169 -0
- package/template/templates/asas/src/migrations/20260920230505_zippy_ben_urich/migration.sql +1 -0
- package/template/templates/asas/src/migrations/20260920230505_zippy_ben_urich/snapshot.json +2182 -0
- package/template/templates/asas/src/migrations/20260920231219_slim_retro_girl/migration.sql +20 -0
- package/template/templates/asas/src/migrations/20260920231219_slim_retro_girl/snapshot.json +2409 -0
- package/template/templates/asas/src/migrations/20260922082945_military_ironclad/migration.sql +15 -0
- package/template/templates/asas/src/migrations/20260922082945_military_ironclad/snapshot.json +2627 -0
- package/template/templates/asas/src/migrations/20260922112630_bitter_chat/migration.sql +5 -0
- package/template/templates/asas/src/migrations/20260922112630_bitter_chat/snapshot.json +2691 -0
- package/template/templates/asas/src/migrations/20260923235915_foamy_rachel_grey/migration.sql +8 -0
- package/template/templates/asas/src/migrations/20260923235915_foamy_rachel_grey/snapshot.json +2795 -0
- package/template/templates/asas/src/migrations/20260926145709_clean_the_initiative/migration.sql +3 -0
- package/template/templates/asas/src/migrations/20260926145709_clean_the_initiative/snapshot.json +2834 -0
- package/template/templates/asas/src/migrations/20260927212525_bent_prowler/migration.sql +9 -0
- package/template/templates/asas/src/migrations/20260927212525_bent_prowler/snapshot.json +2870 -0
- package/template/templates/asas/src/migrations/20260927223441_tranquil_bromley/migration.sql +1 -0
- package/template/templates/asas/src/migrations/20260927223441_tranquil_bromley/snapshot.json +2881 -0
- package/template/templates/asas/src/migrations/20260928030047_outgoing_wraith/migration.sql +15 -0
- package/template/templates/asas/src/migrations/20260928030047_outgoing_wraith/snapshot.json +3073 -0
- package/template/templates/asas/src/migrations/20260929024407_grey_morlun/migration.sql +21 -0
- package/template/templates/asas/src/migrations/20260929024407_grey_morlun/snapshot.json +3098 -0
- package/template/templates/asas/src/migrations/20260929130530_bored_jackal/migration.sql +21 -0
- package/template/templates/asas/src/migrations/20260929130530_bored_jackal/snapshot.json +3124 -0
- package/template/templates/asas/src/migrations/20260930102924_material_newton_destine/migration.sql +32 -0
- package/template/templates/asas/src/migrations/20260930102924_material_newton_destine/snapshot.json +3324 -0
- package/template/templates/asas/src/migrations/20261003022353_clumsy_taskmaster/migration.sql +2 -0
- package/template/templates/asas/src/migrations/20261003022353_clumsy_taskmaster/snapshot.json +3350 -0
- package/template/templates/asas/src/migrations/20261003143249_premium_red_shift/migration.sql +21 -0
- package/template/templates/asas/src/migrations/20261003143249_premium_red_shift/snapshot.json +3635 -0
- package/template/templates/asas/src/migrations/20261008215349_square_norrin_radd/migration.sql +8 -0
- package/template/templates/asas/src/migrations/20261008215349_square_norrin_radd/snapshot.json +3635 -0
- package/template/templates/asas/src/migrations/20261008230204_many_scarecrow/migration.sql +18 -0
- package/template/templates/asas/src/migrations/20261008230204_many_scarecrow/snapshot.json +3635 -0
- package/template/templates/asas/src/migrations/20261010005521_elite_giant_girl/migration.sql +18 -0
- package/template/templates/asas/src/migrations/20261010005521_elite_giant_girl/snapshot.json +3635 -0
- package/template/templates/asas/src/migrations/20261010120822_needy_thunderbolt/migration.sql +11 -0
- package/template/templates/asas/src/migrations/20261010120822_needy_thunderbolt/snapshot.json +3667 -0
- package/template/templates/asas/src/migrations/20261012000752_organization_ownership/migration.sql +48 -0
- package/template/templates/asas/src/migrations/20261012000752_organization_ownership/snapshot.json +3891 -0
- package/template/templates/asas/src/migrations/20261014221355_tags_restrict/migration.sql +1 -0
- package/template/templates/asas/src/migrations/20261014221355_tags_restrict/snapshot.json +3881 -0
- package/template/templates/asas/src/migrations/20261014222218_one_seat_per_session/migration.sql +1 -0
- package/template/templates/asas/src/migrations/20261014222218_one_seat_per_session/snapshot.json +3894 -0
- package/template/templates/asas/src/migrations/20261014223522_session_archive_reason/migration.sql +1 -0
- package/template/templates/asas/src/migrations/20261014223522_session_archive_reason/snapshot.json +3907 -0
- package/template/templates/asas/src/migrations/20261014225728_course_archive_by_operation/migration.sql +1 -0
- package/template/templates/asas/src/migrations/20261014225728_course_archive_by_operation/snapshot.json +3907 -0
- package/template/templates/asas/src/migrations/20261015042444_organization_names/migration.sql +1 -0
- package/template/templates/asas/src/migrations/20261015042444_organization_names/snapshot.json +3920 -0
- package/template/templates/asas/src/migrations/20261015053212_member_last_seen/migration.sql +1 -0
- package/template/templates/asas/src/migrations/20261015053212_member_last_seen/snapshot.json +3933 -0
- package/template/templates/asas/src/migrations/20261015093203_activity_and_staff_history/migration.sql +15 -0
- package/template/templates/asas/src/migrations/20261015093203_activity_and_staff_history/snapshot.json +4113 -0
- package/template/templates/asas/src/migrations/20261015095752_portal/migration.sql +2 -0
- package/template/templates/asas/src/migrations/20261015095752_portal/snapshot.json +4113 -0
- package/template/templates/asas/src/migrations/20261015135529_better_auth_indexes/migration.sql +9 -0
- package/template/templates/asas/src/migrations/20261015135529_better_auth_indexes/snapshot.json +4302 -0
- package/template/templates/asas/src/migrations/20261015162438_audit_changes_json/migration.sql +27 -0
- package/template/templates/asas/src/migrations/20261015162438_audit_changes_json/snapshot.json +4289 -0
- package/template/templates/asas/src/migrations/drizzle.config.ts +11 -0
- package/template/templates/asas/src/migrations/schema.ts +30 -0
- package/template/templates/asas/src/mithaq.ts +4 -0
- package/template/templates/asas/src/organizations.ts +3 -0
- package/template/templates/asas/src/seed.ts +427 -0
- package/template/templates/asas/src/ui-backoffice/home.tsx +63 -0
- package/template/templates/asas/src/ui-backoffice/organization-actions.tsx +121 -0
- package/template/templates/asas/src/ui-backoffice/record-page.tsx +30 -0
- package/template/templates/asas/src/ui-backoffice/role-cells.tsx +59 -0
- package/template/templates/asas/src/ui-backoffice/user-actions.tsx +249 -0
- package/template/templates/asas/src/ui-dashboard/dashboard.tsx +300 -0
- package/template/templates/asas/src/ui-dashboard/home.tsx +73 -0
- package/template/templates/asas/src/ui-dashboard/members-card.tsx +147 -0
- package/template/templates/asas/src/ui-dashboard/members.tsx +829 -0
- package/template/templates/asas/src/ui-dashboard/theme-provider.tsx +10 -0
- package/template/templates/asas/src/ui-dashboard/versions.tsx +446 -0
- package/template/templates/asas/src/ui-portal/courses-home.tsx +96 -0
- package/template/templates/asas/src/ui-portal/home.tsx +73 -0
- package/template/templates/asas/src/ui-portal/page.tsx +11 -0
- package/template/templates/features/audit-log/audit-log/audit-log.ts +284 -0
- package/template/templates/features/audit-log/backoffice-log/backoffice-log.ts +67 -0
- package/template/templates/features/audit-log/index.ts +4 -0
- package/template/templates/features/audit-log/package.json +13 -0
- package/template/templates/features/auth/index.ts +11 -0
- package/template/templates/features/auth/local-users/local-users.ts +143 -0
- package/template/templates/features/auth/package.json +13 -0
- package/template/templates/features/auth/user/account.access.ts +8 -0
- package/template/templates/features/auth/user/account.ts +31 -0
- package/template/templates/features/auth/user/index.ts +18 -0
- package/template/templates/features/auth/user/invitation.access.ts +4 -0
- package/template/templates/features/auth/user/invitation.ts +27 -0
- package/template/templates/features/auth/user/member.access.ts +10 -0
- package/template/templates/features/auth/user/member.screens.ts +37 -0
- package/template/templates/features/auth/user/member.ts +26 -0
- package/template/templates/features/auth/user/organization.access.ts +20 -0
- package/template/templates/features/auth/user/organization.screens.ts +37 -0
- package/template/templates/features/auth/user/organization.ts +25 -0
- package/template/templates/features/auth/user/organizationRole.access.ts +8 -0
- package/template/templates/features/auth/user/organizationRole.ts +25 -0
- package/template/templates/features/auth/user/session.access.ts +8 -0
- package/template/templates/features/auth/user/session.ts +28 -0
- package/template/templates/features/auth/user/user.access.ts +30 -0
- package/template/templates/features/auth/user/user.screens.ts +45 -0
- package/template/templates/features/auth/user/user.ts +31 -0
- package/template/templates/features/auth/user/verification.access.ts +8 -0
- package/template/templates/features/auth/user/verification.ts +23 -0
- package/template/templates/features/courses/courses/courses.ts +601 -0
- package/template/templates/features/courses/courses/cover-reads.ts +33 -0
- package/template/templates/features/courses/courses/ui/archived-badge.tsx +9 -0
- package/template/templates/features/courses/courses/ui/fee-cell.tsx +19 -0
- package/template/templates/features/courses/courses/ui/no-courses.tsx +13 -0
- package/template/templates/features/courses/courses/ui/status-cell.tsx +33 -0
- package/template/templates/features/courses/courses/ui/unpublish-button.tsx +15 -0
- package/template/templates/features/courses/domain/archive.ts +39 -0
- package/template/templates/features/courses/domain/on-offer.ts +6 -0
- package/template/templates/features/courses/enrollments/enrollments.ts +309 -0
- package/template/templates/features/courses/enrollments/ui/session-cell.tsx +75 -0
- package/template/templates/features/courses/enrollments/ui/session-labels.tsx +36 -0
- package/template/templates/features/courses/index.ts +8 -0
- package/template/templates/features/courses/package.json +24 -0
- package/template/templates/features/courses/portal/ui/catalogue.tsx +86 -0
- package/template/templates/features/courses/portal/ui/course.tsx +176 -0
- package/template/templates/features/courses/portal/ui/enrollments.tsx +170 -0
- package/template/templates/features/courses/portal/ui/parts.tsx +95 -0
- package/template/templates/features/courses/portal/ui/reads.ts +76 -0
- package/template/templates/features/courses/portal/ui/rules.ts +87 -0
- package/template/templates/features/courses/portal/ui/shell.tsx +194 -0
- package/template/templates/features/courses/sessions/seats.ts +42 -0
- package/template/templates/features/courses/sessions/sessions.ts +186 -0
- package/template/templates/features/media/index.ts +10 -0
- package/template/templates/features/media/media/media.ts +131 -0
- package/template/templates/features/media/media/ui/preview.tsx +143 -0
- package/template/templates/features/media/package.json +19 -0
- package/template/templates/features/roles/index.ts +4 -0
- package/template/templates/features/roles/member-roles/member-roles.ts +207 -0
- package/template/templates/features/roles/package.json +21 -0
- package/template/templates/features/roles/roles/roles.ts +184 -0
- package/template/templates/features/roles/roles/ui/role-form.tsx +267 -0
- package/template/templates/features/roles/roles/ui/roles.tsx +190 -0
- package/template/templates/features/settings/index.ts +3 -0
- package/template/templates/features/settings/package.json +13 -0
- package/template/templates/features/settings/settings/settings.ts +64 -0
- package/template/templates/features/tags/index.ts +3 -0
- package/template/templates/features/tags/package.json +14 -0
- package/template/templates/features/tags/tags/tags.ts +80 -0
- package/template/templates/mithaq/components.json +22 -0
- package/template/templates/mithaq/package.json +33 -0
- package/template/templates/mithaq/src/mail/index.ts +32 -0
- package/template/templates/mithaq/src/mail/mailers.ts +36 -0
- package/template/templates/mithaq/src/mithaq/audit-sink.ts +111 -0
- package/template/templates/mithaq/src/mithaq/auth/backoffice-roles.ts +108 -0
- package/template/templates/mithaq/src/mithaq/auth/index.ts +1859 -0
- package/template/templates/mithaq/src/mithaq/auth/organizations.ts +80 -0
- package/template/templates/mithaq/src/mithaq/pages.ts +83 -0
- package/template/templates/mithaq/src/mithaq/rich-text-nodes.ts +19 -0
- package/template/templates/mithaq/src/mithaq.ts +593 -0
- package/template/templates/mithaq/src/roles/default-member-roles.ts +29 -0
- package/template/templates/mithaq/src/roles/grants.ts +47 -0
- package/template/templates/mithaq/src/roles/member-access.ts +11 -0
- package/template/templates/mithaq/src/roles/statements.ts +13 -0
- package/template/templates/mithaq/src/roles/system-roles.ts +16 -0
- package/template/templates/mithaq/src/roles/tenant-access.ts +260 -0
- package/template/templates/mithaq/src/roles/tenant-roles.ts +321 -0
- package/template/templates/mithaq/src/steps/expected-updated-at.ts +41 -0
- package/template/templates/mithaq/src/storage/defaults.ts +156 -0
- package/template/templates/mithaq/src/storage/index.ts +97 -0
- package/template/templates/mithaq/src/storage/local.ts +143 -0
- package/template/templates/mithaq/src/storage/r2.ts +5 -0
- package/template/templates/mithaq/src/storage/s3.ts +91 -0
- package/template/templates/mithaq/src/storage/uploadthing.ts +59 -0
- package/template/templates/mithaq/src/ui/activity-page.tsx +33 -0
- package/template/templates/mithaq/src/ui/activity.tsx +527 -0
- package/template/templates/mithaq/src/ui/app-shell.tsx +268 -0
- package/template/templates/mithaq/src/ui/kpi-card.tsx +25 -0
- package/template/templates/mithaq/src/ui/record-page.tsx +50 -0
- package/template/templates/mithaq/src/ui/rich-text-nodes.tsx +33 -0
- package/template/templates/mithaq/src/ui/screen-nav.tsx +28 -0
- package/template/templates/mithaq/src/ui/table-page.tsx +9 -0
- package/template/templates/mithaq/src/ui/top-shell.tsx +60 -0
- 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
|