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,57 @@
|
|
|
1
|
+
import {
|
|
2
|
+
Home,
|
|
3
|
+
Users,
|
|
4
|
+
Settings,
|
|
5
|
+
User,
|
|
6
|
+
Lock,
|
|
7
|
+
Palette,
|
|
8
|
+
BookOpen,
|
|
9
|
+
Github,
|
|
10
|
+
type LucideIcon,
|
|
11
|
+
} from 'lucide-vue-next'
|
|
12
|
+
|
|
13
|
+
export interface NavItem {
|
|
14
|
+
title: string
|
|
15
|
+
url: string
|
|
16
|
+
icon: LucideIcon
|
|
17
|
+
badge?: string
|
|
18
|
+
external?: boolean
|
|
19
|
+
items?: NavItem[]
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export interface NavGroup {
|
|
23
|
+
title: string
|
|
24
|
+
items: NavItem[]
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export const sidebarData: NavGroup[] = [
|
|
28
|
+
{
|
|
29
|
+
title: 'General',
|
|
30
|
+
items: [
|
|
31
|
+
{ title: 'Dashboard', url: '/dashboard', icon: Home },
|
|
32
|
+
{ title: 'Users', url: '/users', icon: Users },
|
|
33
|
+
],
|
|
34
|
+
},
|
|
35
|
+
{
|
|
36
|
+
title: 'Documentation',
|
|
37
|
+
items: [
|
|
38
|
+
{ title: 'Docs', url: 'https://github.com/mantiqjs/mantiq#readme', icon: BookOpen, external: true },
|
|
39
|
+
{ title: 'GitHub', url: 'https://github.com/mantiqjs/mantiq', icon: Github, external: true },
|
|
40
|
+
],
|
|
41
|
+
},
|
|
42
|
+
{
|
|
43
|
+
title: 'Account',
|
|
44
|
+
items: [
|
|
45
|
+
{
|
|
46
|
+
title: 'Settings',
|
|
47
|
+
url: '/account/profile',
|
|
48
|
+
icon: Settings,
|
|
49
|
+
items: [
|
|
50
|
+
{ title: 'Profile', url: '/account/profile', icon: User },
|
|
51
|
+
{ title: 'Security', url: '/account/security', icon: Lock },
|
|
52
|
+
{ title: 'Preferences', url: '/account/preferences', icon: Palette },
|
|
53
|
+
],
|
|
54
|
+
},
|
|
55
|
+
],
|
|
56
|
+
},
|
|
57
|
+
]
|
|
@@ -0,0 +1,195 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import { AuthenticatedLayout } from '@/components/layout'
|
|
3
|
+
import { Header } from '@/components/layout'
|
|
4
|
+
import { Main } from '@/components/layout'
|
|
5
|
+
import { TopNav } from '@/components/layout'
|
|
6
|
+
import type { TopNavLink } from '@/components/layout/TopNav.vue'
|
|
7
|
+
import { Download } from 'lucide-vue-next'
|
|
8
|
+
|
|
9
|
+
const props = withDefaults(defineProps<{
|
|
10
|
+
appName?: string
|
|
11
|
+
currentUser?: { id: number; name: string; email: string; role: string } | null
|
|
12
|
+
navigate: (href: string) => void
|
|
13
|
+
}>(), {
|
|
14
|
+
appName: 'Mantiq',
|
|
15
|
+
})
|
|
16
|
+
|
|
17
|
+
const topNav: TopNavLink[] = [
|
|
18
|
+
{ title: 'Overview', href: '/dashboard', isActive: true },
|
|
19
|
+
{ title: 'Sales', href: '/dashboard', isActive: false, disabled: true },
|
|
20
|
+
{ title: 'Tickets', href: '/dashboard', isActive: false, disabled: true },
|
|
21
|
+
{ title: 'Performance', href: '/dashboard', isActive: false, disabled: true },
|
|
22
|
+
]
|
|
23
|
+
|
|
24
|
+
const recentSales = [
|
|
25
|
+
{ name: 'Olivia Martin', email: 'olivia.martin@email.com', amount: '+$1,999.00' },
|
|
26
|
+
{ name: 'Jackson Lee', email: 'jackson.lee@email.com', amount: '+$39.00' },
|
|
27
|
+
{ name: 'Isabella Nguyen', email: 'isabella.nguyen@email.com', amount: '+$299.00' },
|
|
28
|
+
{ name: 'William Kim', email: 'will@email.com', amount: '+$99.00' },
|
|
29
|
+
{ name: 'Sofia Davis', email: 'sofia.davis@email.com', amount: '+$39.00' },
|
|
30
|
+
]
|
|
31
|
+
|
|
32
|
+
function getInitials(name: string) {
|
|
33
|
+
return name
|
|
34
|
+
.split(' ')
|
|
35
|
+
.map((n) => n[0])
|
|
36
|
+
.join('')
|
|
37
|
+
.toUpperCase()
|
|
38
|
+
.slice(0, 2)
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
const bars = [40, 30, 55, 45, 70, 60, 80, 50, 65, 45, 75, 55]
|
|
42
|
+
const months = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec']
|
|
43
|
+
const maxH = 160
|
|
44
|
+
const barW = 32
|
|
45
|
+
const gap = 48
|
|
46
|
+
const svgW = bars.length * gap
|
|
47
|
+
const svgH = maxH + 30
|
|
48
|
+
</script>
|
|
49
|
+
|
|
50
|
+
<template>
|
|
51
|
+
<AuthenticatedLayout
|
|
52
|
+
:current-user="currentUser"
|
|
53
|
+
:app-name="appName"
|
|
54
|
+
:navigate="navigate"
|
|
55
|
+
active-path="/dashboard"
|
|
56
|
+
>
|
|
57
|
+
<Header :navigate="navigate">
|
|
58
|
+
<TopNav :links="topNav" @link-click="navigate" />
|
|
59
|
+
</Header>
|
|
60
|
+
<Main>
|
|
61
|
+
<div class="space-y-4">
|
|
62
|
+
<!-- Page title row -->
|
|
63
|
+
<div class="flex items-center justify-between">
|
|
64
|
+
<h2 class="text-3xl font-bold tracking-tight">Dashboard</h2>
|
|
65
|
+
<button
|
|
66
|
+
disabled
|
|
67
|
+
class="inline-flex items-center gap-2 rounded-md border border-input bg-background px-3 py-1.5 text-sm font-medium text-muted-foreground shadow-sm"
|
|
68
|
+
>
|
|
69
|
+
<Download class="h-4 w-4" />
|
|
70
|
+
Download
|
|
71
|
+
</button>
|
|
72
|
+
</div>
|
|
73
|
+
|
|
74
|
+
<!-- Content -->
|
|
75
|
+
<div class="space-y-4">
|
|
76
|
+
<!-- Stat cards -->
|
|
77
|
+
<div class="grid gap-4 sm:grid-cols-2 lg:grid-cols-4">
|
|
78
|
+
<!-- Total Revenue -->
|
|
79
|
+
<div class="rounded-xl border bg-card p-6 text-card-foreground shadow-sm">
|
|
80
|
+
<div class="flex items-center justify-between space-y-0 pb-2">
|
|
81
|
+
<h3 class="text-sm font-medium">Total Revenue</h3>
|
|
82
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" class="h-4 w-4 text-muted-foreground">
|
|
83
|
+
<path d="M12 2v20M17 5H9.5a3.5 3.5 0 0 0 0 7h5a3.5 3.5 0 0 1 0 7H6" />
|
|
84
|
+
</svg>
|
|
85
|
+
</div>
|
|
86
|
+
<div class="text-2xl font-bold">$45,231.89</div>
|
|
87
|
+
<p class="text-xs text-muted-foreground">+20.1% from last month</p>
|
|
88
|
+
</div>
|
|
89
|
+
|
|
90
|
+
<!-- Subscriptions -->
|
|
91
|
+
<div class="rounded-xl border bg-card p-6 text-card-foreground shadow-sm">
|
|
92
|
+
<div class="flex items-center justify-between space-y-0 pb-2">
|
|
93
|
+
<h3 class="text-sm font-medium">Subscriptions</h3>
|
|
94
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" class="h-4 w-4 text-muted-foreground">
|
|
95
|
+
<path d="M16 21v-2a4 4 0 0 0-4-4H6a4 4 0 0 0-4 4v2" />
|
|
96
|
+
<circle cx="9" cy="7" r="4" />
|
|
97
|
+
<path d="M22 21v-2a4 4 0 0 0-3-3.87M16 3.13a4 4 0 0 1 0 7.75" />
|
|
98
|
+
</svg>
|
|
99
|
+
</div>
|
|
100
|
+
<div class="text-2xl font-bold">+2,350</div>
|
|
101
|
+
<p class="text-xs text-muted-foreground">+180.1% from last month</p>
|
|
102
|
+
</div>
|
|
103
|
+
|
|
104
|
+
<!-- Sales -->
|
|
105
|
+
<div class="rounded-xl border bg-card p-6 text-card-foreground shadow-sm">
|
|
106
|
+
<div class="flex items-center justify-between space-y-0 pb-2">
|
|
107
|
+
<h3 class="text-sm font-medium">Sales</h3>
|
|
108
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" class="h-4 w-4 text-muted-foreground">
|
|
109
|
+
<rect width="20" height="14" x="2" y="5" rx="2" />
|
|
110
|
+
<path d="M2 10h20" />
|
|
111
|
+
</svg>
|
|
112
|
+
</div>
|
|
113
|
+
<div class="text-2xl font-bold">+12,234</div>
|
|
114
|
+
<p class="text-xs text-muted-foreground">+19% from last month</p>
|
|
115
|
+
</div>
|
|
116
|
+
|
|
117
|
+
<!-- Active Now -->
|
|
118
|
+
<div class="rounded-xl border bg-card p-6 text-card-foreground shadow-sm">
|
|
119
|
+
<div class="flex items-center justify-between space-y-0 pb-2">
|
|
120
|
+
<h3 class="text-sm font-medium">Active Now</h3>
|
|
121
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" class="h-4 w-4 text-muted-foreground">
|
|
122
|
+
<path d="M22 12h-4l-3 9L9 3l-3 9H2" />
|
|
123
|
+
</svg>
|
|
124
|
+
</div>
|
|
125
|
+
<div class="text-2xl font-bold">+573</div>
|
|
126
|
+
<p class="text-xs text-muted-foreground">+201 since last hour</p>
|
|
127
|
+
</div>
|
|
128
|
+
</div>
|
|
129
|
+
|
|
130
|
+
<!-- Bottom row: chart + recent sales -->
|
|
131
|
+
<div class="grid gap-4 lg:grid-cols-7">
|
|
132
|
+
<!-- Chart card -->
|
|
133
|
+
<div class="rounded-xl border bg-card text-card-foreground shadow-sm lg:col-span-4">
|
|
134
|
+
<div class="p-6 pb-2">
|
|
135
|
+
<h3 class="text-lg font-semibold leading-none tracking-tight">Overview</h3>
|
|
136
|
+
</div>
|
|
137
|
+
<div class="p-6 pt-0 pl-2">
|
|
138
|
+
<svg
|
|
139
|
+
:viewBox="`0 0 ${svgW} ${svgH}`"
|
|
140
|
+
class="h-[350px] w-full"
|
|
141
|
+
preserveAspectRatio="none"
|
|
142
|
+
>
|
|
143
|
+
<g v-for="(h, i) in bars" :key="i">
|
|
144
|
+
<rect
|
|
145
|
+
:x="i * gap + (gap - barW) / 2"
|
|
146
|
+
:y="maxH - (h / 100) * maxH"
|
|
147
|
+
:width="barW"
|
|
148
|
+
:height="(h / 100) * maxH"
|
|
149
|
+
rx="4"
|
|
150
|
+
class="fill-foreground/15"
|
|
151
|
+
/>
|
|
152
|
+
<text
|
|
153
|
+
:x="i * gap + gap / 2"
|
|
154
|
+
:y="maxH + 18"
|
|
155
|
+
text-anchor="middle"
|
|
156
|
+
class="fill-muted-foreground text-[10px]"
|
|
157
|
+
>
|
|
158
|
+
{{ months[i] }}
|
|
159
|
+
</text>
|
|
160
|
+
</g>
|
|
161
|
+
</svg>
|
|
162
|
+
</div>
|
|
163
|
+
</div>
|
|
164
|
+
|
|
165
|
+
<!-- Recent sales card -->
|
|
166
|
+
<div class="rounded-xl border bg-card text-card-foreground shadow-sm lg:col-span-3">
|
|
167
|
+
<div class="p-6 pb-2">
|
|
168
|
+
<h3 class="text-lg font-semibold leading-none tracking-tight">Recent Sales</h3>
|
|
169
|
+
<p class="mt-1.5 text-sm text-muted-foreground">You made 265 sales this month.</p>
|
|
170
|
+
</div>
|
|
171
|
+
<div class="p-6 pt-0">
|
|
172
|
+
<div class="space-y-8">
|
|
173
|
+
<div
|
|
174
|
+
v-for="sale in recentSales"
|
|
175
|
+
:key="sale.email"
|
|
176
|
+
class="flex items-center"
|
|
177
|
+
>
|
|
178
|
+
<span class="flex h-9 w-9 shrink-0 items-center justify-center rounded-full bg-muted text-xs font-medium">
|
|
179
|
+
{{ getInitials(sale.name) }}
|
|
180
|
+
</span>
|
|
181
|
+
<div class="ml-4 space-y-1">
|
|
182
|
+
<p class="text-sm font-medium leading-none">{{ sale.name }}</p>
|
|
183
|
+
<p class="text-sm text-muted-foreground">{{ sale.email }}</p>
|
|
184
|
+
</div>
|
|
185
|
+
<div class="ml-auto font-medium">{{ sale.amount }}</div>
|
|
186
|
+
</div>
|
|
187
|
+
</div>
|
|
188
|
+
</div>
|
|
189
|
+
</div>
|
|
190
|
+
</div>
|
|
191
|
+
</div>
|
|
192
|
+
</div>
|
|
193
|
+
</Main>
|
|
194
|
+
</AuthenticatedLayout>
|
|
195
|
+
</template>
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import { ref } from 'vue'
|
|
3
|
+
import { post } from '@/lib/api'
|
|
4
|
+
|
|
5
|
+
const props = withDefaults(defineProps<{
|
|
6
|
+
appName?: string
|
|
7
|
+
navigate: (href: string) => void
|
|
8
|
+
}>(), {
|
|
9
|
+
appName: 'Mantiq',
|
|
10
|
+
})
|
|
11
|
+
|
|
12
|
+
const email = ref('')
|
|
13
|
+
const password = ref('')
|
|
14
|
+
const error = ref('')
|
|
15
|
+
const loading = ref(false)
|
|
16
|
+
|
|
17
|
+
async function handleSubmit() {
|
|
18
|
+
error.value = ''
|
|
19
|
+
loading.value = true
|
|
20
|
+
const { ok, data } = await post('/login', { email: email.value, password: password.value })
|
|
21
|
+
if (ok) props.navigate('/dashboard')
|
|
22
|
+
else error.value = data?.error ?? 'Invalid email or password. Please try again.'
|
|
23
|
+
loading.value = false
|
|
24
|
+
}
|
|
25
|
+
</script>
|
|
26
|
+
|
|
27
|
+
<template>
|
|
28
|
+
<div class="min-h-screen flex bg-background">
|
|
29
|
+
<!-- Left brand panel -->
|
|
30
|
+
<div class="hidden lg:flex lg:w-[45%] bg-foreground text-background flex-col justify-between p-10">
|
|
31
|
+
<div class="flex items-center gap-3">
|
|
32
|
+
<div class="flex h-8 w-8 items-center justify-center rounded bg-background text-foreground text-xs font-bold">
|
|
33
|
+
M
|
|
34
|
+
</div>
|
|
35
|
+
<span class="text-lg font-semibold tracking-tight">{{ appName }}</span>
|
|
36
|
+
</div>
|
|
37
|
+
|
|
38
|
+
<div>
|
|
39
|
+
<blockquote class="text-2xl font-medium leading-snug tracking-tight">
|
|
40
|
+
"The framework that gets out of your way."
|
|
41
|
+
</blockquote>
|
|
42
|
+
</div>
|
|
43
|
+
|
|
44
|
+
<p class="text-sm text-background/50">
|
|
45
|
+
© {{ new Date().getFullYear() }} {{ appName }}. All rights reserved.
|
|
46
|
+
</p>
|
|
47
|
+
</div>
|
|
48
|
+
|
|
49
|
+
<!-- Right form panel -->
|
|
50
|
+
<div class="flex flex-1 flex-col items-center justify-center px-6 py-12">
|
|
51
|
+
<!-- Mobile-only logo -->
|
|
52
|
+
<div class="mb-10 flex items-center gap-3 lg:hidden">
|
|
53
|
+
<div class="flex h-8 w-8 items-center justify-center rounded bg-foreground text-background text-xs font-bold">
|
|
54
|
+
M
|
|
55
|
+
</div>
|
|
56
|
+
<span class="text-lg font-semibold tracking-tight">{{ appName }}</span>
|
|
57
|
+
</div>
|
|
58
|
+
|
|
59
|
+
<div class="w-full max-w-sm">
|
|
60
|
+
<div class="mb-8">
|
|
61
|
+
<h1 class="text-2xl font-semibold tracking-tight">Sign in</h1>
|
|
62
|
+
<p class="mt-2 text-sm text-muted-foreground">
|
|
63
|
+
Enter your credentials to continue
|
|
64
|
+
</p>
|
|
65
|
+
</div>
|
|
66
|
+
|
|
67
|
+
<div
|
|
68
|
+
v-if="error"
|
|
69
|
+
class="mb-6 rounded-md border border-destructive px-4 py-3 text-sm text-destructive"
|
|
70
|
+
>
|
|
71
|
+
{{ error }}
|
|
72
|
+
</div>
|
|
73
|
+
|
|
74
|
+
<form class="space-y-4" @submit.prevent="handleSubmit">
|
|
75
|
+
<div class="space-y-2">
|
|
76
|
+
<label for="email" class="text-sm font-medium leading-none">Email</label>
|
|
77
|
+
<input
|
|
78
|
+
id="email"
|
|
79
|
+
v-model="email"
|
|
80
|
+
type="email"
|
|
81
|
+
required
|
|
82
|
+
placeholder="you@example.com"
|
|
83
|
+
autocomplete="email"
|
|
84
|
+
class="flex h-9 w-full rounded-md border border-input bg-transparent px-3 py-1 text-sm shadow-sm transition-colors placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring"
|
|
85
|
+
/>
|
|
86
|
+
</div>
|
|
87
|
+
<div class="space-y-2">
|
|
88
|
+
<label for="password" class="text-sm font-medium leading-none">Password</label>
|
|
89
|
+
<input
|
|
90
|
+
id="password"
|
|
91
|
+
v-model="password"
|
|
92
|
+
type="password"
|
|
93
|
+
required
|
|
94
|
+
placeholder="Enter your password"
|
|
95
|
+
autocomplete="current-password"
|
|
96
|
+
class="flex h-9 w-full rounded-md border border-input bg-transparent px-3 py-1 text-sm shadow-sm transition-colors placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring"
|
|
97
|
+
/>
|
|
98
|
+
</div>
|
|
99
|
+
<button
|
|
100
|
+
type="submit"
|
|
101
|
+
class="inline-flex h-9 w-full items-center justify-center rounded-md bg-primary px-4 py-2 text-sm font-medium text-primary-foreground shadow hover:bg-primary/90 disabled:pointer-events-none disabled:opacity-50"
|
|
102
|
+
:disabled="loading"
|
|
103
|
+
>
|
|
104
|
+
{{ loading ? 'Signing in...' : 'Sign in' }}
|
|
105
|
+
</button>
|
|
106
|
+
</form>
|
|
107
|
+
|
|
108
|
+
<p class="mt-6 text-center text-sm text-muted-foreground">
|
|
109
|
+
Don't have an account?
|
|
110
|
+
<button
|
|
111
|
+
type="button"
|
|
112
|
+
class="font-medium text-foreground underline underline-offset-4"
|
|
113
|
+
@click="navigate('/register')"
|
|
114
|
+
>
|
|
115
|
+
Register
|
|
116
|
+
</button>
|
|
117
|
+
</p>
|
|
118
|
+
</div>
|
|
119
|
+
</div>
|
|
120
|
+
</div>
|
|
121
|
+
</template>
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import { ref } from 'vue'
|
|
3
|
+
import { post } from '@/lib/api'
|
|
4
|
+
|
|
5
|
+
const props = withDefaults(defineProps<{
|
|
6
|
+
appName?: string
|
|
7
|
+
navigate: (href: string) => void
|
|
8
|
+
}>(), {
|
|
9
|
+
appName: 'Mantiq',
|
|
10
|
+
})
|
|
11
|
+
|
|
12
|
+
const name = ref('')
|
|
13
|
+
const email = ref('')
|
|
14
|
+
const password = ref('')
|
|
15
|
+
const error = ref('')
|
|
16
|
+
const loading = ref(false)
|
|
17
|
+
|
|
18
|
+
async function handleSubmit() {
|
|
19
|
+
error.value = ''
|
|
20
|
+
loading.value = true
|
|
21
|
+
const { ok, data } = await post('/register', {
|
|
22
|
+
name: name.value,
|
|
23
|
+
email: email.value,
|
|
24
|
+
password: password.value,
|
|
25
|
+
})
|
|
26
|
+
if (ok) props.navigate('/dashboard')
|
|
27
|
+
else error.value = data?.error?.message ?? data?.error ?? 'Registration failed'
|
|
28
|
+
loading.value = false
|
|
29
|
+
}
|
|
30
|
+
</script>
|
|
31
|
+
|
|
32
|
+
<template>
|
|
33
|
+
<div class="min-h-screen flex bg-background">
|
|
34
|
+
<!-- Left brand panel -->
|
|
35
|
+
<div class="hidden lg:flex lg:w-[45%] bg-foreground text-background flex-col justify-between p-10">
|
|
36
|
+
<div class="flex items-center gap-3">
|
|
37
|
+
<div class="flex h-8 w-8 items-center justify-center rounded bg-background text-foreground text-xs font-bold">
|
|
38
|
+
M
|
|
39
|
+
</div>
|
|
40
|
+
<span class="text-lg font-semibold tracking-tight">{{ appName }}</span>
|
|
41
|
+
</div>
|
|
42
|
+
|
|
43
|
+
<div>
|
|
44
|
+
<blockquote class="text-2xl font-medium leading-snug tracking-tight">
|
|
45
|
+
"The framework that gets out of your way."
|
|
46
|
+
</blockquote>
|
|
47
|
+
</div>
|
|
48
|
+
|
|
49
|
+
<p class="text-sm text-background/50">
|
|
50
|
+
© {{ new Date().getFullYear() }} {{ appName }}. All rights reserved.
|
|
51
|
+
</p>
|
|
52
|
+
</div>
|
|
53
|
+
|
|
54
|
+
<!-- Right form panel -->
|
|
55
|
+
<div class="flex flex-1 flex-col items-center justify-center px-6 py-12">
|
|
56
|
+
<!-- Mobile-only logo -->
|
|
57
|
+
<div class="mb-10 flex items-center gap-3 lg:hidden">
|
|
58
|
+
<div class="flex h-8 w-8 items-center justify-center rounded bg-foreground text-background text-xs font-bold">
|
|
59
|
+
M
|
|
60
|
+
</div>
|
|
61
|
+
<span class="text-lg font-semibold tracking-tight">{{ appName }}</span>
|
|
62
|
+
</div>
|
|
63
|
+
|
|
64
|
+
<div class="w-full max-w-sm">
|
|
65
|
+
<div class="mb-8">
|
|
66
|
+
<h1 class="text-2xl font-semibold tracking-tight">Create an account</h1>
|
|
67
|
+
<p class="mt-2 text-sm text-muted-foreground">
|
|
68
|
+
Get started with {{ appName }}
|
|
69
|
+
</p>
|
|
70
|
+
</div>
|
|
71
|
+
|
|
72
|
+
<div
|
|
73
|
+
v-if="error"
|
|
74
|
+
class="mb-6 rounded-md border border-destructive px-4 py-3 text-sm text-destructive"
|
|
75
|
+
>
|
|
76
|
+
{{ error }}
|
|
77
|
+
</div>
|
|
78
|
+
|
|
79
|
+
<form class="space-y-4" @submit.prevent="handleSubmit">
|
|
80
|
+
<div class="space-y-2">
|
|
81
|
+
<label for="name" class="text-sm font-medium leading-none">Name</label>
|
|
82
|
+
<input
|
|
83
|
+
id="name"
|
|
84
|
+
v-model="name"
|
|
85
|
+
required
|
|
86
|
+
placeholder="Your name"
|
|
87
|
+
autocomplete="name"
|
|
88
|
+
class="flex h-9 w-full rounded-md border border-input bg-transparent px-3 py-1 text-sm shadow-sm transition-colors placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring"
|
|
89
|
+
/>
|
|
90
|
+
</div>
|
|
91
|
+
<div class="space-y-2">
|
|
92
|
+
<label for="email" class="text-sm font-medium leading-none">Email</label>
|
|
93
|
+
<input
|
|
94
|
+
id="email"
|
|
95
|
+
v-model="email"
|
|
96
|
+
type="email"
|
|
97
|
+
required
|
|
98
|
+
placeholder="you@example.com"
|
|
99
|
+
autocomplete="email"
|
|
100
|
+
class="flex h-9 w-full rounded-md border border-input bg-transparent px-3 py-1 text-sm shadow-sm transition-colors placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring"
|
|
101
|
+
/>
|
|
102
|
+
</div>
|
|
103
|
+
<div class="space-y-2">
|
|
104
|
+
<label for="password" class="text-sm font-medium leading-none">Password</label>
|
|
105
|
+
<input
|
|
106
|
+
id="password"
|
|
107
|
+
v-model="password"
|
|
108
|
+
type="password"
|
|
109
|
+
required
|
|
110
|
+
placeholder="Create a password"
|
|
111
|
+
autocomplete="new-password"
|
|
112
|
+
class="flex h-9 w-full rounded-md border border-input bg-transparent px-3 py-1 text-sm shadow-sm transition-colors placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring"
|
|
113
|
+
/>
|
|
114
|
+
</div>
|
|
115
|
+
<button
|
|
116
|
+
type="submit"
|
|
117
|
+
class="inline-flex h-9 w-full items-center justify-center rounded-md bg-primary px-4 py-2 text-sm font-medium text-primary-foreground shadow hover:bg-primary/90 disabled:pointer-events-none disabled:opacity-50"
|
|
118
|
+
:disabled="loading"
|
|
119
|
+
>
|
|
120
|
+
{{ loading ? 'Creating account...' : 'Create account' }}
|
|
121
|
+
</button>
|
|
122
|
+
</form>
|
|
123
|
+
|
|
124
|
+
<p class="mt-6 text-center text-sm text-muted-foreground">
|
|
125
|
+
Already have an account?
|
|
126
|
+
<button
|
|
127
|
+
type="button"
|
|
128
|
+
class="font-medium text-foreground underline underline-offset-4"
|
|
129
|
+
@click="navigate('/login')"
|
|
130
|
+
>
|
|
131
|
+
Sign in
|
|
132
|
+
</button>
|
|
133
|
+
</p>
|
|
134
|
+
</div>
|
|
135
|
+
</div>
|
|
136
|
+
</div>
|
|
137
|
+
</template>
|