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,30 @@
|
|
|
1
|
+
import handler, { createServerEntry } from '@tanstack/react-start/server-entry'
|
|
2
|
+
import type { ScreenClient } from 'mithaq/admin'
|
|
3
|
+
import { checkComponents } from 'mithaq-admin/components.tsx'
|
|
4
|
+
import { components } from './components.ts'
|
|
5
|
+
import { withQuietAbort } from '../../asas-tanstack/src/quiet-abort.ts'
|
|
6
|
+
|
|
7
|
+
// Every screen this app serves, checked against its components map as it starts, so a field whose kind has no
|
|
8
|
+
// control fails the start, naming the kind and the field, and never reaches a page. Read on the server alone,
|
|
9
|
+
// since a screen.json names every field of the screen.
|
|
10
|
+
for (const client of Object.values(import.meta.glob<ScreenClient>('./routes/(screens)/*/screen.json', { eager: true, import: 'default' }))) {
|
|
11
|
+
checkComponents({ client, components })
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
// A refused start (a migration not applied) exits 1 rather than leave a server up that cannot serve.
|
|
15
|
+
const { env } = process
|
|
16
|
+
if (env.NODE_ENV === 'production') {
|
|
17
|
+
await (await import('asas')).asas.connect().catch((error: unknown) => {
|
|
18
|
+
console.error(error)
|
|
19
|
+
process.exit(1)
|
|
20
|
+
})
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
// Every request runs as the backoffice; nothing a request carries decides it, and the tenant app never does.
|
|
24
|
+
// `asas` is imported per request so it is the same module every route reads.
|
|
25
|
+
export default createServerEntry({
|
|
26
|
+
fetch: async (request: Request) => {
|
|
27
|
+
const { asBackoffice } = await import('asas')
|
|
28
|
+
return asBackoffice(() => handler.fetch(withQuietAbort(request)))
|
|
29
|
+
},
|
|
30
|
+
})
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The provider's own session read, which refreshes a session past its `updateAge` and forwards the cookie.
|
|
3
|
+
* Run it before the actor is resolved: a read that refreshes the row first leaves this one nothing due.
|
|
4
|
+
*/
|
|
5
|
+
export const slide = async ({
|
|
6
|
+
strategies,
|
|
7
|
+
headers,
|
|
8
|
+
setResponseHeader,
|
|
9
|
+
}: {
|
|
10
|
+
strategies: readonly unknown[]
|
|
11
|
+
headers: Headers
|
|
12
|
+
setResponseHeader: (name: 'set-cookie', value: string[]) => void
|
|
13
|
+
}) => {
|
|
14
|
+
const cookies: string[] = []
|
|
15
|
+
for (const strategy of strategies) {
|
|
16
|
+
const read = (strategy as { server?: { api?: { getSession?: (args: object) => Promise<{ headers: Headers }> } } }).server?.api?.getSession
|
|
17
|
+
if (read) cookies.push(...(await read({ headers, returnHeaders: true })).headers.getSetCookie())
|
|
18
|
+
}
|
|
19
|
+
// Set once with every strategy's cookies: a second set replaces the first.
|
|
20
|
+
if (cookies.length) setResponseHeader('set-cookie', cookies)
|
|
21
|
+
}
|
|
22
|
+
// mithaq-admin 2d3ab739c92c6c2b397041b76bf4b23b2902b7a2 bc0221e1088103f8638e0333b84aafb6b326e3991a187ced198bce2d090ecbe9
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { createCsrfMiddleware, createMiddleware, createStart } from '@tanstack/react-start'
|
|
2
|
+
import { dirOf, localeCookie, localeOf } from './copy.ts'
|
|
3
|
+
|
|
4
|
+
// Not decoded: the only caller accepts two ASCII letters.
|
|
5
|
+
const cookieOf = (header: string | null, name: string): string | undefined =>
|
|
6
|
+
header
|
|
7
|
+
?.split(';')
|
|
8
|
+
.map((one) => one.trim().split('='))
|
|
9
|
+
.find(([key]) => key === name)
|
|
10
|
+
?.slice(1)
|
|
11
|
+
.join('=')
|
|
12
|
+
|
|
13
|
+
// The locale comes from the `locale` cookie alone, and `en` when there is none.
|
|
14
|
+
const locale = createMiddleware({ type: 'request' }).server(({ request, next }) => {
|
|
15
|
+
const asked = localeOf(cookieOf(request.headers.get('cookie'), localeCookie))
|
|
16
|
+
return next({ context: { locale: asked, dir: dirOf(asked) } })
|
|
17
|
+
})
|
|
18
|
+
|
|
19
|
+
// Checks server function calls only; page renders and the plain HTTP arm under `/api` are untouched.
|
|
20
|
+
const csrf = createCsrfMiddleware({ filter: (ctx) => ctx.handlerType === 'serverFn' })
|
|
21
|
+
|
|
22
|
+
// The location is a path of this origin, so nothing the request's host header says reaches it.
|
|
23
|
+
const home = createMiddleware({ type: 'request' }).server(({ request, next }) =>
|
|
24
|
+
new URL(request.url).pathname === '/' ? new Response(null, { status: 307, headers: { location: '/backoffice' } }) : next(),
|
|
25
|
+
)
|
|
26
|
+
|
|
27
|
+
// Only this origin may frame the pages, so no other site can lay this app under its own page and steer a click.
|
|
28
|
+
// First, so it covers every other middleware's answer; the answer is rebuilt because a redirect's headers are immutable.
|
|
29
|
+
const framing = createMiddleware({ type: 'request' }).server(async ({ next }) => {
|
|
30
|
+
const result = await next()
|
|
31
|
+
const answer = result.response
|
|
32
|
+
const html = answer.headers.get('content-type')?.startsWith('text/html') === true
|
|
33
|
+
if (answer.headers.has('content-security-policy') && !html) return result
|
|
34
|
+
const headers = new Headers(answer.headers)
|
|
35
|
+
if (!headers.has('content-security-policy')) headers.set('content-security-policy', "frame-ancestors 'self'")
|
|
36
|
+
// Every HTML answer is drawn for one session and locale, so a signed-out page must not come back
|
|
37
|
+
// from the cache on Back; the root document's script reloads a page the back-forward cache restores.
|
|
38
|
+
if (html) headers.set('cache-control', 'no-store')
|
|
39
|
+
return { ...result, response: new Response(answer.body, { status: answer.status, statusText: answer.statusText, headers }) }
|
|
40
|
+
})
|
|
41
|
+
|
|
42
|
+
export const startInstance = createStart(() => ({ requestMiddleware: [framing, home, csrf, locale] }))
|
|
@@ -0,0 +1,202 @@
|
|
|
1
|
+
/* The owner's shadcn preset, `bunx --bun shadcn@latest init --preset b1Nt9T8mlk
|
|
2
|
+
--template start` (style `base-sera`), as it wrote this file, with RTL on for
|
|
3
|
+
Arabic and the two additions marked below. */
|
|
4
|
+
@import "tailwindcss";
|
|
5
|
+
@import "tw-animate-css";
|
|
6
|
+
@import "shadcn/tailwind.css";
|
|
7
|
+
@import "@fontsource-variable/noto-sans";
|
|
8
|
+
@import "@fontsource-variable/figtree";
|
|
9
|
+
/* Arabic, which the preset's two faces do not draw: the tenant app's own face
|
|
10
|
+
for it, listed first in `--font-sans` below so an Arabic page reads in it and
|
|
11
|
+
a Latin one falls through to the preset's. */
|
|
12
|
+
@import "@fontsource/ibm-plex-sans-arabic";
|
|
13
|
+
/* The admin's pieces in node_modules, which Tailwind never scans on its own. */
|
|
14
|
+
@import "mithaq-admin/source.css";
|
|
15
|
+
|
|
16
|
+
/* The project's own shells, pages and components, which the backoffice screen
|
|
17
|
+
loads, as the tenant app lists them (`apps/asas-tanstack/src/styles.css`).
|
|
18
|
+
Tailwind finds this app's own files, an ejected `src/components/mithaq`
|
|
19
|
+
among them, without one. */
|
|
20
|
+
@source "../../../templates";
|
|
21
|
+
|
|
22
|
+
@custom-variant dark (&:is(.dark *));
|
|
23
|
+
|
|
24
|
+
:root {
|
|
25
|
+
--background: oklch(1 0 0);
|
|
26
|
+
--foreground: oklch(0.145 0.008 326);
|
|
27
|
+
--card: oklch(1 0 0);
|
|
28
|
+
--card-foreground: oklch(0.145 0.008 326);
|
|
29
|
+
--popover: oklch(1 0 0);
|
|
30
|
+
--popover-foreground: oklch(0.145 0.008 326);
|
|
31
|
+
--primary: oklch(0.491 0.27 292.581);
|
|
32
|
+
--primary-foreground: oklch(0.969 0.016 293.756);
|
|
33
|
+
--secondary: oklch(0.967 0.001 286.375);
|
|
34
|
+
--secondary-foreground: oklch(0.21 0.006 285.885);
|
|
35
|
+
--muted: oklch(0.96 0.003 325.6);
|
|
36
|
+
--muted-foreground: oklch(0.542 0.034 322.5);
|
|
37
|
+
--accent: oklch(0.96 0.003 325.6);
|
|
38
|
+
--accent-foreground: oklch(0.212 0.019 322.12);
|
|
39
|
+
/* Darker than the preset's red 600, which is 4.0:1 on the pale red fill the destructive button and menu item draw it on and 3.3:1 on its hover; this is 5.9:1 on the fill and 4.9:1 on the hover. */
|
|
40
|
+
--destructive: oklch(0.48 0.2 27.5);
|
|
41
|
+
--border: oklch(0.922 0.005 325.62);
|
|
42
|
+
--input: oklch(0.922 0.005 325.62);
|
|
43
|
+
--ring: oklch(0.711 0.019 323.02);
|
|
44
|
+
--chart-1: oklch(0.811 0.111 293.571);
|
|
45
|
+
--chart-2: oklch(0.606 0.25 292.717);
|
|
46
|
+
--chart-3: oklch(0.541 0.281 293.009);
|
|
47
|
+
--chart-4: oklch(0.491 0.27 292.581);
|
|
48
|
+
--chart-5: oklch(0.432 0.232 292.759);
|
|
49
|
+
--radius: 0.625rem;
|
|
50
|
+
--sidebar: oklch(0.985 0 0);
|
|
51
|
+
--sidebar-foreground: oklch(0.145 0.008 326);
|
|
52
|
+
--sidebar-primary: oklch(0.541 0.281 293.009);
|
|
53
|
+
--sidebar-primary-foreground: oklch(0.969 0.016 293.756);
|
|
54
|
+
--sidebar-accent: oklch(0.96 0.003 325.6);
|
|
55
|
+
--sidebar-accent-foreground: oklch(0.212 0.019 322.12);
|
|
56
|
+
--sidebar-border: oklch(0.922 0.005 325.62);
|
|
57
|
+
--sidebar-ring: oklch(0.711 0.019 323.02);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
.dark {
|
|
61
|
+
--background: oklch(0.145 0.008 326);
|
|
62
|
+
--foreground: oklch(0.985 0 0);
|
|
63
|
+
--card: oklch(0.212 0.019 322.12);
|
|
64
|
+
--card-foreground: oklch(0.985 0 0);
|
|
65
|
+
--popover: oklch(0.212 0.019 322.12);
|
|
66
|
+
--popover-foreground: oklch(0.985 0 0);
|
|
67
|
+
--primary: oklch(0.432 0.232 292.759);
|
|
68
|
+
--primary-foreground: oklch(0.969 0.016 293.756);
|
|
69
|
+
--secondary: oklch(0.274 0.006 286.033);
|
|
70
|
+
--secondary-foreground: oklch(0.985 0 0);
|
|
71
|
+
--muted: oklch(0.263 0.024 320.12);
|
|
72
|
+
--muted-foreground: oklch(0.711 0.019 323.02);
|
|
73
|
+
--accent: oklch(0.263 0.024 320.12);
|
|
74
|
+
--accent-foreground: oklch(0.985 0 0);
|
|
75
|
+
--destructive: oklch(0.704 0.191 22.216);
|
|
76
|
+
--border: oklch(1 0 0 / 10%);
|
|
77
|
+
--input: oklch(1 0 0 / 15%);
|
|
78
|
+
--ring: oklch(0.542 0.034 322.5);
|
|
79
|
+
--chart-1: oklch(0.811 0.111 293.571);
|
|
80
|
+
--chart-2: oklch(0.606 0.25 292.717);
|
|
81
|
+
--chart-3: oklch(0.541 0.281 293.009);
|
|
82
|
+
--chart-4: oklch(0.491 0.27 292.581);
|
|
83
|
+
--chart-5: oklch(0.432 0.232 292.759);
|
|
84
|
+
--sidebar: oklch(0.212 0.019 322.12);
|
|
85
|
+
--sidebar-foreground: oklch(0.985 0 0);
|
|
86
|
+
--sidebar-primary: oklch(0.606 0.25 292.717);
|
|
87
|
+
--sidebar-primary-foreground: oklch(0.969 0.016 293.756);
|
|
88
|
+
--sidebar-accent: oklch(0.263 0.024 320.12);
|
|
89
|
+
--sidebar-accent-foreground: oklch(0.985 0 0);
|
|
90
|
+
--sidebar-border: oklch(1 0 0 / 10%);
|
|
91
|
+
--sidebar-ring: oklch(0.542 0.034 322.5);
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
@theme inline {
|
|
95
|
+
--font-sans: 'IBM Plex Sans Arabic', 'Noto Sans Variable', sans-serif;
|
|
96
|
+
--font-heading: 'Figtree Variable', sans-serif;
|
|
97
|
+
--color-sidebar-ring: var(--sidebar-ring);
|
|
98
|
+
--color-sidebar-border: var(--sidebar-border);
|
|
99
|
+
--color-sidebar-accent-foreground: var(--sidebar-accent-foreground);
|
|
100
|
+
--color-sidebar-accent: var(--sidebar-accent);
|
|
101
|
+
--color-sidebar-primary-foreground: var(--sidebar-primary-foreground);
|
|
102
|
+
--color-sidebar-primary: var(--sidebar-primary);
|
|
103
|
+
--color-sidebar-foreground: var(--sidebar-foreground);
|
|
104
|
+
--color-sidebar: var(--sidebar);
|
|
105
|
+
--color-chart-5: var(--chart-5);
|
|
106
|
+
--color-chart-4: var(--chart-4);
|
|
107
|
+
--color-chart-3: var(--chart-3);
|
|
108
|
+
--color-chart-2: var(--chart-2);
|
|
109
|
+
--color-chart-1: var(--chart-1);
|
|
110
|
+
--color-ring: var(--ring);
|
|
111
|
+
--color-input: var(--input);
|
|
112
|
+
--color-border: var(--border);
|
|
113
|
+
--color-destructive: var(--destructive);
|
|
114
|
+
--color-accent-foreground: var(--accent-foreground);
|
|
115
|
+
--color-accent: var(--accent);
|
|
116
|
+
--color-muted-foreground: var(--muted-foreground);
|
|
117
|
+
--color-muted: var(--muted);
|
|
118
|
+
--color-secondary-foreground: var(--secondary-foreground);
|
|
119
|
+
--color-secondary: var(--secondary);
|
|
120
|
+
--color-primary-foreground: var(--primary-foreground);
|
|
121
|
+
--color-primary: var(--primary);
|
|
122
|
+
--color-popover-foreground: var(--popover-foreground);
|
|
123
|
+
--color-popover: var(--popover);
|
|
124
|
+
--color-card-foreground: var(--card-foreground);
|
|
125
|
+
--color-card: var(--card);
|
|
126
|
+
--color-foreground: var(--foreground);
|
|
127
|
+
--color-background: var(--background);
|
|
128
|
+
--radius-sm: calc(var(--radius) * 0.6);
|
|
129
|
+
--radius-md: calc(var(--radius) * 0.8);
|
|
130
|
+
--radius-lg: var(--radius);
|
|
131
|
+
--radius-xl: calc(var(--radius) * 1.4);
|
|
132
|
+
--radius-2xl: calc(var(--radius) * 1.8);
|
|
133
|
+
--radius-3xl: calc(var(--radius) * 2.2);
|
|
134
|
+
--radius-4xl: calc(var(--radius) * 2.6);
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
@layer base {
|
|
138
|
+
* {
|
|
139
|
+
@apply border-border outline-ring/50;
|
|
140
|
+
}
|
|
141
|
+
body {
|
|
142
|
+
@apply bg-background text-foreground;
|
|
143
|
+
}
|
|
144
|
+
html {
|
|
145
|
+
@apply font-sans;
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
/* What the backoffice draws beyond the preset, each a rule over a marker the
|
|
149
|
+
shared pieces already carry, so the tenant app, which does not load this
|
|
150
|
+
file, is unchanged. */
|
|
151
|
+
|
|
152
|
+
/* A role's keys as readable groups. The preset's badge is 10px, uppercase and
|
|
153
|
+
unframed, so a run of them read as one wall of letters; here each key is a
|
|
154
|
+
small framed word in the page's own case, and each group is a block. */
|
|
155
|
+
[data-permissions] > section + section {
|
|
156
|
+
border-top: 1px solid var(--border);
|
|
157
|
+
padding-top: 0.75rem;
|
|
158
|
+
}
|
|
159
|
+
[data-permissions] h3 {
|
|
160
|
+
color: var(--foreground);
|
|
161
|
+
font-size: 0.875rem;
|
|
162
|
+
font-weight: 600;
|
|
163
|
+
}
|
|
164
|
+
[data-permissions] [data-slot='badge'] {
|
|
165
|
+
border: 1px solid var(--border);
|
|
166
|
+
border-radius: 0;
|
|
167
|
+
padding: 0.125rem 0.5rem;
|
|
168
|
+
font-size: 0.8125rem;
|
|
169
|
+
font-weight: 400;
|
|
170
|
+
letter-spacing: normal;
|
|
171
|
+
text-transform: none;
|
|
172
|
+
}
|
|
173
|
+
/* A key the editor toggles (a button) is filled while it is on, which the
|
|
174
|
+
preset's badge shows by its text colour alone, and that the rule above
|
|
175
|
+
would otherwise have made the same as off. */
|
|
176
|
+
[data-permissions] button[data-slot='badge'][aria-pressed='true'] {
|
|
177
|
+
background: var(--primary);
|
|
178
|
+
border-color: var(--primary);
|
|
179
|
+
color: var(--primary-foreground);
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
/* An empty field reads as a dash, in a list's cell and on a record page, as the
|
|
183
|
+
checkbox cell already draws a question never answered. */
|
|
184
|
+
td[data-slot='table-cell']:empty::before,
|
|
185
|
+
a[data-row-link]:empty::before,
|
|
186
|
+
[data-field-value] > span:empty::before {
|
|
187
|
+
content: '—';
|
|
188
|
+
/* An empty alternative text, where it is understood, so a screen reader does not say "dash". */
|
|
189
|
+
content: '—' / '';
|
|
190
|
+
color: var(--muted-foreground);
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
/* The backoffice's cards are square, as its buttons, inputs and home cards are:
|
|
194
|
+
the section and form cards the registry draws with a rounded class of their
|
|
195
|
+
own, the frame a list's table sits in, the filter panel above it and the
|
|
196
|
+
danger region of a record page. */
|
|
197
|
+
[data-slot='card'],
|
|
198
|
+
:has(> [data-slot='table-container']),
|
|
199
|
+
[data-mithaq-filters],
|
|
200
|
+
[data-region='danger'] {
|
|
201
|
+
border-radius: 0;
|
|
202
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"target": "es2024",
|
|
4
|
+
"lib": ["es2024", "dom", "dom.iterable"],
|
|
5
|
+
"module": "preserve",
|
|
6
|
+
"moduleResolution": "bundler",
|
|
7
|
+
"jsx": "react-jsx",
|
|
8
|
+
"strict": true,
|
|
9
|
+
"noUncheckedIndexedAccess": true,
|
|
10
|
+
"noEmit": true,
|
|
11
|
+
"skipLibCheck": true,
|
|
12
|
+
"erasableSyntaxOnly": true,
|
|
13
|
+
"allowImportingTsExtensions": true,
|
|
14
|
+
"resolveJsonModule": true,
|
|
15
|
+
"types": ["node", "vite/client"],
|
|
16
|
+
"paths": { "@/*": ["./src/*"], "mithaq-admin/*": ["./src/components/mithaq/*", "./node_modules/mithaq-admin/registry/*"] }
|
|
17
|
+
},
|
|
18
|
+
"include": ["src/**/*.ts", "src/**/*.tsx", "vite.config.ts", "../../templates/mithaq/src/ui/*.tsx", "../../templates/asas/src/ui-*/*.tsx"]
|
|
19
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import tailwindcss from '@tailwindcss/vite'
|
|
2
|
+
import { tanstackStart } from '@tanstack/react-start/plugin/vite'
|
|
3
|
+
import viteReact from '@vitejs/plugin-react'
|
|
4
|
+
import { mithaqAdmin } from 'mithaq/vite'
|
|
5
|
+
import { defineConfig } from 'vite'
|
|
6
|
+
import { fileURLToPath } from 'node:url'
|
|
7
|
+
|
|
8
|
+
export default defineConfig({
|
|
9
|
+
// An alias rather than `resolve.tsconfigPaths`: the shells in the language package's `src/ui/` fall under a tsconfig
|
|
10
|
+
// with no `@/` and still import it. `^@/` leaves `@tanstack/*` alone.
|
|
11
|
+
resolve: { alias: [{ find: /^@\//, replacement: fileURLToPath(new URL('./src/', import.meta.url)) }] },
|
|
12
|
+
plugins: [
|
|
13
|
+
tailwindcss(),
|
|
14
|
+
// A file under this folder takes the place of mithaq-admin's file of the same name, for every importer.
|
|
15
|
+
mithaqAdmin(fileURLToPath(new URL('./src/components/mithaq/', import.meta.url))),
|
|
16
|
+
// `mithaq generate screens` writes `files.ts` and `types.ts` beside the route files; neither is a route.
|
|
17
|
+
tanstackStart({ router: { routeFileIgnorePattern: '^(files|types)\\.ts$' } }),
|
|
18
|
+
viteReact(),
|
|
19
|
+
],
|
|
20
|
+
// Behind the proxy that routes by Host: bound to the loopback address it targets, and answering
|
|
21
|
+
// the public name in BACKOFFICE_URL, which Vite refuses in a Host header unless named here.
|
|
22
|
+
preview: { host: '127.0.0.1', allowedHosts: process.env.BACKOFFICE_URL ? [new URL(process.env.BACKOFFICE_URL).hostname] : [] },
|
|
23
|
+
environments: {
|
|
24
|
+
ssr: {
|
|
25
|
+
// `asas` and `mithaq` stay external: Vite bundles linked workspace packages by default, and bundling
|
|
26
|
+
// would follow the drizzle adapter's `drizzle-kit/api-postgres` import, which must never be followed.
|
|
27
|
+
// Both keys are needed: `resolve.external` is what dev reads, `build.rollupOptions.external` what the build reads.
|
|
28
|
+
resolve: { external: ['asas', 'mithaq'] },
|
|
29
|
+
build: { rollupOptions: { external: [/^asas$/, /^mithaq(\/|$)/] } },
|
|
30
|
+
},
|
|
31
|
+
},
|
|
32
|
+
})
|
|
@@ -0,0 +1,25 @@
|
|
|
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": "src/styles.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
|
+
"menuColor": "default",
|
|
23
|
+
"menuAccent": "subtle",
|
|
24
|
+
"registries": {}
|
|
25
|
+
}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "asas-tanstack",
|
|
3
|
+
"version": "0.0.0",
|
|
4
|
+
"private": true,
|
|
5
|
+
"type": "module",
|
|
6
|
+
"scripts": {
|
|
7
|
+
"build": "(cd ../../templates/asas && mithaq lock --check) && vite build",
|
|
8
|
+
"dev": "(cd ../../templates/asas && mithaq lock --check) && vite dev",
|
|
9
|
+
"preview": "(cd ../../templates/asas && mithaq lock --check) && vite preview"
|
|
10
|
+
},
|
|
11
|
+
"dependencies": {
|
|
12
|
+
"@base-ui/react": "^1.5.0",
|
|
13
|
+
"@fontsource-variable/raleway": "^5.2.8",
|
|
14
|
+
"@fontsource/ibm-plex-sans-arabic": "^5.2.8",
|
|
15
|
+
"@hugeicons/core-free-icons": "4.3.5",
|
|
16
|
+
"@hugeicons/react": "^1.1.6",
|
|
17
|
+
"@lexical/extension": "0.52.0",
|
|
18
|
+
"@lexical/link": "0.52.0",
|
|
19
|
+
"@lexical/list": "0.52.0",
|
|
20
|
+
"@lexical/react": "0.52.0",
|
|
21
|
+
"@lexical/rich-text": "0.52.0",
|
|
22
|
+
"@lexical/selection": "0.52.0",
|
|
23
|
+
"@lexical/utils": "0.52.0",
|
|
24
|
+
"@orpc/client": "1.15.1",
|
|
25
|
+
"@orpc/server": "1.15.1",
|
|
26
|
+
"@tanstack/react-form": "^1.33.5",
|
|
27
|
+
"@tanstack/react-query": "^5.104.0",
|
|
28
|
+
"@tanstack/react-router": "^1.170.29",
|
|
29
|
+
"@tanstack/react-start": "^1.168.56",
|
|
30
|
+
"asas": "*",
|
|
31
|
+
"class-variance-authority": "^0.7.1",
|
|
32
|
+
"clsx": "^2.1.1",
|
|
33
|
+
"input-otp": "^1.5.0",
|
|
34
|
+
"lexical": "0.52.0",
|
|
35
|
+
"lucide-react": "^1.16.0",
|
|
36
|
+
"mithaq": "*",
|
|
37
|
+
"mithaq-admin": "*",
|
|
38
|
+
"react": "^19.3.0",
|
|
39
|
+
"react-day-picker": "^10.0.1",
|
|
40
|
+
"react-dom": "^19.3.0",
|
|
41
|
+
"react-is": "^19.3.0",
|
|
42
|
+
"react-resizable-panels": "^4.13.3",
|
|
43
|
+
"recharts": "3.8.0",
|
|
44
|
+
"tailwind-merge": "^3.6.0",
|
|
45
|
+
"tw-animate-css": "^1.4.0"
|
|
46
|
+
},
|
|
47
|
+
"devDependencies": {
|
|
48
|
+
"@tailwindcss/vite": "^4.3.0",
|
|
49
|
+
"@tanstack/start-client-core": "^1.170.1",
|
|
50
|
+
"@types/react": "^19.3.0",
|
|
51
|
+
"@types/react-dom": "^19.3.0",
|
|
52
|
+
"@vitejs/plugin-react": "^6.0.2",
|
|
53
|
+
"shadcn": "4.18.0",
|
|
54
|
+
"tailwindcss": "^4.3.0",
|
|
55
|
+
"typescript": "^7.0.2",
|
|
56
|
+
"vite": "^8.0.13"
|
|
57
|
+
}
|
|
58
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32"><rect width="32" height="32" rx="7" fill="#18181b"/><path d="M8 24h16M11 19h10M14 14h4M16 9v0" stroke="#fafafa" stroke-width="3" stroke-linecap="round"/></svg>
|
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
import { afterCommitHeader, type ClientKayan, type FetchClient } from 'mithaq/admin'
|
|
2
|
+
|
|
3
|
+
/** What a write may ask beside its input on a kayan that keeps drafts: a draft save, and an autosave. The fetch plug reads them off the query string only. */
|
|
4
|
+
export type DraftOptions = { readonly draft?: boolean; readonly autosave?: boolean }
|
|
5
|
+
|
|
6
|
+
const queryOf = ({ draft, autosave }: DraftOptions = {}) => (draft ? `?draft=true${autosave ? '&autosave=true' : ''}` : '')
|
|
7
|
+
|
|
8
|
+
const call = async ({ kayan, operation, input, ...options }: { kayan: string; operation: string; input: Record<string, unknown> } & DraftOptions) => {
|
|
9
|
+
const response = await fetch(`/api/${encodeURIComponent(kayan)}/${encodeURIComponent(operation)}${queryOf(options)}`, {
|
|
10
|
+
method: 'POST',
|
|
11
|
+
headers: { 'content-type': 'application/json' },
|
|
12
|
+
body: JSON.stringify(input),
|
|
13
|
+
})
|
|
14
|
+
const answer = await response.json().catch(() => null)
|
|
15
|
+
if (!response.ok) {
|
|
16
|
+
const error = new Error(typeof answer?.message === 'string' ? answer.message : `HTTP ${response.status}`)
|
|
17
|
+
Object.assign(error, { status: response.status, code: answer?.code, data: answer?.data })
|
|
18
|
+
throw error
|
|
19
|
+
}
|
|
20
|
+
// A failed `afterCommit` raises the screens' banner and a committed write
|
|
21
|
+
// clears it. Only the server knows an operation's effect, so the plug sets
|
|
22
|
+
// the header on writes and never on reads.
|
|
23
|
+
const said = response.headers.get(afterCommitHeader)
|
|
24
|
+
if (typeof window !== 'undefined' && said) {
|
|
25
|
+
window.dispatchEvent(new CustomEvent('mithaq:after-commit', { detail: said === 'failed' }))
|
|
26
|
+
}
|
|
27
|
+
return answer
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
// Restated, not imported from the `.tsx` file that declares them: a `.ts` file
|
|
31
|
+
// must not need the JSX compiler.
|
|
32
|
+
type FoundPage = { docs: readonly Record<string, unknown>[]; hasNextPage: boolean; nextPage: number | null }
|
|
33
|
+
type FindApi = Readonly<
|
|
34
|
+
Record<
|
|
35
|
+
string,
|
|
36
|
+
| {
|
|
37
|
+
find: (input: Record<string, unknown>) => Promise<FoundPage>
|
|
38
|
+
create?: (input: Record<string, unknown>) => Promise<unknown>
|
|
39
|
+
delete?: (input: Record<string, unknown>) => Promise<unknown>
|
|
40
|
+
update?: (input: Record<string, unknown>) => Promise<unknown>
|
|
41
|
+
}
|
|
42
|
+
| undefined
|
|
43
|
+
>
|
|
44
|
+
>
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Every operation of every kayan, keyed by name strings. `arm[kayan][operation](input)`
|
|
48
|
+
* posts to `POST /api/<kayan>/<operation>`; the proxy never answers `undefined`,
|
|
49
|
+
* and a name no kayan has is the server's not-found.
|
|
50
|
+
*/
|
|
51
|
+
export type Arm = Readonly<Record<string, Readonly<Record<string, (input: Record<string, unknown>, options?: DraftOptions) => Promise<unknown>>>>>
|
|
52
|
+
export const arm = new Proxy(Object.create(null) as Arm, {
|
|
53
|
+
get: (_target, kayan: string) => new Proxy(Object.create(null) as Record<string, (input: Record<string, unknown>, options?: DraftOptions) => Promise<unknown>>, {
|
|
54
|
+
get: (_operations, operation: string) => (input: Record<string, unknown>, options?: DraftOptions) => call({ kayan, operation, input, ...options }),
|
|
55
|
+
}),
|
|
56
|
+
})
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Whether an operation's input names a key: probes the schema with a value no
|
|
60
|
+
* browser-written schema accepts and looks for an issue at that key. A schema
|
|
61
|
+
* that answers asynchronously, or throws, reads as naming nothing.
|
|
62
|
+
*/
|
|
63
|
+
export const takesKey = (input: { '~standard': { validate: (value: unknown) => unknown } }, key: string) => {
|
|
64
|
+
let answer: { issues?: readonly { path?: readonly unknown[] }[] }
|
|
65
|
+
try {
|
|
66
|
+
answer = input['~standard'].validate({ [key]: Symbol(key) }) as typeof answer
|
|
67
|
+
} catch {
|
|
68
|
+
return false
|
|
69
|
+
}
|
|
70
|
+
if (answer instanceof Promise) {
|
|
71
|
+
answer.catch(() => {})
|
|
72
|
+
return false
|
|
73
|
+
}
|
|
74
|
+
return answer.issues?.some(({ path }) => {
|
|
75
|
+
const segment = path?.[0]
|
|
76
|
+
return String(typeof segment === 'object' && segment ? (segment as { key: unknown }).key : segment) === key
|
|
77
|
+
}) ?? false
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/** What a screen's `client` is over an arm; the draft words ride along with the input. */
|
|
81
|
+
export const clientOf = (through: Arm): FetchClient => ({ kayan, operation, input, draft, autosave }) => through[kayan]![operation]!(input, { draft, autosave })
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* What the server's `client` is: each operation in process, as `actor`. Only the
|
|
85
|
+
* two draft words are read off the caller's options and the actor goes last, so
|
|
86
|
+
* a loader cannot run as someone else.
|
|
87
|
+
*/
|
|
88
|
+
export const actingAs = (
|
|
89
|
+
through: Readonly<Record<string, { operations: Record<string, (input: object, options: { actor?: unknown } & DraftOptions) => Promise<unknown>> }>>,
|
|
90
|
+
actor: unknown,
|
|
91
|
+
): FetchClient => ({ kayan, operation, input, draft, autosave }) => through[kayan]!.operations[operation]!(input, { draft, autosave, actor })
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* What a relationship control and cell read a second kayan through. Only the
|
|
95
|
+
* kayans the screen's loader already narrowed to what this actor may read are
|
|
96
|
+
* in the record, so a picker cannot page any other.
|
|
97
|
+
*/
|
|
98
|
+
export const readsOf = (kayans: readonly ClientKayan[]): FindApi =>
|
|
99
|
+
Object.fromEntries(
|
|
100
|
+
kayans.map((kayan) => [
|
|
101
|
+
kayan.name,
|
|
102
|
+
{
|
|
103
|
+
find: (input: Record<string, unknown>) => arm[kayan.name]!.find!(input) as Promise<FoundPage>,
|
|
104
|
+
},
|
|
105
|
+
]),
|
|
106
|
+
)
|
|
107
|
+
|
|
108
|
+
type FieldReads = {
|
|
109
|
+
readonly collections?: Readonly<
|
|
110
|
+
Record<string, { readonly fields?: Readonly<Record<string, Readonly<Record<string, boolean>> | undefined>> } | undefined>
|
|
111
|
+
>
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
* One kayan of the projection with every field this actor may not read taken
|
|
116
|
+
* off it. A loader's result is serialized into the document, so an unreadable
|
|
117
|
+
* field's name, label or rule would otherwise reach the viewer.
|
|
118
|
+
*/
|
|
119
|
+
export const readable = (one: ClientKayan, report: FieldReads): ClientKayan => ({
|
|
120
|
+
...one,
|
|
121
|
+
fields: one.fields.filter((field) => report.collections?.[one.name]?.fields?.[field.name]?.read === true),
|
|
122
|
+
})
|
|
123
|
+
|
|
124
|
+
/**
|
|
125
|
+
* Whether a thrown refusal is the caller's. Those carry an HTTP status; a store
|
|
126
|
+
* that is down or a hook bug carries none and must be rethrown, not reported
|
|
127
|
+
* as a missing row.
|
|
128
|
+
*/
|
|
129
|
+
export const caused = (thrown: unknown): boolean =>
|
|
130
|
+
typeof (thrown as { status?: unknown } | null | undefined)?.status === 'number'
|
|
131
|
+
|
|
132
|
+
/**
|
|
133
|
+
* One POST at the auth arm (Better Auth's router under `/api/auth/user`) with the session cookie.
|
|
134
|
+
* Returns the answer instead of throwing: a refusal is a sentence for the viewer; no reply is status 0.
|
|
135
|
+
*/
|
|
136
|
+
export const authArm = async (path: string, body: unknown = {}) => {
|
|
137
|
+
const response = await fetch(`/api/auth/user/${path}`, {
|
|
138
|
+
method: 'POST',
|
|
139
|
+
headers: { 'content-type': 'application/json' },
|
|
140
|
+
body: JSON.stringify(body),
|
|
141
|
+
}).catch(() => null)
|
|
142
|
+
if (!response) return { ok: false, status: 0, message: undefined, body: null }
|
|
143
|
+
const answered = (await response.json().catch(() => null)) as ({ message?: string } & Record<string, unknown>) | null
|
|
144
|
+
// The status too, because a rate limit's refusal carries no code to read.
|
|
145
|
+
return { ok: response.ok, status: response.status, message: answered?.message, body: answered }
|
|
146
|
+
}
|
|
147
|
+
// mithaq-admin 2d3ab739c92c6c2b397041b76bf4b23b2902b7a2 94db2fc49ad4391c0d1c2183cb41001dd08d62017c76af86cc5f611488c22c68
|