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,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">👋</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">💡</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
|
+
🗂️ 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
|
+
📄 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
|
+
📝 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>
|