create-mantiq 0.7.0 → 0.7.2
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/skeleton/.env.example +64 -0
- package/skeleton/README.md +46 -0
- package/skeleton/app/Console/Commands/.gitkeep +0 -0
- package/skeleton/app/Enums/UserStatus.ts +7 -0
- package/skeleton/app/Http/Controllers/HomeController.ts +78 -0
- package/skeleton/app/Http/Middleware/.gitkeep +0 -0
- package/skeleton/app/Models/User.ts +7 -0
- package/skeleton/app/Providers/AppServiceProvider.ts +25 -0
- package/skeleton/app/Providers/DatabaseServiceProvider.ts +17 -0
- package/skeleton/bootstrap/.gitkeep +0 -0
- package/skeleton/config/ai.ts +51 -0
- package/skeleton/config/app.ts +108 -0
- package/skeleton/config/auth.ts +51 -0
- package/skeleton/config/broadcasting.ts +93 -0
- package/skeleton/config/cache.ts +61 -0
- package/skeleton/config/cors.ts +77 -0
- package/skeleton/config/database.ts +120 -0
- package/skeleton/config/filesystem.ts +58 -0
- package/skeleton/config/hashing.ts +47 -0
- package/skeleton/config/heartbeat.ts +112 -0
- package/skeleton/config/logging.ts +58 -0
- package/skeleton/config/mail.ts +93 -0
- package/skeleton/config/notify.ts +141 -0
- package/skeleton/config/queue.ts +59 -0
- package/skeleton/config/search.ts +96 -0
- package/skeleton/config/services.ts +110 -0
- package/skeleton/config/session.ts +84 -0
- package/skeleton/config/vite.ts +33 -0
- package/skeleton/database/factories/.gitkeep +0 -0
- package/skeleton/database/migrations/001_create_users_table.ts +19 -0
- package/skeleton/database/migrations/002_create_personal_access_tokens_table.ts +22 -0
- package/skeleton/database/seeders/DatabaseSeeder.ts +7 -0
- package/skeleton/index.ts +20 -0
- package/skeleton/mantiq.ts +8 -0
- package/skeleton/package.json +34 -0
- package/skeleton/public/.gitkeep +0 -0
- package/skeleton/routes/api.ts +8 -0
- package/skeleton/routes/channels.ts +23 -0
- package/skeleton/routes/console.ts +24 -0
- package/skeleton/routes/web.ts +6 -0
- package/skeleton/storage/cache/.gitkeep +0 -0
- package/skeleton/storage/framework/.gitkeep +0 -0
- package/skeleton/tests/feature/api.test.ts +14 -0
- package/skeleton/tests/feature/home.test.ts +17 -0
- package/skeleton/tests/unit/example.test.ts +11 -0
- package/skeleton/tsconfig.json +27 -0
- package/src/index.ts +289 -25
- package/src/templates.ts +141 -945
- package/src/terminal.ts +64 -0
- package/stubs/api-only/routes/api.ts.stub +24 -0
- package/stubs/api-only/tests/feature/token-auth.test.ts.stub +69 -0
- package/stubs/auth/api/app/Http/Controllers/ApiAuthController.ts.stub +57 -0
- package/stubs/auth/api/routes/api.ts.stub +24 -0
- package/stubs/auth/api/tests/feature/token-auth.test.ts.stub +69 -0
- package/stubs/auth/shared/app/Http/Requests/LoginRequest.ts.stub +10 -0
- package/stubs/auth/shared/app/Http/Requests/RegisterRequest.ts.stub +11 -0
- package/stubs/auth/web/app/Http/Controllers/AuthController.ts.stub +43 -0
- package/stubs/auth/web/app/Http/Controllers/PageController.ts.stub +66 -0
- package/stubs/auth/web/routes/web.ts.stub +25 -0
- package/stubs/auth/web/svelte/src/App.svelte.stub +77 -0
- package/stubs/auth/web/svelte/src/pages.ts.stub +17 -0
- package/stubs/auth/web/tests/feature/auth.test.ts.stub +69 -0
- package/stubs/auth/web/vue/src/App.vue.stub +74 -0
- package/stubs/auth/web/vue/src/pages.ts.stub +17 -0
- package/stubs/manifest.json +630 -2
- package/stubs/noauth/app/Http/Controllers/PageController.ts.stub +41 -0
- package/stubs/noauth/app/Models/User.ts.stub +5 -0
- package/stubs/noauth/database/migrations/001_create_users_table.ts.stub +17 -0
- package/stubs/noauth/routes/api.ts.stub +16 -0
- package/stubs/noauth/routes/web.ts.stub +15 -0
- package/stubs/noauth/svelte/src/App.svelte.stub +68 -0
- package/stubs/noauth/svelte/src/pages.ts.stub +7 -0
- package/stubs/noauth/vue/src/App.vue.stub +62 -0
- package/stubs/noauth/vue/src/pages.ts.stub +7 -0
- package/stubs/react/src/App.tsx.stub +4 -2
- package/stubs/react/src/components/layout/search-dialog.tsx.stub +2 -2
- package/stubs/react/src/components/layout/sidebar-data.ts.stub +2 -2
- package/stubs/react/src/lib/api.ts.stub +30 -6
- package/stubs/react/src/pages/Login.tsx.stub +3 -3
- package/stubs/react/src/pages/users/dialogs.tsx.stub +7 -26
- package/stubs/react/vite.config.ts.stub +26 -3
- package/stubs/shared/app/Http/Controllers/ApiAuthController.ts.stub +57 -0
- package/stubs/shared/app/Http/Controllers/AuthController.ts.stub +14 -38
- package/stubs/shared/app/Http/Controllers/PageController.ts.stub +3 -3
- package/stubs/shared/app/Http/Controllers/UserController.ts.stub +61 -0
- package/stubs/shared/app/Http/Requests/LoginRequest.ts.stub +10 -0
- package/stubs/shared/app/Http/Requests/RegisterRequest.ts.stub +11 -0
- package/stubs/shared/app/Http/Requests/StoreUserRequest.ts.stub +11 -0
- package/stubs/shared/app/Http/Requests/UpdateUserRequest.ts.stub +11 -0
- package/stubs/shared/config/app.ts.stub +36 -0
- package/stubs/shared/config/vite.ts.stub +8 -0
- package/stubs/shared/database/factories/UserFactory.ts.stub +4 -6
- package/stubs/shared/routes/api.ts.stub +12 -102
- package/stubs/shared/routes/web.ts.stub +5 -3
- package/stubs/shared/tests/feature/auth.test.ts.stub +69 -0
- package/stubs/shared/tests/feature/users.test.ts.stub +90 -0
- package/stubs/svelte/src/App.svelte.stub +1 -1
- package/stubs/svelte/src/lib/api.ts.stub +30 -6
- package/stubs/svelte/src/main.ts.stub +3 -1
- package/stubs/svelte/src/pages/Login.svelte.stub +3 -3
- package/stubs/svelte/vite.config.ts.stub +20 -1
- package/stubs/tailwind-only/react/src/components/layout/app-sidebar.tsx.stub +68 -0
- package/stubs/tailwind-only/react/src/components/layout/authenticated-layout.tsx.stub +57 -0
- package/stubs/tailwind-only/react/src/components/layout/header.tsx.stub +52 -0
- package/stubs/tailwind-only/react/src/components/layout/main.tsx.stub +21 -0
- package/stubs/tailwind-only/react/src/components/layout/nav-group.tsx.stub +185 -0
- package/stubs/tailwind-only/react/src/components/layout/nav-user.tsx.stub +106 -0
- package/stubs/tailwind-only/react/src/components/layout/sidebar-data.ts.stub +58 -0
- package/stubs/tailwind-only/react/src/components/layout/theme-toggle.tsx.stub +36 -0
- package/stubs/tailwind-only/react/src/components/layout/top-nav.tsx.stub +72 -0
- package/stubs/tailwind-only/react/src/lib/utils.ts.stub +6 -0
- package/stubs/tailwind-only/react/src/pages/Dashboard.tsx.stub +205 -0
- package/stubs/tailwind-only/react/src/pages/Login.tsx.stub +122 -0
- package/stubs/tailwind-only/react/src/pages/Register.tsx.stub +137 -0
- package/stubs/tailwind-only/react/src/pages/Users.tsx.stub +426 -0
- package/stubs/tailwind-only/react/src/pages/account/layout.tsx.stub +64 -0
- package/stubs/tailwind-only/react/src/pages/account/preferences.tsx.stub +80 -0
- package/stubs/tailwind-only/react/src/pages/account/profile.tsx.stub +67 -0
- package/stubs/tailwind-only/react/src/pages/account/security.tsx.stub +91 -0
- package/stubs/tailwind-only/react/src/style.css.stub +14 -0
- package/stubs/tailwind-only/svelte/src/lib/components/layout/app-sidebar.svelte.stub +104 -0
- package/stubs/tailwind-only/svelte/src/lib/components/layout/authenticated-layout.svelte.stub +51 -0
- package/stubs/tailwind-only/svelte/src/lib/components/layout/header.svelte.stub +66 -0
- package/stubs/tailwind-only/svelte/src/lib/components/layout/main.svelte.stub +26 -0
- package/stubs/tailwind-only/svelte/src/lib/components/layout/nav-group.svelte.stub +131 -0
- package/stubs/tailwind-only/svelte/src/lib/components/layout/nav-user.svelte.stub +104 -0
- package/stubs/tailwind-only/svelte/src/lib/components/layout/sidebar-data.ts.stub +57 -0
- package/stubs/tailwind-only/svelte/src/lib/components/layout/theme-toggle.svelte.stub +28 -0
- package/stubs/tailwind-only/svelte/src/lib/components/layout/top-nav.svelte.stub +99 -0
- package/stubs/tailwind-only/svelte/src/lib/utils.ts.stub +6 -0
- package/stubs/tailwind-only/svelte/src/pages/Dashboard.svelte.stub +192 -0
- package/stubs/tailwind-only/svelte/src/pages/Login.svelte.stub +120 -0
- package/stubs/tailwind-only/svelte/src/pages/Register.svelte.stub +134 -0
- package/stubs/tailwind-only/svelte/src/pages/Users.svelte.stub +293 -0
- package/stubs/tailwind-only/svelte/src/pages/account/Layout.svelte.stub +61 -0
- package/stubs/tailwind-only/svelte/src/pages/account/Preferences.svelte.stub +81 -0
- package/stubs/tailwind-only/svelte/src/pages/account/Profile.svelte.stub +76 -0
- package/stubs/tailwind-only/svelte/src/pages/account/Security.svelte.stub +140 -0
- package/stubs/tailwind-only/svelte/src/style.css.stub +127 -0
- package/stubs/tailwind-only/vue/src/components/layout/AppSidebar.vue.stub +60 -0
- package/stubs/tailwind-only/vue/src/components/layout/AuthenticatedLayout.vue.stub +73 -0
- package/stubs/tailwind-only/vue/src/components/layout/Header.vue.stub +54 -0
- package/stubs/tailwind-only/vue/src/components/layout/Main.vue.stub +22 -0
- package/stubs/tailwind-only/vue/src/components/layout/NavGroup.vue.stub +107 -0
- package/stubs/tailwind-only/vue/src/components/layout/NavUser.vue.stub +104 -0
- package/stubs/tailwind-only/vue/src/components/layout/ThemeToggle.vue.stub +28 -0
- package/stubs/tailwind-only/vue/src/components/layout/TopNav.vue.stub +86 -0
- package/stubs/tailwind-only/vue/src/components/layout/sidebar-data.ts.stub +57 -0
- package/stubs/tailwind-only/vue/src/lib/utils.ts.stub +7 -0
- package/stubs/tailwind-only/vue/src/pages/Dashboard.vue.stub +195 -0
- package/stubs/tailwind-only/vue/src/pages/Login.vue.stub +121 -0
- package/stubs/tailwind-only/vue/src/pages/Register.vue.stub +137 -0
- package/stubs/tailwind-only/vue/src/pages/Users.vue.stub +401 -0
- package/stubs/tailwind-only/vue/src/pages/account/Layout.vue.stub +56 -0
- package/stubs/tailwind-only/vue/src/pages/account/Preferences.vue.stub +81 -0
- package/stubs/tailwind-only/vue/src/pages/account/Profile.vue.stub +69 -0
- package/stubs/tailwind-only/vue/src/pages/account/Security.vue.stub +85 -0
- package/stubs/tailwind-only/vue/src/style.css.stub +26 -0
- package/stubs/themes/corporate/react/src/components/layout/app-sidebar.tsx.stub +74 -0
- package/stubs/themes/corporate/react/src/components/layout/authenticated-layout.tsx.stub +42 -0
- package/stubs/themes/corporate/react/src/pages/Dashboard.tsx.stub +310 -0
- package/stubs/themes/corporate/react/src/pages/Login.tsx.stub +122 -0
- package/stubs/themes/corporate/react/src/pages/Register.tsx.stub +144 -0
- package/stubs/themes/corporate/react/src/style.css.stub +135 -0
- package/stubs/themes/corporate/svelte/src/pages/Dashboard.svelte.stub +271 -0
- package/stubs/themes/corporate/svelte/src/pages/Login.svelte.stub +117 -0
- package/stubs/themes/corporate/svelte/src/pages/Register.svelte.stub +138 -0
- package/stubs/themes/corporate/svelte/src/style.css.stub +134 -0
- package/stubs/themes/corporate/vue/src/pages/Dashboard.vue.stub +325 -0
- package/stubs/themes/corporate/vue/src/pages/Login.vue.stub +118 -0
- package/stubs/themes/corporate/vue/src/pages/Register.vue.stub +139 -0
- package/stubs/themes/corporate/vue/src/style.css.stub +134 -0
- package/stubs/themes/minimal/react/src/components/layout/app-sidebar.tsx.stub +68 -0
- package/stubs/themes/minimal/react/src/components/layout/authenticated-layout.tsx.stub +42 -0
- package/stubs/themes/minimal/react/src/pages/Dashboard.tsx.stub +166 -0
- package/stubs/themes/minimal/react/src/pages/Login.tsx.stub +95 -0
- package/stubs/themes/minimal/react/src/pages/Register.tsx.stub +73 -0
- package/stubs/themes/minimal/react/src/style.css.stub +142 -0
- package/stubs/themes/minimal/svelte/src/pages/Dashboard.svelte.stub +149 -0
- package/stubs/themes/minimal/svelte/src/pages/Login.svelte.stub +90 -0
- package/stubs/themes/minimal/svelte/src/pages/Register.svelte.stub +70 -0
- package/stubs/themes/minimal/svelte/src/style.css.stub +142 -0
- package/stubs/themes/minimal/vue/src/pages/Dashboard.vue.stub +163 -0
- package/stubs/themes/minimal/vue/src/pages/Login.vue.stub +91 -0
- package/stubs/themes/minimal/vue/src/pages/Register.vue.stub +73 -0
- package/stubs/themes/minimal/vue/src/style.css.stub +142 -0
- package/stubs/themes/starter/react/src/components/layout/app-sidebar.tsx.stub +74 -0
- package/stubs/themes/starter/react/src/components/layout/authenticated-layout.tsx.stub +42 -0
- package/stubs/themes/starter/react/src/pages/Dashboard.tsx.stub +236 -0
- package/stubs/themes/starter/react/src/pages/Login.tsx.stub +131 -0
- package/stubs/themes/starter/react/src/pages/Register.tsx.stub +145 -0
- package/stubs/themes/starter/react/src/style.css.stub +141 -0
- package/stubs/themes/starter/svelte/src/pages/Dashboard.svelte.stub +212 -0
- package/stubs/themes/starter/svelte/src/pages/Login.svelte.stub +126 -0
- package/stubs/themes/starter/svelte/src/pages/Register.svelte.stub +139 -0
- package/stubs/themes/starter/svelte/src/style.css.stub +141 -0
- package/stubs/themes/starter/vue/src/pages/Dashboard.vue.stub +228 -0
- package/stubs/themes/starter/vue/src/pages/Login.vue.stub +127 -0
- package/stubs/themes/starter/vue/src/pages/Register.vue.stub +140 -0
- package/stubs/themes/starter/vue/src/style.css.stub +141 -0
- package/stubs/themes/workspace/react/src/components/layout/app-sidebar.tsx.stub +97 -0
- package/stubs/themes/workspace/react/src/components/layout/authenticated-layout.tsx.stub +42 -0
- package/stubs/themes/workspace/react/src/pages/Dashboard.tsx.stub +304 -0
- package/stubs/themes/workspace/react/src/pages/Login.tsx.stub +131 -0
- package/stubs/themes/workspace/react/src/pages/Register.tsx.stub +82 -0
- package/stubs/themes/workspace/react/src/style.css.stub +138 -0
- package/stubs/themes/workspace/svelte/src/pages/Dashboard.svelte.stub +215 -0
- package/stubs/themes/workspace/svelte/src/pages/Login.svelte.stub +124 -0
- package/stubs/themes/workspace/svelte/src/pages/Register.svelte.stub +76 -0
- package/stubs/themes/workspace/svelte/src/style.css.stub +134 -0
- package/stubs/themes/workspace/vue/src/pages/Dashboard.vue.stub +220 -0
- package/stubs/themes/workspace/vue/src/pages/Login.vue.stub +128 -0
- package/stubs/themes/workspace/vue/src/pages/Register.vue.stub +80 -0
- package/stubs/themes/workspace/vue/src/style.css.stub +134 -0
- package/stubs/vue/src/App.vue.stub +2 -1
- package/stubs/vue/src/lib/api.ts.stub +30 -6
- package/stubs/vue/src/main.ts.stub +3 -1
- package/stubs/vue/src/pages/Login.vue.stub +3 -3
- package/stubs/vue/vite.config.ts.stub +20 -1
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
@import "tailwindcss";
|
|
2
|
+
@custom-variant dark (&:where(.dark, .dark *));
|
|
3
|
+
|
|
4
|
+
/*
|
|
5
|
+
* shadcn/ui theme — Starter
|
|
6
|
+
*
|
|
7
|
+
* Bold, gradient-heavy palette with violet/purple primary.
|
|
8
|
+
* Dark sidebar in both light and dark modes for a two-tone SaaS feel.
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
@theme inline {
|
|
12
|
+
--color-background: var(--background);
|
|
13
|
+
--color-foreground: var(--foreground);
|
|
14
|
+
--color-card: var(--card);
|
|
15
|
+
--color-card-foreground: var(--card-foreground);
|
|
16
|
+
--color-popover: var(--popover);
|
|
17
|
+
--color-popover-foreground: var(--popover-foreground);
|
|
18
|
+
--color-primary: var(--primary);
|
|
19
|
+
--color-primary-foreground: var(--primary-foreground);
|
|
20
|
+
--color-secondary: var(--secondary);
|
|
21
|
+
--color-secondary-foreground: var(--secondary-foreground);
|
|
22
|
+
--color-muted: var(--muted);
|
|
23
|
+
--color-muted-foreground: var(--muted-foreground);
|
|
24
|
+
--color-accent: var(--accent);
|
|
25
|
+
--color-accent-foreground: var(--accent-foreground);
|
|
26
|
+
--color-destructive: var(--destructive);
|
|
27
|
+
--color-destructive-foreground: var(--destructive-foreground);
|
|
28
|
+
--color-border: var(--border);
|
|
29
|
+
--color-input: var(--input);
|
|
30
|
+
--color-ring: var(--ring);
|
|
31
|
+
--color-chart-1: var(--chart-1);
|
|
32
|
+
--color-chart-2: var(--chart-2);
|
|
33
|
+
--color-chart-3: var(--chart-3);
|
|
34
|
+
--color-chart-4: var(--chart-4);
|
|
35
|
+
--color-chart-5: var(--chart-5);
|
|
36
|
+
--color-sidebar: var(--sidebar);
|
|
37
|
+
--color-sidebar-foreground: var(--sidebar-foreground);
|
|
38
|
+
--color-sidebar-primary: var(--sidebar-primary);
|
|
39
|
+
--color-sidebar-primary-foreground: var(--sidebar-primary-foreground);
|
|
40
|
+
--color-sidebar-accent: var(--sidebar-accent);
|
|
41
|
+
--color-sidebar-accent-foreground: var(--sidebar-accent-foreground);
|
|
42
|
+
--color-sidebar-border: var(--sidebar-border);
|
|
43
|
+
--color-sidebar-ring: var(--sidebar-ring);
|
|
44
|
+
--radius-sm: calc(var(--radius) - 4px);
|
|
45
|
+
--radius-md: calc(var(--radius) - 2px);
|
|
46
|
+
--radius-lg: var(--radius);
|
|
47
|
+
--radius-xl: calc(var(--radius) + 4px);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
:root {
|
|
51
|
+
--radius: 0.625rem;
|
|
52
|
+
--background: oklch(0.985 0.002 286);
|
|
53
|
+
--foreground: oklch(0.141 0.005 285.823);
|
|
54
|
+
--card: oklch(1 0 0);
|
|
55
|
+
--card-foreground: oklch(0.141 0.005 285.823);
|
|
56
|
+
--popover: oklch(1 0 0);
|
|
57
|
+
--popover-foreground: oklch(0.141 0.005 285.823);
|
|
58
|
+
--primary: oklch(0.606 0.25 292.717);
|
|
59
|
+
--primary-foreground: oklch(0.985 0 0);
|
|
60
|
+
--secondary: oklch(0.967 0.001 286.375);
|
|
61
|
+
--secondary-foreground: oklch(0.21 0.006 285.885);
|
|
62
|
+
--muted: oklch(0.967 0.001 286.375);
|
|
63
|
+
--muted-foreground: oklch(0.552 0.016 285.938);
|
|
64
|
+
--accent: oklch(0.967 0.001 286.375);
|
|
65
|
+
--accent-foreground: oklch(0.21 0.006 285.885);
|
|
66
|
+
--destructive: oklch(0.577 0.245 27.325);
|
|
67
|
+
--destructive-foreground: oklch(0.577 0.245 27.325);
|
|
68
|
+
--border: oklch(0.92 0.004 286.32);
|
|
69
|
+
--input: oklch(0.92 0.004 286.32);
|
|
70
|
+
--ring: oklch(0.606 0.25 292.717);
|
|
71
|
+
--chart-1: oklch(0.606 0.25 292.717);
|
|
72
|
+
--chart-2: oklch(0.546 0.245 262.881);
|
|
73
|
+
--chart-3: oklch(0.6 0.118 184.714);
|
|
74
|
+
--chart-4: oklch(0.828 0.189 84.429);
|
|
75
|
+
--chart-5: oklch(0.769 0.188 70.08);
|
|
76
|
+
/* Dark sidebar even in light mode */
|
|
77
|
+
--sidebar: oklch(0.16 0.015 286);
|
|
78
|
+
--sidebar-foreground: oklch(0.95 0.002 286);
|
|
79
|
+
--sidebar-primary: oklch(0.7 0.2 292.717);
|
|
80
|
+
--sidebar-primary-foreground: oklch(0.985 0 0);
|
|
81
|
+
--sidebar-accent: oklch(0.22 0.012 286);
|
|
82
|
+
--sidebar-accent-foreground: oklch(0.95 0.002 286);
|
|
83
|
+
--sidebar-border: oklch(0.25 0.01 286);
|
|
84
|
+
--sidebar-ring: oklch(0.7 0.2 292.717);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
.dark {
|
|
88
|
+
--background: oklch(0.13 0.015 286);
|
|
89
|
+
--foreground: oklch(0.985 0 0);
|
|
90
|
+
--card: oklch(0.17 0.012 286);
|
|
91
|
+
--card-foreground: oklch(0.985 0 0);
|
|
92
|
+
--popover: oklch(0.17 0.012 286);
|
|
93
|
+
--popover-foreground: oklch(0.985 0 0);
|
|
94
|
+
--primary: oklch(0.7 0.2 292.717);
|
|
95
|
+
--primary-foreground: oklch(0.16 0.04 292.717);
|
|
96
|
+
--secondary: oklch(0.269 0.006 286.033);
|
|
97
|
+
--secondary-foreground: oklch(0.985 0 0);
|
|
98
|
+
--muted: oklch(0.269 0.006 286.033);
|
|
99
|
+
--muted-foreground: oklch(0.708 0.014 286.067);
|
|
100
|
+
--accent: oklch(0.269 0.006 286.033);
|
|
101
|
+
--accent-foreground: oklch(0.985 0 0);
|
|
102
|
+
--destructive: oklch(0.704 0.191 22.216);
|
|
103
|
+
--destructive-foreground: oklch(0.704 0.191 22.216);
|
|
104
|
+
--border: oklch(0.274 0.006 286.033);
|
|
105
|
+
--input: oklch(0.274 0.006 286.033);
|
|
106
|
+
--ring: oklch(0.7 0.2 292.717);
|
|
107
|
+
--chart-1: oklch(0.7 0.2 292.717);
|
|
108
|
+
--chart-2: oklch(0.623 0.214 262.881);
|
|
109
|
+
--chart-3: oklch(0.769 0.188 70.08);
|
|
110
|
+
--chart-4: oklch(0.627 0.265 303.9);
|
|
111
|
+
--chart-5: oklch(0.645 0.246 16.439);
|
|
112
|
+
--sidebar: oklch(0.13 0.015 286);
|
|
113
|
+
--sidebar-foreground: oklch(0.95 0.002 286);
|
|
114
|
+
--sidebar-primary: oklch(0.7 0.2 292.717);
|
|
115
|
+
--sidebar-primary-foreground: oklch(0.985 0 0);
|
|
116
|
+
--sidebar-accent: oklch(0.22 0.012 286);
|
|
117
|
+
--sidebar-accent-foreground: oklch(0.95 0.002 286);
|
|
118
|
+
--sidebar-border: oklch(0.25 0.01 286);
|
|
119
|
+
--sidebar-ring: oklch(0.7 0.2 292.717);
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
@layer base {
|
|
123
|
+
* {
|
|
124
|
+
@apply border-border;
|
|
125
|
+
}
|
|
126
|
+
body {
|
|
127
|
+
@apply bg-background text-foreground;
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
@keyframes fadeUp {
|
|
132
|
+
from { opacity: 0; transform: translateY(8px); }
|
|
133
|
+
to { opacity: 1; transform: translateY(0); }
|
|
134
|
+
}
|
|
135
|
+
.animate-fade-up { animation: fadeUp 0.4s ease-out; }
|
|
136
|
+
|
|
137
|
+
@keyframes gradient-shift {
|
|
138
|
+
0%, 100% { background-position: 0% 50%; }
|
|
139
|
+
50% { background-position: 100% 50%; }
|
|
140
|
+
}
|
|
141
|
+
.animate-gradient { animation: gradient-shift 6s ease infinite; background-size: 200% 200%; }
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
import {
|
|
2
|
+
Sidebar,
|
|
3
|
+
SidebarContent,
|
|
4
|
+
SidebarFooter,
|
|
5
|
+
SidebarHeader,
|
|
6
|
+
SidebarMenu,
|
|
7
|
+
SidebarMenuButton,
|
|
8
|
+
SidebarMenuItem,
|
|
9
|
+
SidebarRail,
|
|
10
|
+
SidebarSeparator,
|
|
11
|
+
SidebarGroup,
|
|
12
|
+
SidebarGroupLabel,
|
|
13
|
+
} from '@/components/ui/sidebar'
|
|
14
|
+
import { NavGroup } from './nav-group'
|
|
15
|
+
import { NavUser, type NavUserProps } from './nav-user'
|
|
16
|
+
import { sidebarData } from './sidebar-data'
|
|
17
|
+
import { Command, Star } from 'lucide-react'
|
|
18
|
+
|
|
19
|
+
export interface AppSidebarProps {
|
|
20
|
+
user: NavUserProps['user']
|
|
21
|
+
appName: string
|
|
22
|
+
activePath: string
|
|
23
|
+
navigate: (href: string) => void
|
|
24
|
+
onLogout: () => void
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export function AppSidebar({
|
|
28
|
+
user,
|
|
29
|
+
appName,
|
|
30
|
+
activePath,
|
|
31
|
+
navigate,
|
|
32
|
+
onLogout,
|
|
33
|
+
}: AppSidebarProps) {
|
|
34
|
+
return (
|
|
35
|
+
<Sidebar variant="inset" collapsible="icon">
|
|
36
|
+
<SidebarHeader>
|
|
37
|
+
<SidebarMenu>
|
|
38
|
+
<SidebarMenuItem>
|
|
39
|
+
<SidebarMenuButton
|
|
40
|
+
size="lg"
|
|
41
|
+
className="data-[state=open]:bg-sidebar-accent data-[state=open]:text-sidebar-accent-foreground"
|
|
42
|
+
onClick={() => navigate('/dashboard')}
|
|
43
|
+
tooltip={appName}
|
|
44
|
+
>
|
|
45
|
+
<div className="flex aspect-square size-8 items-center justify-center rounded-xl bg-sidebar-primary text-sidebar-primary-foreground">
|
|
46
|
+
<Command className="size-4" />
|
|
47
|
+
</div>
|
|
48
|
+
<div className="grid flex-1 text-left text-sm leading-tight">
|
|
49
|
+
<span className="truncate font-semibold">{appName}</span>
|
|
50
|
+
<span className="truncate text-xs text-muted-foreground">
|
|
51
|
+
Workspace
|
|
52
|
+
</span>
|
|
53
|
+
</div>
|
|
54
|
+
</SidebarMenuButton>
|
|
55
|
+
</SidebarMenuItem>
|
|
56
|
+
</SidebarMenu>
|
|
57
|
+
</SidebarHeader>
|
|
58
|
+
|
|
59
|
+
<SidebarContent>
|
|
60
|
+
{sidebarData.map((group) => (
|
|
61
|
+
<NavGroup
|
|
62
|
+
key={group.title}
|
|
63
|
+
group={group}
|
|
64
|
+
activePath={activePath}
|
|
65
|
+
navigate={navigate}
|
|
66
|
+
/>
|
|
67
|
+
))}
|
|
68
|
+
|
|
69
|
+
<SidebarSeparator />
|
|
70
|
+
|
|
71
|
+
<SidebarGroup>
|
|
72
|
+
<SidebarGroupLabel>
|
|
73
|
+
<Star className="mr-1 size-3" />
|
|
74
|
+
Favorites
|
|
75
|
+
</SidebarGroupLabel>
|
|
76
|
+
<SidebarMenu>
|
|
77
|
+
<SidebarMenuItem>
|
|
78
|
+
<SidebarMenuButton
|
|
79
|
+
tooltip="Dashboard"
|
|
80
|
+
isActive={activePath === '/dashboard'}
|
|
81
|
+
onClick={() => navigate('/dashboard')}
|
|
82
|
+
>
|
|
83
|
+
<span className="text-muted-foreground">Dashboard</span>
|
|
84
|
+
</SidebarMenuButton>
|
|
85
|
+
</SidebarMenuItem>
|
|
86
|
+
</SidebarMenu>
|
|
87
|
+
</SidebarGroup>
|
|
88
|
+
</SidebarContent>
|
|
89
|
+
|
|
90
|
+
<SidebarFooter>
|
|
91
|
+
<NavUser user={user} navigate={navigate} onLogout={onLogout} />
|
|
92
|
+
</SidebarFooter>
|
|
93
|
+
|
|
94
|
+
<SidebarRail />
|
|
95
|
+
</Sidebar>
|
|
96
|
+
)
|
|
97
|
+
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { useCallback } from 'react'
|
|
2
|
+
import { post } from '@/lib/api'
|
|
3
|
+
import { SidebarProvider, SidebarInset } from '@/components/ui/sidebar'
|
|
4
|
+
import { AppSidebar } from './app-sidebar'
|
|
5
|
+
|
|
6
|
+
interface AuthenticatedLayoutProps {
|
|
7
|
+
children: React.ReactNode
|
|
8
|
+
currentUser?: { name: string; email: string; role?: string } | null
|
|
9
|
+
appName?: string
|
|
10
|
+
navigate: (href: string) => void
|
|
11
|
+
activePath: string
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export function AuthenticatedLayout({
|
|
15
|
+
children,
|
|
16
|
+
currentUser,
|
|
17
|
+
appName = 'Mantiq',
|
|
18
|
+
navigate,
|
|
19
|
+
activePath,
|
|
20
|
+
}: AuthenticatedLayoutProps) {
|
|
21
|
+
const handleLogout = useCallback(async () => {
|
|
22
|
+
await post('/logout', {})
|
|
23
|
+
navigate('/login')
|
|
24
|
+
}, [navigate])
|
|
25
|
+
|
|
26
|
+
const user = currentUser ?? { name: 'User', email: 'user@example.com' }
|
|
27
|
+
|
|
28
|
+
return (
|
|
29
|
+
<SidebarProvider defaultOpen={true}>
|
|
30
|
+
<AppSidebar
|
|
31
|
+
user={user}
|
|
32
|
+
appName={appName}
|
|
33
|
+
activePath={activePath}
|
|
34
|
+
navigate={navigate}
|
|
35
|
+
onLogout={handleLogout}
|
|
36
|
+
/>
|
|
37
|
+
<SidebarInset>
|
|
38
|
+
{children}
|
|
39
|
+
</SidebarInset>
|
|
40
|
+
</SidebarProvider>
|
|
41
|
+
)
|
|
42
|
+
}
|
|
@@ -0,0 +1,304 @@
|
|
|
1
|
+
import { useState } from 'react'
|
|
2
|
+
import { AuthenticatedLayout } from '@/components/layout/authenticated-layout'
|
|
3
|
+
import { Header } from '@/components/layout/header'
|
|
4
|
+
import { Main } from '@/components/layout/main'
|
|
5
|
+
import {
|
|
6
|
+
Card,
|
|
7
|
+
CardContent,
|
|
8
|
+
} from '@/components/ui/card'
|
|
9
|
+
import {
|
|
10
|
+
ChevronRight,
|
|
11
|
+
ChevronDown,
|
|
12
|
+
GripVertical,
|
|
13
|
+
ClipboardList,
|
|
14
|
+
Hammer,
|
|
15
|
+
CheckCircle2,
|
|
16
|
+
Lightbulb,
|
|
17
|
+
KanbanSquare,
|
|
18
|
+
FileText,
|
|
19
|
+
BookOpen,
|
|
20
|
+
Building2,
|
|
21
|
+
Radio,
|
|
22
|
+
Rocket,
|
|
23
|
+
StickyNote,
|
|
24
|
+
Hand,
|
|
25
|
+
} from 'lucide-react'
|
|
26
|
+
|
|
27
|
+
interface DashboardProps {
|
|
28
|
+
appName?: string
|
|
29
|
+
currentUser?: { id: number; name: string; email: string; role: string } | null
|
|
30
|
+
navigate: (href: string) => void
|
|
31
|
+
[key: string]: any
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
type Status = 'not-started' | 'in-progress' | 'done'
|
|
35
|
+
|
|
36
|
+
interface Task {
|
|
37
|
+
id: number
|
|
38
|
+
title: string
|
|
39
|
+
status: Status
|
|
40
|
+
tag?: string
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
const kanbanColumns: { status: Status; label: string; icon: typeof ClipboardList; iconClass: string }[] = [
|
|
44
|
+
{ status: 'not-started', label: 'Not Started', icon: ClipboardList, iconClass: 'text-muted-foreground' },
|
|
45
|
+
{ status: 'in-progress', label: 'In Progress', icon: Hammer, iconClass: 'text-amber-500' },
|
|
46
|
+
{ status: 'done', label: 'Done', icon: CheckCircle2, iconClass: 'text-emerald-500' },
|
|
47
|
+
]
|
|
48
|
+
|
|
49
|
+
const initialTasks: Task[] = [
|
|
50
|
+
{ id: 1, title: 'Design database schema', status: 'done', tag: 'Backend' },
|
|
51
|
+
{ id: 2, title: 'Set up authentication flow', status: 'done', tag: 'Auth' },
|
|
52
|
+
{ id: 3, title: 'Build API endpoints', status: 'in-progress', tag: 'Backend' },
|
|
53
|
+
{ id: 4, title: 'Create dashboard layout', status: 'in-progress', tag: 'Frontend' },
|
|
54
|
+
{ id: 5, title: 'Write unit tests', status: 'not-started', tag: 'Testing' },
|
|
55
|
+
{ id: 6, title: 'Set up CI/CD pipeline', status: 'not-started', tag: 'DevOps' },
|
|
56
|
+
{ id: 7, title: 'Configure email notifications', status: 'not-started', tag: 'Backend' },
|
|
57
|
+
{ id: 8, title: 'Deploy to staging', status: 'not-started', tag: 'DevOps' },
|
|
58
|
+
]
|
|
59
|
+
|
|
60
|
+
const tagColors: Record<string, string> = {
|
|
61
|
+
Backend: 'bg-blue-100 text-blue-700 dark:bg-blue-900/40 dark:text-blue-300',
|
|
62
|
+
Frontend: 'bg-purple-100 text-purple-700 dark:bg-purple-900/40 dark:text-purple-300',
|
|
63
|
+
Auth: 'bg-amber-100 text-amber-700 dark:bg-amber-900/40 dark:text-amber-300',
|
|
64
|
+
Testing: 'bg-emerald-100 text-emerald-700 dark:bg-emerald-900/40 dark:text-emerald-300',
|
|
65
|
+
DevOps: 'bg-rose-100 text-rose-700 dark:bg-rose-900/40 dark:text-rose-300',
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
const pageItems = [
|
|
69
|
+
{ icon: BookOpen, title: 'Getting Started', desc: 'Quick introduction and setup guide' },
|
|
70
|
+
{ icon: Building2, title: 'Architecture', desc: 'How the application is structured' },
|
|
71
|
+
{ icon: Radio, title: 'API Reference', desc: 'Endpoints, params, and responses' },
|
|
72
|
+
{ icon: Rocket, title: 'Deployment', desc: 'Deploy your app to production' },
|
|
73
|
+
]
|
|
74
|
+
|
|
75
|
+
function getGreeting() {
|
|
76
|
+
const hour = new Date().getHours()
|
|
77
|
+
if (hour < 12) return 'Good morning'
|
|
78
|
+
if (hour < 18) return 'Good afternoon'
|
|
79
|
+
return 'Good evening'
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
export default function Dashboard({
|
|
83
|
+
appName = 'Mantiq',
|
|
84
|
+
currentUser,
|
|
85
|
+
navigate,
|
|
86
|
+
}: DashboardProps) {
|
|
87
|
+
const [tasks, setTasks] = useState<Task[]>(initialTasks)
|
|
88
|
+
const [expandedSections, setExpandedSections] = useState<Record<string, boolean>>({
|
|
89
|
+
kanban: true,
|
|
90
|
+
pages: true,
|
|
91
|
+
notes: false,
|
|
92
|
+
})
|
|
93
|
+
const [draggedId, setDraggedId] = useState<number | null>(null)
|
|
94
|
+
const [dropTarget, setDropTarget] = useState<Status | null>(null)
|
|
95
|
+
|
|
96
|
+
const toggle = (section: string) =>
|
|
97
|
+
setExpandedSections((prev) => ({ ...prev, [section]: !prev[section] }))
|
|
98
|
+
|
|
99
|
+
const handleDragStart = (e: React.DragEvent, taskId: number) => {
|
|
100
|
+
setDraggedId(taskId)
|
|
101
|
+
e.dataTransfer.effectAllowed = 'move'
|
|
102
|
+
if (e.currentTarget instanceof HTMLElement) {
|
|
103
|
+
e.currentTarget.style.opacity = '0.5'
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
const handleDragEnd = (e: React.DragEvent) => {
|
|
108
|
+
setDraggedId(null)
|
|
109
|
+
setDropTarget(null)
|
|
110
|
+
if (e.currentTarget instanceof HTMLElement) {
|
|
111
|
+
e.currentTarget.style.opacity = '1'
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
const handleDragOver = (e: React.DragEvent, status: Status) => {
|
|
116
|
+
e.preventDefault()
|
|
117
|
+
e.dataTransfer.dropEffect = 'move'
|
|
118
|
+
setDropTarget(status)
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
const handleDragLeave = () => {
|
|
122
|
+
setDropTarget(null)
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
const handleDrop = (e: React.DragEvent, targetStatus: Status) => {
|
|
126
|
+
e.preventDefault()
|
|
127
|
+
if (draggedId === null) return
|
|
128
|
+
setTasks((prev) =>
|
|
129
|
+
prev.map((t) => (t.id === draggedId ? { ...t, status: targetStatus } : t)),
|
|
130
|
+
)
|
|
131
|
+
setDraggedId(null)
|
|
132
|
+
setDropTarget(null)
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
return (
|
|
136
|
+
<AuthenticatedLayout
|
|
137
|
+
currentUser={currentUser}
|
|
138
|
+
appName={appName}
|
|
139
|
+
navigate={navigate}
|
|
140
|
+
activePath="/dashboard"
|
|
141
|
+
>
|
|
142
|
+
<Header navigate={navigate} />
|
|
143
|
+
<Main>
|
|
144
|
+
<div className="max-w-4xl mx-auto space-y-8">
|
|
145
|
+
{/* Cover & title */}
|
|
146
|
+
<div>
|
|
147
|
+
<div className="h-40 rounded-2xl bg-gradient-to-r from-amber-300 via-orange-300 to-rose-300 dark:from-amber-800 dark:via-orange-900 dark:to-rose-900 mb-6 shadow-sm" />
|
|
148
|
+
<div className="-mt-10 ml-3">
|
|
149
|
+
<div className="flex h-14 w-14 items-center justify-center rounded-2xl bg-card border shadow-md">
|
|
150
|
+
<Hand className="h-7 w-7 text-amber-500" />
|
|
151
|
+
</div>
|
|
152
|
+
</div>
|
|
153
|
+
<h1 className="mt-4 text-3xl font-bold tracking-tight">
|
|
154
|
+
{getGreeting()}, {currentUser?.name?.split(' ')[0] ?? 'there'}
|
|
155
|
+
</h1>
|
|
156
|
+
<p className="text-muted-foreground mt-1">
|
|
157
|
+
Here's your workspace overview. Drag tasks between columns to update their status.
|
|
158
|
+
</p>
|
|
159
|
+
</div>
|
|
160
|
+
|
|
161
|
+
{/* Callout block */}
|
|
162
|
+
<div className="flex gap-3 rounded-xl bg-amber-50 dark:bg-amber-950/30 border border-amber-300 dark:border-amber-700 p-4 shadow-sm">
|
|
163
|
+
<Lightbulb className="h-5 w-5 text-amber-500 shrink-0 mt-0.5" />
|
|
164
|
+
<div>
|
|
165
|
+
<p className="text-sm font-medium">Tip</p>
|
|
166
|
+
<p className="text-sm text-muted-foreground">
|
|
167
|
+
Customize this dashboard by editing <code className="rounded bg-muted px-1 py-0.5 text-xs font-mono">src/pages/Dashboard.tsx</code>.
|
|
168
|
+
Add your own widgets, data sources, and integrations.
|
|
169
|
+
</p>
|
|
170
|
+
</div>
|
|
171
|
+
</div>
|
|
172
|
+
|
|
173
|
+
{/* Kanban board section */}
|
|
174
|
+
<div>
|
|
175
|
+
<button
|
|
176
|
+
type="button"
|
|
177
|
+
onClick={() => toggle('kanban')}
|
|
178
|
+
className="flex items-center gap-1.5 mb-3 text-sm font-semibold text-muted-foreground hover:text-foreground transition-colors"
|
|
179
|
+
>
|
|
180
|
+
{expandedSections.kanban ? <ChevronDown className="h-4 w-4" /> : <ChevronRight className="h-4 w-4" />}
|
|
181
|
+
<KanbanSquare className="h-4 w-4" />
|
|
182
|
+
Project Board
|
|
183
|
+
</button>
|
|
184
|
+
|
|
185
|
+
{expandedSections.kanban && (
|
|
186
|
+
<div className="grid gap-4 md:grid-cols-3">
|
|
187
|
+
{kanbanColumns.map((col) => {
|
|
188
|
+
const Icon = col.icon
|
|
189
|
+
const columnTasks = tasks.filter((t) => t.status === col.status)
|
|
190
|
+
const isOver = dropTarget === col.status && draggedId !== null
|
|
191
|
+
return (
|
|
192
|
+
<div
|
|
193
|
+
key={col.status}
|
|
194
|
+
onDragOver={(e) => handleDragOver(e, col.status)}
|
|
195
|
+
onDragLeave={handleDragLeave}
|
|
196
|
+
onDrop={(e) => handleDrop(e, col.status)}
|
|
197
|
+
className={`rounded-xl p-2 -m-2 transition-colors ${isOver ? 'bg-primary/5 ring-2 ring-primary/20' : ''}`}
|
|
198
|
+
>
|
|
199
|
+
<div className="flex items-center gap-2 mb-3 px-2">
|
|
200
|
+
<Icon className={`h-4 w-4 ${col.iconClass}`} />
|
|
201
|
+
<span className="text-sm font-medium">{col.label}</span>
|
|
202
|
+
<span className="ml-auto flex h-5 w-5 items-center justify-center rounded-full bg-muted text-xs text-muted-foreground">
|
|
203
|
+
{columnTasks.length}
|
|
204
|
+
</span>
|
|
205
|
+
</div>
|
|
206
|
+
<div className="space-y-2 min-h-[60px]">
|
|
207
|
+
{columnTasks.map((task) => (
|
|
208
|
+
<Card
|
|
209
|
+
key={task.id}
|
|
210
|
+
draggable
|
|
211
|
+
onDragStart={(e) => handleDragStart(e, task.id)}
|
|
212
|
+
onDragEnd={handleDragEnd}
|
|
213
|
+
className={`group cursor-grab active:cursor-grabbing shadow-sm hover:shadow-md transition-all border-border/60 ${
|
|
214
|
+
draggedId === task.id ? 'opacity-50 scale-95' : ''
|
|
215
|
+
}`}
|
|
216
|
+
>
|
|
217
|
+
<CardContent className="p-3">
|
|
218
|
+
<div className="flex items-start gap-2">
|
|
219
|
+
<GripVertical className="h-4 w-4 mt-0.5 text-muted-foreground/30 group-hover:text-muted-foreground/60 transition-colors shrink-0" />
|
|
220
|
+
<div className="flex-1 min-w-0">
|
|
221
|
+
<p className="text-sm">{task.title}</p>
|
|
222
|
+
{task.tag && (
|
|
223
|
+
<span className={`mt-1.5 inline-block rounded-md px-1.5 py-0.5 text-[10px] font-medium ${tagColors[task.tag] ?? 'bg-muted text-muted-foreground'}`}>
|
|
224
|
+
{task.tag}
|
|
225
|
+
</span>
|
|
226
|
+
)}
|
|
227
|
+
</div>
|
|
228
|
+
</div>
|
|
229
|
+
</CardContent>
|
|
230
|
+
</Card>
|
|
231
|
+
))}
|
|
232
|
+
{columnTasks.length === 0 && (
|
|
233
|
+
<div className={`rounded-xl border-2 border-dashed p-6 text-center text-xs text-muted-foreground transition-colors ${isOver ? 'border-primary/40 bg-primary/5' : ''}`}>
|
|
234
|
+
Drop tasks here
|
|
235
|
+
</div>
|
|
236
|
+
)}
|
|
237
|
+
</div>
|
|
238
|
+
</div>
|
|
239
|
+
)
|
|
240
|
+
})}
|
|
241
|
+
</div>
|
|
242
|
+
)}
|
|
243
|
+
</div>
|
|
244
|
+
|
|
245
|
+
{/* Pages section */}
|
|
246
|
+
<div>
|
|
247
|
+
<button
|
|
248
|
+
type="button"
|
|
249
|
+
onClick={() => toggle('pages')}
|
|
250
|
+
className="flex items-center gap-1.5 mb-3 text-sm font-semibold text-muted-foreground hover:text-foreground transition-colors"
|
|
251
|
+
>
|
|
252
|
+
{expandedSections.pages ? <ChevronDown className="h-4 w-4" /> : <ChevronRight className="h-4 w-4" />}
|
|
253
|
+
<FileText className="h-4 w-4" />
|
|
254
|
+
Pages
|
|
255
|
+
</button>
|
|
256
|
+
|
|
257
|
+
{expandedSections.pages && (
|
|
258
|
+
<div className="space-y-1">
|
|
259
|
+
{pageItems.map((page) => {
|
|
260
|
+
const Icon = page.icon
|
|
261
|
+
return (
|
|
262
|
+
<div
|
|
263
|
+
key={page.title}
|
|
264
|
+
className="flex items-center gap-3 rounded-lg px-3 py-2.5 transition-colors hover:bg-muted/50 cursor-pointer"
|
|
265
|
+
>
|
|
266
|
+
<Icon className="h-4 w-4 text-muted-foreground shrink-0" />
|
|
267
|
+
<div className="flex-1 min-w-0">
|
|
268
|
+
<p className="text-sm font-medium">{page.title}</p>
|
|
269
|
+
<p className="text-xs text-muted-foreground truncate">{page.desc}</p>
|
|
270
|
+
</div>
|
|
271
|
+
</div>
|
|
272
|
+
)
|
|
273
|
+
})}
|
|
274
|
+
</div>
|
|
275
|
+
)}
|
|
276
|
+
</div>
|
|
277
|
+
|
|
278
|
+
{/* Notes toggle */}
|
|
279
|
+
<div>
|
|
280
|
+
<button
|
|
281
|
+
type="button"
|
|
282
|
+
onClick={() => toggle('notes')}
|
|
283
|
+
className="flex items-center gap-1.5 mb-3 text-sm font-semibold text-muted-foreground hover:text-foreground transition-colors"
|
|
284
|
+
>
|
|
285
|
+
{expandedSections.notes ? <ChevronDown className="h-4 w-4" /> : <ChevronRight className="h-4 w-4" />}
|
|
286
|
+
<StickyNote className="h-4 w-4" />
|
|
287
|
+
Quick Notes
|
|
288
|
+
</button>
|
|
289
|
+
|
|
290
|
+
{expandedSections.notes && (
|
|
291
|
+
<Card className="shadow-none">
|
|
292
|
+
<CardContent className="p-4">
|
|
293
|
+
<div className="rounded-lg bg-muted/30 p-4 text-sm text-muted-foreground italic">
|
|
294
|
+
Start typing your notes here... This is a placeholder for a rich-text editor.
|
|
295
|
+
</div>
|
|
296
|
+
</CardContent>
|
|
297
|
+
</Card>
|
|
298
|
+
)}
|
|
299
|
+
</div>
|
|
300
|
+
</div>
|
|
301
|
+
</Main>
|
|
302
|
+
</AuthenticatedLayout>
|
|
303
|
+
)
|
|
304
|
+
}
|