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.
Files changed (220) hide show
  1. package/package.json +2 -1
  2. package/skeleton/.env.example +64 -0
  3. package/skeleton/README.md +46 -0
  4. package/skeleton/app/Console/Commands/.gitkeep +0 -0
  5. package/skeleton/app/Enums/UserStatus.ts +7 -0
  6. package/skeleton/app/Http/Controllers/HomeController.ts +78 -0
  7. package/skeleton/app/Http/Middleware/.gitkeep +0 -0
  8. package/skeleton/app/Models/User.ts +7 -0
  9. package/skeleton/app/Providers/AppServiceProvider.ts +25 -0
  10. package/skeleton/app/Providers/DatabaseServiceProvider.ts +17 -0
  11. package/skeleton/bootstrap/.gitkeep +0 -0
  12. package/skeleton/config/ai.ts +51 -0
  13. package/skeleton/config/app.ts +108 -0
  14. package/skeleton/config/auth.ts +51 -0
  15. package/skeleton/config/broadcasting.ts +93 -0
  16. package/skeleton/config/cache.ts +61 -0
  17. package/skeleton/config/cors.ts +77 -0
  18. package/skeleton/config/database.ts +120 -0
  19. package/skeleton/config/filesystem.ts +58 -0
  20. package/skeleton/config/hashing.ts +47 -0
  21. package/skeleton/config/heartbeat.ts +112 -0
  22. package/skeleton/config/logging.ts +58 -0
  23. package/skeleton/config/mail.ts +93 -0
  24. package/skeleton/config/notify.ts +141 -0
  25. package/skeleton/config/queue.ts +59 -0
  26. package/skeleton/config/search.ts +96 -0
  27. package/skeleton/config/services.ts +110 -0
  28. package/skeleton/config/session.ts +84 -0
  29. package/skeleton/config/vite.ts +33 -0
  30. package/skeleton/database/factories/.gitkeep +0 -0
  31. package/skeleton/database/migrations/001_create_users_table.ts +19 -0
  32. package/skeleton/database/migrations/002_create_personal_access_tokens_table.ts +22 -0
  33. package/skeleton/database/seeders/DatabaseSeeder.ts +7 -0
  34. package/skeleton/index.ts +20 -0
  35. package/skeleton/mantiq.ts +8 -0
  36. package/skeleton/package.json +34 -0
  37. package/skeleton/public/.gitkeep +0 -0
  38. package/skeleton/routes/api.ts +8 -0
  39. package/skeleton/routes/channels.ts +23 -0
  40. package/skeleton/routes/console.ts +24 -0
  41. package/skeleton/routes/web.ts +6 -0
  42. package/skeleton/storage/cache/.gitkeep +0 -0
  43. package/skeleton/storage/framework/.gitkeep +0 -0
  44. package/skeleton/tests/feature/api.test.ts +14 -0
  45. package/skeleton/tests/feature/home.test.ts +17 -0
  46. package/skeleton/tests/unit/example.test.ts +11 -0
  47. package/skeleton/tsconfig.json +27 -0
  48. package/src/index.ts +289 -25
  49. package/src/templates.ts +141 -945
  50. package/src/terminal.ts +64 -0
  51. package/stubs/api-only/routes/api.ts.stub +24 -0
  52. package/stubs/api-only/tests/feature/token-auth.test.ts.stub +69 -0
  53. package/stubs/auth/api/app/Http/Controllers/ApiAuthController.ts.stub +57 -0
  54. package/stubs/auth/api/routes/api.ts.stub +24 -0
  55. package/stubs/auth/api/tests/feature/token-auth.test.ts.stub +69 -0
  56. package/stubs/auth/shared/app/Http/Requests/LoginRequest.ts.stub +10 -0
  57. package/stubs/auth/shared/app/Http/Requests/RegisterRequest.ts.stub +11 -0
  58. package/stubs/auth/web/app/Http/Controllers/AuthController.ts.stub +43 -0
  59. package/stubs/auth/web/app/Http/Controllers/PageController.ts.stub +66 -0
  60. package/stubs/auth/web/routes/web.ts.stub +25 -0
  61. package/stubs/auth/web/svelte/src/App.svelte.stub +77 -0
  62. package/stubs/auth/web/svelte/src/pages.ts.stub +17 -0
  63. package/stubs/auth/web/tests/feature/auth.test.ts.stub +69 -0
  64. package/stubs/auth/web/vue/src/App.vue.stub +74 -0
  65. package/stubs/auth/web/vue/src/pages.ts.stub +17 -0
  66. package/stubs/manifest.json +630 -2
  67. package/stubs/noauth/app/Http/Controllers/PageController.ts.stub +41 -0
  68. package/stubs/noauth/app/Models/User.ts.stub +5 -0
  69. package/stubs/noauth/database/migrations/001_create_users_table.ts.stub +17 -0
  70. package/stubs/noauth/routes/api.ts.stub +16 -0
  71. package/stubs/noauth/routes/web.ts.stub +15 -0
  72. package/stubs/noauth/svelte/src/App.svelte.stub +68 -0
  73. package/stubs/noauth/svelte/src/pages.ts.stub +7 -0
  74. package/stubs/noauth/vue/src/App.vue.stub +62 -0
  75. package/stubs/noauth/vue/src/pages.ts.stub +7 -0
  76. package/stubs/react/src/App.tsx.stub +4 -2
  77. package/stubs/react/src/components/layout/search-dialog.tsx.stub +2 -2
  78. package/stubs/react/src/components/layout/sidebar-data.ts.stub +2 -2
  79. package/stubs/react/src/lib/api.ts.stub +30 -6
  80. package/stubs/react/src/pages/Login.tsx.stub +3 -3
  81. package/stubs/react/src/pages/users/dialogs.tsx.stub +7 -26
  82. package/stubs/react/vite.config.ts.stub +26 -3
  83. package/stubs/shared/app/Http/Controllers/ApiAuthController.ts.stub +57 -0
  84. package/stubs/shared/app/Http/Controllers/AuthController.ts.stub +14 -38
  85. package/stubs/shared/app/Http/Controllers/PageController.ts.stub +3 -3
  86. package/stubs/shared/app/Http/Controllers/UserController.ts.stub +61 -0
  87. package/stubs/shared/app/Http/Requests/LoginRequest.ts.stub +10 -0
  88. package/stubs/shared/app/Http/Requests/RegisterRequest.ts.stub +11 -0
  89. package/stubs/shared/app/Http/Requests/StoreUserRequest.ts.stub +11 -0
  90. package/stubs/shared/app/Http/Requests/UpdateUserRequest.ts.stub +11 -0
  91. package/stubs/shared/config/app.ts.stub +36 -0
  92. package/stubs/shared/config/vite.ts.stub +8 -0
  93. package/stubs/shared/database/factories/UserFactory.ts.stub +4 -6
  94. package/stubs/shared/routes/api.ts.stub +12 -102
  95. package/stubs/shared/routes/web.ts.stub +5 -3
  96. package/stubs/shared/tests/feature/auth.test.ts.stub +69 -0
  97. package/stubs/shared/tests/feature/users.test.ts.stub +90 -0
  98. package/stubs/svelte/src/App.svelte.stub +1 -1
  99. package/stubs/svelte/src/lib/api.ts.stub +30 -6
  100. package/stubs/svelte/src/main.ts.stub +3 -1
  101. package/stubs/svelte/src/pages/Login.svelte.stub +3 -3
  102. package/stubs/svelte/vite.config.ts.stub +20 -1
  103. package/stubs/tailwind-only/react/src/components/layout/app-sidebar.tsx.stub +68 -0
  104. package/stubs/tailwind-only/react/src/components/layout/authenticated-layout.tsx.stub +57 -0
  105. package/stubs/tailwind-only/react/src/components/layout/header.tsx.stub +52 -0
  106. package/stubs/tailwind-only/react/src/components/layout/main.tsx.stub +21 -0
  107. package/stubs/tailwind-only/react/src/components/layout/nav-group.tsx.stub +185 -0
  108. package/stubs/tailwind-only/react/src/components/layout/nav-user.tsx.stub +106 -0
  109. package/stubs/tailwind-only/react/src/components/layout/sidebar-data.ts.stub +58 -0
  110. package/stubs/tailwind-only/react/src/components/layout/theme-toggle.tsx.stub +36 -0
  111. package/stubs/tailwind-only/react/src/components/layout/top-nav.tsx.stub +72 -0
  112. package/stubs/tailwind-only/react/src/lib/utils.ts.stub +6 -0
  113. package/stubs/tailwind-only/react/src/pages/Dashboard.tsx.stub +205 -0
  114. package/stubs/tailwind-only/react/src/pages/Login.tsx.stub +122 -0
  115. package/stubs/tailwind-only/react/src/pages/Register.tsx.stub +137 -0
  116. package/stubs/tailwind-only/react/src/pages/Users.tsx.stub +426 -0
  117. package/stubs/tailwind-only/react/src/pages/account/layout.tsx.stub +64 -0
  118. package/stubs/tailwind-only/react/src/pages/account/preferences.tsx.stub +80 -0
  119. package/stubs/tailwind-only/react/src/pages/account/profile.tsx.stub +67 -0
  120. package/stubs/tailwind-only/react/src/pages/account/security.tsx.stub +91 -0
  121. package/stubs/tailwind-only/react/src/style.css.stub +14 -0
  122. package/stubs/tailwind-only/svelte/src/lib/components/layout/app-sidebar.svelte.stub +104 -0
  123. package/stubs/tailwind-only/svelte/src/lib/components/layout/authenticated-layout.svelte.stub +51 -0
  124. package/stubs/tailwind-only/svelte/src/lib/components/layout/header.svelte.stub +66 -0
  125. package/stubs/tailwind-only/svelte/src/lib/components/layout/main.svelte.stub +26 -0
  126. package/stubs/tailwind-only/svelte/src/lib/components/layout/nav-group.svelte.stub +131 -0
  127. package/stubs/tailwind-only/svelte/src/lib/components/layout/nav-user.svelte.stub +104 -0
  128. package/stubs/tailwind-only/svelte/src/lib/components/layout/sidebar-data.ts.stub +57 -0
  129. package/stubs/tailwind-only/svelte/src/lib/components/layout/theme-toggle.svelte.stub +28 -0
  130. package/stubs/tailwind-only/svelte/src/lib/components/layout/top-nav.svelte.stub +99 -0
  131. package/stubs/tailwind-only/svelte/src/lib/utils.ts.stub +6 -0
  132. package/stubs/tailwind-only/svelte/src/pages/Dashboard.svelte.stub +192 -0
  133. package/stubs/tailwind-only/svelte/src/pages/Login.svelte.stub +120 -0
  134. package/stubs/tailwind-only/svelte/src/pages/Register.svelte.stub +134 -0
  135. package/stubs/tailwind-only/svelte/src/pages/Users.svelte.stub +293 -0
  136. package/stubs/tailwind-only/svelte/src/pages/account/Layout.svelte.stub +61 -0
  137. package/stubs/tailwind-only/svelte/src/pages/account/Preferences.svelte.stub +81 -0
  138. package/stubs/tailwind-only/svelte/src/pages/account/Profile.svelte.stub +76 -0
  139. package/stubs/tailwind-only/svelte/src/pages/account/Security.svelte.stub +140 -0
  140. package/stubs/tailwind-only/svelte/src/style.css.stub +127 -0
  141. package/stubs/tailwind-only/vue/src/components/layout/AppSidebar.vue.stub +60 -0
  142. package/stubs/tailwind-only/vue/src/components/layout/AuthenticatedLayout.vue.stub +73 -0
  143. package/stubs/tailwind-only/vue/src/components/layout/Header.vue.stub +54 -0
  144. package/stubs/tailwind-only/vue/src/components/layout/Main.vue.stub +22 -0
  145. package/stubs/tailwind-only/vue/src/components/layout/NavGroup.vue.stub +107 -0
  146. package/stubs/tailwind-only/vue/src/components/layout/NavUser.vue.stub +104 -0
  147. package/stubs/tailwind-only/vue/src/components/layout/ThemeToggle.vue.stub +28 -0
  148. package/stubs/tailwind-only/vue/src/components/layout/TopNav.vue.stub +86 -0
  149. package/stubs/tailwind-only/vue/src/components/layout/sidebar-data.ts.stub +57 -0
  150. package/stubs/tailwind-only/vue/src/lib/utils.ts.stub +7 -0
  151. package/stubs/tailwind-only/vue/src/pages/Dashboard.vue.stub +195 -0
  152. package/stubs/tailwind-only/vue/src/pages/Login.vue.stub +121 -0
  153. package/stubs/tailwind-only/vue/src/pages/Register.vue.stub +137 -0
  154. package/stubs/tailwind-only/vue/src/pages/Users.vue.stub +401 -0
  155. package/stubs/tailwind-only/vue/src/pages/account/Layout.vue.stub +56 -0
  156. package/stubs/tailwind-only/vue/src/pages/account/Preferences.vue.stub +81 -0
  157. package/stubs/tailwind-only/vue/src/pages/account/Profile.vue.stub +69 -0
  158. package/stubs/tailwind-only/vue/src/pages/account/Security.vue.stub +85 -0
  159. package/stubs/tailwind-only/vue/src/style.css.stub +26 -0
  160. package/stubs/themes/corporate/react/src/components/layout/app-sidebar.tsx.stub +74 -0
  161. package/stubs/themes/corporate/react/src/components/layout/authenticated-layout.tsx.stub +42 -0
  162. package/stubs/themes/corporate/react/src/pages/Dashboard.tsx.stub +310 -0
  163. package/stubs/themes/corporate/react/src/pages/Login.tsx.stub +122 -0
  164. package/stubs/themes/corporate/react/src/pages/Register.tsx.stub +144 -0
  165. package/stubs/themes/corporate/react/src/style.css.stub +135 -0
  166. package/stubs/themes/corporate/svelte/src/pages/Dashboard.svelte.stub +271 -0
  167. package/stubs/themes/corporate/svelte/src/pages/Login.svelte.stub +117 -0
  168. package/stubs/themes/corporate/svelte/src/pages/Register.svelte.stub +138 -0
  169. package/stubs/themes/corporate/svelte/src/style.css.stub +134 -0
  170. package/stubs/themes/corporate/vue/src/pages/Dashboard.vue.stub +325 -0
  171. package/stubs/themes/corporate/vue/src/pages/Login.vue.stub +118 -0
  172. package/stubs/themes/corporate/vue/src/pages/Register.vue.stub +139 -0
  173. package/stubs/themes/corporate/vue/src/style.css.stub +134 -0
  174. package/stubs/themes/minimal/react/src/components/layout/app-sidebar.tsx.stub +68 -0
  175. package/stubs/themes/minimal/react/src/components/layout/authenticated-layout.tsx.stub +42 -0
  176. package/stubs/themes/minimal/react/src/pages/Dashboard.tsx.stub +166 -0
  177. package/stubs/themes/minimal/react/src/pages/Login.tsx.stub +95 -0
  178. package/stubs/themes/minimal/react/src/pages/Register.tsx.stub +73 -0
  179. package/stubs/themes/minimal/react/src/style.css.stub +142 -0
  180. package/stubs/themes/minimal/svelte/src/pages/Dashboard.svelte.stub +149 -0
  181. package/stubs/themes/minimal/svelte/src/pages/Login.svelte.stub +90 -0
  182. package/stubs/themes/minimal/svelte/src/pages/Register.svelte.stub +70 -0
  183. package/stubs/themes/minimal/svelte/src/style.css.stub +142 -0
  184. package/stubs/themes/minimal/vue/src/pages/Dashboard.vue.stub +163 -0
  185. package/stubs/themes/minimal/vue/src/pages/Login.vue.stub +91 -0
  186. package/stubs/themes/minimal/vue/src/pages/Register.vue.stub +73 -0
  187. package/stubs/themes/minimal/vue/src/style.css.stub +142 -0
  188. package/stubs/themes/starter/react/src/components/layout/app-sidebar.tsx.stub +74 -0
  189. package/stubs/themes/starter/react/src/components/layout/authenticated-layout.tsx.stub +42 -0
  190. package/stubs/themes/starter/react/src/pages/Dashboard.tsx.stub +236 -0
  191. package/stubs/themes/starter/react/src/pages/Login.tsx.stub +131 -0
  192. package/stubs/themes/starter/react/src/pages/Register.tsx.stub +145 -0
  193. package/stubs/themes/starter/react/src/style.css.stub +141 -0
  194. package/stubs/themes/starter/svelte/src/pages/Dashboard.svelte.stub +212 -0
  195. package/stubs/themes/starter/svelte/src/pages/Login.svelte.stub +126 -0
  196. package/stubs/themes/starter/svelte/src/pages/Register.svelte.stub +139 -0
  197. package/stubs/themes/starter/svelte/src/style.css.stub +141 -0
  198. package/stubs/themes/starter/vue/src/pages/Dashboard.vue.stub +228 -0
  199. package/stubs/themes/starter/vue/src/pages/Login.vue.stub +127 -0
  200. package/stubs/themes/starter/vue/src/pages/Register.vue.stub +140 -0
  201. package/stubs/themes/starter/vue/src/style.css.stub +141 -0
  202. package/stubs/themes/workspace/react/src/components/layout/app-sidebar.tsx.stub +97 -0
  203. package/stubs/themes/workspace/react/src/components/layout/authenticated-layout.tsx.stub +42 -0
  204. package/stubs/themes/workspace/react/src/pages/Dashboard.tsx.stub +304 -0
  205. package/stubs/themes/workspace/react/src/pages/Login.tsx.stub +131 -0
  206. package/stubs/themes/workspace/react/src/pages/Register.tsx.stub +82 -0
  207. package/stubs/themes/workspace/react/src/style.css.stub +138 -0
  208. package/stubs/themes/workspace/svelte/src/pages/Dashboard.svelte.stub +215 -0
  209. package/stubs/themes/workspace/svelte/src/pages/Login.svelte.stub +124 -0
  210. package/stubs/themes/workspace/svelte/src/pages/Register.svelte.stub +76 -0
  211. package/stubs/themes/workspace/svelte/src/style.css.stub +134 -0
  212. package/stubs/themes/workspace/vue/src/pages/Dashboard.vue.stub +220 -0
  213. package/stubs/themes/workspace/vue/src/pages/Login.vue.stub +128 -0
  214. package/stubs/themes/workspace/vue/src/pages/Register.vue.stub +80 -0
  215. package/stubs/themes/workspace/vue/src/style.css.stub +134 -0
  216. package/stubs/vue/src/App.vue.stub +2 -1
  217. package/stubs/vue/src/lib/api.ts.stub +30 -6
  218. package/stubs/vue/src/main.ts.stub +3 -1
  219. package/stubs/vue/src/pages/Login.vue.stub +3 -3
  220. package/stubs/vue/vite.config.ts.stub +20 -1
@@ -0,0 +1,142 @@
1
+ @import "tailwindcss";
2
+ @custom-variant dark (&:where(.dark, .dark *));
3
+
4
+ /*
5
+ * shadcn/ui theme — Minimal
6
+ *
7
+ * Extreme minimalism: zero shadows, hairline borders, sharp corners.
8
+ * Monospace numbers. Compact spacing. Inspired by Minimal.app.
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.375rem;
52
+ --background: oklch(1 0 0);
53
+ --foreground: oklch(0.145 0.004 285.823);
54
+ --card: oklch(1 0 0);
55
+ --card-foreground: oklch(0.145 0.004 285.823);
56
+ --popover: oklch(1 0 0);
57
+ --popover-foreground: oklch(0.145 0.004 285.823);
58
+ --primary: oklch(0.546 0.245 262.881);
59
+ --primary-foreground: oklch(0.985 0 0);
60
+ --secondary: oklch(0.967 0.001 286.375);
61
+ --secondary-foreground: oklch(0.205 0.006 285.885);
62
+ --muted: oklch(0.967 0.001 286.375);
63
+ --muted-foreground: oklch(0.556 0.014 285.938);
64
+ --accent: oklch(0.967 0.001 286.375);
65
+ --accent-foreground: oklch(0.205 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.935 0.004 286.32);
69
+ --input: oklch(0.935 0.004 286.32);
70
+ --ring: oklch(0.546 0.245 262.881);
71
+ --chart-1: oklch(0.546 0.245 262.881);
72
+ --chart-2: oklch(0.6 0.118 184.714);
73
+ --chart-3: oklch(0.398 0.07 227.392);
74
+ --chart-4: oklch(0.828 0.189 84.429);
75
+ --chart-5: oklch(0.769 0.188 70.08);
76
+ --sidebar: oklch(0.985 0.001 286.375);
77
+ --sidebar-foreground: oklch(0.145 0.004 285.823);
78
+ --sidebar-primary: oklch(0.546 0.245 262.881);
79
+ --sidebar-primary-foreground: oklch(0.985 0 0);
80
+ --sidebar-accent: oklch(0.967 0.001 286.375);
81
+ --sidebar-accent-foreground: oklch(0.205 0.006 285.885);
82
+ --sidebar-border: oklch(0.935 0.004 286.32);
83
+ --sidebar-ring: oklch(0.546 0.245 262.881);
84
+ }
85
+
86
+ .dark {
87
+ --background: oklch(0.13 0.004 285.823);
88
+ --foreground: oklch(0.985 0 0);
89
+ --card: oklch(0.155 0.004 285.823);
90
+ --card-foreground: oklch(0.985 0 0);
91
+ --popover: oklch(0.155 0.004 285.823);
92
+ --popover-foreground: oklch(0.985 0 0);
93
+ --primary: oklch(0.623 0.214 262.881);
94
+ --primary-foreground: oklch(0.13 0.004 285.823);
95
+ --secondary: oklch(0.22 0.006 286.033);
96
+ --secondary-foreground: oklch(0.985 0 0);
97
+ --muted: oklch(0.22 0.006 286.033);
98
+ --muted-foreground: oklch(0.63 0.014 286.067);
99
+ --accent: oklch(0.22 0.006 286.033);
100
+ --accent-foreground: oklch(0.985 0 0);
101
+ --destructive: oklch(0.704 0.191 22.216);
102
+ --destructive-foreground: oklch(0.704 0.191 22.216);
103
+ --border: oklch(0.24 0.005 286.033);
104
+ --input: oklch(0.24 0.005 286.033);
105
+ --ring: oklch(0.623 0.214 262.881);
106
+ --chart-1: oklch(0.623 0.214 262.881);
107
+ --chart-2: oklch(0.696 0.17 162.48);
108
+ --chart-3: oklch(0.769 0.188 70.08);
109
+ --chart-4: oklch(0.627 0.265 303.9);
110
+ --chart-5: oklch(0.645 0.246 16.439);
111
+ --sidebar: oklch(0.14 0.004 285.823);
112
+ --sidebar-foreground: oklch(0.985 0 0);
113
+ --sidebar-primary: oklch(0.623 0.214 262.881);
114
+ --sidebar-primary-foreground: oklch(0.13 0.004 285.823);
115
+ --sidebar-accent: oklch(0.22 0.006 286.033);
116
+ --sidebar-accent-foreground: oklch(0.985 0 0);
117
+ --sidebar-border: oklch(0.24 0.005 286.033);
118
+ --sidebar-ring: oklch(0.623 0.214 262.881);
119
+ }
120
+
121
+ @layer base {
122
+ * {
123
+ @apply border-border;
124
+ }
125
+ body {
126
+ @apply bg-background text-foreground;
127
+ font-feature-settings: "ss01", "ss02", "cv01";
128
+ -webkit-font-smoothing: antialiased;
129
+ }
130
+ }
131
+
132
+ /* Monospace tabular figures for numbers */
133
+ .font-mono-num {
134
+ font-variant-numeric: tabular-nums;
135
+ font-feature-settings: "tnum";
136
+ }
137
+
138
+ @keyframes fadeUp {
139
+ from { opacity: 0; transform: translateY(8px); }
140
+ to { opacity: 1; transform: translateY(0); }
141
+ }
142
+ .animate-fade-up { animation: fadeUp 0.3s ease-out; }
@@ -0,0 +1,163 @@
1
+ <script setup lang="ts">
2
+ import AuthenticatedLayout from '@/components/layout/AuthenticatedLayout.vue'
3
+ import Header from '@/components/layout/Header.vue'
4
+ import Main from '@/components/layout/Main.vue'
5
+ import {
6
+ Circle,
7
+ CheckCircle2,
8
+ ArrowUpRight,
9
+ Clock,
10
+ AlertCircle,
11
+ Minus,
12
+ } from 'lucide-vue-next'
13
+
14
+ type Priority = 'urgent' | 'high' | 'medium' | 'low' | 'none'
15
+ type Status = 'done' | 'in-progress' | 'todo' | 'backlog'
16
+
17
+ const props = withDefaults(defineProps<{
18
+ appName?: string
19
+ currentUser?: { id: number; name: string; email: string; role: string } | null
20
+ navigate?: (href: string) => void
21
+ }>(), {
22
+ appName: 'Mantiq',
23
+ })
24
+
25
+ const issues: {
26
+ id: string
27
+ title: string
28
+ status: Status
29
+ priority: Priority
30
+ assignee: string
31
+ updated: string
32
+ }[] = [
33
+ { id: 'MNT-142', title: 'Fix authentication token refresh race condition', status: 'in-progress', priority: 'urgent', assignee: 'AK', updated: '2m' },
34
+ { id: 'MNT-141', title: 'Add rate limiting to public API endpoints', status: 'todo', priority: 'high', assignee: 'SC', updated: '15m' },
35
+ { id: 'MNT-140', title: 'Migrate user sessions to Redis store', status: 'in-progress', priority: 'high', assignee: 'AK', updated: '1h' },
36
+ { id: 'MNT-139', title: 'Update Stripe webhook handler for v2024-12', status: 'todo', priority: 'medium', assignee: 'JL', updated: '2h' },
37
+ { id: 'MNT-138', title: 'Implement CSV export for analytics dashboard', status: 'done', priority: 'medium', assignee: 'NR', updated: '3h' },
38
+ { id: 'MNT-137', title: 'Add OpenTelemetry tracing to queue workers', status: 'backlog', priority: 'low', assignee: 'SC', updated: '5h' },
39
+ { id: 'MNT-136', title: 'Refactor notification preferences into per-channel config', status: 'done', priority: 'medium', assignee: 'AK', updated: '6h' },
40
+ { id: 'MNT-135', title: 'Set up staging environment with seed data', status: 'backlog', priority: 'low', assignee: 'JL', updated: '1d' },
41
+ { id: 'MNT-134', title: 'Write integration tests for payment flow', status: 'todo', priority: 'high', assignee: 'NR', updated: '1d' },
42
+ { id: 'MNT-133', title: 'Audit and update npm dependencies', status: 'backlog', priority: 'none', assignee: 'SC', updated: '2d' },
43
+ ]
44
+
45
+ const priorityColors: Record<Priority, string> = {
46
+ urgent: 'bg-red-500',
47
+ high: 'bg-orange-500',
48
+ medium: 'bg-yellow-500',
49
+ low: 'bg-blue-400',
50
+ none: 'bg-muted-foreground/30',
51
+ }
52
+
53
+ function isPriorityBarActive(priority: Priority, index: number): boolean {
54
+ return (
55
+ (priority === 'urgent' && index <= 3) ||
56
+ (priority === 'high' && index <= 2) ||
57
+ (priority === 'medium' && index <= 1) ||
58
+ (priority === 'low' && index <= 0)
59
+ )
60
+ }
61
+
62
+ const doneCount = issues.filter((i) => i.status === 'done').length
63
+ const inProgressCount = issues.filter((i) => i.status === 'in-progress').length
64
+ const todoCount = issues.filter((i) => i.status === 'todo').length
65
+ const urgentCount = issues.filter((i) => i.priority === 'urgent').length
66
+ </script>
67
+
68
+ <template>
69
+ <AuthenticatedLayout
70
+ :current-user="currentUser"
71
+ :app-name="appName"
72
+ :navigate="navigate"
73
+ active-path="/dashboard"
74
+ >
75
+ <Header :navigate="navigate" />
76
+ <Main>
77
+ <div class="space-y-1">
78
+ <!-- Compact header row -->
79
+ <div class="flex items-center justify-between py-3">
80
+ <div class="flex items-center gap-6">
81
+ <h2 class="text-sm font-semibold">My Issues</h2>
82
+ <div class="flex items-center gap-4 text-xs text-muted-foreground">
83
+ <span class="flex items-center gap-1.5">
84
+ <Clock class="h-3 w-3 text-amber-500" />
85
+ {{ inProgressCount }} in progress
86
+ </span>
87
+ <span class="flex items-center gap-1.5">
88
+ <Circle class="h-3 w-3" />
89
+ {{ todoCount }} todo
90
+ </span>
91
+ <span class="flex items-center gap-1.5">
92
+ <CheckCircle2 class="h-3 w-3 text-primary" />
93
+ {{ doneCount }} done
94
+ </span>
95
+ </div>
96
+ </div>
97
+ <div class="flex items-center gap-2">
98
+ <kbd class="hidden sm:inline-flex items-center gap-0.5 rounded border bg-muted px-1.5 py-0.5 text-[10px] font-medium text-muted-foreground">
99
+ <span class="text-xs">&#8984;</span>K
100
+ </kbd>
101
+ </div>
102
+ </div>
103
+
104
+ <!-- Issue list — no cards, just rows -->
105
+ <div class="border-t">
106
+ <div
107
+ v-for="issue in issues"
108
+ :key="issue.id"
109
+ class="group flex items-center gap-3 border-b px-1 py-2 transition-colors hover:bg-muted/50 cursor-default"
110
+ >
111
+ <!-- Priority indicator -->
112
+ <div class="flex items-center gap-0.5" :title="issue.priority">
113
+ <div
114
+ v-for="i in 4"
115
+ :key="i"
116
+ :class="[
117
+ 'h-2.5 w-[3px] rounded-[1px]',
118
+ isPriorityBarActive(issue.priority, i - 1)
119
+ ? priorityColors[issue.priority]
120
+ : 'bg-muted-foreground/15',
121
+ ]"
122
+ />
123
+ </div>
124
+
125
+ <!-- Status icon -->
126
+ <CheckCircle2 v-if="issue.status === 'done'" class="h-3.5 w-3.5 text-primary" />
127
+ <Clock v-else-if="issue.status === 'in-progress'" class="h-3.5 w-3.5 text-amber-500" />
128
+ <Circle v-else-if="issue.status === 'todo'" class="h-3.5 w-3.5 text-muted-foreground" />
129
+ <Minus v-else class="h-3.5 w-3.5 text-muted-foreground/50" />
130
+
131
+ <span class="font-mono-num text-xs text-muted-foreground w-16 shrink-0">
132
+ {{ issue.id }}
133
+ </span>
134
+ <span
135
+ :class="[
136
+ 'flex-1 text-sm truncate',
137
+ issue.status === 'done' ? 'line-through text-muted-foreground' : '',
138
+ ]"
139
+ >
140
+ {{ issue.title }}
141
+ </span>
142
+ <div class="flex h-5 w-5 shrink-0 items-center justify-center rounded-full bg-muted text-[10px] font-medium text-muted-foreground">
143
+ {{ issue.assignee }}
144
+ </div>
145
+ <span class="font-mono-num text-xs text-muted-foreground w-8 text-right shrink-0">
146
+ {{ issue.updated }}
147
+ </span>
148
+ <ArrowUpRight class="h-3.5 w-3.5 text-muted-foreground/0 group-hover:text-muted-foreground transition-colors shrink-0" />
149
+ </div>
150
+ </div>
151
+
152
+ <!-- Bottom counts -->
153
+ <div class="flex items-center justify-between pt-2 text-xs text-muted-foreground">
154
+ <span>{{ issues.length }} issues</span>
155
+ <span class="flex items-center gap-1">
156
+ <AlertCircle class="h-3 w-3" />
157
+ {{ urgentCount }} urgent
158
+ </span>
159
+ </div>
160
+ </div>
161
+ </Main>
162
+ </AuthenticatedLayout>
163
+ </template>
@@ -0,0 +1,91 @@
1
+ <script setup lang="ts">
2
+ import { ref } from 'vue'
3
+ import { post } from '@/lib/api'
4
+ import { Button } from '@/components/ui/button'
5
+ import { Input } from '@/components/ui/input'
6
+ import { Label } from '@/components/ui/label'
7
+
8
+ const props = withDefaults(defineProps<{
9
+ appName?: string
10
+ navigate?: (href: string) => void
11
+ }>(), {
12
+ appName: 'Mantiq',
13
+ })
14
+
15
+ const email = ref('')
16
+ const password = ref('')
17
+ const error = ref('')
18
+ const loading = ref(false)
19
+
20
+ async function handleSubmit() {
21
+ error.value = ''
22
+ loading.value = true
23
+ const { ok, data } = await post('/login', { email: email.value, password: password.value })
24
+ if (ok) props.navigate?.('/dashboard')
25
+ else error.value = data?.error ?? 'Invalid credentials.'
26
+ loading.value = false
27
+ }
28
+ </script>
29
+
30
+ <template>
31
+ <div class="flex min-h-screen flex-col items-center justify-center bg-background px-4">
32
+ <div class="w-full max-w-[320px]">
33
+ <!-- Logo — just text, minimal -->
34
+ <div class="mb-10">
35
+ <div class="flex items-center gap-2">
36
+ <div class="flex h-7 w-7 items-center justify-center rounded bg-foreground text-background text-xs font-bold">
37
+ M
38
+ </div>
39
+ <span class="text-sm font-semibold tracking-tight">{{ appName }}</span>
40
+ </div>
41
+ </div>
42
+
43
+ <h1 class="text-sm font-medium mb-6">Sign in to continue</h1>
44
+
45
+ <div v-if="error" class="mb-4 text-sm text-destructive">
46
+ {{ error }}
47
+ </div>
48
+
49
+ <form class="space-y-3" @submit.prevent="handleSubmit">
50
+ <div class="space-y-1.5">
51
+ <Label for="email" class="text-xs text-muted-foreground">Email</Label>
52
+ <Input
53
+ id="email"
54
+ v-model="email"
55
+ type="email"
56
+ required
57
+ placeholder="you@example.com"
58
+ autocomplete="email"
59
+ class="h-8 text-sm"
60
+ />
61
+ </div>
62
+ <div class="space-y-1.5">
63
+ <Label for="password" class="text-xs text-muted-foreground">Password</Label>
64
+ <Input
65
+ id="password"
66
+ v-model="password"
67
+ type="password"
68
+ required
69
+ placeholder="••••••••"
70
+ autocomplete="current-password"
71
+ class="h-8 text-sm"
72
+ />
73
+ </div>
74
+ <Button type="submit" class="w-full h-8 text-sm" :disabled="loading">
75
+ {{ loading ? 'Signing in\u2026' : 'Continue' }}
76
+ </Button>
77
+ </form>
78
+
79
+ <p class="mt-4 text-xs text-muted-foreground">
80
+ No account?{{ ' ' }}
81
+ <button
82
+ type="button"
83
+ class="text-foreground hover:underline underline-offset-2"
84
+ @click="navigate?.('/register')"
85
+ >
86
+ Create one
87
+ </button>
88
+ </p>
89
+ </div>
90
+ </div>
91
+ </template>
@@ -0,0 +1,73 @@
1
+ <script setup lang="ts">
2
+ import { ref } from 'vue'
3
+ import { post } from '@/lib/api'
4
+ import { Button } from '@/components/ui/button'
5
+ import { Input } from '@/components/ui/input'
6
+ import { Label } from '@/components/ui/label'
7
+
8
+ const props = withDefaults(defineProps<{
9
+ appName?: string
10
+ navigate?: (href: string) => void
11
+ }>(), {
12
+ appName: 'Mantiq',
13
+ })
14
+
15
+ const name = ref('')
16
+ const email = ref('')
17
+ const password = ref('')
18
+ const error = ref('')
19
+ const loading = ref(false)
20
+
21
+ async function handleSubmit() {
22
+ error.value = ''
23
+ loading.value = true
24
+ const { ok, data } = await post('/register', { name: name.value, email: email.value, password: password.value })
25
+ if (ok) props.navigate?.('/dashboard')
26
+ else error.value = data?.error ?? 'Registration failed.'
27
+ loading.value = false
28
+ }
29
+ </script>
30
+
31
+ <template>
32
+ <div class="flex min-h-screen flex-col items-center justify-center bg-background px-4">
33
+ <div class="w-full max-w-[320px]">
34
+ <div class="mb-10">
35
+ <div class="flex items-center gap-2">
36
+ <div class="flex h-7 w-7 items-center justify-center rounded bg-foreground text-background text-xs font-bold">
37
+ M
38
+ </div>
39
+ <span class="text-sm font-semibold tracking-tight">{{ appName }}</span>
40
+ </div>
41
+ </div>
42
+
43
+ <h1 class="text-sm font-medium mb-6">Create your account</h1>
44
+
45
+ <div v-if="error" class="mb-4 text-sm text-destructive">
46
+ {{ error }}
47
+ </div>
48
+
49
+ <form class="space-y-3" @submit.prevent="handleSubmit">
50
+ <div class="space-y-1.5">
51
+ <Label for="name" class="text-xs text-muted-foreground">Name</Label>
52
+ <Input id="name" v-model="name" required placeholder="Your name" class="h-8 text-sm" />
53
+ </div>
54
+ <div class="space-y-1.5">
55
+ <Label for="email" class="text-xs text-muted-foreground">Email</Label>
56
+ <Input id="email" v-model="email" type="email" required placeholder="you@example.com" autocomplete="email" class="h-8 text-sm" />
57
+ </div>
58
+ <div class="space-y-1.5">
59
+ <Label for="password" class="text-xs text-muted-foreground">Password</Label>
60
+ <Input id="password" v-model="password" type="password" required placeholder="••••••••" class="h-8 text-sm" />
61
+ </div>
62
+ <Button type="submit" class="w-full h-8 text-sm" :disabled="loading">
63
+ {{ loading ? 'Creating\u2026' : 'Create account' }}
64
+ </Button>
65
+ </form>
66
+
67
+ <p class="mt-4 text-xs text-muted-foreground">
68
+ Already have an account?{{ ' ' }}
69
+ <button type="button" class="text-foreground hover:underline underline-offset-2" @click="navigate?.('/login')">Sign in</button>
70
+ </p>
71
+ </div>
72
+ </div>
73
+ </template>
@@ -0,0 +1,142 @@
1
+ @import "tailwindcss";
2
+ @custom-variant dark (&:where(.dark, .dark *));
3
+
4
+ /*
5
+ * shadcn/ui theme — Minimal
6
+ *
7
+ * Extreme minimalism: zero shadows, hairline borders, sharp corners.
8
+ * Monospace numbers. Compact spacing. Inspired by Minimal.app.
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.375rem;
52
+ --background: oklch(1 0 0);
53
+ --foreground: oklch(0.145 0.004 285.823);
54
+ --card: oklch(1 0 0);
55
+ --card-foreground: oklch(0.145 0.004 285.823);
56
+ --popover: oklch(1 0 0);
57
+ --popover-foreground: oklch(0.145 0.004 285.823);
58
+ --primary: oklch(0.546 0.245 262.881);
59
+ --primary-foreground: oklch(0.985 0 0);
60
+ --secondary: oklch(0.967 0.001 286.375);
61
+ --secondary-foreground: oklch(0.205 0.006 285.885);
62
+ --muted: oklch(0.967 0.001 286.375);
63
+ --muted-foreground: oklch(0.556 0.014 285.938);
64
+ --accent: oklch(0.967 0.001 286.375);
65
+ --accent-foreground: oklch(0.205 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.935 0.004 286.32);
69
+ --input: oklch(0.935 0.004 286.32);
70
+ --ring: oklch(0.546 0.245 262.881);
71
+ --chart-1: oklch(0.546 0.245 262.881);
72
+ --chart-2: oklch(0.6 0.118 184.714);
73
+ --chart-3: oklch(0.398 0.07 227.392);
74
+ --chart-4: oklch(0.828 0.189 84.429);
75
+ --chart-5: oklch(0.769 0.188 70.08);
76
+ --sidebar: oklch(0.985 0.001 286.375);
77
+ --sidebar-foreground: oklch(0.145 0.004 285.823);
78
+ --sidebar-primary: oklch(0.546 0.245 262.881);
79
+ --sidebar-primary-foreground: oklch(0.985 0 0);
80
+ --sidebar-accent: oklch(0.967 0.001 286.375);
81
+ --sidebar-accent-foreground: oklch(0.205 0.006 285.885);
82
+ --sidebar-border: oklch(0.935 0.004 286.32);
83
+ --sidebar-ring: oklch(0.546 0.245 262.881);
84
+ }
85
+
86
+ .dark {
87
+ --background: oklch(0.13 0.004 285.823);
88
+ --foreground: oklch(0.985 0 0);
89
+ --card: oklch(0.155 0.004 285.823);
90
+ --card-foreground: oklch(0.985 0 0);
91
+ --popover: oklch(0.155 0.004 285.823);
92
+ --popover-foreground: oklch(0.985 0 0);
93
+ --primary: oklch(0.623 0.214 262.881);
94
+ --primary-foreground: oklch(0.13 0.004 285.823);
95
+ --secondary: oklch(0.22 0.006 286.033);
96
+ --secondary-foreground: oklch(0.985 0 0);
97
+ --muted: oklch(0.22 0.006 286.033);
98
+ --muted-foreground: oklch(0.63 0.014 286.067);
99
+ --accent: oklch(0.22 0.006 286.033);
100
+ --accent-foreground: oklch(0.985 0 0);
101
+ --destructive: oklch(0.704 0.191 22.216);
102
+ --destructive-foreground: oklch(0.704 0.191 22.216);
103
+ --border: oklch(0.24 0.005 286.033);
104
+ --input: oklch(0.24 0.005 286.033);
105
+ --ring: oklch(0.623 0.214 262.881);
106
+ --chart-1: oklch(0.623 0.214 262.881);
107
+ --chart-2: oklch(0.696 0.17 162.48);
108
+ --chart-3: oklch(0.769 0.188 70.08);
109
+ --chart-4: oklch(0.627 0.265 303.9);
110
+ --chart-5: oklch(0.645 0.246 16.439);
111
+ --sidebar: oklch(0.14 0.004 285.823);
112
+ --sidebar-foreground: oklch(0.985 0 0);
113
+ --sidebar-primary: oklch(0.623 0.214 262.881);
114
+ --sidebar-primary-foreground: oklch(0.13 0.004 285.823);
115
+ --sidebar-accent: oklch(0.22 0.006 286.033);
116
+ --sidebar-accent-foreground: oklch(0.985 0 0);
117
+ --sidebar-border: oklch(0.24 0.005 286.033);
118
+ --sidebar-ring: oklch(0.623 0.214 262.881);
119
+ }
120
+
121
+ @layer base {
122
+ * {
123
+ @apply border-border;
124
+ }
125
+ body {
126
+ @apply bg-background text-foreground;
127
+ font-feature-settings: "ss01", "ss02", "cv01";
128
+ -webkit-font-smoothing: antialiased;
129
+ }
130
+ }
131
+
132
+ /* Monospace tabular figures for numbers */
133
+ .font-mono-num {
134
+ font-variant-numeric: tabular-nums;
135
+ font-feature-settings: "tnum";
136
+ }
137
+
138
+ @keyframes fadeUp {
139
+ from { opacity: 0; transform: translateY(8px); }
140
+ to { opacity: 1; transform: translateY(0); }
141
+ }
142
+ .animate-fade-up { animation: fadeUp 0.3s ease-out; }
@@ -0,0 +1,74 @@
1
+ import {
2
+ Sidebar,
3
+ SidebarContent,
4
+ SidebarFooter,
5
+ SidebarHeader,
6
+ SidebarMenu,
7
+ SidebarMenuButton,
8
+ SidebarMenuItem,
9
+ SidebarRail,
10
+ } from '@/components/ui/sidebar'
11
+ import { NavGroup } from './nav-group'
12
+ import { NavUser, type NavUserProps } from './nav-user'
13
+ import { sidebarData } from './sidebar-data'
14
+ import { Command } from 'lucide-react'
15
+
16
+ export interface AppSidebarProps {
17
+ user: NavUserProps['user']
18
+ appName: string
19
+ activePath: string
20
+ navigate: (href: string) => void
21
+ onLogout: () => void
22
+ }
23
+
24
+ export function AppSidebar({
25
+ user,
26
+ appName,
27
+ activePath,
28
+ navigate,
29
+ onLogout,
30
+ }: AppSidebarProps) {
31
+ return (
32
+ <Sidebar variant="inset" collapsible="icon">
33
+ <SidebarHeader>
34
+ <SidebarMenu>
35
+ <SidebarMenuItem>
36
+ <SidebarMenuButton
37
+ size="lg"
38
+ className="data-[state=open]:bg-sidebar-accent data-[state=open]:text-sidebar-accent-foreground"
39
+ onClick={() => navigate('/dashboard')}
40
+ tooltip={appName}
41
+ >
42
+ <div className="flex aspect-square size-8 items-center justify-center rounded-lg bg-sidebar-primary text-sidebar-primary-foreground">
43
+ <Command className="size-4" />
44
+ </div>
45
+ <div className="grid flex-1 text-left text-sm leading-tight">
46
+ <span className="truncate font-semibold">{appName}</span>
47
+ <span className="truncate text-xs text-muted-foreground">
48
+ Admin Panel
49
+ </span>
50
+ </div>
51
+ </SidebarMenuButton>
52
+ </SidebarMenuItem>
53
+ </SidebarMenu>
54
+ </SidebarHeader>
55
+
56
+ <SidebarContent>
57
+ {sidebarData.map((group) => (
58
+ <NavGroup
59
+ key={group.title}
60
+ group={group}
61
+ activePath={activePath}
62
+ navigate={navigate}
63
+ />
64
+ ))}
65
+ </SidebarContent>
66
+
67
+ <SidebarFooter>
68
+ <NavUser user={user} navigate={navigate} onLogout={onLogout} />
69
+ </SidebarFooter>
70
+
71
+ <SidebarRail />
72
+ </Sidebar>
73
+ )
74
+ }