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,128 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import { ref } from 'vue'
|
|
3
|
+
import { post } from '@/lib/api'
|
|
4
|
+
import { Button } from '@/components/ui/button'
|
|
5
|
+
import { Input } from '@/components/ui/input'
|
|
6
|
+
import { Label } from '@/components/ui/label'
|
|
7
|
+
|
|
8
|
+
const props = withDefaults(defineProps<{
|
|
9
|
+
appName?: string
|
|
10
|
+
navigate?: (href: string) => void
|
|
11
|
+
}>(), {
|
|
12
|
+
appName: 'Mantiq',
|
|
13
|
+
})
|
|
14
|
+
|
|
15
|
+
const email = ref('')
|
|
16
|
+
const password = ref('')
|
|
17
|
+
const error = ref('')
|
|
18
|
+
const loading = ref(false)
|
|
19
|
+
|
|
20
|
+
async function handleSubmit() {
|
|
21
|
+
error.value = ''
|
|
22
|
+
loading.value = true
|
|
23
|
+
const { ok, data } = await post('/login', { email: email.value, password: password.value })
|
|
24
|
+
if (ok) props.navigate?.('/dashboard')
|
|
25
|
+
else error.value = data?.error ?? 'Invalid email or password.'
|
|
26
|
+
loading.value = false
|
|
27
|
+
}
|
|
28
|
+
</script>
|
|
29
|
+
|
|
30
|
+
<template>
|
|
31
|
+
<div class="min-h-screen bg-gradient-to-br from-amber-50 via-orange-50 to-rose-50 dark:from-stone-950 dark:via-stone-900 dark:to-stone-950">
|
|
32
|
+
<!-- Top bar -->
|
|
33
|
+
<div class="flex items-center justify-between px-6 py-4">
|
|
34
|
+
<div class="flex items-center gap-2.5">
|
|
35
|
+
<div class="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 class="font-semibold tracking-tight">{{ appName }}</span>
|
|
39
|
+
</div>
|
|
40
|
+
<Button
|
|
41
|
+
variant="ghost"
|
|
42
|
+
size="sm"
|
|
43
|
+
class="text-sm"
|
|
44
|
+
@click="navigate?.('/register')"
|
|
45
|
+
>
|
|
46
|
+
Create account
|
|
47
|
+
</Button>
|
|
48
|
+
</div>
|
|
49
|
+
|
|
50
|
+
<!-- Centered content -->
|
|
51
|
+
<div class="flex flex-col items-center justify-center px-4" :style="{ minHeight: 'calc(100vh - 64px)' }">
|
|
52
|
+
<div class="w-full max-w-sm">
|
|
53
|
+
<!-- Emoji greeting -->
|
|
54
|
+
<div class="mb-6 text-center">
|
|
55
|
+
<span class="text-5xl select-none">👋</span>
|
|
56
|
+
<h1 class="mt-4 text-2xl font-bold tracking-tight">Welcome back</h1>
|
|
57
|
+
<p class="mt-2 text-sm text-muted-foreground">
|
|
58
|
+
Log in to your {{ appName }} workspace
|
|
59
|
+
</p>
|
|
60
|
+
</div>
|
|
61
|
+
|
|
62
|
+
<div
|
|
63
|
+
v-if="error"
|
|
64
|
+
class="mb-4 flex items-center gap-2 rounded-xl bg-red-50 dark:bg-red-950/20 border border-red-200/50 dark:border-red-800/30 px-4 py-3 text-sm text-red-700 dark:text-red-400"
|
|
65
|
+
>
|
|
66
|
+
<span class="select-none">⚠️</span>
|
|
67
|
+
{{ error }}
|
|
68
|
+
</div>
|
|
69
|
+
|
|
70
|
+
<form class="space-y-4" @submit.prevent="handleSubmit">
|
|
71
|
+
<div class="space-y-2">
|
|
72
|
+
<Label for="email">Email</Label>
|
|
73
|
+
<Input
|
|
74
|
+
id="email"
|
|
75
|
+
v-model="email"
|
|
76
|
+
type="email"
|
|
77
|
+
required
|
|
78
|
+
placeholder="you@example.com"
|
|
79
|
+
autocomplete="email"
|
|
80
|
+
class="h-11 rounded-xl"
|
|
81
|
+
/>
|
|
82
|
+
</div>
|
|
83
|
+
<div class="space-y-2">
|
|
84
|
+
<Label for="password">Password</Label>
|
|
85
|
+
<Input
|
|
86
|
+
id="password"
|
|
87
|
+
v-model="password"
|
|
88
|
+
type="password"
|
|
89
|
+
required
|
|
90
|
+
placeholder="Enter your password"
|
|
91
|
+
autocomplete="current-password"
|
|
92
|
+
class="h-11 rounded-xl"
|
|
93
|
+
/>
|
|
94
|
+
</div>
|
|
95
|
+
<Button type="submit" class="w-full h-11 rounded-xl text-sm font-medium" :disabled="loading">
|
|
96
|
+
{{ loading ? 'Logging in\u2026' : 'Continue with email' }}
|
|
97
|
+
</Button>
|
|
98
|
+
</form>
|
|
99
|
+
|
|
100
|
+
<!-- Divider -->
|
|
101
|
+
<div class="my-6 flex items-center gap-4">
|
|
102
|
+
<div class="h-px flex-1 bg-border" />
|
|
103
|
+
<span class="text-xs text-muted-foreground select-none">or</span>
|
|
104
|
+
<div class="h-px flex-1 bg-border" />
|
|
105
|
+
</div>
|
|
106
|
+
|
|
107
|
+
<!-- Social buttons -->
|
|
108
|
+
<div class="space-y-2.5">
|
|
109
|
+
<Button variant="outline" class="w-full h-11 rounded-xl gap-2.5 text-sm" disabled>
|
|
110
|
+
<svg class="h-4 w-4" viewBox="0 0 24 24">
|
|
111
|
+
<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" />
|
|
112
|
+
<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" />
|
|
113
|
+
<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" />
|
|
114
|
+
<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" />
|
|
115
|
+
</svg>
|
|
116
|
+
Continue with Google
|
|
117
|
+
</Button>
|
|
118
|
+
<Button variant="outline" class="w-full h-11 rounded-xl gap-2.5 text-sm" disabled>
|
|
119
|
+
<svg class="h-4 w-4" viewBox="0 0 24 24" fill="currentColor">
|
|
120
|
+
<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" />
|
|
121
|
+
</svg>
|
|
122
|
+
Continue with GitHub
|
|
123
|
+
</Button>
|
|
124
|
+
</div>
|
|
125
|
+
</div>
|
|
126
|
+
</div>
|
|
127
|
+
</div>
|
|
128
|
+
</template>
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import { ref } from 'vue'
|
|
3
|
+
import { post } from '@/lib/api'
|
|
4
|
+
import { Button } from '@/components/ui/button'
|
|
5
|
+
import { Input } from '@/components/ui/input'
|
|
6
|
+
import { Label } from '@/components/ui/label'
|
|
7
|
+
|
|
8
|
+
const props = withDefaults(defineProps<{
|
|
9
|
+
appName?: string
|
|
10
|
+
navigate?: (href: string) => void
|
|
11
|
+
}>(), {
|
|
12
|
+
appName: 'Mantiq',
|
|
13
|
+
})
|
|
14
|
+
|
|
15
|
+
const name = ref('')
|
|
16
|
+
const email = ref('')
|
|
17
|
+
const password = ref('')
|
|
18
|
+
const error = ref('')
|
|
19
|
+
const loading = ref(false)
|
|
20
|
+
|
|
21
|
+
async function handleSubmit() {
|
|
22
|
+
error.value = ''
|
|
23
|
+
loading.value = true
|
|
24
|
+
const { ok, data } = await post('/register', { name: name.value, email: email.value, password: password.value })
|
|
25
|
+
if (ok) props.navigate?.('/dashboard')
|
|
26
|
+
else error.value = data?.error ?? 'Registration failed.'
|
|
27
|
+
loading.value = false
|
|
28
|
+
}
|
|
29
|
+
</script>
|
|
30
|
+
|
|
31
|
+
<template>
|
|
32
|
+
<div class="min-h-screen bg-gradient-to-br from-amber-50 via-orange-50 to-rose-50 dark:from-stone-950 dark:via-stone-900 dark:to-stone-950">
|
|
33
|
+
<div class="flex items-center justify-between px-6 py-4">
|
|
34
|
+
<div class="flex items-center gap-2.5">
|
|
35
|
+
<div class="flex h-8 w-8 items-center justify-center rounded-full bg-primary text-primary-foreground text-xs font-bold shadow-sm">M</div>
|
|
36
|
+
<span class="font-semibold tracking-tight">{{ appName }}</span>
|
|
37
|
+
</div>
|
|
38
|
+
<Button variant="ghost" size="sm" class="text-sm" @click="navigate?.('/login')">Sign in</Button>
|
|
39
|
+
</div>
|
|
40
|
+
|
|
41
|
+
<div class="flex flex-col items-center justify-center px-4" :style="{ minHeight: 'calc(100vh - 64px)' }">
|
|
42
|
+
<div class="w-full max-w-sm">
|
|
43
|
+
<div class="mb-6 text-center">
|
|
44
|
+
<span class="text-5xl select-none">🚀</span>
|
|
45
|
+
<h1 class="mt-4 text-2xl font-bold tracking-tight">Get started</h1>
|
|
46
|
+
<p class="mt-2 text-sm text-muted-foreground">Create your {{ appName }} workspace</p>
|
|
47
|
+
</div>
|
|
48
|
+
|
|
49
|
+
<div
|
|
50
|
+
v-if="error"
|
|
51
|
+
class="mb-4 flex items-center gap-2 rounded-xl bg-red-50 dark:bg-red-950/20 border border-red-200/50 dark:border-red-800/30 px-4 py-3 text-sm text-red-700 dark:text-red-400"
|
|
52
|
+
>
|
|
53
|
+
<span class="select-none">⚠️</span> {{ error }}
|
|
54
|
+
</div>
|
|
55
|
+
|
|
56
|
+
<form class="space-y-4" @submit.prevent="handleSubmit">
|
|
57
|
+
<div class="space-y-2">
|
|
58
|
+
<Label for="name">Name</Label>
|
|
59
|
+
<Input id="name" v-model="name" required placeholder="Your name" class="h-11 rounded-xl" />
|
|
60
|
+
</div>
|
|
61
|
+
<div class="space-y-2">
|
|
62
|
+
<Label for="email">Email</Label>
|
|
63
|
+
<Input id="email" v-model="email" type="email" required placeholder="you@example.com" autocomplete="email" class="h-11 rounded-xl" />
|
|
64
|
+
</div>
|
|
65
|
+
<div class="space-y-2">
|
|
66
|
+
<Label for="password">Password</Label>
|
|
67
|
+
<Input id="password" v-model="password" type="password" required placeholder="Min 8 characters" class="h-11 rounded-xl" />
|
|
68
|
+
</div>
|
|
69
|
+
<Button type="submit" class="w-full h-11 rounded-xl text-sm font-medium" :disabled="loading">
|
|
70
|
+
{{ loading ? 'Creating\u2026' : 'Create workspace' }}
|
|
71
|
+
</Button>
|
|
72
|
+
</form>
|
|
73
|
+
|
|
74
|
+
<p class="mt-6 text-center text-xs text-muted-foreground">
|
|
75
|
+
By continuing, you agree to our Terms of Service and Privacy Policy.
|
|
76
|
+
</p>
|
|
77
|
+
</div>
|
|
78
|
+
</div>
|
|
79
|
+
</div>
|
|
80
|
+
</template>
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
@import "tailwindcss";
|
|
2
|
+
@custom-variant dark (&:where(.dark, .dark *));
|
|
3
|
+
|
|
4
|
+
/*
|
|
5
|
+
* shadcn/ui theme — Workspace
|
|
6
|
+
*
|
|
7
|
+
* Warm, approachable, generous spacing. Stone/sand tones.
|
|
8
|
+
* Large radius, soft shadows, content-first layout.
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
@theme inline {
|
|
12
|
+
--color-background: var(--background);
|
|
13
|
+
--color-foreground: var(--foreground);
|
|
14
|
+
--color-card: var(--card);
|
|
15
|
+
--color-card-foreground: var(--card-foreground);
|
|
16
|
+
--color-popover: var(--popover);
|
|
17
|
+
--color-popover-foreground: var(--popover-foreground);
|
|
18
|
+
--color-primary: var(--primary);
|
|
19
|
+
--color-primary-foreground: var(--primary-foreground);
|
|
20
|
+
--color-secondary: var(--secondary);
|
|
21
|
+
--color-secondary-foreground: var(--secondary-foreground);
|
|
22
|
+
--color-muted: var(--muted);
|
|
23
|
+
--color-muted-foreground: var(--muted-foreground);
|
|
24
|
+
--color-accent: var(--accent);
|
|
25
|
+
--color-accent-foreground: var(--accent-foreground);
|
|
26
|
+
--color-destructive: var(--destructive);
|
|
27
|
+
--color-destructive-foreground: var(--destructive-foreground);
|
|
28
|
+
--color-border: var(--border);
|
|
29
|
+
--color-input: var(--input);
|
|
30
|
+
--color-ring: var(--ring);
|
|
31
|
+
--color-chart-1: var(--chart-1);
|
|
32
|
+
--color-chart-2: var(--chart-2);
|
|
33
|
+
--color-chart-3: var(--chart-3);
|
|
34
|
+
--color-chart-4: var(--chart-4);
|
|
35
|
+
--color-chart-5: var(--chart-5);
|
|
36
|
+
--color-sidebar: var(--sidebar);
|
|
37
|
+
--color-sidebar-foreground: var(--sidebar-foreground);
|
|
38
|
+
--color-sidebar-primary: var(--sidebar-primary);
|
|
39
|
+
--color-sidebar-primary-foreground: var(--sidebar-primary-foreground);
|
|
40
|
+
--color-sidebar-accent: var(--sidebar-accent);
|
|
41
|
+
--color-sidebar-accent-foreground: var(--sidebar-accent-foreground);
|
|
42
|
+
--color-sidebar-border: var(--sidebar-border);
|
|
43
|
+
--color-sidebar-ring: var(--sidebar-ring);
|
|
44
|
+
--radius-sm: calc(var(--radius) - 4px);
|
|
45
|
+
--radius-md: calc(var(--radius) - 2px);
|
|
46
|
+
--radius-lg: var(--radius);
|
|
47
|
+
--radius-xl: calc(var(--radius) + 4px);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
:root {
|
|
51
|
+
--radius: 0.75rem;
|
|
52
|
+
--background: oklch(0.993 0.003 75);
|
|
53
|
+
--foreground: oklch(0.216 0.006 56.043);
|
|
54
|
+
--card: oklch(1 0.002 75);
|
|
55
|
+
--card-foreground: oklch(0.216 0.006 56.043);
|
|
56
|
+
--popover: oklch(1 0.002 75);
|
|
57
|
+
--popover-foreground: oklch(0.216 0.006 56.043);
|
|
58
|
+
--primary: oklch(0.705 0.168 41.3);
|
|
59
|
+
--primary-foreground: oklch(0.995 0.001 75);
|
|
60
|
+
--secondary: oklch(0.96 0.007 67);
|
|
61
|
+
--secondary-foreground: oklch(0.29 0.008 56);
|
|
62
|
+
--muted: oklch(0.96 0.007 67);
|
|
63
|
+
--muted-foreground: oklch(0.553 0.013 58.071);
|
|
64
|
+
--accent: oklch(0.96 0.007 67);
|
|
65
|
+
--accent-foreground: oklch(0.29 0.008 56);
|
|
66
|
+
--destructive: oklch(0.577 0.245 27.325);
|
|
67
|
+
--destructive-foreground: oklch(0.577 0.245 27.325);
|
|
68
|
+
--border: oklch(0.923 0.008 67);
|
|
69
|
+
--input: oklch(0.923 0.008 67);
|
|
70
|
+
--ring: oklch(0.705 0.168 41.3);
|
|
71
|
+
--chart-1: oklch(0.705 0.168 41.3);
|
|
72
|
+
--chart-2: oklch(0.6 0.118 184.714);
|
|
73
|
+
--chart-3: oklch(0.398 0.07 227.392);
|
|
74
|
+
--chart-4: oklch(0.828 0.189 84.429);
|
|
75
|
+
--chart-5: oklch(0.769 0.188 70.08);
|
|
76
|
+
--sidebar: oklch(0.975 0.005 67);
|
|
77
|
+
--sidebar-foreground: oklch(0.216 0.006 56.043);
|
|
78
|
+
--sidebar-primary: oklch(0.705 0.168 41.3);
|
|
79
|
+
--sidebar-primary-foreground: oklch(0.995 0.001 75);
|
|
80
|
+
--sidebar-accent: oklch(0.945 0.01 67);
|
|
81
|
+
--sidebar-accent-foreground: oklch(0.29 0.008 56);
|
|
82
|
+
--sidebar-border: oklch(0.923 0.008 67);
|
|
83
|
+
--sidebar-ring: oklch(0.705 0.168 41.3);
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
.dark {
|
|
87
|
+
--background: oklch(0.155 0.008 49.25);
|
|
88
|
+
--foreground: oklch(0.96 0.007 67);
|
|
89
|
+
--card: oklch(0.185 0.008 49.25);
|
|
90
|
+
--card-foreground: oklch(0.96 0.007 67);
|
|
91
|
+
--popover: oklch(0.185 0.008 49.25);
|
|
92
|
+
--popover-foreground: oklch(0.96 0.007 67);
|
|
93
|
+
--primary: oklch(0.745 0.178 41.3);
|
|
94
|
+
--primary-foreground: oklch(0.155 0.008 49.25);
|
|
95
|
+
--secondary: oklch(0.25 0.008 49.25);
|
|
96
|
+
--secondary-foreground: oklch(0.96 0.007 67);
|
|
97
|
+
--muted: oklch(0.25 0.008 49.25);
|
|
98
|
+
--muted-foreground: oklch(0.64 0.013 58.071);
|
|
99
|
+
--accent: oklch(0.25 0.008 49.25);
|
|
100
|
+
--accent-foreground: oklch(0.96 0.007 67);
|
|
101
|
+
--destructive: oklch(0.704 0.191 22.216);
|
|
102
|
+
--destructive-foreground: oklch(0.704 0.191 22.216);
|
|
103
|
+
--border: oklch(0.28 0.008 49.25);
|
|
104
|
+
--input: oklch(0.28 0.008 49.25);
|
|
105
|
+
--ring: oklch(0.745 0.178 41.3);
|
|
106
|
+
--chart-1: oklch(0.745 0.178 41.3);
|
|
107
|
+
--chart-2: oklch(0.696 0.17 162.48);
|
|
108
|
+
--chart-3: oklch(0.769 0.188 70.08);
|
|
109
|
+
--chart-4: oklch(0.627 0.265 303.9);
|
|
110
|
+
--chart-5: oklch(0.645 0.246 16.439);
|
|
111
|
+
--sidebar: oklch(0.165 0.008 49.25);
|
|
112
|
+
--sidebar-foreground: oklch(0.96 0.007 67);
|
|
113
|
+
--sidebar-primary: oklch(0.745 0.178 41.3);
|
|
114
|
+
--sidebar-primary-foreground: oklch(0.155 0.008 49.25);
|
|
115
|
+
--sidebar-accent: oklch(0.25 0.008 49.25);
|
|
116
|
+
--sidebar-accent-foreground: oklch(0.96 0.007 67);
|
|
117
|
+
--sidebar-border: oklch(0.28 0.008 49.25);
|
|
118
|
+
--sidebar-ring: oklch(0.745 0.178 41.3);
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
@layer base {
|
|
122
|
+
* {
|
|
123
|
+
@apply border-border;
|
|
124
|
+
}
|
|
125
|
+
body {
|
|
126
|
+
@apply bg-background text-foreground;
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
@keyframes fadeUp {
|
|
131
|
+
from { opacity: 0; transform: translateY(12px); }
|
|
132
|
+
to { opacity: 1; transform: translateY(0); }
|
|
133
|
+
}
|
|
134
|
+
.animate-fade-up { animation: fadeUp 0.5s ease-out; }
|
|
@@ -6,7 +6,8 @@ const props = defineProps<{
|
|
|
6
6
|
initialData?: Record<string, any>
|
|
7
7
|
}>()
|
|
8
8
|
|
|
9
|
-
|
|
9
|
+
declare global { interface Window { __MANTIQ_DATA__?: Record<string, any> } }
|
|
10
|
+
const windowData = typeof window !== 'undefined' ? window.__MANTIQ_DATA__ ?? {} : {}
|
|
10
11
|
const initial = props.initialData ?? windowData
|
|
11
12
|
|
|
12
13
|
const currentPage = ref<string>(initial._page ?? 'Login')
|
|
@@ -1,17 +1,41 @@
|
|
|
1
|
-
|
|
2
|
-
const
|
|
1
|
+
function getCookie(name: string): string | null {
|
|
2
|
+
const match = document.cookie.match(new RegExp(`(?:^|;\\s*)${name}=([^;]*)`))
|
|
3
|
+
return match ? decodeURIComponent(match[1]!) : null
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
type ApiResult<T> = { ok: true; status: number; data: T } | { ok: false; status: number; data: any }
|
|
7
|
+
|
|
8
|
+
export async function api<T = any>(url: string, opts: RequestInit = {}): Promise<ApiResult<T>> {
|
|
9
|
+
const headers: Record<string, string> = { Accept: 'application/json', ...(opts.headers as Record<string, string>) }
|
|
10
|
+
|
|
11
|
+
// Attach XSRF token for CSRF protection on mutating requests
|
|
12
|
+
if (opts.method && !['GET', 'HEAD', 'OPTIONS'].includes(opts.method)) {
|
|
13
|
+
const xsrf = getCookie('XSRF-TOKEN')
|
|
14
|
+
if (xsrf) headers['X-XSRF-TOKEN'] = xsrf
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
const res = await fetch(url, { ...opts, credentials: 'same-origin', headers })
|
|
3
18
|
|
|
4
19
|
// Session expired — redirect to login
|
|
5
20
|
if (res.status === 401 || res.status === 419) {
|
|
6
21
|
window.location.href = '/login'
|
|
7
|
-
return { ok: false, status: res.status, data: null
|
|
22
|
+
return { ok: false, status: res.status, data: null }
|
|
8
23
|
}
|
|
9
24
|
|
|
10
25
|
const ct = res.headers.get('content-type') ?? ''
|
|
11
26
|
const data = ct.includes('json') ? await res.json() : null
|
|
12
|
-
return { ok:
|
|
27
|
+
if (!res.ok) return { ok: false, status: res.status, data }
|
|
28
|
+
return { ok: true, status: res.status, data }
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export function post<T = any>(url: string, body: object) {
|
|
32
|
+
return api<T>(url, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(body) })
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export function put<T = any>(url: string, body: object) {
|
|
36
|
+
return api<T>(url, { method: 'PUT', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(body) })
|
|
13
37
|
}
|
|
14
38
|
|
|
15
|
-
export function
|
|
16
|
-
return api(url, { method: '
|
|
39
|
+
export function del<T = any>(url: string) {
|
|
40
|
+
return api<T>(url, { method: 'DELETE' })
|
|
17
41
|
}
|
|
@@ -1,10 +1,12 @@
|
|
|
1
|
+
declare global { interface Window { __MANTIQ_DATA__?: Record<string, any> } }
|
|
2
|
+
|
|
1
3
|
import './style.css'
|
|
2
4
|
import { createApp, createSSRApp } from 'vue'
|
|
3
5
|
import App from './App.vue'
|
|
4
6
|
import { pages } from './pages.ts'
|
|
5
7
|
|
|
6
8
|
const root = document.getElementById('app')!
|
|
7
|
-
const data =
|
|
9
|
+
const data = window.__MANTIQ_DATA__ ?? {}
|
|
8
10
|
|
|
9
11
|
const app = root.innerHTML.trim()
|
|
10
12
|
? createSSRApp(App, { pages, initialData: data })
|
|
@@ -12,8 +12,8 @@ const props = withDefaults(defineProps<{
|
|
|
12
12
|
appName: 'Mantiq',
|
|
13
13
|
})
|
|
14
14
|
|
|
15
|
-
const email = ref('
|
|
16
|
-
const password = ref('
|
|
15
|
+
const email = ref('')
|
|
16
|
+
const password = ref('')
|
|
17
17
|
const error = ref('')
|
|
18
18
|
const loading = ref(false)
|
|
19
19
|
|
|
@@ -82,7 +82,7 @@ async function handleSubmit() {
|
|
|
82
82
|
v-model="email"
|
|
83
83
|
type="email"
|
|
84
84
|
required
|
|
85
|
-
placeholder="
|
|
85
|
+
placeholder="you@example.com"
|
|
86
86
|
autocomplete="email"
|
|
87
87
|
/>
|
|
88
88
|
</div>
|
|
@@ -2,9 +2,28 @@ import { defineConfig } from 'vite'
|
|
|
2
2
|
import vue from '@vitejs/plugin-vue'
|
|
3
3
|
import tailwindcss from '@tailwindcss/vite'
|
|
4
4
|
import path from 'path'
|
|
5
|
+
import { writeFileSync, unlinkSync } from 'node:fs'
|
|
5
6
|
|
|
6
7
|
export default defineConfig({
|
|
7
|
-
plugins: [
|
|
8
|
+
plugins: [
|
|
9
|
+
vue(),
|
|
10
|
+
tailwindcss(),
|
|
11
|
+
{
|
|
12
|
+
name: 'mantiq-hot',
|
|
13
|
+
configureServer(server) {
|
|
14
|
+
const hotPath = path.resolve(__dirname, 'public/hot')
|
|
15
|
+
server.httpServer?.once('listening', () => {
|
|
16
|
+
const addr = server.httpServer!.address()
|
|
17
|
+
const url = typeof addr === 'string' ? addr : `http://localhost:${addr?.port}`
|
|
18
|
+
writeFileSync(hotPath, url)
|
|
19
|
+
})
|
|
20
|
+
const cleanup = () => { try { unlinkSync(hotPath) } catch {} }
|
|
21
|
+
process.on('exit', cleanup)
|
|
22
|
+
process.on('SIGINT', () => { cleanup(); process.exit() })
|
|
23
|
+
process.on('SIGTERM', () => { cleanup(); process.exit() })
|
|
24
|
+
},
|
|
25
|
+
},
|
|
26
|
+
],
|
|
8
27
|
publicDir: false,
|
|
9
28
|
resolve: {
|
|
10
29
|
alias: {
|