create-better-t-stack 3.12.5 → 3.12.7-pr780.f6206a0
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/dist/cli.mjs +1 -1
- package/dist/index.d.mts +13 -53
- package/dist/index.mjs +1 -1
- package/dist/{src-D3EHRs6z.mjs → src-CvDgQFF4.mjs} +711 -304
- package/package.json +3 -2
- package/templates/api/orpc/web/react/base/src/utils/orpc.ts.hbs +3 -3
- package/templates/auth/better-auth/convex/native/uniwind/components/sign-in.tsx.hbs +32 -50
- package/templates/auth/better-auth/convex/native/uniwind/components/sign-up.tsx.hbs +40 -57
- package/templates/auth/better-auth/native/uniwind/components/sign-in.tsx.hbs +32 -35
- package/templates/auth/better-auth/native/uniwind/components/sign-up.tsx.hbs +49 -37
- package/templates/auth/better-auth/web/nuxt/app/components/SignInForm.vue.hbs +40 -35
- package/templates/auth/better-auth/web/nuxt/app/components/SignUpForm.vue.hbs +47 -40
- package/templates/auth/better-auth/web/nuxt/app/middleware/auth.ts.hbs +9 -7
- package/templates/auth/better-auth/web/nuxt/app/pages/dashboard.vue.hbs +61 -29
- package/templates/auth/better-auth/web/nuxt/app/pages/login.vue.hbs +6 -3
- package/templates/backend/server/elysia/src/index.ts.hbs +8 -3
- package/templates/backend/server/express/src/index.ts.hbs +8 -3
- package/templates/backend/server/fastify/src/index.ts.hbs +8 -3
- package/templates/backend/server/hono/src/index.ts.hbs +16 -6
- package/templates/examples/ai/fullstack/next/src/app/api/ai/route.ts.hbs +8 -3
- package/templates/examples/ai/fullstack/tanstack-start/src/routes/api/ai/$.ts.hbs +8 -3
- package/templates/examples/ai/native/bare/polyfills.js +14 -11
- package/templates/examples/ai/native/uniwind/app/(drawer)/ai.tsx.hbs +104 -124
- package/templates/examples/ai/web/nuxt/app/pages/ai.vue.hbs +22 -22
- package/templates/examples/todo/native/uniwind/app/(drawer)/todos.tsx.hbs +67 -80
- package/templates/examples/todo/web/nuxt/app/pages/todos.vue.hbs +115 -90
- package/templates/frontend/native/uniwind/app/(drawer)/index.tsx.hbs +60 -54
- package/templates/frontend/native/uniwind/app/+not-found.tsx.hbs +16 -21
- package/templates/frontend/native/uniwind/app/modal.tsx.hbs +18 -34
- package/templates/frontend/native/uniwind/package.json.hbs +10 -10
- package/templates/frontend/nuxt/app/components/Header.vue.hbs +25 -29
- package/templates/frontend/nuxt/app/layouts/default.vue.hbs +7 -8
- package/templates/frontend/nuxt/app/pages/index.vue.hbs +58 -39
- package/templates/frontend/nuxt/package.json.hbs +1 -1
- package/templates/frontend/react/tanstack-router/package.json.hbs +1 -1
- package/templates/frontend/react/web-base/src/components/ui/skeleton.tsx.hbs +5 -5
- package/templates/frontend/nuxt/app/components/Loader.vue.hbs +0 -5
- package/templates/frontend/nuxt/app/components/ModeToggle.vue.hbs +0 -25
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "create-better-t-stack",
|
|
3
|
-
"version": "3.12.
|
|
3
|
+
"version": "3.12.7-pr780.f6206a0",
|
|
4
4
|
"description": "A modern CLI tool for scaffolding end-to-end type-safe TypeScript projects with best practices and customizable configurations",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"better-auth",
|
|
@@ -67,7 +67,8 @@
|
|
|
67
67
|
"prepublishOnly": "npm run build"
|
|
68
68
|
},
|
|
69
69
|
"dependencies": {
|
|
70
|
-
"@better-t-stack/types": "
|
|
70
|
+
"@better-t-stack/types": "3.12.7-pr780.f6206a0",
|
|
71
|
+
"@clack/core": "^0.5.0",
|
|
71
72
|
"@clack/prompts": "^1.0.0-alpha.8",
|
|
72
73
|
"@orpc/server": "^1.13.0",
|
|
73
74
|
"consola": "^3.4.2",
|
|
@@ -11,7 +11,7 @@ import { appRouter } from "@{{projectName}}/api/routers/index";
|
|
|
11
11
|
import { createContext } from "@{{projectName}}/api/context";
|
|
12
12
|
{{else if (includes frontend "tanstack-start")}}
|
|
13
13
|
import type { RouterClient } from "@orpc/server";
|
|
14
|
-
import {
|
|
14
|
+
import type { AppRouter } from "@{{projectName}}/api/routers/index";
|
|
15
15
|
import { env } from "@{{projectName}}/env/web";
|
|
16
16
|
{{else}}
|
|
17
17
|
import type { AppRouterClient } from "@{{projectName}}/api/routers/index";
|
|
@@ -73,10 +73,10 @@ const link = new RPCLink({
|
|
|
73
73
|
});
|
|
74
74
|
|
|
75
75
|
const getORPCClient = () => {
|
|
76
|
-
return createORPCClient(link) as RouterClient<
|
|
76
|
+
return createORPCClient(link) as RouterClient<AppRouter>;
|
|
77
77
|
};
|
|
78
78
|
|
|
79
|
-
export const client: RouterClient<
|
|
79
|
+
export const client: RouterClient<AppRouter> = getORPCClient();
|
|
80
80
|
{{else}}
|
|
81
81
|
export const link = new RPCLink({
|
|
82
82
|
{{#if (and (eq backend "self") (includes frontend "next"))}}
|
|
@@ -1,13 +1,7 @@
|
|
|
1
1
|
import { authClient } from "@/lib/auth-client";
|
|
2
2
|
import { useState } from "react";
|
|
3
|
-
import {
|
|
4
|
-
|
|
5
|
-
Text,
|
|
6
|
-
TextInput,
|
|
7
|
-
Pressable,
|
|
8
|
-
View,
|
|
9
|
-
} from "react-native";
|
|
10
|
-
import { Card, useThemeColor } from "heroui-native";
|
|
3
|
+
import { Text, View } from "react-native";
|
|
4
|
+
import { Button, ErrorView, Spinner, Surface, TextField } from "heroui-native";
|
|
11
5
|
|
|
12
6
|
export function SignIn() {
|
|
13
7
|
const [email, setEmail] = useState("");
|
|
@@ -15,11 +9,6 @@ export function SignIn() {
|
|
|
15
9
|
const [isLoading, setIsLoading] = useState(false);
|
|
16
10
|
const [error, setError] = useState<string | null>(null);
|
|
17
11
|
|
|
18
|
-
const mutedColor = useThemeColor("muted");
|
|
19
|
-
const accentColor = useThemeColor("accent");
|
|
20
|
-
const foregroundColor = useThemeColor("foreground");
|
|
21
|
-
const dangerColor = useThemeColor("danger");
|
|
22
|
-
|
|
23
12
|
const handleLogin = async () => {
|
|
24
13
|
setIsLoading(true);
|
|
25
14
|
setError(null);
|
|
@@ -46,46 +35,39 @@ export function SignIn() {
|
|
|
46
35
|
};
|
|
47
36
|
|
|
48
37
|
return (
|
|
49
|
-
<
|
|
50
|
-
<
|
|
38
|
+
<Surface variant="secondary" className="p-4 rounded-lg">
|
|
39
|
+
<Text className="text-foreground font-medium mb-4">Sign In</Text>
|
|
51
40
|
|
|
52
|
-
{error
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
</View>
|
|
56
|
-
)}
|
|
41
|
+
<ErrorView isInvalid={!!error} className="mb-3">
|
|
42
|
+
{error}
|
|
43
|
+
</ErrorView>
|
|
57
44
|
|
|
58
|
-
<
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
45
|
+
<View className="gap-3">
|
|
46
|
+
<TextField>
|
|
47
|
+
<TextField.Label>Email</TextField.Label>
|
|
48
|
+
<TextField.Input
|
|
49
|
+
value={email}
|
|
50
|
+
onChangeText={setEmail}
|
|
51
|
+
placeholder="email@example.com"
|
|
52
|
+
keyboardType="email-address"
|
|
53
|
+
autoCapitalize="none"
|
|
54
|
+
/>
|
|
55
|
+
</TextField>
|
|
67
56
|
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
57
|
+
<TextField>
|
|
58
|
+
<TextField.Label>Password</TextField.Label>
|
|
59
|
+
<TextField.Input
|
|
60
|
+
value={password}
|
|
61
|
+
onChangeText={setPassword}
|
|
62
|
+
placeholder="••••••••"
|
|
63
|
+
secureTextEntry
|
|
64
|
+
/>
|
|
65
|
+
</TextField>
|
|
76
66
|
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
{isLoading ? (
|
|
83
|
-
<ActivityIndicator size="small" color={foregroundColor} />
|
|
84
|
-
) : (
|
|
85
|
-
<Text className="text-foreground font-medium">Sign In</Text>
|
|
86
|
-
)}
|
|
87
|
-
</Pressable>
|
|
88
|
-
</Card>
|
|
67
|
+
<Button onPress={handleLogin} isDisabled={isLoading} className="mt-1">
|
|
68
|
+
{isLoading ? <Spinner size="sm" color="default" /> : <Button.Label>Sign In</Button.Label>}
|
|
69
|
+
</Button>
|
|
70
|
+
</View>
|
|
71
|
+
</Surface>
|
|
89
72
|
);
|
|
90
73
|
}
|
|
91
|
-
|
|
@@ -1,13 +1,7 @@
|
|
|
1
1
|
import { authClient } from "@/lib/auth-client";
|
|
2
2
|
import { useState } from "react";
|
|
3
|
-
import {
|
|
4
|
-
|
|
5
|
-
Text,
|
|
6
|
-
TextInput,
|
|
7
|
-
Pressable,
|
|
8
|
-
View,
|
|
9
|
-
} from "react-native";
|
|
10
|
-
import { Card, useThemeColor } from "heroui-native";
|
|
3
|
+
import { Text, View } from "react-native";
|
|
4
|
+
import { Button, ErrorView, Spinner, Surface, TextField } from "heroui-native";
|
|
11
5
|
|
|
12
6
|
export function SignUp() {
|
|
13
7
|
const [name, setName] = useState("");
|
|
@@ -16,11 +10,6 @@ export function SignUp() {
|
|
|
16
10
|
const [isLoading, setIsLoading] = useState(false);
|
|
17
11
|
const [error, setError] = useState<string | null>(null);
|
|
18
12
|
|
|
19
|
-
const mutedColor = useThemeColor("muted");
|
|
20
|
-
const accentColor = useThemeColor("accent");
|
|
21
|
-
const foregroundColor = useThemeColor("foreground");
|
|
22
|
-
const dangerColor = useThemeColor("danger");
|
|
23
|
-
|
|
24
13
|
const handleSignUp = async () => {
|
|
25
14
|
setIsLoading(true);
|
|
26
15
|
setError(null);
|
|
@@ -49,54 +38,48 @@ export function SignUp() {
|
|
|
49
38
|
};
|
|
50
39
|
|
|
51
40
|
return (
|
|
52
|
-
<
|
|
53
|
-
<
|
|
41
|
+
<Surface variant="secondary" className="p-4 rounded-lg">
|
|
42
|
+
<Text className="text-foreground font-medium mb-4">Create Account</Text>
|
|
54
43
|
|
|
55
|
-
{error
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
</View>
|
|
59
|
-
)}
|
|
44
|
+
<ErrorView isInvalid={!!error} className="mb-3">
|
|
45
|
+
{error}
|
|
46
|
+
</ErrorView>
|
|
60
47
|
|
|
61
|
-
<
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
placeholderTextColor={mutedColor}
|
|
67
|
-
/>
|
|
48
|
+
<View className="gap-3">
|
|
49
|
+
<TextField>
|
|
50
|
+
<TextField.Label>Name</TextField.Label>
|
|
51
|
+
<TextField.Input value={name} onChangeText={setName} placeholder="John Doe" />
|
|
52
|
+
</TextField>
|
|
68
53
|
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
54
|
+
<TextField>
|
|
55
|
+
<TextField.Label>Email</TextField.Label>
|
|
56
|
+
<TextField.Input
|
|
57
|
+
value={email}
|
|
58
|
+
onChangeText={setEmail}
|
|
59
|
+
placeholder="email@example.com"
|
|
60
|
+
keyboardType="email-address"
|
|
61
|
+
autoCapitalize="none"
|
|
62
|
+
/>
|
|
63
|
+
</TextField>
|
|
78
64
|
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
65
|
+
<TextField>
|
|
66
|
+
<TextField.Label>Password</TextField.Label>
|
|
67
|
+
<TextField.Input
|
|
68
|
+
value={password}
|
|
69
|
+
onChangeText={setPassword}
|
|
70
|
+
placeholder="••••••••"
|
|
71
|
+
secureTextEntry
|
|
72
|
+
/>
|
|
73
|
+
</TextField>
|
|
87
74
|
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
)}
|
|
98
|
-
</Pressable>
|
|
99
|
-
</Card>
|
|
75
|
+
<Button onPress={handleSignUp} isDisabled={isLoading} className="mt-1">
|
|
76
|
+
{isLoading ? (
|
|
77
|
+
<Spinner size="sm" color="default" />
|
|
78
|
+
) : (
|
|
79
|
+
<Button.Label>Create Account</Button.Label>
|
|
80
|
+
)}
|
|
81
|
+
</Button>
|
|
82
|
+
</View>
|
|
83
|
+
</Surface>
|
|
100
84
|
);
|
|
101
85
|
}
|
|
102
|
-
|
|
@@ -6,14 +6,8 @@ import { queryClient } from "@/utils/trpc";
|
|
|
6
6
|
import { queryClient } from "@/utils/orpc";
|
|
7
7
|
{{/if}}
|
|
8
8
|
import { useState } from "react";
|
|
9
|
-
import {
|
|
10
|
-
|
|
11
|
-
Text,
|
|
12
|
-
TextInput,
|
|
13
|
-
Pressable,
|
|
14
|
-
View,
|
|
15
|
-
} from "react-native";
|
|
16
|
-
import { Card, useThemeColor } from "heroui-native";
|
|
9
|
+
import { Text, View } from "react-native";
|
|
10
|
+
import { Button, ErrorView, Spinner, Surface, TextField } from "heroui-native";
|
|
17
11
|
|
|
18
12
|
function SignIn() {
|
|
19
13
|
const [email, setEmail] = useState("");
|
|
@@ -21,11 +15,6 @@ const [password, setPassword] = useState("");
|
|
|
21
15
|
const [isLoading, setIsLoading] = useState(false);
|
|
22
16
|
const [error, setError] = useState<string | null>(null);
|
|
23
17
|
|
|
24
|
-
const mutedColor = useThemeColor("muted");
|
|
25
|
-
const accentColor = useThemeColor("accent");
|
|
26
|
-
const foregroundColor = useThemeColor("foreground");
|
|
27
|
-
const dangerColor = useThemeColor("danger");
|
|
28
|
-
|
|
29
18
|
async function handleLogin() {
|
|
30
19
|
setIsLoading(true);
|
|
31
20
|
setError(null);
|
|
@@ -58,32 +47,40 @@ const [error, setError] = useState<string | null>(null);
|
|
|
58
47
|
}
|
|
59
48
|
|
|
60
49
|
return (
|
|
61
|
-
<
|
|
62
|
-
<
|
|
50
|
+
<Surface variant="secondary" className="p-4 rounded-lg">
|
|
51
|
+
<Text className="text-foreground font-medium mb-4">Sign In</Text>
|
|
63
52
|
|
|
64
|
-
{error
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
</View>
|
|
68
|
-
) : null}
|
|
53
|
+
<ErrorView isInvalid={!!error} className="mb-3">
|
|
54
|
+
{error}
|
|
55
|
+
</ErrorView>
|
|
69
56
|
|
|
70
|
-
<
|
|
71
|
-
|
|
72
|
-
|
|
57
|
+
<View className="gap-3">
|
|
58
|
+
<TextField>
|
|
59
|
+
<TextField.Label>Email</TextField.Label>
|
|
60
|
+
<TextField.Input
|
|
61
|
+
value={email}
|
|
62
|
+
onChangeText={setEmail}
|
|
63
|
+
placeholder="email@example.com"
|
|
64
|
+
keyboardType="email-address"
|
|
65
|
+
autoCapitalize="none"
|
|
66
|
+
/>
|
|
67
|
+
</TextField>
|
|
73
68
|
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
69
|
+
<TextField>
|
|
70
|
+
<TextField.Label>Password</TextField.Label>
|
|
71
|
+
<TextField.Input
|
|
72
|
+
value={password}
|
|
73
|
+
onChangeText={setPassword}
|
|
74
|
+
placeholder="••••••••"
|
|
75
|
+
secureTextEntry
|
|
76
|
+
/>
|
|
77
|
+
</TextField>
|
|
77
78
|
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
<Text className="text-foreground font-medium">Sign In</Text>
|
|
84
|
-
)}
|
|
85
|
-
</Pressable>
|
|
86
|
-
</Card>
|
|
79
|
+
<Button onPress={handleLogin} isDisabled={isLoading} className="mt-1">
|
|
80
|
+
{isLoading ? <Spinner size="sm" color="default" /> : <Button.Label>Sign In</Button.Label>}
|
|
81
|
+
</Button>
|
|
82
|
+
</View>
|
|
83
|
+
</Surface>
|
|
87
84
|
);
|
|
88
85
|
}
|
|
89
86
|
|
|
@@ -6,14 +6,8 @@ import { queryClient } from "@/utils/trpc";
|
|
|
6
6
|
import { queryClient } from "@/utils/orpc";
|
|
7
7
|
{{/if}}
|
|
8
8
|
import { useState } from "react";
|
|
9
|
-
import {
|
|
10
|
-
|
|
11
|
-
Text,
|
|
12
|
-
TextInput,
|
|
13
|
-
Pressable,
|
|
14
|
-
View,
|
|
15
|
-
} from "react-native";
|
|
16
|
-
import { Card, useThemeColor } from "heroui-native";
|
|
9
|
+
import { Text, View } from "react-native";
|
|
10
|
+
import { Button, ErrorView, Spinner, Surface, TextField } from "heroui-native";
|
|
17
11
|
|
|
18
12
|
function signUpHandler({
|
|
19
13
|
name,
|
|
@@ -24,6 +18,15 @@ setIsLoading,
|
|
|
24
18
|
setName,
|
|
25
19
|
setEmail,
|
|
26
20
|
setPassword,
|
|
21
|
+
}: {
|
|
22
|
+
name: string;
|
|
23
|
+
email: string;
|
|
24
|
+
password: string;
|
|
25
|
+
setError: (error: string | null) => void;
|
|
26
|
+
setIsLoading: (loading: boolean) => void;
|
|
27
|
+
setName: (name: string) => void;
|
|
28
|
+
setEmail: (email: string) => void;
|
|
29
|
+
setPassword: (password: string) => void;
|
|
27
30
|
}) {
|
|
28
31
|
setIsLoading(true);
|
|
29
32
|
setError(null);
|
|
@@ -64,11 +67,6 @@ const [password, setPassword] = useState("");
|
|
|
64
67
|
const [isLoading, setIsLoading] = useState(false);
|
|
65
68
|
const [error, setError] = useState<string | null>(null);
|
|
66
69
|
|
|
67
|
-
const mutedColor = useThemeColor("muted");
|
|
68
|
-
const accentColor = useThemeColor("accent");
|
|
69
|
-
const foregroundColor = useThemeColor("foreground");
|
|
70
|
-
const dangerColor = useThemeColor("danger");
|
|
71
|
-
|
|
72
70
|
function handlePress() {
|
|
73
71
|
signUpHandler({
|
|
74
72
|
name,
|
|
@@ -83,34 +81,48 @@ const [error, setError] = useState<string | null>(null);
|
|
|
83
81
|
}
|
|
84
82
|
|
|
85
83
|
return (
|
|
86
|
-
<
|
|
87
|
-
<
|
|
84
|
+
<Surface variant="secondary" className="p-4 rounded-lg">
|
|
85
|
+
<Text className="text-foreground font-medium mb-4">Create Account</Text>
|
|
88
86
|
|
|
89
|
-
{error
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
</View>
|
|
93
|
-
)}
|
|
87
|
+
<ErrorView isInvalid={!!error} className="mb-3">
|
|
88
|
+
{error}
|
|
89
|
+
</ErrorView>
|
|
94
90
|
|
|
95
|
-
<
|
|
96
|
-
|
|
91
|
+
<View className="gap-3">
|
|
92
|
+
<TextField>
|
|
93
|
+
<TextField.Label>Name</TextField.Label>
|
|
94
|
+
<TextField.Input value={name} onChangeText={setName} placeholder="John Doe" />
|
|
95
|
+
</TextField>
|
|
97
96
|
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
97
|
+
<TextField>
|
|
98
|
+
<TextField.Label>Email</TextField.Label>
|
|
99
|
+
<TextField.Input
|
|
100
|
+
value={email}
|
|
101
|
+
onChangeText={setEmail}
|
|
102
|
+
placeholder="email@example.com"
|
|
103
|
+
keyboardType="email-address"
|
|
104
|
+
autoCapitalize="none"
|
|
105
|
+
/>
|
|
106
|
+
</TextField>
|
|
101
107
|
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
108
|
+
<TextField>
|
|
109
|
+
<TextField.Label>Password</TextField.Label>
|
|
110
|
+
<TextField.Input
|
|
111
|
+
value={password}
|
|
112
|
+
onChangeText={setPassword}
|
|
113
|
+
placeholder="••••••••"
|
|
114
|
+
secureTextEntry
|
|
115
|
+
/>
|
|
116
|
+
</TextField>
|
|
105
117
|
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
</
|
|
114
|
-
</
|
|
118
|
+
<Button onPress={handlePress} isDisabled={isLoading} className="mt-1">
|
|
119
|
+
{isLoading ? (
|
|
120
|
+
<Spinner size="sm" color="default" />
|
|
121
|
+
) : (
|
|
122
|
+
<Button.Label>Create Account</Button.Label>
|
|
123
|
+
)}
|
|
124
|
+
</Button>
|
|
125
|
+
</View>
|
|
126
|
+
</Surface>
|
|
115
127
|
);
|
|
116
128
|
}
|
|
@@ -1,13 +1,31 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
|
-
import z from 'zod'
|
|
3
|
-
|
|
4
|
-
|
|
2
|
+
import * as z from 'zod'
|
|
3
|
+
import type { FormSubmitEvent, AuthFormField } from '@nuxt/ui'
|
|
4
|
+
|
|
5
|
+
const { $authClient } = useNuxtApp()
|
|
5
6
|
|
|
6
7
|
const emit = defineEmits(['switchToSignUp'])
|
|
7
8
|
|
|
8
9
|
const toast = useToast()
|
|
9
10
|
const loading = ref(false)
|
|
10
11
|
|
|
12
|
+
const fields: AuthFormField[] = [
|
|
13
|
+
{
|
|
14
|
+
name: 'email',
|
|
15
|
+
type: 'email',
|
|
16
|
+
label: 'Email',
|
|
17
|
+
placeholder: 'Enter your email',
|
|
18
|
+
required: true
|
|
19
|
+
},
|
|
20
|
+
{
|
|
21
|
+
name: 'password',
|
|
22
|
+
type: 'password',
|
|
23
|
+
label: 'Password',
|
|
24
|
+
placeholder: 'Enter your password',
|
|
25
|
+
required: true
|
|
26
|
+
}
|
|
27
|
+
]
|
|
28
|
+
|
|
11
29
|
const schema = z.object({
|
|
12
30
|
email: z.email('Invalid email address'),
|
|
13
31
|
password: z.string().min(8, 'Password must be at least 8 characters'),
|
|
@@ -15,12 +33,7 @@ const schema = z.object({
|
|
|
15
33
|
|
|
16
34
|
type Schema = z.output<typeof schema>
|
|
17
35
|
|
|
18
|
-
|
|
19
|
-
email: '',
|
|
20
|
-
password: '',
|
|
21
|
-
})
|
|
22
|
-
|
|
23
|
-
async function onSubmit (event: FormSubmitEvent<Schema>) {
|
|
36
|
+
async function onSubmit(event: FormSubmitEvent<Schema>) {
|
|
24
37
|
loading.value = true
|
|
25
38
|
try {
|
|
26
39
|
await $authClient.signIn.email(
|
|
@@ -39,7 +52,7 @@ async function onSubmit (event: FormSubmitEvent<Schema>) {
|
|
|
39
52
|
},
|
|
40
53
|
)
|
|
41
54
|
} catch (error: any) {
|
|
42
|
-
|
|
55
|
+
toast.add({ title: 'An unexpected error occurred', description: error.message || 'Please try again.' })
|
|
43
56
|
} finally {
|
|
44
57
|
loading.value = false
|
|
45
58
|
}
|
|
@@ -47,31 +60,23 @@ async function onSubmit (event: FormSubmitEvent<Schema>) {
|
|
|
47
60
|
</script>
|
|
48
61
|
|
|
49
62
|
<template>
|
|
50
|
-
<div class="
|
|
51
|
-
<
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
<UInput v-model="state.password" type="password" class="w-full" />
|
|
60
|
-
</UFormField>
|
|
61
|
-
|
|
62
|
-
<UButton type="submit" block :loading="loading">
|
|
63
|
-
Sign In
|
|
64
|
-
</UButton>
|
|
65
|
-
</UForm>
|
|
66
|
-
|
|
67
|
-
<div class="mt-4 text-center">
|
|
68
|
-
<UButton
|
|
69
|
-
variant="link"
|
|
70
|
-
@click="$emit('switchToSignUp')"
|
|
71
|
-
class="text-primary hover:text-primary-dark"
|
|
63
|
+
<div class="flex flex-col items-center justify-center gap-4 p-4">
|
|
64
|
+
<UPageCard class="w-full max-w-md">
|
|
65
|
+
<UAuthForm
|
|
66
|
+
:schema="schema"
|
|
67
|
+
:fields="fields"
|
|
68
|
+
title="Welcome Back"
|
|
69
|
+
icon="i-lucide-log-in"
|
|
70
|
+
:submit="{ label: 'Sign In', loading }"
|
|
71
|
+
@submit="onSubmit"
|
|
72
72
|
>
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
73
|
+
<template #description>
|
|
74
|
+
Need an account?
|
|
75
|
+
<ULink class="text-primary font-medium" @click="$emit('switchToSignUp')">
|
|
76
|
+
Sign Up
|
|
77
|
+
</ULink>
|
|
78
|
+
</template>
|
|
79
|
+
</UAuthForm>
|
|
80
|
+
</UPageCard>
|
|
76
81
|
</div>
|
|
77
82
|
</template>
|