create-mantiq 0.4.0 → 0.5.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/package.json +2 -1
- package/src/index.ts +44 -18
- package/src/templates.ts +14 -326
- package/src/ui/shadcn.ts +450 -192
- package/stubs/manifest.json +1415 -0
- package/stubs/react/components.json.stub +19 -0
- package/stubs/react/src/App.tsx.stub +64 -0
- package/stubs/react/src/components/data-table.tsx.stub +548 -0
- package/stubs/react/src/components/layout/app-sidebar.tsx.stub +74 -0
- package/stubs/react/src/components/layout/authenticated-layout.tsx.stub +42 -0
- package/stubs/react/src/components/layout/header.tsx.stub +88 -0
- package/stubs/react/src/components/layout/index.ts.stub +10 -0
- package/stubs/react/src/components/layout/main.tsx.stub +22 -0
- package/stubs/react/src/components/layout/nav-group.tsx.stub +193 -0
- package/stubs/react/src/components/layout/nav-user.tsx.stub +111 -0
- package/stubs/react/src/components/layout/search-dialog.tsx.stub +121 -0
- package/stubs/react/src/components/layout/sidebar-data.ts.stub +57 -0
- package/stubs/react/src/components/layout/theme-toggle.tsx.stub +43 -0
- package/stubs/react/src/components/layout/top-nav.tsx.stub +94 -0
- package/stubs/react/src/components/ui/avatar.tsx.stub +107 -0
- package/stubs/react/src/components/ui/badge.tsx.stub +48 -0
- package/stubs/react/src/components/ui/button.tsx.stub +64 -0
- package/stubs/react/src/components/ui/card.tsx.stub +92 -0
- package/stubs/react/src/components/ui/dialog.tsx.stub +156 -0
- package/stubs/react/src/components/ui/dropdown-menu.tsx.stub +255 -0
- package/stubs/react/src/components/ui/input.tsx.stub +21 -0
- package/stubs/react/src/components/ui/label.tsx.stub +22 -0
- package/stubs/react/src/components/ui/separator.tsx.stub +28 -0
- package/stubs/react/src/components/ui/sheet.tsx.stub +143 -0
- package/stubs/react/src/components/ui/sidebar.tsx.stub +726 -0
- package/stubs/react/src/components/ui/skeleton.tsx.stub +13 -0
- package/stubs/react/src/components/ui/table.tsx.stub +116 -0
- package/stubs/react/src/components/ui/tabs.tsx.stub +69 -0
- package/stubs/react/src/components/ui/tooltip.tsx.stub +55 -0
- package/stubs/react/src/hooks/use-mobile.ts.stub +19 -0
- package/stubs/react/src/lib/api.ts.stub +17 -0
- package/stubs/react/src/lib/utils.ts.stub +6 -0
- package/stubs/react/src/main.tsx.stub +10 -0
- package/stubs/react/src/pages/Dashboard.tsx.stub +283 -0
- package/stubs/react/src/pages/Login.tsx.stub +119 -0
- package/stubs/react/src/pages/Register.tsx.stub +133 -0
- package/stubs/react/src/pages/Users.tsx.stub +411 -0
- package/stubs/react/src/pages/account/layout.tsx.stub +59 -0
- package/stubs/react/src/pages/account/preferences.tsx.stub +80 -0
- package/stubs/react/src/pages/account/profile.tsx.stub +65 -0
- package/stubs/react/src/pages/account/security.tsx.stub +105 -0
- package/stubs/react/src/pages/users/dialogs.tsx.stub +309 -0
- package/stubs/react/src/pages.ts.stub +17 -0
- package/stubs/react/src/ssr.tsx.stub +7 -0
- package/stubs/react/src/style.css.stub +138 -0
- package/stubs/react/tsconfig.json.stub +36 -0
- package/stubs/react/vite.config.ts.stub +22 -0
- package/stubs/shared/app/Http/Controllers/AuthController.ts.stub +67 -0
- package/stubs/shared/app/Http/Controllers/HomeController.ts.stub +62 -0
- package/stubs/shared/app/Http/Controllers/PageController.ts.stub +66 -0
- package/stubs/shared/database/factories/UserFactory.ts.stub +16 -0
- package/stubs/shared/database/seeders/DatabaseSeeder.ts.stub +42 -0
- package/stubs/shared/routes/api.ts.stub +106 -0
- package/stubs/shared/routes/web.ts.stub +23 -0
- package/stubs/svelte/components.json.stub +15 -0
- package/stubs/svelte/src/App.svelte.stub +77 -0
- package/stubs/svelte/src/lib/api.ts.stub +17 -0
- package/stubs/svelte/src/lib/components/DataTable.svelte.stub +399 -0
- package/stubs/svelte/src/lib/components/layout/AppSidebar.svelte.stub +62 -0
- package/stubs/svelte/src/lib/components/layout/AuthenticatedLayout.svelte.stub +40 -0
- package/stubs/svelte/src/lib/components/layout/Header.svelte.stub +98 -0
- package/stubs/svelte/src/lib/components/layout/Main.svelte.stub +26 -0
- package/stubs/svelte/src/lib/components/layout/NavGroup.svelte.stub +142 -0
- package/stubs/svelte/src/lib/components/layout/NavUser.svelte.stub +100 -0
- package/stubs/svelte/src/lib/components/layout/SearchDialog.svelte.stub +124 -0
- package/stubs/svelte/src/lib/components/layout/ThemeToggle.svelte.stub +30 -0
- package/stubs/svelte/src/lib/components/layout/TopNav.svelte.stub +81 -0
- package/stubs/svelte/src/lib/components/layout/sidebar-data.ts.stub +57 -0
- package/stubs/svelte/src/lib/components/ui/avatar/avatar-badge.svelte.stub +26 -0
- package/stubs/svelte/src/lib/components/ui/avatar/avatar-fallback.svelte.stub +20 -0
- package/stubs/svelte/src/lib/components/ui/avatar/avatar-group-count.svelte.stub +23 -0
- package/stubs/svelte/src/lib/components/ui/avatar/avatar-group.svelte.stub +23 -0
- package/stubs/svelte/src/lib/components/ui/avatar/avatar-image.svelte.stub +17 -0
- package/stubs/svelte/src/lib/components/ui/avatar/avatar.svelte.stub +26 -0
- package/stubs/svelte/src/lib/components/ui/avatar/index.ts.stub +22 -0
- package/stubs/svelte/src/lib/components/ui/badge/badge.svelte.stub +49 -0
- package/stubs/svelte/src/lib/components/ui/badge/index.ts.stub +2 -0
- package/stubs/svelte/src/lib/components/ui/button/button.svelte.stub +82 -0
- package/stubs/svelte/src/lib/components/ui/button/index.ts.stub +17 -0
- package/stubs/svelte/src/lib/components/ui/card/card-action.svelte.stub +23 -0
- package/stubs/svelte/src/lib/components/ui/card/card-content.svelte.stub +20 -0
- package/stubs/svelte/src/lib/components/ui/card/card-description.svelte.stub +20 -0
- package/stubs/svelte/src/lib/components/ui/card/card-footer.svelte.stub +20 -0
- package/stubs/svelte/src/lib/components/ui/card/card-header.svelte.stub +23 -0
- package/stubs/svelte/src/lib/components/ui/card/card-title.svelte.stub +15 -0
- package/stubs/svelte/src/lib/components/ui/card/card.svelte.stub +22 -0
- package/stubs/svelte/src/lib/components/ui/card/index.ts.stub +25 -0
- package/stubs/svelte/src/lib/components/ui/dialog/dialog-close.svelte.stub +11 -0
- package/stubs/svelte/src/lib/components/ui/dialog/dialog-content.svelte.stub +48 -0
- package/stubs/svelte/src/lib/components/ui/dialog/dialog-description.svelte.stub +17 -0
- package/stubs/svelte/src/lib/components/ui/dialog/dialog-footer.svelte.stub +32 -0
- package/stubs/svelte/src/lib/components/ui/dialog/dialog-header.svelte.stub +20 -0
- package/stubs/svelte/src/lib/components/ui/dialog/dialog-overlay.svelte.stub +17 -0
- package/stubs/svelte/src/lib/components/ui/dialog/dialog-portal.svelte.stub +7 -0
- package/stubs/svelte/src/lib/components/ui/dialog/dialog-title.svelte.stub +17 -0
- package/stubs/svelte/src/lib/components/ui/dialog/dialog-trigger.svelte.stub +11 -0
- package/stubs/svelte/src/lib/components/ui/dialog/dialog.svelte.stub +7 -0
- package/stubs/svelte/src/lib/components/ui/dialog/index.ts.stub +34 -0
- package/stubs/svelte/src/lib/components/ui/dropdown-menu/dropdown-menu-checkbox-group.svelte.stub +16 -0
- package/stubs/svelte/src/lib/components/ui/dropdown-menu/dropdown-menu-checkbox-item.svelte.stub +44 -0
- package/stubs/svelte/src/lib/components/ui/dropdown-menu/dropdown-menu-content.svelte.stub +31 -0
- package/stubs/svelte/src/lib/components/ui/dropdown-menu/dropdown-menu-group-heading.svelte.stub +22 -0
- package/stubs/svelte/src/lib/components/ui/dropdown-menu/dropdown-menu-group.svelte.stub +7 -0
- package/stubs/svelte/src/lib/components/ui/dropdown-menu/dropdown-menu-item.svelte.stub +27 -0
- package/stubs/svelte/src/lib/components/ui/dropdown-menu/dropdown-menu-label.svelte.stub +24 -0
- package/stubs/svelte/src/lib/components/ui/dropdown-menu/dropdown-menu-portal.svelte.stub +7 -0
- package/stubs/svelte/src/lib/components/ui/dropdown-menu/dropdown-menu-radio-group.svelte.stub +16 -0
- package/stubs/svelte/src/lib/components/ui/dropdown-menu/dropdown-menu-radio-item.svelte.stub +34 -0
- package/stubs/svelte/src/lib/components/ui/dropdown-menu/dropdown-menu-separator.svelte.stub +17 -0
- package/stubs/svelte/src/lib/components/ui/dropdown-menu/dropdown-menu-shortcut.svelte.stub +20 -0
- package/stubs/svelte/src/lib/components/ui/dropdown-menu/dropdown-menu-sub-content.svelte.stub +17 -0
- package/stubs/svelte/src/lib/components/ui/dropdown-menu/dropdown-menu-sub-trigger.svelte.stub +29 -0
- package/stubs/svelte/src/lib/components/ui/dropdown-menu/dropdown-menu-sub.svelte.stub +7 -0
- package/stubs/svelte/src/lib/components/ui/dropdown-menu/dropdown-menu-trigger.svelte.stub +7 -0
- package/stubs/svelte/src/lib/components/ui/dropdown-menu/dropdown-menu.svelte.stub +7 -0
- package/stubs/svelte/src/lib/components/ui/dropdown-menu/index.ts.stub +54 -0
- package/stubs/svelte/src/lib/components/ui/input/index.ts.stub +7 -0
- package/stubs/svelte/src/lib/components/ui/input/input.svelte.stub +48 -0
- package/stubs/svelte/src/lib/components/ui/label/index.ts.stub +7 -0
- package/stubs/svelte/src/lib/components/ui/label/label.svelte.stub +20 -0
- package/stubs/svelte/src/lib/components/ui/separator/index.ts.stub +7 -0
- package/stubs/svelte/src/lib/components/ui/separator/separator.svelte.stub +23 -0
- package/stubs/svelte/src/lib/components/ui/sheet/index.ts.stub +34 -0
- package/stubs/svelte/src/lib/components/ui/sheet/sheet-close.svelte.stub +7 -0
- package/stubs/svelte/src/lib/components/ui/sheet/sheet-content.svelte.stub +55 -0
- package/stubs/svelte/src/lib/components/ui/sheet/sheet-description.svelte.stub +17 -0
- package/stubs/svelte/src/lib/components/ui/sheet/sheet-footer.svelte.stub +20 -0
- package/stubs/svelte/src/lib/components/ui/sheet/sheet-header.svelte.stub +20 -0
- package/stubs/svelte/src/lib/components/ui/sheet/sheet-overlay.svelte.stub +17 -0
- package/stubs/svelte/src/lib/components/ui/sheet/sheet-portal.svelte.stub +7 -0
- package/stubs/svelte/src/lib/components/ui/sheet/sheet-title.svelte.stub +17 -0
- package/stubs/svelte/src/lib/components/ui/sheet/sheet-trigger.svelte.stub +7 -0
- package/stubs/svelte/src/lib/components/ui/sheet/sheet.svelte.stub +7 -0
- package/stubs/svelte/src/lib/components/ui/sidebar/constants.ts.stub +6 -0
- package/stubs/svelte/src/lib/components/ui/sidebar/context.svelte.ts.stub +81 -0
- package/stubs/svelte/src/lib/components/ui/sidebar/index.ts.stub +75 -0
- package/stubs/svelte/src/lib/components/ui/sidebar/sidebar-content.svelte.stub +24 -0
- package/stubs/svelte/src/lib/components/ui/sidebar/sidebar-footer.svelte.stub +21 -0
- package/stubs/svelte/src/lib/components/ui/sidebar/sidebar-group-action.svelte.stub +33 -0
- package/stubs/svelte/src/lib/components/ui/sidebar/sidebar-group-content.svelte.stub +21 -0
- package/stubs/svelte/src/lib/components/ui/sidebar/sidebar-group-label.svelte.stub +33 -0
- package/stubs/svelte/src/lib/components/ui/sidebar/sidebar-group.svelte.stub +21 -0
- package/stubs/svelte/src/lib/components/ui/sidebar/sidebar-header.svelte.stub +21 -0
- package/stubs/svelte/src/lib/components/ui/sidebar/sidebar-input.svelte.stub +21 -0
- package/stubs/svelte/src/lib/components/ui/sidebar/sidebar-inset.svelte.stub +20 -0
- package/stubs/svelte/src/lib/components/ui/sidebar/sidebar-menu-action.svelte.stub +37 -0
- package/stubs/svelte/src/lib/components/ui/sidebar/sidebar-menu-badge.svelte.stub +24 -0
- package/stubs/svelte/src/lib/components/ui/sidebar/sidebar-menu-button.svelte.stub +102 -0
- package/stubs/svelte/src/lib/components/ui/sidebar/sidebar-menu-item.svelte.stub +21 -0
- package/stubs/svelte/src/lib/components/ui/sidebar/sidebar-menu-skeleton.svelte.stub +36 -0
- package/stubs/svelte/src/lib/components/ui/sidebar/sidebar-menu-sub-button.svelte.stub +39 -0
- package/stubs/svelte/src/lib/components/ui/sidebar/sidebar-menu-sub-item.svelte.stub +21 -0
- package/stubs/svelte/src/lib/components/ui/sidebar/sidebar-menu-sub.svelte.stub +21 -0
- package/stubs/svelte/src/lib/components/ui/sidebar/sidebar-menu.svelte.stub +21 -0
- package/stubs/svelte/src/lib/components/ui/sidebar/sidebar-provider.svelte.stub +53 -0
- package/stubs/svelte/src/lib/components/ui/sidebar/sidebar-rail.svelte.stub +36 -0
- package/stubs/svelte/src/lib/components/ui/sidebar/sidebar-separator.svelte.stub +19 -0
- package/stubs/svelte/src/lib/components/ui/sidebar/sidebar-trigger.svelte.stub +36 -0
- package/stubs/svelte/src/lib/components/ui/sidebar/sidebar.svelte.stub +108 -0
- package/stubs/svelte/src/lib/components/ui/skeleton/index.ts.stub +7 -0
- package/stubs/svelte/src/lib/components/ui/skeleton/skeleton.svelte.stub +17 -0
- package/stubs/svelte/src/lib/components/ui/table/index.ts.stub +28 -0
- package/stubs/svelte/src/lib/components/ui/table/table-body.svelte.stub +15 -0
- package/stubs/svelte/src/lib/components/ui/table/table-caption.svelte.stub +20 -0
- package/stubs/svelte/src/lib/components/ui/table/table-cell.svelte.stub +15 -0
- package/stubs/svelte/src/lib/components/ui/table/table-footer.svelte.stub +20 -0
- package/stubs/svelte/src/lib/components/ui/table/table-head.svelte.stub +15 -0
- package/stubs/svelte/src/lib/components/ui/table/table-header.svelte.stub +20 -0
- package/stubs/svelte/src/lib/components/ui/table/table-row.svelte.stub +15 -0
- package/stubs/svelte/src/lib/components/ui/table/table.svelte.stub +17 -0
- package/stubs/svelte/src/lib/components/ui/tabs/index.ts.stub +18 -0
- package/stubs/svelte/src/lib/components/ui/tabs/tabs-content.svelte.stub +17 -0
- package/stubs/svelte/src/lib/components/ui/tabs/tabs-list.svelte.stub +40 -0
- package/stubs/svelte/src/lib/components/ui/tabs/tabs-trigger.svelte.stub +23 -0
- package/stubs/svelte/src/lib/components/ui/tabs/tabs.svelte.stub +19 -0
- package/stubs/svelte/src/lib/components/ui/tooltip/index.ts.stub +19 -0
- package/stubs/svelte/src/lib/components/ui/tooltip/tooltip-content.svelte.stub +52 -0
- package/stubs/svelte/src/lib/components/ui/tooltip/tooltip-portal.svelte.stub +7 -0
- package/stubs/svelte/src/lib/components/ui/tooltip/tooltip-provider.svelte.stub +7 -0
- package/stubs/svelte/src/lib/components/ui/tooltip/tooltip-trigger.svelte.stub +7 -0
- package/stubs/svelte/src/lib/components/ui/tooltip/tooltip.svelte.stub +10 -0
- package/stubs/svelte/src/lib/hooks/is-mobile.svelte.ts.stub +9 -0
- package/stubs/svelte/src/lib/utils.ts.stub +6 -0
- package/stubs/svelte/src/main.ts.stub +12 -0
- package/stubs/svelte/src/pages/Dashboard.svelte.stub +201 -0
- package/stubs/svelte/src/pages/Login.svelte.stub +117 -0
- package/stubs/svelte/src/pages/Register.svelte.stub +130 -0
- package/stubs/svelte/src/pages/Users.svelte.stub +347 -0
- package/stubs/svelte/src/pages/account/layout.svelte.stub +62 -0
- package/stubs/svelte/src/pages/account/preferences.svelte.stub +79 -0
- package/stubs/svelte/src/pages/account/profile.svelte.stub +66 -0
- package/stubs/svelte/src/pages/account/security.svelte.stub +110 -0
- package/stubs/svelte/src/pages/users/AddUserDialog.svelte.stub +103 -0
- package/stubs/svelte/src/pages/users/DeleteUserDialog.svelte.stub +78 -0
- package/stubs/svelte/src/pages/users/EditUserDialog.svelte.stub +104 -0
- package/stubs/svelte/src/pages.ts.stub +17 -0
- package/stubs/svelte/src/ssr.ts.stub +10 -0
- package/stubs/svelte/src/style.css.stub +127 -0
- package/stubs/svelte/svelte.config.js.stub +5 -0
- package/stubs/svelte/tsconfig.json.stub +25 -0
- package/stubs/svelte/vite.config.ts.stub +23 -0
- package/stubs/vue/components.json.stub +21 -0
- package/stubs/vue/src/App.vue.stub +73 -0
- package/stubs/vue/src/components/DataTable.vue.stub +68 -0
- package/stubs/vue/src/components/layout/AppSidebar.vue.stub +68 -0
- package/stubs/vue/src/components/layout/AuthenticatedLayout.vue.stub +36 -0
- package/stubs/vue/src/components/layout/Header.vue.stub +89 -0
- package/stubs/vue/src/components/layout/Main.vue.stub +22 -0
- package/stubs/vue/src/components/layout/NavGroup.vue.stub +152 -0
- package/stubs/vue/src/components/layout/NavUser.vue.stub +113 -0
- package/stubs/vue/src/components/layout/SearchDialog.vue.stub +121 -0
- package/stubs/vue/src/components/layout/ThemeToggle.vue.stub +40 -0
- package/stubs/vue/src/components/layout/TopNav.vue.stub +77 -0
- package/stubs/vue/src/components/layout/index.ts.stub +9 -0
- package/stubs/vue/src/components/layout/sidebar-data.ts.stub +57 -0
- package/stubs/vue/src/components/ui/avatar/Avatar.vue.stub +18 -0
- package/stubs/vue/src/components/ui/avatar/AvatarFallback.vue.stub +21 -0
- package/stubs/vue/src/components/ui/avatar/AvatarImage.vue.stub +16 -0
- package/stubs/vue/src/components/ui/avatar/index.ts.stub +3 -0
- package/stubs/vue/src/components/ui/badge/Badge.vue.stub +26 -0
- package/stubs/vue/src/components/ui/badge/index.ts.stub +26 -0
- package/stubs/vue/src/components/ui/button/Button.vue.stub +31 -0
- package/stubs/vue/src/components/ui/button/index.ts.stub +38 -0
- package/stubs/vue/src/components/ui/card/Card.vue.stub +22 -0
- package/stubs/vue/src/components/ui/card/CardAction.vue.stub +17 -0
- package/stubs/vue/src/components/ui/card/CardContent.vue.stub +17 -0
- package/stubs/vue/src/components/ui/card/CardDescription.vue.stub +17 -0
- package/stubs/vue/src/components/ui/card/CardFooter.vue.stub +17 -0
- package/stubs/vue/src/components/ui/card/CardHeader.vue.stub +17 -0
- package/stubs/vue/src/components/ui/card/CardTitle.vue.stub +17 -0
- package/stubs/vue/src/components/ui/card/index.ts.stub +7 -0
- package/stubs/vue/src/components/ui/dialog/Dialog.vue.stub +19 -0
- package/stubs/vue/src/components/ui/dialog/DialogClose.vue.stub +15 -0
- package/stubs/vue/src/components/ui/dialog/DialogContent.vue.stub +53 -0
- package/stubs/vue/src/components/ui/dialog/DialogDescription.vue.stub +23 -0
- package/stubs/vue/src/components/ui/dialog/DialogFooter.vue.stub +27 -0
- package/stubs/vue/src/components/ui/dialog/DialogHeader.vue.stub +17 -0
- package/stubs/vue/src/components/ui/dialog/DialogOverlay.vue.stub +21 -0
- package/stubs/vue/src/components/ui/dialog/DialogScrollContent.vue.stub +59 -0
- package/stubs/vue/src/components/ui/dialog/DialogTitle.vue.stub +23 -0
- package/stubs/vue/src/components/ui/dialog/DialogTrigger.vue.stub +15 -0
- package/stubs/vue/src/components/ui/dialog/index.ts.stub +10 -0
- package/stubs/vue/src/components/ui/dropdown-menu/DropdownMenu.vue.stub +19 -0
- package/stubs/vue/src/components/ui/dropdown-menu/DropdownMenuCheckboxItem.vue.stub +39 -0
- package/stubs/vue/src/components/ui/dropdown-menu/DropdownMenuContent.vue.stub +39 -0
- package/stubs/vue/src/components/ui/dropdown-menu/DropdownMenuGroup.vue.stub +15 -0
- package/stubs/vue/src/components/ui/dropdown-menu/DropdownMenuItem.vue.stub +31 -0
- package/stubs/vue/src/components/ui/dropdown-menu/DropdownMenuLabel.vue.stub +23 -0
- package/stubs/vue/src/components/ui/dropdown-menu/DropdownMenuRadioGroup.vue.stub +21 -0
- package/stubs/vue/src/components/ui/dropdown-menu/DropdownMenuRadioItem.vue.stub +40 -0
- package/stubs/vue/src/components/ui/dropdown-menu/DropdownMenuSeparator.vue.stub +23 -0
- package/stubs/vue/src/components/ui/dropdown-menu/DropdownMenuShortcut.vue.stub +17 -0
- package/stubs/vue/src/components/ui/dropdown-menu/DropdownMenuSub.vue.stub +18 -0
- package/stubs/vue/src/components/ui/dropdown-menu/DropdownMenuSubContent.vue.stub +27 -0
- package/stubs/vue/src/components/ui/dropdown-menu/DropdownMenuSubTrigger.vue.stub +31 -0
- package/stubs/vue/src/components/ui/dropdown-menu/DropdownMenuTrigger.vue.stub +17 -0
- package/stubs/vue/src/components/ui/dropdown-menu/index.ts.stub +16 -0
- package/stubs/vue/src/components/ui/input/Input.vue.stub +33 -0
- package/stubs/vue/src/components/ui/input/index.ts.stub +1 -0
- package/stubs/vue/src/components/ui/label/Label.vue.stub +26 -0
- package/stubs/vue/src/components/ui/label/index.ts.stub +1 -0
- package/stubs/vue/src/components/ui/separator/Separator.vue.stub +29 -0
- package/stubs/vue/src/components/ui/separator/index.ts.stub +1 -0
- package/stubs/vue/src/components/ui/sheet/Sheet.vue.stub +19 -0
- package/stubs/vue/src/components/ui/sheet/SheetClose.vue.stub +15 -0
- package/stubs/vue/src/components/ui/sheet/SheetContent.vue.stub +62 -0
- package/stubs/vue/src/components/ui/sheet/SheetDescription.vue.stub +21 -0
- package/stubs/vue/src/components/ui/sheet/SheetFooter.vue.stub +16 -0
- package/stubs/vue/src/components/ui/sheet/SheetHeader.vue.stub +15 -0
- package/stubs/vue/src/components/ui/sheet/SheetOverlay.vue.stub +21 -0
- package/stubs/vue/src/components/ui/sheet/SheetTitle.vue.stub +21 -0
- package/stubs/vue/src/components/ui/sheet/SheetTrigger.vue.stub +15 -0
- package/stubs/vue/src/components/ui/sheet/index.ts.stub +8 -0
- package/stubs/vue/src/components/ui/sidebar/Sidebar.vue.stub +96 -0
- package/stubs/vue/src/components/ui/sidebar/SidebarContent.vue.stub +18 -0
- package/stubs/vue/src/components/ui/sidebar/SidebarFooter.vue.stub +18 -0
- package/stubs/vue/src/components/ui/sidebar/SidebarGroup.vue.stub +18 -0
- package/stubs/vue/src/components/ui/sidebar/SidebarGroupAction.vue.stub +27 -0
- package/stubs/vue/src/components/ui/sidebar/SidebarGroupContent.vue.stub +18 -0
- package/stubs/vue/src/components/ui/sidebar/SidebarGroupLabel.vue.stub +25 -0
- package/stubs/vue/src/components/ui/sidebar/SidebarHeader.vue.stub +18 -0
- package/stubs/vue/src/components/ui/sidebar/SidebarInput.vue.stub +22 -0
- package/stubs/vue/src/components/ui/sidebar/SidebarInset.vue.stub +21 -0
- package/stubs/vue/src/components/ui/sidebar/SidebarMenu.vue.stub +18 -0
- package/stubs/vue/src/components/ui/sidebar/SidebarMenuAction.vue.stub +35 -0
- package/stubs/vue/src/components/ui/sidebar/SidebarMenuBadge.vue.stub +26 -0
- package/stubs/vue/src/components/ui/sidebar/SidebarMenuButton.vue.stub +48 -0
- package/stubs/vue/src/components/ui/sidebar/SidebarMenuButtonChild.vue.stub +36 -0
- package/stubs/vue/src/components/ui/sidebar/SidebarMenuItem.vue.stub +18 -0
- package/stubs/vue/src/components/ui/sidebar/SidebarMenuSkeleton.vue.stub +35 -0
- package/stubs/vue/src/components/ui/sidebar/SidebarMenuSub.vue.stub +22 -0
- package/stubs/vue/src/components/ui/sidebar/SidebarMenuSubButton.vue.stub +36 -0
- package/stubs/vue/src/components/ui/sidebar/SidebarMenuSubItem.vue.stub +18 -0
- package/stubs/vue/src/components/ui/sidebar/SidebarProvider.vue.stub +82 -0
- package/stubs/vue/src/components/ui/sidebar/SidebarRail.vue.stub +33 -0
- package/stubs/vue/src/components/ui/sidebar/SidebarSeparator.vue.stub +19 -0
- package/stubs/vue/src/components/ui/sidebar/SidebarTrigger.vue.stub +27 -0
- package/stubs/vue/src/components/ui/sidebar/index.ts.stub +60 -0
- package/stubs/vue/src/components/ui/sidebar/utils.ts.stub +19 -0
- package/stubs/vue/src/components/ui/skeleton/Skeleton.vue.stub +17 -0
- package/stubs/vue/src/components/ui/skeleton/index.ts.stub +1 -0
- package/stubs/vue/src/components/ui/table/Table.vue.stub +16 -0
- package/stubs/vue/src/components/ui/table/TableBody.vue.stub +17 -0
- package/stubs/vue/src/components/ui/table/TableCaption.vue.stub +17 -0
- package/stubs/vue/src/components/ui/table/TableCell.vue.stub +22 -0
- package/stubs/vue/src/components/ui/table/TableEmpty.vue.stub +34 -0
- package/stubs/vue/src/components/ui/table/TableFooter.vue.stub +17 -0
- package/stubs/vue/src/components/ui/table/TableHead.vue.stub +17 -0
- package/stubs/vue/src/components/ui/table/TableHeader.vue.stub +17 -0
- package/stubs/vue/src/components/ui/table/TableRow.vue.stub +17 -0
- package/stubs/vue/src/components/ui/table/index.ts.stub +9 -0
- package/stubs/vue/src/components/ui/table/utils.ts.stub +10 -0
- package/stubs/vue/src/components/ui/tabs/Tabs.vue.stub +24 -0
- package/stubs/vue/src/components/ui/tabs/TabsContent.vue.stub +21 -0
- package/stubs/vue/src/components/ui/tabs/TabsList.vue.stub +24 -0
- package/stubs/vue/src/components/ui/tabs/TabsTrigger.vue.stub +26 -0
- package/stubs/vue/src/components/ui/tabs/index.ts.stub +4 -0
- package/stubs/vue/src/components/ui/tooltip/Tooltip.vue.stub +19 -0
- package/stubs/vue/src/components/ui/tooltip/TooltipContent.vue.stub +34 -0
- package/stubs/vue/src/components/ui/tooltip/TooltipProvider.vue.stub +14 -0
- package/stubs/vue/src/components/ui/tooltip/TooltipTrigger.vue.stub +15 -0
- package/stubs/vue/src/components/ui/tooltip/index.ts.stub +4 -0
- package/stubs/vue/src/lib/api.ts.stub +17 -0
- package/stubs/vue/src/lib/utils.ts.stub +7 -0
- package/stubs/vue/src/main.ts.stub +13 -0
- package/stubs/vue/src/pages/Dashboard.vue.stub +211 -0
- package/stubs/vue/src/pages/Login.vue.stub +118 -0
- package/stubs/vue/src/pages/Register.vue.stub +133 -0
- package/stubs/vue/src/pages/Users.vue.stub +209 -0
- package/stubs/vue/src/pages/account/layout.vue.stub +57 -0
- package/stubs/vue/src/pages/account/preferences.vue.stub +88 -0
- package/stubs/vue/src/pages/account/profile.vue.stub +78 -0
- package/stubs/vue/src/pages/account/security.vue.stub +93 -0
- package/stubs/vue/src/pages/users/dialogs/AddUserDialog.vue.stub +86 -0
- package/stubs/vue/src/pages/users/dialogs/DeleteUserDialog.vue.stub +62 -0
- package/stubs/vue/src/pages/users/dialogs/EditUserDialog.vue.stub +94 -0
- package/stubs/vue/src/pages.ts.stub +17 -0
- package/stubs/vue/src/ssr.ts.stub +9 -0
- package/stubs/vue/src/style.css.stub +27 -0
- package/stubs/vue/tsconfig.json.stub +23 -0
- package/stubs/vue/vite.config.ts.stub +22 -0
package/src/ui/shadcn.ts
CHANGED
|
@@ -227,8 +227,8 @@ export function cn(...inputs: ClassValue[]) {
|
|
|
227
227
|
`,
|
|
228
228
|
|
|
229
229
|
// Components (button, input, label, card, badge, table, avatar,
|
|
230
|
-
// separator,
|
|
231
|
-
// scaffold — no hand-rolled templates needed.
|
|
230
|
+
// separator, dropdown-menu, sheet, tooltip, sidebar) are installed
|
|
231
|
+
// by the shadcn CLI during scaffold — no hand-rolled templates needed.
|
|
232
232
|
|
|
233
233
|
// ── Login page (shadcn) ─────────────────────────────────────────────────
|
|
234
234
|
'src/pages/Login.tsx': `import { useState } from 'react'
|
|
@@ -236,7 +236,8 @@ import { post } from '../lib/api.ts'
|
|
|
236
236
|
import { Button } from '@/components/ui/button'
|
|
237
237
|
import { Input } from '@/components/ui/input'
|
|
238
238
|
import { Label } from '@/components/ui/label'
|
|
239
|
-
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card'
|
|
239
|
+
import { Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle } from '@/components/ui/card'
|
|
240
|
+
import { LogIn } from 'lucide-react'
|
|
240
241
|
|
|
241
242
|
interface LoginProps {
|
|
242
243
|
appName?: string
|
|
@@ -267,52 +268,61 @@ export default function Login({ appName = '${ctx.name}', navigate }: LoginProps)
|
|
|
267
268
|
<h2 className="text-lg font-semibold text-foreground">{appName}</h2>
|
|
268
269
|
</div>
|
|
269
270
|
<Card>
|
|
270
|
-
<CardHeader>
|
|
271
|
+
<CardHeader className="text-center">
|
|
271
272
|
<CardTitle className="text-xl">Welcome back</CardTitle>
|
|
272
|
-
<CardDescription>Sign in to your account</CardDescription>
|
|
273
|
+
<CardDescription>Sign in to your account to continue</CardDescription>
|
|
273
274
|
</CardHeader>
|
|
274
275
|
<CardContent>
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
<
|
|
282
|
-
<
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
276
|
+
{error && (
|
|
277
|
+
<div className="mb-4 rounded-lg border border-destructive/50 bg-destructive/10 px-4 py-3 text-sm text-destructive">
|
|
278
|
+
{error}
|
|
279
|
+
</div>
|
|
280
|
+
)}
|
|
281
|
+
<form onSubmit={handleSubmit} className="space-y-4">
|
|
282
|
+
<div className="space-y-2">
|
|
283
|
+
<Label htmlFor="email">Email</Label>
|
|
284
|
+
<Input
|
|
285
|
+
id="email"
|
|
286
|
+
type="email"
|
|
287
|
+
value={email}
|
|
288
|
+
onChange={(e) => setEmail(e.target.value)}
|
|
289
|
+
required
|
|
290
|
+
placeholder="admin@example.com"
|
|
291
|
+
autoComplete="email"
|
|
292
|
+
/>
|
|
293
|
+
</div>
|
|
294
|
+
<div className="space-y-2">
|
|
295
|
+
<Label htmlFor="password">Password</Label>
|
|
296
|
+
<Input
|
|
297
|
+
id="password"
|
|
298
|
+
type="password"
|
|
299
|
+
value={password}
|
|
300
|
+
onChange={(e) => setPassword(e.target.value)}
|
|
301
|
+
required
|
|
302
|
+
placeholder="Enter your password"
|
|
303
|
+
autoComplete="current-password"
|
|
304
|
+
/>
|
|
305
|
+
</div>
|
|
306
|
+
<Button type="submit" className="w-full" disabled={loading}>
|
|
307
|
+
{loading ? (
|
|
308
|
+
'Signing in\u2026'
|
|
309
|
+
) : (
|
|
310
|
+
<>
|
|
311
|
+
<LogIn className="mr-2 h-4 w-4" />
|
|
312
|
+
Sign in
|
|
313
|
+
</>
|
|
314
|
+
)}
|
|
315
|
+
</Button>
|
|
316
|
+
</form>
|
|
315
317
|
</CardContent>
|
|
318
|
+
<CardFooter className="justify-center">
|
|
319
|
+
<p className="text-sm text-muted-foreground">
|
|
320
|
+
Don't have an account?{' '}
|
|
321
|
+
<Button variant="link" className="h-auto p-0 text-sm" onClick={() => navigate('/register')}>
|
|
322
|
+
Register
|
|
323
|
+
</Button>
|
|
324
|
+
</p>
|
|
325
|
+
</CardFooter>
|
|
316
326
|
</Card>
|
|
317
327
|
</div>
|
|
318
328
|
</div>
|
|
@@ -326,7 +336,8 @@ import { post } from '../lib/api.ts'
|
|
|
326
336
|
import { Button } from '@/components/ui/button'
|
|
327
337
|
import { Input } from '@/components/ui/input'
|
|
328
338
|
import { Label } from '@/components/ui/label'
|
|
329
|
-
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card'
|
|
339
|
+
import { Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle } from '@/components/ui/card'
|
|
340
|
+
import { UserPlus } from 'lucide-react'
|
|
330
341
|
|
|
331
342
|
interface RegisterProps {
|
|
332
343
|
appName?: string
|
|
@@ -358,62 +369,72 @@ export default function Register({ appName = '${ctx.name}', navigate }: Register
|
|
|
358
369
|
<h2 className="text-lg font-semibold text-foreground">{appName}</h2>
|
|
359
370
|
</div>
|
|
360
371
|
<Card>
|
|
361
|
-
<CardHeader>
|
|
372
|
+
<CardHeader className="text-center">
|
|
362
373
|
<CardTitle className="text-xl">Create an account</CardTitle>
|
|
363
374
|
<CardDescription>Get started with {appName}</CardDescription>
|
|
364
375
|
</CardHeader>
|
|
365
376
|
<CardContent>
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
<
|
|
373
|
-
<
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
377
|
+
{error && (
|
|
378
|
+
<div className="mb-4 rounded-lg border border-destructive/50 bg-destructive/10 px-4 py-3 text-sm text-destructive">
|
|
379
|
+
{error}
|
|
380
|
+
</div>
|
|
381
|
+
)}
|
|
382
|
+
<form onSubmit={handleSubmit} className="space-y-4">
|
|
383
|
+
<div className="space-y-2">
|
|
384
|
+
<Label htmlFor="name">Name</Label>
|
|
385
|
+
<Input
|
|
386
|
+
id="name"
|
|
387
|
+
value={name}
|
|
388
|
+
onChange={(e) => setName(e.target.value)}
|
|
389
|
+
required
|
|
390
|
+
placeholder="Your name"
|
|
391
|
+
autoComplete="name"
|
|
392
|
+
/>
|
|
393
|
+
</div>
|
|
394
|
+
<div className="space-y-2">
|
|
395
|
+
<Label htmlFor="email">Email</Label>
|
|
396
|
+
<Input
|
|
397
|
+
id="email"
|
|
398
|
+
type="email"
|
|
399
|
+
value={email}
|
|
400
|
+
onChange={(e) => setEmail(e.target.value)}
|
|
401
|
+
required
|
|
402
|
+
placeholder="you@example.com"
|
|
403
|
+
autoComplete="email"
|
|
404
|
+
/>
|
|
405
|
+
</div>
|
|
406
|
+
<div className="space-y-2">
|
|
407
|
+
<Label htmlFor="password">Password</Label>
|
|
408
|
+
<Input
|
|
409
|
+
id="password"
|
|
410
|
+
type="password"
|
|
411
|
+
value={password}
|
|
412
|
+
onChange={(e) => setPassword(e.target.value)}
|
|
413
|
+
required
|
|
414
|
+
placeholder="Create a password"
|
|
415
|
+
autoComplete="new-password"
|
|
416
|
+
/>
|
|
417
|
+
</div>
|
|
418
|
+
<Button type="submit" className="w-full" disabled={loading}>
|
|
419
|
+
{loading ? (
|
|
420
|
+
'Creating account\u2026'
|
|
421
|
+
) : (
|
|
422
|
+
<>
|
|
423
|
+
<UserPlus className="mr-2 h-4 w-4" />
|
|
424
|
+
Create account
|
|
425
|
+
</>
|
|
426
|
+
)}
|
|
427
|
+
</Button>
|
|
428
|
+
</form>
|
|
416
429
|
</CardContent>
|
|
430
|
+
<CardFooter className="justify-center">
|
|
431
|
+
<p className="text-sm text-muted-foreground">
|
|
432
|
+
Already have an account?{' '}
|
|
433
|
+
<Button variant="link" className="h-auto p-0 text-sm" onClick={() => navigate('/login')}>
|
|
434
|
+
Sign in
|
|
435
|
+
</Button>
|
|
436
|
+
</p>
|
|
437
|
+
</CardFooter>
|
|
417
438
|
</Card>
|
|
418
439
|
</div>
|
|
419
440
|
</div>
|
|
@@ -437,13 +458,53 @@ import {
|
|
|
437
458
|
TableHeader,
|
|
438
459
|
TableRow,
|
|
439
460
|
} from '@/components/ui/table'
|
|
440
|
-
|
|
441
|
-
|
|
461
|
+
import {
|
|
462
|
+
DropdownMenu,
|
|
463
|
+
DropdownMenuContent,
|
|
464
|
+
DropdownMenuItem,
|
|
465
|
+
DropdownMenuLabel,
|
|
466
|
+
DropdownMenuSeparator,
|
|
467
|
+
DropdownMenuTrigger,
|
|
468
|
+
} from '@/components/ui/dropdown-menu'
|
|
469
|
+
import {
|
|
470
|
+
Tooltip,
|
|
471
|
+
TooltipContent,
|
|
472
|
+
TooltipProvider,
|
|
473
|
+
TooltipTrigger,
|
|
474
|
+
} from '@/components/ui/tooltip'
|
|
475
|
+
import { Sheet, SheetContent, SheetTrigger } from '@/components/ui/sheet'
|
|
476
|
+
import {
|
|
477
|
+
Home,
|
|
478
|
+
Users,
|
|
479
|
+
PanelLeftClose,
|
|
480
|
+
PanelLeft,
|
|
481
|
+
Sun,
|
|
482
|
+
Moon,
|
|
483
|
+
Menu,
|
|
484
|
+
Github,
|
|
485
|
+
ChevronDown,
|
|
486
|
+
LogOut,
|
|
487
|
+
Settings,
|
|
488
|
+
User,
|
|
489
|
+
Activity,
|
|
490
|
+
Zap,
|
|
491
|
+
TrendingUp,
|
|
492
|
+
UserCheck,
|
|
493
|
+
UserPlus,
|
|
494
|
+
Shield,
|
|
495
|
+
} from 'lucide-react'
|
|
496
|
+
|
|
497
|
+
interface UserRecord {
|
|
498
|
+
id: number
|
|
499
|
+
name: string
|
|
500
|
+
email: string
|
|
501
|
+
role: string
|
|
502
|
+
}
|
|
442
503
|
|
|
443
504
|
interface DashboardProps {
|
|
444
505
|
appName?: string
|
|
445
|
-
currentUser?:
|
|
446
|
-
users?:
|
|
506
|
+
currentUser?: UserRecord | null
|
|
507
|
+
users?: UserRecord[]
|
|
447
508
|
navigate: (href: string) => void
|
|
448
509
|
[key: string]: any
|
|
449
510
|
}
|
|
@@ -457,12 +518,166 @@ function getInitials(name: string) {
|
|
|
457
518
|
.slice(0, 2)
|
|
458
519
|
}
|
|
459
520
|
|
|
460
|
-
|
|
461
|
-
|
|
521
|
+
/* ── Sidebar nav items ────────────────────────────────────────────────────── */
|
|
522
|
+
const mainNav = [
|
|
523
|
+
{ label: 'Dashboard', icon: Home, href: '/dashboard', active: true },
|
|
524
|
+
{ label: 'Users', icon: Users, href: '#users-section', scroll: true },
|
|
525
|
+
]
|
|
526
|
+
const secondaryNav = [
|
|
527
|
+
{ label: 'Heartbeat', icon: Activity, href: '/_heartbeat' },
|
|
528
|
+
{ label: 'API Ping', icon: Zap, href: '/api/ping' },
|
|
529
|
+
]
|
|
530
|
+
|
|
531
|
+
/* ── Sidebar content (shared between desktop aside & mobile Sheet) ──────── */
|
|
532
|
+
function SidebarNav({
|
|
533
|
+
appName,
|
|
534
|
+
collapsed,
|
|
535
|
+
onToggle,
|
|
536
|
+
onNavigate,
|
|
537
|
+
}: {
|
|
538
|
+
appName: string
|
|
539
|
+
collapsed: boolean
|
|
540
|
+
onToggle: () => void
|
|
541
|
+
onNavigate?: () => void
|
|
542
|
+
}) {
|
|
543
|
+
return (
|
|
544
|
+
<TooltipProvider delayDuration={0}>
|
|
545
|
+
<div className="flex h-full flex-col bg-sidebar text-sidebar-foreground">
|
|
546
|
+
{/* Brand */}
|
|
547
|
+
<div className="flex h-14 items-center border-b border-sidebar-border px-3">
|
|
548
|
+
{!collapsed && (
|
|
549
|
+
<span className="flex-1 truncate pl-2 text-sm font-semibold">{appName}</span>
|
|
550
|
+
)}
|
|
551
|
+
<Tooltip>
|
|
552
|
+
<TooltipTrigger asChild>
|
|
553
|
+
<Button
|
|
554
|
+
variant="ghost"
|
|
555
|
+
size="icon"
|
|
556
|
+
className="ml-auto h-8 w-8 text-sidebar-foreground/60 hover:text-sidebar-foreground"
|
|
557
|
+
onClick={onToggle}
|
|
558
|
+
>
|
|
559
|
+
{collapsed ? <PanelLeft className="h-4 w-4" /> : <PanelLeftClose className="h-4 w-4" />}
|
|
560
|
+
</Button>
|
|
561
|
+
</TooltipTrigger>
|
|
562
|
+
<TooltipContent side="right">{collapsed ? 'Expand sidebar' : 'Collapse sidebar'}</TooltipContent>
|
|
563
|
+
</Tooltip>
|
|
564
|
+
</div>
|
|
565
|
+
|
|
566
|
+
{/* Main nav */}
|
|
567
|
+
<nav className="flex-1 space-y-1 px-2 py-3">
|
|
568
|
+
{mainNav.map((item) => {
|
|
569
|
+
const Icon = item.icon
|
|
570
|
+
const btn = (
|
|
571
|
+
<Button
|
|
572
|
+
key={item.label}
|
|
573
|
+
variant="ghost"
|
|
574
|
+
className={\`w-full \${collapsed ? 'justify-center px-0' : 'justify-start gap-3'} \${
|
|
575
|
+
item.active
|
|
576
|
+
? 'bg-sidebar-accent text-sidebar-accent-foreground'
|
|
577
|
+
: 'text-sidebar-foreground/60 hover:bg-sidebar-accent/50 hover:text-sidebar-foreground'
|
|
578
|
+
}\`}
|
|
579
|
+
asChild
|
|
580
|
+
>
|
|
581
|
+
<a
|
|
582
|
+
href={item.href}
|
|
583
|
+
onClick={
|
|
584
|
+
item.scroll
|
|
585
|
+
? (e: React.MouseEvent) => {
|
|
586
|
+
e.preventDefault()
|
|
587
|
+
document.getElementById('users-section')?.scrollIntoView({ behavior: 'smooth' })
|
|
588
|
+
onNavigate?.()
|
|
589
|
+
}
|
|
590
|
+
: onNavigate
|
|
591
|
+
? (e: React.MouseEvent) => { onNavigate() }
|
|
592
|
+
: undefined
|
|
593
|
+
}
|
|
594
|
+
>
|
|
595
|
+
<Icon className="h-4 w-4 shrink-0" />
|
|
596
|
+
{!collapsed && item.label}
|
|
597
|
+
</a>
|
|
598
|
+
</Button>
|
|
599
|
+
)
|
|
600
|
+
if (collapsed) {
|
|
601
|
+
return (
|
|
602
|
+
<Tooltip key={item.label}>
|
|
603
|
+
<TooltipTrigger asChild>{btn}</TooltipTrigger>
|
|
604
|
+
<TooltipContent side="right">{item.label}</TooltipContent>
|
|
605
|
+
</Tooltip>
|
|
606
|
+
)
|
|
607
|
+
}
|
|
608
|
+
return btn
|
|
609
|
+
})}
|
|
610
|
+
</nav>
|
|
611
|
+
|
|
612
|
+
<Separator className="bg-sidebar-border" />
|
|
613
|
+
|
|
614
|
+
{/* Secondary nav */}
|
|
615
|
+
<div className="space-y-1 px-2 py-3">
|
|
616
|
+
{secondaryNav.map((item) => {
|
|
617
|
+
const Icon = item.icon
|
|
618
|
+
const btn = (
|
|
619
|
+
<Button
|
|
620
|
+
key={item.label}
|
|
621
|
+
variant="ghost"
|
|
622
|
+
className={\`w-full \${collapsed ? 'justify-center px-0' : 'justify-start gap-3'} text-sidebar-foreground/60 hover:bg-sidebar-accent/50 hover:text-sidebar-foreground\`}
|
|
623
|
+
asChild
|
|
624
|
+
>
|
|
625
|
+
<a href={item.href} onClick={onNavigate ? () => onNavigate() : undefined}>
|
|
626
|
+
<Icon className="h-4 w-4 shrink-0" />
|
|
627
|
+
{!collapsed && item.label}
|
|
628
|
+
</a>
|
|
629
|
+
</Button>
|
|
630
|
+
)
|
|
631
|
+
if (collapsed) {
|
|
632
|
+
return (
|
|
633
|
+
<Tooltip key={item.label}>
|
|
634
|
+
<TooltipTrigger asChild>{btn}</TooltipTrigger>
|
|
635
|
+
<TooltipContent side="right">{item.label}</TooltipContent>
|
|
636
|
+
</Tooltip>
|
|
637
|
+
)
|
|
638
|
+
}
|
|
639
|
+
return btn
|
|
640
|
+
})}
|
|
641
|
+
</div>
|
|
642
|
+
|
|
643
|
+
{/* GitHub link */}
|
|
644
|
+
<div className="border-t border-sidebar-border px-2 py-3">
|
|
645
|
+
{collapsed ? (
|
|
646
|
+
<Tooltip>
|
|
647
|
+
<TooltipTrigger asChild>
|
|
648
|
+
<Button variant="ghost" size="icon" className="w-full text-sidebar-foreground/60 hover:text-sidebar-foreground" asChild>
|
|
649
|
+
<a href="https://github.com" target="_blank" rel="noreferrer">
|
|
650
|
+
<Github className="h-4 w-4" />
|
|
651
|
+
</a>
|
|
652
|
+
</Button>
|
|
653
|
+
</TooltipTrigger>
|
|
654
|
+
<TooltipContent side="right">GitHub</TooltipContent>
|
|
655
|
+
</Tooltip>
|
|
656
|
+
) : (
|
|
657
|
+
<Button variant="ghost" className="w-full justify-start gap-3 text-sidebar-foreground/60 hover:text-sidebar-foreground" asChild>
|
|
658
|
+
<a href="https://github.com" target="_blank" rel="noreferrer">
|
|
659
|
+
<Github className="h-4 w-4 shrink-0" />
|
|
660
|
+
GitHub
|
|
661
|
+
</a>
|
|
662
|
+
</Button>
|
|
663
|
+
)}
|
|
664
|
+
</div>
|
|
665
|
+
</div>
|
|
666
|
+
</TooltipProvider>
|
|
667
|
+
)
|
|
668
|
+
}
|
|
669
|
+
|
|
670
|
+
export default function Dashboard({
|
|
671
|
+
appName = '${ctx.name}',
|
|
672
|
+
currentUser,
|
|
673
|
+
users: initialUsers,
|
|
674
|
+
navigate,
|
|
675
|
+
}: DashboardProps) {
|
|
676
|
+
const [users, setUsers] = useState<UserRecord[]>(initialUsers ?? [])
|
|
462
677
|
const [loading, setLoading] = useState(!initialUsers?.length)
|
|
463
|
-
const [
|
|
678
|
+
const [collapsed, setCollapsed] = useState(false)
|
|
464
679
|
const [isDark, setIsDark] = useState(() =>
|
|
465
|
-
typeof document !== 'undefined' ? document.documentElement.classList.contains('dark') : true
|
|
680
|
+
typeof document !== 'undefined' ? document.documentElement.classList.contains('dark') : true,
|
|
466
681
|
)
|
|
467
682
|
|
|
468
683
|
const toggleTheme = () => {
|
|
@@ -487,117 +702,160 @@ export default function Dashboard({ appName = '${ctx.name}', currentUser, users:
|
|
|
487
702
|
navigate('/login')
|
|
488
703
|
}
|
|
489
704
|
|
|
705
|
+
/* Stats — derived from real user data + mock extras */
|
|
706
|
+
const stats = [
|
|
707
|
+
{ label: 'Total Users', value: users.length, icon: Users, change: '+12%' },
|
|
708
|
+
{ label: 'Active Now', value: Math.max(1, Math.ceil(users.length * 0.6)), icon: UserCheck, change: '+3%' },
|
|
709
|
+
{ label: 'New Today', value: Math.min(users.length, 2), icon: UserPlus, change: '+18%' },
|
|
710
|
+
{ label: 'Admin Users', value: users.filter((u) => u.role === 'admin').length, icon: Shield, change: '0%' },
|
|
711
|
+
]
|
|
712
|
+
|
|
713
|
+
const sidebarWidth = collapsed ? 'w-16' : 'w-60'
|
|
714
|
+
|
|
490
715
|
return (
|
|
491
|
-
<div className="min-h-screen
|
|
492
|
-
{/*
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
716
|
+
<div className="min-h-screen bg-background">
|
|
717
|
+
{/* ── Desktop sidebar ──────────────────────────────────────────────── */}
|
|
718
|
+
<aside
|
|
719
|
+
className={\`fixed inset-y-0 left-0 z-30 hidden border-r border-sidebar-border transition-all duration-200 lg:block \${sidebarWidth}\`}
|
|
720
|
+
>
|
|
721
|
+
<SidebarNav
|
|
722
|
+
appName={appName}
|
|
723
|
+
collapsed={collapsed}
|
|
724
|
+
onToggle={() => setCollapsed((c) => !c)}
|
|
497
725
|
/>
|
|
498
|
-
)}
|
|
499
|
-
|
|
500
|
-
{/* Sidebar */}
|
|
501
|
-
<aside className={\`fixed inset-y-0 left-0 z-50 w-60 bg-sidebar border-r border-sidebar-border flex flex-col transition-transform lg:translate-x-0 \${sidebarOpen ? 'translate-x-0' : '-translate-x-full'}\`}>
|
|
502
|
-
<div className="h-14 flex items-center px-5 border-b border-sidebar-border">
|
|
503
|
-
<span className="text-sm font-semibold text-sidebar-foreground">{appName}</span>
|
|
504
|
-
</div>
|
|
505
|
-
<nav className="flex-1 px-3 py-3 space-y-1">
|
|
506
|
-
<Button variant="ghost" className="w-full justify-start gap-2.5 bg-sidebar-accent text-sidebar-accent-foreground" asChild>
|
|
507
|
-
<a href="/dashboard">
|
|
508
|
-
<svg className="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path strokeLinecap="round" strokeLinejoin="round" strokeWidth={1.5} d="M3 12l2-2m0 0l7-7 7 7M5 10v10a1 1 0 001 1h3m10-11l2 2m-2-2v10a1 1 0 01-1 1h-3m-6 0a1 1 0 001-1v-4a1 1 0 011-1h2a1 1 0 011 1v4a1 1 0 001 1m-6 0h6" /></svg>
|
|
509
|
-
Dashboard
|
|
510
|
-
</a>
|
|
511
|
-
</Button>
|
|
512
|
-
<Button variant="ghost" className="w-full justify-start gap-2.5 text-sidebar-foreground/60 hover:text-sidebar-foreground" asChild>
|
|
513
|
-
<a href="#users-section" onClick={(e) => { e.preventDefault(); document.getElementById('users-section')?.scrollIntoView({ behavior: 'smooth' }); setSidebarOpen(false) }}>
|
|
514
|
-
<svg className="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path strokeLinecap="round" strokeLinejoin="round" strokeWidth={1.5} d="M12 4.354a4 4 0 110 5.292M15 21H3v-1a6 6 0 0112 0v1zm0 0h6v-1a6 6 0 00-9-5.197M13 7a4 4 0 11-8 0 4 4 0 018 0z" /></svg>
|
|
515
|
-
Users
|
|
516
|
-
</a>
|
|
517
|
-
</Button>
|
|
518
|
-
</nav>
|
|
519
|
-
<Separator />
|
|
520
|
-
<div className="px-3 py-3 space-y-1">
|
|
521
|
-
<Button variant="ghost" className="w-full justify-start gap-2.5 text-sidebar-foreground/60 hover:text-sidebar-foreground" asChild>
|
|
522
|
-
<a href="/_heartbeat">
|
|
523
|
-
<svg className="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path strokeLinecap="round" strokeLinejoin="round" strokeWidth={1.5} d="M3.055 11H5a2 2 0 012 2v1a2 2 0 002 2 2 2 0 012 2v2.945M8 3.935V5.5A2.5 2.5 0 0010.5 8h.5a2 2 0 012 2 2 2 0 104 0 2 2 0 012-2h1.064M15 20.488V18a2 2 0 012-2h3.064" /></svg>
|
|
524
|
-
Heartbeat
|
|
525
|
-
</a>
|
|
526
|
-
</Button>
|
|
527
|
-
<Button variant="ghost" className="w-full justify-start gap-2.5 text-sidebar-foreground/60 hover:text-sidebar-foreground" asChild>
|
|
528
|
-
<a href="/api/ping">
|
|
529
|
-
<svg className="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path strokeLinecap="round" strokeLinejoin="round" strokeWidth={1.5} d="M13 10V3L4 14h7v7l9-11h-7z" /></svg>
|
|
530
|
-
API Ping
|
|
531
|
-
</a>
|
|
532
|
-
</Button>
|
|
533
|
-
</div>
|
|
534
726
|
</aside>
|
|
535
727
|
|
|
536
|
-
{/* Main */}
|
|
537
|
-
<div className=
|
|
728
|
+
{/* ── Main area ────────────────────────────────────────────────────── */}
|
|
729
|
+
<div className={\`transition-all duration-200 \${collapsed ? 'lg:ml-16' : 'lg:ml-60'}\`}>
|
|
538
730
|
{/* Top bar */}
|
|
539
|
-
<header className="h-14 border-b border-border bg-background/
|
|
731
|
+
<header className="sticky top-0 z-20 flex h-14 items-center justify-between border-b border-border bg-background/95 px-4 backdrop-blur supports-[backdrop-filter]:bg-background/60 lg:px-6">
|
|
540
732
|
<div className="flex items-center gap-3">
|
|
541
|
-
{/* Mobile hamburger */}
|
|
542
|
-
<
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
733
|
+
{/* Mobile hamburger via Sheet */}
|
|
734
|
+
<Sheet>
|
|
735
|
+
<SheetTrigger asChild>
|
|
736
|
+
<Button variant="ghost" size="icon" className="lg:hidden">
|
|
737
|
+
<Menu className="h-5 w-5" />
|
|
738
|
+
<span className="sr-only">Toggle sidebar</span>
|
|
739
|
+
</Button>
|
|
740
|
+
</SheetTrigger>
|
|
741
|
+
<SheetContent side="left" className="w-60 p-0">
|
|
742
|
+
<SidebarNav
|
|
743
|
+
appName={appName}
|
|
744
|
+
collapsed={false}
|
|
745
|
+
onToggle={() => {}}
|
|
746
|
+
/>
|
|
747
|
+
</SheetContent>
|
|
748
|
+
</Sheet>
|
|
552
749
|
<h1 className="text-sm font-medium text-foreground">Dashboard</h1>
|
|
553
750
|
</div>
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
751
|
+
|
|
752
|
+
<div className="flex items-center gap-2">
|
|
753
|
+
{/* Theme toggle */}
|
|
754
|
+
<TooltipProvider>
|
|
755
|
+
<Tooltip>
|
|
756
|
+
<TooltipTrigger asChild>
|
|
757
|
+
<Button variant="ghost" size="icon" onClick={toggleTheme}>
|
|
758
|
+
{isDark ? <Sun className="h-4 w-4" /> : <Moon className="h-4 w-4" />}
|
|
759
|
+
<span className="sr-only">Toggle theme</span>
|
|
760
|
+
</Button>
|
|
761
|
+
</TooltipTrigger>
|
|
762
|
+
<TooltipContent>{isDark ? 'Light mode' : 'Dark mode'}</TooltipContent>
|
|
763
|
+
</Tooltip>
|
|
764
|
+
</TooltipProvider>
|
|
765
|
+
|
|
766
|
+
{/* Account dropdown */}
|
|
767
|
+
<DropdownMenu>
|
|
768
|
+
<DropdownMenuTrigger asChild>
|
|
769
|
+
<Button variant="ghost" className="gap-2 px-2">
|
|
770
|
+
<Avatar className="h-7 w-7">
|
|
771
|
+
<AvatarFallback className="bg-primary/10 text-[10px] text-primary">
|
|
772
|
+
{currentUser?.name ? getInitials(currentUser.name) : '?'}
|
|
773
|
+
</AvatarFallback>
|
|
774
|
+
</Avatar>
|
|
775
|
+
<span className="hidden text-sm font-medium sm:inline-block">
|
|
776
|
+
{currentUser?.name}
|
|
777
|
+
</span>
|
|
778
|
+
<ChevronDown className="hidden h-4 w-4 text-muted-foreground sm:inline-block" />
|
|
779
|
+
</Button>
|
|
780
|
+
</DropdownMenuTrigger>
|
|
781
|
+
<DropdownMenuContent align="end" className="w-56">
|
|
782
|
+
<DropdownMenuLabel className="font-normal">
|
|
783
|
+
<div className="flex flex-col space-y-1">
|
|
784
|
+
<p className="text-sm font-medium leading-none">{currentUser?.name}</p>
|
|
785
|
+
<p className="text-xs leading-none text-muted-foreground">{currentUser?.email}</p>
|
|
786
|
+
</div>
|
|
787
|
+
</DropdownMenuLabel>
|
|
788
|
+
<DropdownMenuSeparator />
|
|
789
|
+
<DropdownMenuItem>
|
|
790
|
+
<User className="mr-2 h-4 w-4" />
|
|
791
|
+
Profile
|
|
792
|
+
</DropdownMenuItem>
|
|
793
|
+
<DropdownMenuItem>
|
|
794
|
+
<Settings className="mr-2 h-4 w-4" />
|
|
795
|
+
Settings
|
|
796
|
+
</DropdownMenuItem>
|
|
797
|
+
<DropdownMenuSeparator />
|
|
798
|
+
<DropdownMenuItem onClick={handleLogout}>
|
|
799
|
+
<LogOut className="mr-2 h-4 w-4" />
|
|
800
|
+
Sign out
|
|
801
|
+
</DropdownMenuItem>
|
|
802
|
+
</DropdownMenuContent>
|
|
803
|
+
</DropdownMenu>
|
|
577
804
|
</div>
|
|
578
805
|
</header>
|
|
579
806
|
|
|
580
807
|
{/* Content */}
|
|
581
|
-
<main className="
|
|
808
|
+
<main className="animate-fade-up space-y-6 p-4 lg:p-6">
|
|
809
|
+
{/* Welcome card */}
|
|
582
810
|
<Card>
|
|
583
811
|
<CardHeader>
|
|
584
812
|
<CardTitle>Welcome back, {currentUser?.name}</CardTitle>
|
|
585
|
-
<CardDescription>
|
|
813
|
+
<CardDescription>
|
|
814
|
+
Here's what's happening with your application today.
|
|
815
|
+
</CardDescription>
|
|
586
816
|
</CardHeader>
|
|
587
817
|
</Card>
|
|
588
818
|
|
|
819
|
+
{/* Stats row */}
|
|
820
|
+
<div className="grid gap-4 sm:grid-cols-2 lg:grid-cols-4">
|
|
821
|
+
{stats.map((stat) => {
|
|
822
|
+
const Icon = stat.icon
|
|
823
|
+
return (
|
|
824
|
+
<Card key={stat.label}>
|
|
825
|
+
<CardHeader className="flex flex-row items-center justify-between pb-2">
|
|
826
|
+
<CardDescription className="text-sm font-medium">{stat.label}</CardDescription>
|
|
827
|
+
<Icon className="h-4 w-4 text-muted-foreground" />
|
|
828
|
+
</CardHeader>
|
|
829
|
+
<CardContent>
|
|
830
|
+
<div className="text-2xl font-bold">{stat.value}</div>
|
|
831
|
+
<p className="flex items-center gap-1 text-xs text-muted-foreground">
|
|
832
|
+
<TrendingUp className="h-3 w-3" />
|
|
833
|
+
{stat.change} from last month
|
|
834
|
+
</p>
|
|
835
|
+
</CardContent>
|
|
836
|
+
</Card>
|
|
837
|
+
)
|
|
838
|
+
})}
|
|
839
|
+
</div>
|
|
840
|
+
|
|
841
|
+
{/* Users table */}
|
|
589
842
|
<Card id="users-section">
|
|
590
843
|
<CardHeader className="flex flex-row items-center justify-between">
|
|
591
844
|
<div className="space-y-1">
|
|
592
845
|
<CardTitle className="text-base">Users</CardTitle>
|
|
593
|
-
<CardDescription>
|
|
846
|
+
<CardDescription>
|
|
847
|
+
{loading ? 'Loading\u2026' : \`\${users.length} registered user\${users.length === 1 ? '' : 's'}\`}
|
|
848
|
+
</CardDescription>
|
|
594
849
|
</div>
|
|
850
|
+
<Button variant="outline" size="sm" onClick={fetchUsers} disabled={loading}>
|
|
851
|
+
Refresh
|
|
852
|
+
</Button>
|
|
595
853
|
</CardHeader>
|
|
596
854
|
<CardContent>
|
|
597
855
|
<Table>
|
|
598
856
|
<TableHeader>
|
|
599
857
|
<TableRow>
|
|
600
|
-
<TableHead className="w-[
|
|
858
|
+
<TableHead className="w-[240px]">Name</TableHead>
|
|
601
859
|
<TableHead>Email</TableHead>
|
|
602
860
|
<TableHead className="w-[100px]">Role</TableHead>
|
|
603
861
|
</TableRow>
|
|
@@ -606,9 +864,9 @@ export default function Dashboard({ appName = '${ctx.name}', currentUser, users:
|
|
|
606
864
|
{users.map((u) => (
|
|
607
865
|
<TableRow key={u.id}>
|
|
608
866
|
<TableCell>
|
|
609
|
-
<div className="flex items-center gap-
|
|
610
|
-
<Avatar className="h-
|
|
611
|
-
<AvatarFallback className="
|
|
867
|
+
<div className="flex items-center gap-3">
|
|
868
|
+
<Avatar className="h-8 w-8">
|
|
869
|
+
<AvatarFallback className="bg-muted text-xs">
|
|
612
870
|
{getInitials(u.name)}
|
|
613
871
|
</AvatarFallback>
|
|
614
872
|
</Avatar>
|
|
@@ -626,7 +884,7 @@ export default function Dashboard({ appName = '${ctx.name}', currentUser, users:
|
|
|
626
884
|
{users.length === 0 && !loading && (
|
|
627
885
|
<TableRow>
|
|
628
886
|
<TableCell colSpan={3} className="h-24 text-center text-muted-foreground">
|
|
629
|
-
No users found
|
|
887
|
+
No users found.
|
|
630
888
|
</TableCell>
|
|
631
889
|
</TableRow>
|
|
632
890
|
)}
|