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,131 @@
|
|
|
1
|
+
import { useState } from 'react'
|
|
2
|
+
import { post } from '../lib/api.ts'
|
|
3
|
+
import { Button } from '@/components/ui/button'
|
|
4
|
+
import { Input } from '@/components/ui/input'
|
|
5
|
+
import { Label } from '@/components/ui/label'
|
|
6
|
+
import { Hand, AlertTriangle } from 'lucide-react'
|
|
7
|
+
|
|
8
|
+
interface LoginProps {
|
|
9
|
+
appName?: string
|
|
10
|
+
navigate: (href: string) => void
|
|
11
|
+
[key: string]: any
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export default function Login({ appName = 'Mantiq', navigate }: LoginProps) {
|
|
15
|
+
const [email, setEmail] = useState('')
|
|
16
|
+
const [password, setPassword] = useState('')
|
|
17
|
+
const [error, setError] = useState('')
|
|
18
|
+
const [loading, setLoading] = useState(false)
|
|
19
|
+
|
|
20
|
+
const handleSubmit = async (e: React.FormEvent) => {
|
|
21
|
+
e.preventDefault()
|
|
22
|
+
setError('')
|
|
23
|
+
setLoading(true)
|
|
24
|
+
const { ok, data } = await post('/login', { email, password })
|
|
25
|
+
if (ok) navigate('/dashboard')
|
|
26
|
+
else setError(data?.error ?? 'Invalid email or password.')
|
|
27
|
+
setLoading(false)
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
return (
|
|
31
|
+
<div className="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">
|
|
32
|
+
{/* Top bar */}
|
|
33
|
+
<div className="flex items-center justify-between px-6 py-4">
|
|
34
|
+
<div className="flex items-center gap-2.5">
|
|
35
|
+
<div className="flex h-8 w-8 items-center justify-center rounded-full bg-primary text-primary-foreground text-xs font-bold shadow-sm">
|
|
36
|
+
M
|
|
37
|
+
</div>
|
|
38
|
+
<span className="font-semibold tracking-tight">{appName}</span>
|
|
39
|
+
</div>
|
|
40
|
+
<Button
|
|
41
|
+
variant="ghost"
|
|
42
|
+
size="sm"
|
|
43
|
+
className="text-sm"
|
|
44
|
+
onClick={() => navigate('/register')}
|
|
45
|
+
>
|
|
46
|
+
Create account
|
|
47
|
+
</Button>
|
|
48
|
+
</div>
|
|
49
|
+
|
|
50
|
+
{/* Centered content */}
|
|
51
|
+
<div className="flex flex-col items-center justify-center px-4" style={{ minHeight: 'calc(100vh - 64px)' }}>
|
|
52
|
+
<div className="w-full max-w-sm">
|
|
53
|
+
<div className="mb-6 text-center">
|
|
54
|
+
<div className="mx-auto mb-4 flex h-14 w-14 items-center justify-center rounded-2xl bg-primary/10">
|
|
55
|
+
<Hand className="h-7 w-7 text-primary" />
|
|
56
|
+
</div>
|
|
57
|
+
<h1 className="text-2xl font-bold tracking-tight">Welcome back</h1>
|
|
58
|
+
<p className="mt-2 text-sm text-muted-foreground">
|
|
59
|
+
Log in to your {appName} workspace
|
|
60
|
+
</p>
|
|
61
|
+
</div>
|
|
62
|
+
|
|
63
|
+
{error && (
|
|
64
|
+
<div className="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">
|
|
65
|
+
<AlertTriangle className="h-4 w-4 shrink-0" />
|
|
66
|
+
{error}
|
|
67
|
+
</div>
|
|
68
|
+
)}
|
|
69
|
+
|
|
70
|
+
<form onSubmit={handleSubmit} className="space-y-4">
|
|
71
|
+
<div className="space-y-2">
|
|
72
|
+
<Label htmlFor="email">Email</Label>
|
|
73
|
+
<Input
|
|
74
|
+
id="email"
|
|
75
|
+
type="email"
|
|
76
|
+
value={email}
|
|
77
|
+
onChange={(e) => setEmail(e.target.value)}
|
|
78
|
+
required
|
|
79
|
+
placeholder="you@example.com"
|
|
80
|
+
autoComplete="email"
|
|
81
|
+
className="h-11 rounded-xl"
|
|
82
|
+
/>
|
|
83
|
+
</div>
|
|
84
|
+
<div className="space-y-2">
|
|
85
|
+
<Label htmlFor="password">Password</Label>
|
|
86
|
+
<Input
|
|
87
|
+
id="password"
|
|
88
|
+
type="password"
|
|
89
|
+
value={password}
|
|
90
|
+
onChange={(e) => setPassword(e.target.value)}
|
|
91
|
+
required
|
|
92
|
+
placeholder="Enter your password"
|
|
93
|
+
autoComplete="current-password"
|
|
94
|
+
className="h-11 rounded-xl"
|
|
95
|
+
/>
|
|
96
|
+
</div>
|
|
97
|
+
<Button type="submit" className="w-full h-11 rounded-xl text-sm font-medium" disabled={loading}>
|
|
98
|
+
{loading ? 'Logging in…' : 'Continue with email'}
|
|
99
|
+
</Button>
|
|
100
|
+
</form>
|
|
101
|
+
|
|
102
|
+
{/* Divider */}
|
|
103
|
+
<div className="my-6 flex items-center gap-4">
|
|
104
|
+
<div className="h-px flex-1 bg-border" />
|
|
105
|
+
<span className="text-xs text-muted-foreground select-none">or</span>
|
|
106
|
+
<div className="h-px flex-1 bg-border" />
|
|
107
|
+
</div>
|
|
108
|
+
|
|
109
|
+
{/* Social buttons */}
|
|
110
|
+
<div className="space-y-2.5">
|
|
111
|
+
<Button variant="outline" className="w-full h-11 rounded-xl gap-2.5 text-sm" disabled>
|
|
112
|
+
<svg className="h-4 w-4" viewBox="0 0 24 24">
|
|
113
|
+
<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" />
|
|
114
|
+
<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" />
|
|
115
|
+
<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" />
|
|
116
|
+
<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" />
|
|
117
|
+
</svg>
|
|
118
|
+
Continue with Google
|
|
119
|
+
</Button>
|
|
120
|
+
<Button variant="outline" className="w-full h-11 rounded-xl gap-2.5 text-sm" disabled>
|
|
121
|
+
<svg className="h-4 w-4" viewBox="0 0 24 24" fill="currentColor">
|
|
122
|
+
<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" />
|
|
123
|
+
</svg>
|
|
124
|
+
Continue with GitHub
|
|
125
|
+
</Button>
|
|
126
|
+
</div>
|
|
127
|
+
</div>
|
|
128
|
+
</div>
|
|
129
|
+
</div>
|
|
130
|
+
)
|
|
131
|
+
}
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import { useState } from 'react'
|
|
2
|
+
import { post } from '../lib/api.ts'
|
|
3
|
+
import { Button } from '@/components/ui/button'
|
|
4
|
+
import { Input } from '@/components/ui/input'
|
|
5
|
+
import { Label } from '@/components/ui/label'
|
|
6
|
+
import { Rocket, AlertTriangle } from 'lucide-react'
|
|
7
|
+
|
|
8
|
+
interface RegisterProps {
|
|
9
|
+
appName?: string
|
|
10
|
+
navigate: (href: string) => void
|
|
11
|
+
[key: string]: any
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export default function Register({ appName = 'Mantiq', navigate }: RegisterProps) {
|
|
15
|
+
const [name, setName] = useState('')
|
|
16
|
+
const [email, setEmail] = useState('')
|
|
17
|
+
const [password, setPassword] = useState('')
|
|
18
|
+
const [error, setError] = useState('')
|
|
19
|
+
const [loading, setLoading] = useState(false)
|
|
20
|
+
|
|
21
|
+
const handleSubmit = async (e: React.FormEvent) => {
|
|
22
|
+
e.preventDefault()
|
|
23
|
+
setError('')
|
|
24
|
+
setLoading(true)
|
|
25
|
+
const { ok, data } = await post('/register', { name, email, password })
|
|
26
|
+
if (ok) navigate('/dashboard')
|
|
27
|
+
else setError(data?.error ?? 'Registration failed.')
|
|
28
|
+
setLoading(false)
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
return (
|
|
32
|
+
<div className="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">
|
|
33
|
+
<div className="flex items-center justify-between px-6 py-4">
|
|
34
|
+
<div className="flex items-center gap-2.5">
|
|
35
|
+
<div className="flex h-8 w-8 items-center justify-center rounded-full bg-primary text-primary-foreground text-xs font-bold shadow-sm">M</div>
|
|
36
|
+
<span className="font-semibold tracking-tight">{appName}</span>
|
|
37
|
+
</div>
|
|
38
|
+
<Button variant="ghost" size="sm" className="text-sm" onClick={() => navigate('/login')}>Sign in</Button>
|
|
39
|
+
</div>
|
|
40
|
+
|
|
41
|
+
<div className="flex flex-col items-center justify-center px-4" style={{ minHeight: 'calc(100vh - 64px)' }}>
|
|
42
|
+
<div className="w-full max-w-sm">
|
|
43
|
+
<div className="mb-6 text-center">
|
|
44
|
+
<div className="mx-auto mb-4 flex h-14 w-14 items-center justify-center rounded-2xl bg-primary/10">
|
|
45
|
+
<Rocket className="h-7 w-7 text-primary" />
|
|
46
|
+
</div>
|
|
47
|
+
<h1 className="text-2xl font-bold tracking-tight">Get started</h1>
|
|
48
|
+
<p className="mt-2 text-sm text-muted-foreground">Create your {appName} workspace</p>
|
|
49
|
+
</div>
|
|
50
|
+
|
|
51
|
+
{error && (
|
|
52
|
+
<div className="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">
|
|
53
|
+
<AlertTriangle className="h-4 w-4 shrink-0" /> {error}
|
|
54
|
+
</div>
|
|
55
|
+
)}
|
|
56
|
+
|
|
57
|
+
<form onSubmit={handleSubmit} className="space-y-4">
|
|
58
|
+
<div className="space-y-2">
|
|
59
|
+
<Label htmlFor="name">Name</Label>
|
|
60
|
+
<Input id="name" value={name} onChange={(e) => setName(e.target.value)} required placeholder="Your name" className="h-11 rounded-xl" />
|
|
61
|
+
</div>
|
|
62
|
+
<div className="space-y-2">
|
|
63
|
+
<Label htmlFor="email">Email</Label>
|
|
64
|
+
<Input id="email" type="email" value={email} onChange={(e) => setEmail(e.target.value)} required placeholder="you@example.com" autoComplete="email" className="h-11 rounded-xl" />
|
|
65
|
+
</div>
|
|
66
|
+
<div className="space-y-2">
|
|
67
|
+
<Label htmlFor="password">Password</Label>
|
|
68
|
+
<Input id="password" type="password" value={password} onChange={(e) => setPassword(e.target.value)} required placeholder="Min 8 characters" className="h-11 rounded-xl" />
|
|
69
|
+
</div>
|
|
70
|
+
<Button type="submit" className="w-full h-11 rounded-xl text-sm font-medium" disabled={loading}>
|
|
71
|
+
{loading ? 'Creating…' : 'Create workspace'}
|
|
72
|
+
</Button>
|
|
73
|
+
</form>
|
|
74
|
+
|
|
75
|
+
<p className="mt-6 text-center text-xs text-muted-foreground">
|
|
76
|
+
By continuing, you agree to our Terms of Service and Privacy Policy.
|
|
77
|
+
</p>
|
|
78
|
+
</div>
|
|
79
|
+
</div>
|
|
80
|
+
</div>
|
|
81
|
+
)
|
|
82
|
+
}
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
@import "tailwindcss";
|
|
2
|
+
@custom-variant dark (&:where(.dark, .dark *));
|
|
3
|
+
|
|
4
|
+
/*
|
|
5
|
+
* shadcn/ui theme — Workspace
|
|
6
|
+
*
|
|
7
|
+
* Warm, spacious, friendly. Very round corners (1rem). Warm stone/amber tones.
|
|
8
|
+
* Soft card shadows, cream-tinted background. Inspired by Notion.
|
|
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: 1rem;
|
|
52
|
+
--background: oklch(0.988 0.005 75);
|
|
53
|
+
--foreground: oklch(0.216 0.006 56.043);
|
|
54
|
+
--card: oklch(0.997 0.003 75);
|
|
55
|
+
--card-foreground: oklch(0.216 0.006 56.043);
|
|
56
|
+
--popover: oklch(0.997 0.003 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.955 0.01 67);
|
|
61
|
+
--secondary-foreground: oklch(0.29 0.008 56);
|
|
62
|
+
--muted: oklch(0.955 0.01 67);
|
|
63
|
+
--muted-foreground: oklch(0.553 0.013 58.071);
|
|
64
|
+
--accent: oklch(0.955 0.01 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.915 0.012 67);
|
|
69
|
+
--input: oklch(0.915 0.012 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.65 0.14 55);
|
|
73
|
+
--chart-3: oklch(0.6 0.118 184.714);
|
|
74
|
+
--chart-4: oklch(0.828 0.189 84.429);
|
|
75
|
+
--chart-5: oklch(0.769 0.188 70.08);
|
|
76
|
+
--sidebar: oklch(0.97 0.008 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.94 0.014 67);
|
|
81
|
+
--sidebar-accent-foreground: oklch(0.29 0.008 56);
|
|
82
|
+
--sidebar-border: oklch(0.915 0.012 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
|
+
.card,
|
|
129
|
+
[data-slot="card"] {
|
|
130
|
+
box-shadow: 0 1px 3px oklch(0 0 0 / 0.06);
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
@keyframes fadeUp {
|
|
135
|
+
from { opacity: 0; transform: translateY(12px); }
|
|
136
|
+
to { opacity: 1; transform: translateY(0); }
|
|
137
|
+
}
|
|
138
|
+
.animate-fade-up { animation: fadeUp 0.5s ease-out; }
|
|
@@ -0,0 +1,215 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import AuthenticatedLayout from '$lib/components/layout/AuthenticatedLayout.svelte'
|
|
3
|
+
import Header from '$lib/components/layout/Header.svelte'
|
|
4
|
+
import Main from '$lib/components/layout/Main.svelte'
|
|
5
|
+
import * as Card from '$lib/components/ui/card'
|
|
6
|
+
import {
|
|
7
|
+
ChevronRight,
|
|
8
|
+
ChevronDown,
|
|
9
|
+
CheckCircle2,
|
|
10
|
+
Circle,
|
|
11
|
+
GripVertical,
|
|
12
|
+
} from 'lucide-svelte'
|
|
13
|
+
|
|
14
|
+
interface Props {
|
|
15
|
+
appName?: string
|
|
16
|
+
currentUser?: { id: number; name: string; email: string; role: string } | null
|
|
17
|
+
navigate?: (href: string) => void
|
|
18
|
+
}
|
|
19
|
+
let { appName = 'Mantiq', currentUser = null, navigate = () => {} }: Props = $props()
|
|
20
|
+
|
|
21
|
+
type Status = 'not-started' | 'in-progress' | 'done'
|
|
22
|
+
|
|
23
|
+
const kanbanColumns: { status: Status; label: string; emoji: string; color: string }[] = [
|
|
24
|
+
{ status: 'not-started', label: 'Not Started', emoji: '📋', color: 'bg-stone-200 dark:bg-stone-700' },
|
|
25
|
+
{ status: 'in-progress', label: 'In Progress', emoji: '🔨', color: 'bg-amber-200 dark:bg-amber-800' },
|
|
26
|
+
{ status: 'done', label: 'Done', emoji: '✅', color: 'bg-emerald-200 dark:bg-emerald-800' },
|
|
27
|
+
]
|
|
28
|
+
|
|
29
|
+
const tasks: { id: number; title: string; status: Status; tag?: string }[] = [
|
|
30
|
+
{ id: 1, title: 'Design database schema', status: 'done', tag: 'Backend' },
|
|
31
|
+
{ id: 2, title: 'Set up authentication flow', status: 'done', tag: 'Auth' },
|
|
32
|
+
{ id: 3, title: 'Build API endpoints', status: 'in-progress', tag: 'Backend' },
|
|
33
|
+
{ id: 4, title: 'Create dashboard layout', status: 'in-progress', tag: 'Frontend' },
|
|
34
|
+
{ id: 5, title: 'Write unit tests', status: 'not-started', tag: 'Testing' },
|
|
35
|
+
{ id: 6, title: 'Set up CI/CD pipeline', status: 'not-started', tag: 'DevOps' },
|
|
36
|
+
{ id: 7, title: 'Configure email notifications', status: 'not-started', tag: 'Backend' },
|
|
37
|
+
{ id: 8, title: 'Deploy to staging', status: 'not-started', tag: 'DevOps' },
|
|
38
|
+
]
|
|
39
|
+
|
|
40
|
+
const tagColors: Record<string, string> = {
|
|
41
|
+
Backend: 'bg-blue-100 text-blue-700 dark:bg-blue-900/40 dark:text-blue-300',
|
|
42
|
+
Frontend: 'bg-purple-100 text-purple-700 dark:bg-purple-900/40 dark:text-purple-300',
|
|
43
|
+
Auth: 'bg-amber-100 text-amber-700 dark:bg-amber-900/40 dark:text-amber-300',
|
|
44
|
+
Testing: 'bg-emerald-100 text-emerald-700 dark:bg-emerald-900/40 dark:text-emerald-300',
|
|
45
|
+
DevOps: 'bg-rose-100 text-rose-700 dark:bg-rose-900/40 dark:text-rose-300',
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
const pages = [
|
|
49
|
+
{ emoji: '📖', title: 'Getting Started', desc: 'Quick introduction and setup guide' },
|
|
50
|
+
{ emoji: '🏗️', title: 'Architecture', desc: 'How the application is structured' },
|
|
51
|
+
{ emoji: '📡', title: 'API Reference', desc: 'Endpoints, params, and responses' },
|
|
52
|
+
{ emoji: '🚀', title: 'Deployment', desc: 'Deploy your app to production' },
|
|
53
|
+
]
|
|
54
|
+
|
|
55
|
+
function getGreeting() {
|
|
56
|
+
const hour = new Date().getHours()
|
|
57
|
+
if (hour < 12) return 'Good morning'
|
|
58
|
+
if (hour < 18) return 'Good afternoon'
|
|
59
|
+
return 'Good evening'
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
let expandedSections = $state<Record<string, boolean>>({
|
|
63
|
+
kanban: true,
|
|
64
|
+
pages: true,
|
|
65
|
+
notes: false,
|
|
66
|
+
})
|
|
67
|
+
|
|
68
|
+
function toggle(section: string) {
|
|
69
|
+
expandedSections = { ...expandedSections, [section]: !expandedSections[section] }
|
|
70
|
+
}
|
|
71
|
+
</script>
|
|
72
|
+
|
|
73
|
+
<AuthenticatedLayout {currentUser} {appName} {navigate} activePath="/dashboard">
|
|
74
|
+
<Header {navigate} />
|
|
75
|
+
<Main>
|
|
76
|
+
<div class="max-w-4xl mx-auto space-y-8">
|
|
77
|
+
<!-- Cover & title — Notion-style page header -->
|
|
78
|
+
<div>
|
|
79
|
+
<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"></div>
|
|
80
|
+
<div class="-mt-10 ml-2">
|
|
81
|
+
<span class="text-5xl select-none">👋</span>
|
|
82
|
+
</div>
|
|
83
|
+
<h1 class="mt-4 text-3xl font-bold tracking-tight">
|
|
84
|
+
{getGreeting()}, {currentUser?.name?.split(' ')[0] ?? 'there'}
|
|
85
|
+
</h1>
|
|
86
|
+
<p class="text-muted-foreground mt-1">
|
|
87
|
+
Here's your workspace overview. Click any section to expand or collapse it.
|
|
88
|
+
</p>
|
|
89
|
+
</div>
|
|
90
|
+
|
|
91
|
+
<!-- Callout block -->
|
|
92
|
+
<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">
|
|
93
|
+
<span class="text-xl select-none shrink-0">💡</span>
|
|
94
|
+
<div>
|
|
95
|
+
<p class="text-sm font-medium">Tip</p>
|
|
96
|
+
<p class="text-sm text-muted-foreground">
|
|
97
|
+
Customize this dashboard by editing <code class="rounded bg-muted px-1 py-0.5 text-xs font-mono">src/pages/Dashboard.svelte</code>.
|
|
98
|
+
Add your own widgets, data sources, and integrations.
|
|
99
|
+
</p>
|
|
100
|
+
</div>
|
|
101
|
+
</div>
|
|
102
|
+
|
|
103
|
+
<!-- Kanban board section -->
|
|
104
|
+
<div>
|
|
105
|
+
<button
|
|
106
|
+
type="button"
|
|
107
|
+
onclick={() => toggle('kanban')}
|
|
108
|
+
class="flex items-center gap-1.5 mb-3 text-sm font-semibold text-muted-foreground hover:text-foreground transition-colors"
|
|
109
|
+
>
|
|
110
|
+
{#if expandedSections.kanban}
|
|
111
|
+
<ChevronDown class="h-4 w-4" />
|
|
112
|
+
{:else}
|
|
113
|
+
<ChevronRight class="h-4 w-4" />
|
|
114
|
+
{/if}
|
|
115
|
+
🗂️ Project Board
|
|
116
|
+
</button>
|
|
117
|
+
|
|
118
|
+
{#if expandedSections.kanban}
|
|
119
|
+
<div class="grid gap-4 md:grid-cols-3">
|
|
120
|
+
{#each kanbanColumns as col (col.status)}
|
|
121
|
+
{@const columnTasks = tasks.filter((t) => t.status === col.status)}
|
|
122
|
+
<div>
|
|
123
|
+
<div class="flex items-center gap-2 mb-3">
|
|
124
|
+
<span class="text-sm select-none">{col.emoji}</span>
|
|
125
|
+
<span class="text-sm font-medium">{col.label}</span>
|
|
126
|
+
<span class="text-xs text-muted-foreground ml-auto">{columnTasks.length}</span>
|
|
127
|
+
</div>
|
|
128
|
+
<div class="space-y-2">
|
|
129
|
+
{#each columnTasks as task (task.id)}
|
|
130
|
+
<Card.Root class="group cursor-default shadow-none hover:shadow-sm transition-shadow">
|
|
131
|
+
<Card.Content class="p-3">
|
|
132
|
+
<div class="flex items-start gap-2">
|
|
133
|
+
<GripVertical class="h-4 w-4 mt-0.5 text-muted-foreground/0 group-hover:text-muted-foreground/50 transition-colors shrink-0" />
|
|
134
|
+
<div class="flex-1 min-w-0">
|
|
135
|
+
<p class="text-sm">{task.title}</p>
|
|
136
|
+
{#if task.tag}
|
|
137
|
+
<span class="mt-1.5 inline-block rounded-md px-1.5 py-0.5 text-[10px] font-medium {tagColors[task.tag] ?? 'bg-muted text-muted-foreground'}">
|
|
138
|
+
{task.tag}
|
|
139
|
+
</span>
|
|
140
|
+
{/if}
|
|
141
|
+
</div>
|
|
142
|
+
</div>
|
|
143
|
+
</Card.Content>
|
|
144
|
+
</Card.Root>
|
|
145
|
+
{/each}
|
|
146
|
+
{#if columnTasks.length === 0}
|
|
147
|
+
<div class="rounded-xl border border-dashed p-4 text-center text-xs text-muted-foreground">
|
|
148
|
+
No tasks
|
|
149
|
+
</div>
|
|
150
|
+
{/if}
|
|
151
|
+
</div>
|
|
152
|
+
</div>
|
|
153
|
+
{/each}
|
|
154
|
+
</div>
|
|
155
|
+
{/if}
|
|
156
|
+
</div>
|
|
157
|
+
|
|
158
|
+
<!-- Pages section -->
|
|
159
|
+
<div>
|
|
160
|
+
<button
|
|
161
|
+
type="button"
|
|
162
|
+
onclick={() => toggle('pages')}
|
|
163
|
+
class="flex items-center gap-1.5 mb-3 text-sm font-semibold text-muted-foreground hover:text-foreground transition-colors"
|
|
164
|
+
>
|
|
165
|
+
{#if expandedSections.pages}
|
|
166
|
+
<ChevronDown class="h-4 w-4" />
|
|
167
|
+
{:else}
|
|
168
|
+
<ChevronRight class="h-4 w-4" />
|
|
169
|
+
{/if}
|
|
170
|
+
📄 Pages
|
|
171
|
+
</button>
|
|
172
|
+
|
|
173
|
+
{#if expandedSections.pages}
|
|
174
|
+
<div class="space-y-1">
|
|
175
|
+
{#each pages as page (page.title)}
|
|
176
|
+
<div class="flex items-center gap-3 rounded-lg px-3 py-2.5 transition-colors hover:bg-muted/50 cursor-pointer">
|
|
177
|
+
<span class="text-lg select-none">{page.emoji}</span>
|
|
178
|
+
<div class="flex-1 min-w-0">
|
|
179
|
+
<p class="text-sm font-medium">{page.title}</p>
|
|
180
|
+
<p class="text-xs text-muted-foreground truncate">{page.desc}</p>
|
|
181
|
+
</div>
|
|
182
|
+
</div>
|
|
183
|
+
{/each}
|
|
184
|
+
</div>
|
|
185
|
+
{/if}
|
|
186
|
+
</div>
|
|
187
|
+
|
|
188
|
+
<!-- Notes toggle -->
|
|
189
|
+
<div>
|
|
190
|
+
<button
|
|
191
|
+
type="button"
|
|
192
|
+
onclick={() => toggle('notes')}
|
|
193
|
+
class="flex items-center gap-1.5 mb-3 text-sm font-semibold text-muted-foreground hover:text-foreground transition-colors"
|
|
194
|
+
>
|
|
195
|
+
{#if expandedSections.notes}
|
|
196
|
+
<ChevronDown class="h-4 w-4" />
|
|
197
|
+
{:else}
|
|
198
|
+
<ChevronRight class="h-4 w-4" />
|
|
199
|
+
{/if}
|
|
200
|
+
📝 Quick Notes
|
|
201
|
+
</button>
|
|
202
|
+
|
|
203
|
+
{#if expandedSections.notes}
|
|
204
|
+
<Card.Root class="shadow-none">
|
|
205
|
+
<Card.Content class="p-4">
|
|
206
|
+
<div class="rounded-lg bg-muted/30 p-4 text-sm text-muted-foreground italic">
|
|
207
|
+
Start typing your notes here... This is a placeholder for a rich-text editor.
|
|
208
|
+
</div>
|
|
209
|
+
</Card.Content>
|
|
210
|
+
</Card.Root>
|
|
211
|
+
{/if}
|
|
212
|
+
</div>
|
|
213
|
+
</div>
|
|
214
|
+
</Main>
|
|
215
|
+
</AuthenticatedLayout>
|