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,124 @@
1
+ <script lang="ts">
2
+ import { post } from '$lib/api'
3
+ import { Button } from '$lib/components/ui/button'
4
+ import { Input } from '$lib/components/ui/input'
5
+ import { Label } from '$lib/components/ui/label'
6
+
7
+ interface Props {
8
+ appName?: string
9
+ navigate?: (href: string) => void
10
+ }
11
+ let { appName = 'Mantiq', navigate = () => {} }: Props = $props()
12
+
13
+ let email = $state('')
14
+ let password = $state('')
15
+ let error = $state('')
16
+ let loading = $state(false)
17
+
18
+ async function handleSubmit(e: SubmitEvent) {
19
+ e.preventDefault()
20
+ error = ''
21
+ loading = true
22
+ const { ok, data } = await post('/login', { email, password })
23
+ if (ok) navigate('/dashboard')
24
+ else error = data?.error ?? 'Invalid email or password.'
25
+ loading = false
26
+ }
27
+ </script>
28
+
29
+ <div class="min-h-screen bg-gradient-to-br from-amber-50 via-orange-50 to-rose-50 dark:from-stone-950 dark:via-stone-900 dark:to-stone-950">
30
+ <!-- Top bar -->
31
+ <div class="flex items-center justify-between px-6 py-4">
32
+ <div class="flex items-center gap-2.5">
33
+ <div class="flex h-8 w-8 items-center justify-center rounded-full bg-primary text-primary-foreground text-xs font-bold shadow-sm">
34
+ M
35
+ </div>
36
+ <span class="font-semibold tracking-tight">{appName}</span>
37
+ </div>
38
+ <Button
39
+ variant="ghost"
40
+ size="sm"
41
+ class="text-sm"
42
+ onclick={() => navigate('/register')}
43
+ >
44
+ Create account
45
+ </Button>
46
+ </div>
47
+
48
+ <!-- Centered content -->
49
+ <div class="flex flex-col items-center justify-center px-4" style="min-height: calc(100vh - 64px)">
50
+ <div class="w-full max-w-sm">
51
+ <!-- Emoji greeting -->
52
+ <div class="mb-6 text-center">
53
+ <span class="text-5xl select-none">👋</span>
54
+ <h1 class="mt-4 text-2xl font-bold tracking-tight">Welcome back</h1>
55
+ <p class="mt-2 text-sm text-muted-foreground">
56
+ Log in to your {appName} workspace
57
+ </p>
58
+ </div>
59
+
60
+ {#if error}
61
+ <div class="mb-4 flex items-center gap-2 rounded-xl bg-red-50 dark:bg-red-950/20 border border-red-200/50 dark:border-red-800/30 px-4 py-3 text-sm text-red-700 dark:text-red-400">
62
+ <span class="select-none">⚠️</span>
63
+ {error}
64
+ </div>
65
+ {/if}
66
+
67
+ <form onsubmit={handleSubmit} class="space-y-4">
68
+ <div class="space-y-2">
69
+ <Label for="email">Email</Label>
70
+ <Input
71
+ id="email"
72
+ type="email"
73
+ bind:value={email}
74
+ required
75
+ placeholder="you@example.com"
76
+ autocomplete="email"
77
+ class="h-11 rounded-xl"
78
+ />
79
+ </div>
80
+ <div class="space-y-2">
81
+ <Label for="password">Password</Label>
82
+ <Input
83
+ id="password"
84
+ type="password"
85
+ bind:value={password}
86
+ required
87
+ placeholder="Enter your password"
88
+ autocomplete="current-password"
89
+ class="h-11 rounded-xl"
90
+ />
91
+ </div>
92
+ <Button type="submit" class="w-full h-11 rounded-xl text-sm font-medium" disabled={loading}>
93
+ {loading ? 'Logging in…' : 'Continue with email'}
94
+ </Button>
95
+ </form>
96
+
97
+ <!-- Divider -->
98
+ <div class="my-6 flex items-center gap-4">
99
+ <div class="h-px flex-1 bg-border"></div>
100
+ <span class="text-xs text-muted-foreground select-none">or</span>
101
+ <div class="h-px flex-1 bg-border"></div>
102
+ </div>
103
+
104
+ <!-- Social buttons -->
105
+ <div class="space-y-2.5">
106
+ <Button variant="outline" class="w-full h-11 rounded-xl gap-2.5 text-sm" disabled>
107
+ <svg class="h-4 w-4" viewBox="0 0 24 24">
108
+ <path d="M22.56 12.25c0-.78-.07-1.53-.2-2.25H12v4.26h5.92a5.06 5.06 0 0 1-2.2 3.32v2.77h3.57c2.08-1.92 3.28-4.74 3.28-8.1Z" fill="#4285F4" />
109
+ <path d="M12 23c2.97 0 5.46-.98 7.28-2.66l-3.57-2.77c-.98.66-2.23 1.06-3.71 1.06-2.86 0-5.29-1.93-6.16-4.53H2.18v2.84C3.99 20.53 7.7 23 12 23Z" fill="#34A853" />
110
+ <path d="M5.84 14.09c-.22-.66-.35-1.36-.35-2.09s.13-1.43.35-2.09V7.07H2.18C1.43 8.55 1 10.22 1 12s.43 3.45 1.18 4.93l2.85-2.22.81-.62Z" fill="#FBBC05" />
111
+ <path d="M12 5.38c1.62 0 3.06.56 4.21 1.64l3.15-3.15C17.45 2.09 14.97 1 12 1 7.7 1 3.99 3.47 2.18 7.07l3.66 2.84c.87-2.6 3.3-4.53 6.16-4.53Z" fill="#EA4335" />
112
+ </svg>
113
+ Continue with Google
114
+ </Button>
115
+ <Button variant="outline" class="w-full h-11 rounded-xl gap-2.5 text-sm" disabled>
116
+ <svg class="h-4 w-4" viewBox="0 0 24 24" fill="currentColor">
117
+ <path d="M12 0C5.37 0 0 5.37 0 12c0 5.31 3.435 9.795 8.205 11.385.6.105.825-.255.825-.57 0-.285-.015-1.23-.015-2.235-3.015.555-3.795-.735-4.035-1.41-.135-.345-.72-1.41-1.23-1.695-.42-.225-1.02-.78-.015-.795.945-.015 1.62.87 1.845 1.23 1.08 1.815 2.805 1.305 3.495.99.105-.78.42-1.305.765-1.605-2.67-.3-5.46-1.335-5.46-5.925 0-1.305.465-2.385 1.23-3.225-.12-.3-.54-1.53.12-3.18 0 0 1.005-.315 3.3 1.23.96-.27 1.98-.405 3-.405s2.04.135 3 .405c2.295-1.56 3.3-1.23 3.3-1.23.66 1.65.24 2.88.12 3.18.765.84 1.23 1.905 1.23 3.225 0 4.605-2.805 5.625-5.475 5.925.435.375.81 1.095.81 2.22 0 1.605-.015 2.895-.015 3.3 0 .315.225.69.825.57A12.02 12.02 0 0 0 24 12c0-6.63-5.37-12-12-12Z" />
118
+ </svg>
119
+ Continue with GitHub
120
+ </Button>
121
+ </div>
122
+ </div>
123
+ </div>
124
+ </div>
@@ -0,0 +1,76 @@
1
+ <script lang="ts">
2
+ import { post } from '$lib/api'
3
+ import { Button } from '$lib/components/ui/button'
4
+ import { Input } from '$lib/components/ui/input'
5
+ import { Label } from '$lib/components/ui/label'
6
+
7
+ interface Props {
8
+ appName?: string
9
+ navigate?: (href: string) => void
10
+ }
11
+ let { appName = 'Mantiq', navigate = () => {} }: Props = $props()
12
+
13
+ let name = $state('')
14
+ let email = $state('')
15
+ let password = $state('')
16
+ let error = $state('')
17
+ let loading = $state(false)
18
+
19
+ async function handleSubmit(e: SubmitEvent) {
20
+ e.preventDefault()
21
+ error = ''
22
+ loading = true
23
+ const { ok, data } = await post('/register', { name, email, password })
24
+ if (ok) navigate('/dashboard')
25
+ else error = data?.error ?? 'Registration failed.'
26
+ loading = false
27
+ }
28
+ </script>
29
+
30
+ <div class="min-h-screen bg-gradient-to-br from-amber-50 via-orange-50 to-rose-50 dark:from-stone-950 dark:via-stone-900 dark:to-stone-950">
31
+ <div class="flex items-center justify-between px-6 py-4">
32
+ <div class="flex items-center gap-2.5">
33
+ <div class="flex h-8 w-8 items-center justify-center rounded-full bg-primary text-primary-foreground text-xs font-bold shadow-sm">M</div>
34
+ <span class="font-semibold tracking-tight">{appName}</span>
35
+ </div>
36
+ <Button variant="ghost" size="sm" class="text-sm" onclick={() => navigate('/login')}>Sign in</Button>
37
+ </div>
38
+
39
+ <div class="flex flex-col items-center justify-center px-4" style="min-height: calc(100vh - 64px)">
40
+ <div class="w-full max-w-sm">
41
+ <div class="mb-6 text-center">
42
+ <span class="text-5xl select-none">🚀</span>
43
+ <h1 class="mt-4 text-2xl font-bold tracking-tight">Get started</h1>
44
+ <p class="mt-2 text-sm text-muted-foreground">Create your {appName} workspace</p>
45
+ </div>
46
+
47
+ {#if error}
48
+ <div class="mb-4 flex items-center gap-2 rounded-xl bg-red-50 dark:bg-red-950/20 border border-red-200/50 dark:border-red-800/30 px-4 py-3 text-sm text-red-700 dark:text-red-400">
49
+ <span class="select-none">⚠️</span> {error}
50
+ </div>
51
+ {/if}
52
+
53
+ <form onsubmit={handleSubmit} class="space-y-4">
54
+ <div class="space-y-2">
55
+ <Label for="name">Name</Label>
56
+ <Input id="name" bind:value={name} required placeholder="Your name" class="h-11 rounded-xl" />
57
+ </div>
58
+ <div class="space-y-2">
59
+ <Label for="email">Email</Label>
60
+ <Input id="email" type="email" bind:value={email} required placeholder="you@example.com" autocomplete="email" class="h-11 rounded-xl" />
61
+ </div>
62
+ <div class="space-y-2">
63
+ <Label for="password">Password</Label>
64
+ <Input id="password" type="password" bind:value={password} required placeholder="Min 8 characters" class="h-11 rounded-xl" />
65
+ </div>
66
+ <Button type="submit" class="w-full h-11 rounded-xl text-sm font-medium" disabled={loading}>
67
+ {loading ? 'Creating…' : 'Create workspace'}
68
+ </Button>
69
+ </form>
70
+
71
+ <p class="mt-6 text-center text-xs text-muted-foreground">
72
+ By continuing, you agree to our Terms of Service and Privacy Policy.
73
+ </p>
74
+ </div>
75
+ </div>
76
+ </div>
@@ -0,0 +1,134 @@
1
+ @import "tailwindcss";
2
+ @custom-variant dark (&:where(.dark, .dark *));
3
+
4
+ /*
5
+ * shadcn/ui theme — Workspace
6
+ *
7
+ * Warm, approachable, generous spacing. Stone/sand tones.
8
+ * Large radius, soft shadows, content-first layout.
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.75rem;
52
+ --background: oklch(0.993 0.003 75);
53
+ --foreground: oklch(0.216 0.006 56.043);
54
+ --card: oklch(1 0.002 75);
55
+ --card-foreground: oklch(0.216 0.006 56.043);
56
+ --popover: oklch(1 0.002 75);
57
+ --popover-foreground: oklch(0.216 0.006 56.043);
58
+ --primary: oklch(0.705 0.168 41.3);
59
+ --primary-foreground: oklch(0.995 0.001 75);
60
+ --secondary: oklch(0.96 0.007 67);
61
+ --secondary-foreground: oklch(0.29 0.008 56);
62
+ --muted: oklch(0.96 0.007 67);
63
+ --muted-foreground: oklch(0.553 0.013 58.071);
64
+ --accent: oklch(0.96 0.007 67);
65
+ --accent-foreground: oklch(0.29 0.008 56);
66
+ --destructive: oklch(0.577 0.245 27.325);
67
+ --destructive-foreground: oklch(0.577 0.245 27.325);
68
+ --border: oklch(0.923 0.008 67);
69
+ --input: oklch(0.923 0.008 67);
70
+ --ring: oklch(0.705 0.168 41.3);
71
+ --chart-1: oklch(0.705 0.168 41.3);
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.975 0.005 67);
77
+ --sidebar-foreground: oklch(0.216 0.006 56.043);
78
+ --sidebar-primary: oklch(0.705 0.168 41.3);
79
+ --sidebar-primary-foreground: oklch(0.995 0.001 75);
80
+ --sidebar-accent: oklch(0.945 0.01 67);
81
+ --sidebar-accent-foreground: oklch(0.29 0.008 56);
82
+ --sidebar-border: oklch(0.923 0.008 67);
83
+ --sidebar-ring: oklch(0.705 0.168 41.3);
84
+ }
85
+
86
+ .dark {
87
+ --background: oklch(0.155 0.008 49.25);
88
+ --foreground: oklch(0.96 0.007 67);
89
+ --card: oklch(0.185 0.008 49.25);
90
+ --card-foreground: oklch(0.96 0.007 67);
91
+ --popover: oklch(0.185 0.008 49.25);
92
+ --popover-foreground: oklch(0.96 0.007 67);
93
+ --primary: oklch(0.745 0.178 41.3);
94
+ --primary-foreground: oklch(0.155 0.008 49.25);
95
+ --secondary: oklch(0.25 0.008 49.25);
96
+ --secondary-foreground: oklch(0.96 0.007 67);
97
+ --muted: oklch(0.25 0.008 49.25);
98
+ --muted-foreground: oklch(0.64 0.013 58.071);
99
+ --accent: oklch(0.25 0.008 49.25);
100
+ --accent-foreground: oklch(0.96 0.007 67);
101
+ --destructive: oklch(0.704 0.191 22.216);
102
+ --destructive-foreground: oklch(0.704 0.191 22.216);
103
+ --border: oklch(0.28 0.008 49.25);
104
+ --input: oklch(0.28 0.008 49.25);
105
+ --ring: oklch(0.745 0.178 41.3);
106
+ --chart-1: oklch(0.745 0.178 41.3);
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.165 0.008 49.25);
112
+ --sidebar-foreground: oklch(0.96 0.007 67);
113
+ --sidebar-primary: oklch(0.745 0.178 41.3);
114
+ --sidebar-primary-foreground: oklch(0.155 0.008 49.25);
115
+ --sidebar-accent: oklch(0.25 0.008 49.25);
116
+ --sidebar-accent-foreground: oklch(0.96 0.007 67);
117
+ --sidebar-border: oklch(0.28 0.008 49.25);
118
+ --sidebar-ring: oklch(0.745 0.178 41.3);
119
+ }
120
+
121
+ @layer base {
122
+ * {
123
+ @apply border-border;
124
+ }
125
+ body {
126
+ @apply bg-background text-foreground;
127
+ }
128
+ }
129
+
130
+ @keyframes fadeUp {
131
+ from { opacity: 0; transform: translateY(12px); }
132
+ to { opacity: 1; transform: translateY(0); }
133
+ }
134
+ .animate-fade-up { animation: fadeUp 0.5s ease-out; }
@@ -0,0 +1,220 @@
1
+ <script setup lang="ts">
2
+ import { ref } from 'vue'
3
+ import AuthenticatedLayout from '@/components/layout/AuthenticatedLayout.vue'
4
+ import Header from '@/components/layout/Header.vue'
5
+ import Main from '@/components/layout/Main.vue'
6
+ import {
7
+ Card,
8
+ CardContent,
9
+ } from '@/components/ui/card'
10
+ import {
11
+ ChevronRight,
12
+ ChevronDown,
13
+ GripVertical,
14
+ } from 'lucide-vue-next'
15
+
16
+ type Status = 'not-started' | 'in-progress' | 'done'
17
+
18
+ const props = withDefaults(defineProps<{
19
+ appName?: string
20
+ currentUser?: { id: number; name: string; email: string; role: string } | null
21
+ navigate?: (href: string) => void
22
+ }>(), {
23
+ appName: 'Mantiq',
24
+ })
25
+
26
+ const kanbanColumns: { status: Status; label: string; emoji: string; color: string }[] = [
27
+ { status: 'not-started', label: 'Not Started', emoji: '\uD83D\uDCCB', color: 'bg-stone-200 dark:bg-stone-700' },
28
+ { status: 'in-progress', label: 'In Progress', emoji: '\uD83D\uDD28', color: 'bg-amber-200 dark:bg-amber-800' },
29
+ { status: 'done', label: 'Done', emoji: '\u2705', color: 'bg-emerald-200 dark:bg-emerald-800' },
30
+ ]
31
+
32
+ const tasks: { id: number; title: string; status: Status; tag?: string }[] = [
33
+ { id: 1, title: 'Design database schema', status: 'done', tag: 'Backend' },
34
+ { id: 2, title: 'Set up authentication flow', status: 'done', tag: 'Auth' },
35
+ { id: 3, title: 'Build API endpoints', status: 'in-progress', tag: 'Backend' },
36
+ { id: 4, title: 'Create dashboard layout', status: 'in-progress', tag: 'Frontend' },
37
+ { id: 5, title: 'Write unit tests', status: 'not-started', tag: 'Testing' },
38
+ { id: 6, title: 'Set up CI/CD pipeline', status: 'not-started', tag: 'DevOps' },
39
+ { id: 7, title: 'Configure email notifications', status: 'not-started', tag: 'Backend' },
40
+ { id: 8, title: 'Deploy to staging', status: 'not-started', tag: 'DevOps' },
41
+ ]
42
+
43
+ const tagColors: Record<string, string> = {
44
+ Backend: 'bg-blue-100 text-blue-700 dark:bg-blue-900/40 dark:text-blue-300',
45
+ Frontend: 'bg-purple-100 text-purple-700 dark:bg-purple-900/40 dark:text-purple-300',
46
+ Auth: 'bg-amber-100 text-amber-700 dark:bg-amber-900/40 dark:text-amber-300',
47
+ Testing: 'bg-emerald-100 text-emerald-700 dark:bg-emerald-900/40 dark:text-emerald-300',
48
+ DevOps: 'bg-rose-100 text-rose-700 dark:bg-rose-900/40 dark:text-rose-300',
49
+ }
50
+
51
+ const pages = [
52
+ { emoji: '\uD83D\uDCD6', title: 'Getting Started', desc: 'Quick introduction and setup guide' },
53
+ { emoji: '\uD83C\uDFD7\uFE0F', title: 'Architecture', desc: 'How the application is structured' },
54
+ { emoji: '\uD83D\uDCE1', title: 'API Reference', desc: 'Endpoints, params, and responses' },
55
+ { emoji: '\uD83D\uDE80', title: 'Deployment', desc: 'Deploy your app to production' },
56
+ ]
57
+
58
+ function getGreeting() {
59
+ const hour = new Date().getHours()
60
+ if (hour < 12) return 'Good morning'
61
+ if (hour < 18) return 'Good afternoon'
62
+ return 'Good evening'
63
+ }
64
+
65
+ const expandedSections = ref<Record<string, boolean>>({
66
+ kanban: true,
67
+ pages: true,
68
+ notes: false,
69
+ })
70
+
71
+ function toggle(section: string) {
72
+ expandedSections.value[section] = !expandedSections.value[section]
73
+ }
74
+
75
+ function getColumnTasks(status: Status) {
76
+ return tasks.filter((t) => t.status === status)
77
+ }
78
+ </script>
79
+
80
+ <template>
81
+ <AuthenticatedLayout
82
+ :current-user="currentUser"
83
+ :app-name="appName"
84
+ :navigate="navigate"
85
+ active-path="/dashboard"
86
+ >
87
+ <Header :navigate="navigate" />
88
+ <Main>
89
+ <div class="max-w-4xl mx-auto space-y-8">
90
+ <!-- Cover & title — Notion-style page header -->
91
+ <div>
92
+ <div class="h-36 rounded-xl bg-gradient-to-br from-amber-100 via-orange-50 to-rose-100 dark:from-amber-950/30 dark:via-stone-900 dark:to-rose-950/20 mb-6" />
93
+ <div class="-mt-10 ml-2">
94
+ <span class="text-5xl select-none">&#x1F44B;</span>
95
+ </div>
96
+ <h1 class="mt-4 text-3xl font-bold tracking-tight">
97
+ {{ getGreeting() }}, {{ currentUser?.name?.split(' ')[0] ?? 'there' }}
98
+ </h1>
99
+ <p class="text-muted-foreground mt-1">
100
+ Here's your workspace overview. Click any section to expand or collapse it.
101
+ </p>
102
+ </div>
103
+
104
+ <!-- Callout block -->
105
+ <div class="flex gap-3 rounded-xl bg-amber-50 dark:bg-amber-950/20 border border-amber-200/50 dark:border-amber-800/30 p-4">
106
+ <span class="text-xl select-none shrink-0">&#x1F4A1;</span>
107
+ <div>
108
+ <p class="text-sm font-medium">Tip</p>
109
+ <p class="text-sm text-muted-foreground">
110
+ Customize this dashboard by editing <code class="rounded bg-muted px-1 py-0.5 text-xs font-mono">src/pages/Dashboard.vue</code>.
111
+ Add your own widgets, data sources, and integrations.
112
+ </p>
113
+ </div>
114
+ </div>
115
+
116
+ <!-- Kanban board section -->
117
+ <div>
118
+ <button
119
+ type="button"
120
+ class="flex items-center gap-1.5 mb-3 text-sm font-semibold text-muted-foreground hover:text-foreground transition-colors"
121
+ @click="toggle('kanban')"
122
+ >
123
+ <ChevronDown v-if="expandedSections.kanban" class="h-4 w-4" />
124
+ <ChevronRight v-else class="h-4 w-4" />
125
+ &#x1F5C2;&#xFE0F; Project Board
126
+ </button>
127
+
128
+ <div v-if="expandedSections.kanban" class="grid gap-4 md:grid-cols-3">
129
+ <div v-for="col in kanbanColumns" :key="col.status">
130
+ <div class="flex items-center gap-2 mb-3">
131
+ <span class="text-sm select-none">{{ col.emoji }}</span>
132
+ <span class="text-sm font-medium">{{ col.label }}</span>
133
+ <span class="text-xs text-muted-foreground ml-auto">{{ getColumnTasks(col.status).length }}</span>
134
+ </div>
135
+ <div class="space-y-2">
136
+ <Card
137
+ v-for="task in getColumnTasks(col.status)"
138
+ :key="task.id"
139
+ class="group cursor-default shadow-none hover:shadow-sm transition-shadow"
140
+ >
141
+ <CardContent class="p-3">
142
+ <div class="flex items-start gap-2">
143
+ <GripVertical class="h-4 w-4 mt-0.5 text-muted-foreground/0 group-hover:text-muted-foreground/50 transition-colors shrink-0" />
144
+ <div class="flex-1 min-w-0">
145
+ <p class="text-sm">{{ task.title }}</p>
146
+ <span
147
+ v-if="task.tag"
148
+ :class="[
149
+ 'mt-1.5 inline-block rounded-md px-1.5 py-0.5 text-[10px] font-medium',
150
+ tagColors[task.tag] ?? 'bg-muted text-muted-foreground',
151
+ ]"
152
+ >
153
+ {{ task.tag }}
154
+ </span>
155
+ </div>
156
+ </div>
157
+ </CardContent>
158
+ </Card>
159
+ <div
160
+ v-if="getColumnTasks(col.status).length === 0"
161
+ class="rounded-xl border border-dashed p-4 text-center text-xs text-muted-foreground"
162
+ >
163
+ No tasks
164
+ </div>
165
+ </div>
166
+ </div>
167
+ </div>
168
+ </div>
169
+
170
+ <!-- Pages section -->
171
+ <div>
172
+ <button
173
+ type="button"
174
+ class="flex items-center gap-1.5 mb-3 text-sm font-semibold text-muted-foreground hover:text-foreground transition-colors"
175
+ @click="toggle('pages')"
176
+ >
177
+ <ChevronDown v-if="expandedSections.pages" class="h-4 w-4" />
178
+ <ChevronRight v-else class="h-4 w-4" />
179
+ &#x1F4C4; Pages
180
+ </button>
181
+
182
+ <div v-if="expandedSections.pages" class="space-y-1">
183
+ <div
184
+ v-for="page in pages"
185
+ :key="page.title"
186
+ class="flex items-center gap-3 rounded-lg px-3 py-2.5 transition-colors hover:bg-muted/50 cursor-pointer"
187
+ >
188
+ <span class="text-lg select-none">{{ page.emoji }}</span>
189
+ <div class="flex-1 min-w-0">
190
+ <p class="text-sm font-medium">{{ page.title }}</p>
191
+ <p class="text-xs text-muted-foreground truncate">{{ page.desc }}</p>
192
+ </div>
193
+ </div>
194
+ </div>
195
+ </div>
196
+
197
+ <!-- Notes toggle -->
198
+ <div>
199
+ <button
200
+ type="button"
201
+ class="flex items-center gap-1.5 mb-3 text-sm font-semibold text-muted-foreground hover:text-foreground transition-colors"
202
+ @click="toggle('notes')"
203
+ >
204
+ <ChevronDown v-if="expandedSections.notes" class="h-4 w-4" />
205
+ <ChevronRight v-else class="h-4 w-4" />
206
+ &#x1F4DD; Quick Notes
207
+ </button>
208
+
209
+ <Card v-if="expandedSections.notes" class="shadow-none">
210
+ <CardContent class="p-4">
211
+ <div class="rounded-lg bg-muted/30 p-4 text-sm text-muted-foreground italic">
212
+ Start typing your notes here... This is a placeholder for a rich-text editor.
213
+ </div>
214
+ </CardContent>
215
+ </Card>
216
+ </div>
217
+ </div>
218
+ </Main>
219
+ </AuthenticatedLayout>
220
+ </template>