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,42 @@
|
|
|
1
|
+
import { useCallback } from 'react'
|
|
2
|
+
import { post } from '@/lib/api'
|
|
3
|
+
import { SidebarProvider, SidebarInset } from '@/components/ui/sidebar'
|
|
4
|
+
import { AppSidebar } from './app-sidebar'
|
|
5
|
+
|
|
6
|
+
interface AuthenticatedLayoutProps {
|
|
7
|
+
children: React.ReactNode
|
|
8
|
+
currentUser?: { name: string; email: string; role?: string } | null
|
|
9
|
+
appName?: string
|
|
10
|
+
navigate: (href: string) => void
|
|
11
|
+
activePath: string
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export function AuthenticatedLayout({
|
|
15
|
+
children,
|
|
16
|
+
currentUser,
|
|
17
|
+
appName = 'Mantiq',
|
|
18
|
+
navigate,
|
|
19
|
+
activePath,
|
|
20
|
+
}: AuthenticatedLayoutProps) {
|
|
21
|
+
const handleLogout = useCallback(async () => {
|
|
22
|
+
await post('/logout', {})
|
|
23
|
+
navigate('/login')
|
|
24
|
+
}, [navigate])
|
|
25
|
+
|
|
26
|
+
const user = currentUser ?? { name: 'User', email: 'user@example.com' }
|
|
27
|
+
|
|
28
|
+
return (
|
|
29
|
+
<SidebarProvider defaultOpen={true}>
|
|
30
|
+
<AppSidebar
|
|
31
|
+
user={user}
|
|
32
|
+
appName={appName}
|
|
33
|
+
activePath={activePath}
|
|
34
|
+
navigate={navigate}
|
|
35
|
+
onLogout={handleLogout}
|
|
36
|
+
/>
|
|
37
|
+
<SidebarInset>
|
|
38
|
+
{children}
|
|
39
|
+
</SidebarInset>
|
|
40
|
+
</SidebarProvider>
|
|
41
|
+
)
|
|
42
|
+
}
|
|
@@ -0,0 +1,236 @@
|
|
|
1
|
+
import { AuthenticatedLayout } from '@/components/layout/authenticated-layout'
|
|
2
|
+
import { Header } from '@/components/layout/header'
|
|
3
|
+
import { Main } from '@/components/layout/main'
|
|
4
|
+
import { Button } from '@/components/ui/button'
|
|
5
|
+
import {
|
|
6
|
+
Card,
|
|
7
|
+
CardContent,
|
|
8
|
+
CardHeader,
|
|
9
|
+
CardTitle,
|
|
10
|
+
} from '@/components/ui/card'
|
|
11
|
+
import { Badge } from '@/components/ui/badge'
|
|
12
|
+
import {
|
|
13
|
+
Rocket,
|
|
14
|
+
Users,
|
|
15
|
+
TrendingUp,
|
|
16
|
+
ArrowRight,
|
|
17
|
+
Zap,
|
|
18
|
+
Shield,
|
|
19
|
+
Globe,
|
|
20
|
+
Bell,
|
|
21
|
+
FileText,
|
|
22
|
+
BookOpen,
|
|
23
|
+
MessageCircle,
|
|
24
|
+
Headphones,
|
|
25
|
+
} from 'lucide-react'
|
|
26
|
+
|
|
27
|
+
interface DashboardProps {
|
|
28
|
+
appName?: string
|
|
29
|
+
currentUser?: { id: number; name: string; email: string; role: string } | null
|
|
30
|
+
navigate: (href: string) => void
|
|
31
|
+
[key: string]: any
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
const activityItems = [
|
|
35
|
+
{
|
|
36
|
+
color: 'border-violet-500',
|
|
37
|
+
icon: Rocket,
|
|
38
|
+
title: 'New deployment completed',
|
|
39
|
+
description: 'Production v2.4.1 deployed successfully',
|
|
40
|
+
time: '2 minutes ago',
|
|
41
|
+
},
|
|
42
|
+
{
|
|
43
|
+
color: 'border-blue-500',
|
|
44
|
+
icon: Users,
|
|
45
|
+
title: 'Team member joined',
|
|
46
|
+
description: 'Sarah Connor accepted the invitation',
|
|
47
|
+
time: '15 minutes ago',
|
|
48
|
+
},
|
|
49
|
+
{
|
|
50
|
+
color: 'border-emerald-500',
|
|
51
|
+
icon: Shield,
|
|
52
|
+
title: 'Security scan passed',
|
|
53
|
+
description: 'All 142 checks passed with no vulnerabilities',
|
|
54
|
+
time: '1 hour ago',
|
|
55
|
+
},
|
|
56
|
+
{
|
|
57
|
+
color: 'border-amber-500',
|
|
58
|
+
icon: Bell,
|
|
59
|
+
title: 'Usage alert triggered',
|
|
60
|
+
description: 'API calls reached 80% of monthly quota',
|
|
61
|
+
time: '3 hours ago',
|
|
62
|
+
},
|
|
63
|
+
{
|
|
64
|
+
color: 'border-red-500',
|
|
65
|
+
icon: Globe,
|
|
66
|
+
title: 'Domain verified',
|
|
67
|
+
description: 'app.example.com DNS propagation complete',
|
|
68
|
+
time: '5 hours ago',
|
|
69
|
+
},
|
|
70
|
+
]
|
|
71
|
+
|
|
72
|
+
const quickLinks = [
|
|
73
|
+
{ icon: FileText, label: 'Documentation', href: '#' },
|
|
74
|
+
{ icon: BookOpen, label: 'API Reference', href: '#' },
|
|
75
|
+
{ icon: MessageCircle, label: 'Community', href: '#' },
|
|
76
|
+
{ icon: Headphones, label: 'Support', href: '#' },
|
|
77
|
+
]
|
|
78
|
+
|
|
79
|
+
export default function Dashboard({
|
|
80
|
+
appName = 'Mantiq',
|
|
81
|
+
currentUser,
|
|
82
|
+
navigate,
|
|
83
|
+
}: DashboardProps) {
|
|
84
|
+
return (
|
|
85
|
+
<AuthenticatedLayout
|
|
86
|
+
currentUser={currentUser}
|
|
87
|
+
appName={appName}
|
|
88
|
+
navigate={navigate}
|
|
89
|
+
activePath="/dashboard"
|
|
90
|
+
>
|
|
91
|
+
<Header navigate={navigate} />
|
|
92
|
+
<Main>
|
|
93
|
+
<div className="space-y-6">
|
|
94
|
+
{/* Welcome Banner */}
|
|
95
|
+
<Card className="border-0 bg-gradient-to-r from-violet-600 to-indigo-600 text-white shadow-lg">
|
|
96
|
+
<CardContent className="p-8">
|
|
97
|
+
<div className="flex flex-col gap-4 sm:flex-row sm:items-center sm:justify-between">
|
|
98
|
+
<div>
|
|
99
|
+
<h2 className="text-3xl font-bold tracking-tight">
|
|
100
|
+
Welcome back, {currentUser?.name ?? 'there'}!
|
|
101
|
+
</h2>
|
|
102
|
+
<p className="mt-2 text-lg text-white/80">
|
|
103
|
+
Your dashboard is looking great. Here's a quick summary.
|
|
104
|
+
</p>
|
|
105
|
+
</div>
|
|
106
|
+
<Button
|
|
107
|
+
variant="outline"
|
|
108
|
+
className="shrink-0 border-white/30 bg-white/10 text-white backdrop-blur-sm hover:bg-white/20 hover:text-white"
|
|
109
|
+
>
|
|
110
|
+
View Reports
|
|
111
|
+
<ArrowRight className="ml-2 h-4 w-4" />
|
|
112
|
+
</Button>
|
|
113
|
+
</div>
|
|
114
|
+
</CardContent>
|
|
115
|
+
</Card>
|
|
116
|
+
|
|
117
|
+
{/* Stats Strip */}
|
|
118
|
+
<div className="grid gap-4 sm:grid-cols-2 lg:grid-cols-4">
|
|
119
|
+
<Card>
|
|
120
|
+
<CardContent className="p-6">
|
|
121
|
+
<div className="flex items-center gap-4">
|
|
122
|
+
<div className="flex h-12 w-12 shrink-0 items-center justify-center rounded-xl bg-gradient-to-br from-violet-500 to-purple-600 text-white shadow-md">
|
|
123
|
+
<Users className="h-6 w-6" />
|
|
124
|
+
</div>
|
|
125
|
+
<div>
|
|
126
|
+
<p className="text-sm font-medium text-muted-foreground">Total Users</p>
|
|
127
|
+
<p className="text-2xl font-bold">12,345</p>
|
|
128
|
+
</div>
|
|
129
|
+
</div>
|
|
130
|
+
</CardContent>
|
|
131
|
+
</Card>
|
|
132
|
+
|
|
133
|
+
<Card>
|
|
134
|
+
<CardContent className="p-6">
|
|
135
|
+
<div className="flex items-center gap-4">
|
|
136
|
+
<div className="flex h-12 w-12 shrink-0 items-center justify-center rounded-xl bg-gradient-to-br from-blue-500 to-cyan-500 text-white shadow-md">
|
|
137
|
+
<TrendingUp className="h-6 w-6" />
|
|
138
|
+
</div>
|
|
139
|
+
<div>
|
|
140
|
+
<p className="text-sm font-medium text-muted-foreground">Revenue</p>
|
|
141
|
+
<p className="text-2xl font-bold">$89,432</p>
|
|
142
|
+
</div>
|
|
143
|
+
</div>
|
|
144
|
+
</CardContent>
|
|
145
|
+
</Card>
|
|
146
|
+
|
|
147
|
+
<Card>
|
|
148
|
+
<CardContent className="p-6">
|
|
149
|
+
<div className="flex items-center gap-4">
|
|
150
|
+
<div className="flex h-12 w-12 shrink-0 items-center justify-center rounded-xl bg-gradient-to-br from-amber-500 to-orange-500 text-white shadow-md">
|
|
151
|
+
<Zap className="h-6 w-6" />
|
|
152
|
+
</div>
|
|
153
|
+
<div>
|
|
154
|
+
<p className="text-sm font-medium text-muted-foreground">Active Now</p>
|
|
155
|
+
<p className="text-2xl font-bold">573</p>
|
|
156
|
+
</div>
|
|
157
|
+
</div>
|
|
158
|
+
</CardContent>
|
|
159
|
+
</Card>
|
|
160
|
+
|
|
161
|
+
<Card>
|
|
162
|
+
<CardContent className="p-6">
|
|
163
|
+
<div className="flex items-center gap-4">
|
|
164
|
+
<div className="flex h-12 w-12 shrink-0 items-center justify-center rounded-xl bg-gradient-to-br from-emerald-500 to-green-500 text-white shadow-md">
|
|
165
|
+
<Shield className="h-6 w-6" />
|
|
166
|
+
</div>
|
|
167
|
+
<div>
|
|
168
|
+
<p className="text-sm font-medium text-muted-foreground">Uptime</p>
|
|
169
|
+
<p className="text-2xl font-bold">99.98%</p>
|
|
170
|
+
</div>
|
|
171
|
+
</div>
|
|
172
|
+
</CardContent>
|
|
173
|
+
</Card>
|
|
174
|
+
</div>
|
|
175
|
+
|
|
176
|
+
{/* Two Column Section */}
|
|
177
|
+
<div className="grid gap-6 lg:grid-cols-3">
|
|
178
|
+
{/* Activity Feed */}
|
|
179
|
+
<Card className="lg:col-span-2">
|
|
180
|
+
<CardHeader>
|
|
181
|
+
<CardTitle>Recent Activity</CardTitle>
|
|
182
|
+
</CardHeader>
|
|
183
|
+
<CardContent>
|
|
184
|
+
<div className="space-y-4">
|
|
185
|
+
{activityItems.map((item, index) => {
|
|
186
|
+
const Icon = item.icon
|
|
187
|
+
return (
|
|
188
|
+
<div
|
|
189
|
+
key={index}
|
|
190
|
+
className={`flex items-start gap-4 rounded-lg border-l-4 ${item.color} bg-muted/30 p-4`}
|
|
191
|
+
>
|
|
192
|
+
<div className="mt-0.5 text-muted-foreground">
|
|
193
|
+
<Icon className="h-5 w-5" />
|
|
194
|
+
</div>
|
|
195
|
+
<div className="flex-1 space-y-1">
|
|
196
|
+
<p className="text-sm font-medium leading-none">{item.title}</p>
|
|
197
|
+
<p className="text-sm text-muted-foreground">{item.description}</p>
|
|
198
|
+
</div>
|
|
199
|
+
<span className="shrink-0 text-xs text-muted-foreground">{item.time}</span>
|
|
200
|
+
</div>
|
|
201
|
+
)
|
|
202
|
+
})}
|
|
203
|
+
</div>
|
|
204
|
+
</CardContent>
|
|
205
|
+
</Card>
|
|
206
|
+
|
|
207
|
+
{/* Quick Links */}
|
|
208
|
+
<Card>
|
|
209
|
+
<CardHeader>
|
|
210
|
+
<CardTitle>Quick Links</CardTitle>
|
|
211
|
+
</CardHeader>
|
|
212
|
+
<CardContent>
|
|
213
|
+
<div className="space-y-2">
|
|
214
|
+
{quickLinks.map((link) => {
|
|
215
|
+
const Icon = link.icon
|
|
216
|
+
return (
|
|
217
|
+
<button
|
|
218
|
+
key={link.label}
|
|
219
|
+
type="button"
|
|
220
|
+
className="flex w-full items-center gap-3 rounded-lg p-3 text-left transition-colors hover:bg-muted"
|
|
221
|
+
>
|
|
222
|
+
<Icon className="h-5 w-5 text-muted-foreground" />
|
|
223
|
+
<span className="flex-1 text-sm font-medium">{link.label}</span>
|
|
224
|
+
<ArrowRight className="h-4 w-4 text-muted-foreground" />
|
|
225
|
+
</button>
|
|
226
|
+
)
|
|
227
|
+
})}
|
|
228
|
+
</div>
|
|
229
|
+
</CardContent>
|
|
230
|
+
</Card>
|
|
231
|
+
</div>
|
|
232
|
+
</div>
|
|
233
|
+
</Main>
|
|
234
|
+
</AuthenticatedLayout>
|
|
235
|
+
)
|
|
236
|
+
}
|
|
@@ -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
|
+
|
|
7
|
+
interface LoginProps {
|
|
8
|
+
appName?: string
|
|
9
|
+
navigate: (href: string) => void
|
|
10
|
+
[key: string]: any
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export default function Login({ appName = 'Mantiq', navigate }: LoginProps) {
|
|
14
|
+
const [email, setEmail] = useState('')
|
|
15
|
+
const [password, setPassword] = useState('')
|
|
16
|
+
const [error, setError] = useState('')
|
|
17
|
+
const [loading, setLoading] = useState(false)
|
|
18
|
+
|
|
19
|
+
const handleSubmit = async (e: React.FormEvent) => {
|
|
20
|
+
e.preventDefault()
|
|
21
|
+
setError('')
|
|
22
|
+
setLoading(true)
|
|
23
|
+
const { ok, data } = await post('/login', { email, password })
|
|
24
|
+
if (ok) navigate('/dashboard')
|
|
25
|
+
else setError(data?.error ?? 'Invalid email or password. Please try again.')
|
|
26
|
+
setLoading(false)
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
return (
|
|
30
|
+
<div className="min-h-screen flex bg-background">
|
|
31
|
+
{/* Left gradient panel */}
|
|
32
|
+
<div className="hidden lg:flex lg:w-1/2 relative overflow-hidden bg-gradient-to-br from-violet-600 via-purple-600 to-indigo-700 animate-gradient">
|
|
33
|
+
{/* Floating decorative circles */}
|
|
34
|
+
<div className="absolute -top-20 -left-20 h-72 w-72 rounded-full bg-white/10 blur-3xl" />
|
|
35
|
+
<div className="absolute top-1/3 right-10 h-48 w-48 rounded-full bg-indigo-400/20 blur-2xl" />
|
|
36
|
+
<div className="absolute bottom-20 left-1/4 h-56 w-56 rounded-full bg-purple-300/15 blur-3xl" />
|
|
37
|
+
|
|
38
|
+
<div className="relative z-10 flex flex-col justify-center p-16 text-white">
|
|
39
|
+
<h1 className="text-5xl font-bold leading-tight tracking-tight">
|
|
40
|
+
Build something<br />amazing.
|
|
41
|
+
</h1>
|
|
42
|
+
<p className="mt-4 text-xl text-white/70">
|
|
43
|
+
Join thousands of developers shipping faster with {appName}.
|
|
44
|
+
</p>
|
|
45
|
+
|
|
46
|
+
{/* Testimonial card */}
|
|
47
|
+
<div className="mt-12 rounded-2xl bg-white/10 p-6 backdrop-blur-sm">
|
|
48
|
+
<p className="text-lg leading-relaxed text-white/90">
|
|
49
|
+
"This framework completely changed how we build web apps. We shipped our MVP in half the time."
|
|
50
|
+
</p>
|
|
51
|
+
<div className="mt-4">
|
|
52
|
+
<p className="font-semibold">Alex Rivera</p>
|
|
53
|
+
<p className="text-sm text-white/60">CTO at LaunchPad</p>
|
|
54
|
+
</div>
|
|
55
|
+
</div>
|
|
56
|
+
</div>
|
|
57
|
+
</div>
|
|
58
|
+
|
|
59
|
+
{/* Right form panel */}
|
|
60
|
+
<div className="flex flex-1 flex-col items-center justify-center px-6 py-12">
|
|
61
|
+
{/* Logo */}
|
|
62
|
+
<div className="mb-10 flex items-center gap-3">
|
|
63
|
+
<div className="flex h-9 w-9 items-center justify-center rounded-lg bg-gradient-to-br from-violet-600 to-indigo-600 text-white text-sm font-bold">
|
|
64
|
+
M
|
|
65
|
+
</div>
|
|
66
|
+
<span className="text-lg font-semibold tracking-tight">{appName}</span>
|
|
67
|
+
</div>
|
|
68
|
+
|
|
69
|
+
<div className="w-full max-w-sm">
|
|
70
|
+
<div className="mb-8 text-center">
|
|
71
|
+
<h1 className="text-2xl font-bold tracking-tight">Sign in</h1>
|
|
72
|
+
<p className="mt-2 text-sm text-muted-foreground">
|
|
73
|
+
Enter your credentials to continue
|
|
74
|
+
</p>
|
|
75
|
+
</div>
|
|
76
|
+
|
|
77
|
+
{error && (
|
|
78
|
+
<div className="mb-6 rounded-md border border-destructive px-4 py-3 text-sm text-destructive">
|
|
79
|
+
{error}
|
|
80
|
+
</div>
|
|
81
|
+
)}
|
|
82
|
+
|
|
83
|
+
<form onSubmit={handleSubmit} className="space-y-4">
|
|
84
|
+
<div className="space-y-2">
|
|
85
|
+
<Label htmlFor="email">Email</Label>
|
|
86
|
+
<Input
|
|
87
|
+
id="email"
|
|
88
|
+
type="email"
|
|
89
|
+
value={email}
|
|
90
|
+
onChange={(e) => setEmail(e.target.value)}
|
|
91
|
+
required
|
|
92
|
+
placeholder="you@example.com"
|
|
93
|
+
autoComplete="email"
|
|
94
|
+
/>
|
|
95
|
+
</div>
|
|
96
|
+
<div className="space-y-2">
|
|
97
|
+
<Label htmlFor="password">Password</Label>
|
|
98
|
+
<Input
|
|
99
|
+
id="password"
|
|
100
|
+
type="password"
|
|
101
|
+
value={password}
|
|
102
|
+
onChange={(e) => setPassword(e.target.value)}
|
|
103
|
+
required
|
|
104
|
+
placeholder="Enter your password"
|
|
105
|
+
autoComplete="current-password"
|
|
106
|
+
/>
|
|
107
|
+
</div>
|
|
108
|
+
<Button
|
|
109
|
+
type="submit"
|
|
110
|
+
className="w-full bg-gradient-to-r from-violet-600 to-indigo-600 text-white hover:from-violet-700 hover:to-indigo-700"
|
|
111
|
+
disabled={loading}
|
|
112
|
+
>
|
|
113
|
+
{loading ? 'Signing in...' : 'Sign in'}
|
|
114
|
+
</Button>
|
|
115
|
+
</form>
|
|
116
|
+
|
|
117
|
+
<p className="mt-6 text-center text-sm text-muted-foreground">
|
|
118
|
+
Don't have an account?{' '}
|
|
119
|
+
<button
|
|
120
|
+
type="button"
|
|
121
|
+
className="font-medium text-primary underline underline-offset-4"
|
|
122
|
+
onClick={() => navigate('/register')}
|
|
123
|
+
>
|
|
124
|
+
Register
|
|
125
|
+
</button>
|
|
126
|
+
</p>
|
|
127
|
+
</div>
|
|
128
|
+
</div>
|
|
129
|
+
</div>
|
|
130
|
+
)
|
|
131
|
+
}
|
|
@@ -0,0 +1,145 @@
|
|
|
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
|
+
|
|
7
|
+
interface RegisterProps {
|
|
8
|
+
appName?: string
|
|
9
|
+
navigate: (href: string) => void
|
|
10
|
+
[key: string]: any
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export default function Register({ appName = 'Mantiq', navigate }: RegisterProps) {
|
|
14
|
+
const [name, setName] = useState('')
|
|
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('/register', { name, email, password })
|
|
25
|
+
if (ok) navigate('/dashboard')
|
|
26
|
+
else setError(data?.error?.message ?? data?.error ?? 'Registration failed. Please try again.')
|
|
27
|
+
setLoading(false)
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
return (
|
|
31
|
+
<div className="min-h-screen flex bg-background">
|
|
32
|
+
{/* Left gradient panel */}
|
|
33
|
+
<div className="hidden lg:flex lg:w-1/2 relative overflow-hidden bg-gradient-to-br from-violet-600 via-purple-600 to-indigo-700 animate-gradient">
|
|
34
|
+
{/* Floating decorative circles */}
|
|
35
|
+
<div className="absolute -top-20 -right-20 h-72 w-72 rounded-full bg-white/10 blur-3xl" />
|
|
36
|
+
<div className="absolute top-1/2 left-10 h-48 w-48 rounded-full bg-indigo-400/20 blur-2xl" />
|
|
37
|
+
<div className="absolute bottom-10 right-1/4 h-56 w-56 rounded-full bg-purple-300/15 blur-3xl" />
|
|
38
|
+
|
|
39
|
+
<div className="relative z-10 flex flex-col justify-center p-16 text-white">
|
|
40
|
+
<h1 className="text-5xl font-bold leading-tight tracking-tight">
|
|
41
|
+
Start your<br />journey.
|
|
42
|
+
</h1>
|
|
43
|
+
<p className="mt-4 text-xl text-white/70">
|
|
44
|
+
Create your account and start building something extraordinary with {appName}.
|
|
45
|
+
</p>
|
|
46
|
+
|
|
47
|
+
{/* Testimonial card */}
|
|
48
|
+
<div className="mt-12 rounded-2xl bg-white/10 p-6 backdrop-blur-sm">
|
|
49
|
+
<p className="text-lg leading-relaxed text-white/90">
|
|
50
|
+
"From idea to production in days, not months. The developer experience is unmatched."
|
|
51
|
+
</p>
|
|
52
|
+
<div className="mt-4">
|
|
53
|
+
<p className="font-semibold">Jamie Chen</p>
|
|
54
|
+
<p className="text-sm text-white/60">Founder at NovaTech</p>
|
|
55
|
+
</div>
|
|
56
|
+
</div>
|
|
57
|
+
</div>
|
|
58
|
+
</div>
|
|
59
|
+
|
|
60
|
+
{/* Right form panel */}
|
|
61
|
+
<div className="flex flex-1 flex-col items-center justify-center px-6 py-12">
|
|
62
|
+
{/* Logo */}
|
|
63
|
+
<div className="mb-10 flex items-center gap-3">
|
|
64
|
+
<div className="flex h-9 w-9 items-center justify-center rounded-lg bg-gradient-to-br from-violet-600 to-indigo-600 text-white text-sm font-bold">
|
|
65
|
+
M
|
|
66
|
+
</div>
|
|
67
|
+
<span className="text-lg font-semibold tracking-tight">{appName}</span>
|
|
68
|
+
</div>
|
|
69
|
+
|
|
70
|
+
<div className="w-full max-w-sm">
|
|
71
|
+
<div className="mb-8 text-center">
|
|
72
|
+
<h1 className="text-2xl font-bold tracking-tight">Create an account</h1>
|
|
73
|
+
<p className="mt-2 text-sm text-muted-foreground">
|
|
74
|
+
Get started with {appName} today
|
|
75
|
+
</p>
|
|
76
|
+
</div>
|
|
77
|
+
|
|
78
|
+
{error && (
|
|
79
|
+
<div className="mb-6 rounded-md border border-destructive px-4 py-3 text-sm text-destructive">
|
|
80
|
+
{error}
|
|
81
|
+
</div>
|
|
82
|
+
)}
|
|
83
|
+
|
|
84
|
+
<form onSubmit={handleSubmit} className="space-y-4">
|
|
85
|
+
<div className="space-y-2">
|
|
86
|
+
<Label htmlFor="name">Name</Label>
|
|
87
|
+
<Input
|
|
88
|
+
id="name"
|
|
89
|
+
value={name}
|
|
90
|
+
onChange={(e) => setName(e.target.value)}
|
|
91
|
+
required
|
|
92
|
+
placeholder="John Doe"
|
|
93
|
+
autoComplete="name"
|
|
94
|
+
/>
|
|
95
|
+
</div>
|
|
96
|
+
<div className="space-y-2">
|
|
97
|
+
<Label htmlFor="email">Email</Label>
|
|
98
|
+
<Input
|
|
99
|
+
id="email"
|
|
100
|
+
type="email"
|
|
101
|
+
value={email}
|
|
102
|
+
onChange={(e) => setEmail(e.target.value)}
|
|
103
|
+
required
|
|
104
|
+
placeholder="you@example.com"
|
|
105
|
+
autoComplete="email"
|
|
106
|
+
/>
|
|
107
|
+
</div>
|
|
108
|
+
<div className="space-y-2">
|
|
109
|
+
<Label htmlFor="password">Password</Label>
|
|
110
|
+
<Input
|
|
111
|
+
id="password"
|
|
112
|
+
type="password"
|
|
113
|
+
value={password}
|
|
114
|
+
onChange={(e) => setPassword(e.target.value)}
|
|
115
|
+
required
|
|
116
|
+
placeholder="Create a strong password"
|
|
117
|
+
autoComplete="new-password"
|
|
118
|
+
minLength={8}
|
|
119
|
+
/>
|
|
120
|
+
<p className="text-xs text-muted-foreground">Must be at least 8 characters</p>
|
|
121
|
+
</div>
|
|
122
|
+
<Button
|
|
123
|
+
type="submit"
|
|
124
|
+
className="w-full bg-gradient-to-r from-violet-600 to-indigo-600 text-white hover:from-violet-700 hover:to-indigo-700"
|
|
125
|
+
disabled={loading}
|
|
126
|
+
>
|
|
127
|
+
{loading ? 'Creating account...' : 'Create account'}
|
|
128
|
+
</Button>
|
|
129
|
+
</form>
|
|
130
|
+
|
|
131
|
+
<p className="mt-6 text-center text-sm text-muted-foreground">
|
|
132
|
+
Already have an account?{' '}
|
|
133
|
+
<button
|
|
134
|
+
type="button"
|
|
135
|
+
className="font-medium text-primary underline underline-offset-4"
|
|
136
|
+
onClick={() => navigate('/login')}
|
|
137
|
+
>
|
|
138
|
+
Sign in
|
|
139
|
+
</button>
|
|
140
|
+
</p>
|
|
141
|
+
</div>
|
|
142
|
+
</div>
|
|
143
|
+
</div>
|
|
144
|
+
)
|
|
145
|
+
}
|