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.
Files changed (38) hide show
  1. package/dist/cli.mjs +1 -1
  2. package/dist/index.d.mts +13 -53
  3. package/dist/index.mjs +1 -1
  4. package/dist/{src-D3EHRs6z.mjs → src-CvDgQFF4.mjs} +711 -304
  5. package/package.json +3 -2
  6. package/templates/api/orpc/web/react/base/src/utils/orpc.ts.hbs +3 -3
  7. package/templates/auth/better-auth/convex/native/uniwind/components/sign-in.tsx.hbs +32 -50
  8. package/templates/auth/better-auth/convex/native/uniwind/components/sign-up.tsx.hbs +40 -57
  9. package/templates/auth/better-auth/native/uniwind/components/sign-in.tsx.hbs +32 -35
  10. package/templates/auth/better-auth/native/uniwind/components/sign-up.tsx.hbs +49 -37
  11. package/templates/auth/better-auth/web/nuxt/app/components/SignInForm.vue.hbs +40 -35
  12. package/templates/auth/better-auth/web/nuxt/app/components/SignUpForm.vue.hbs +47 -40
  13. package/templates/auth/better-auth/web/nuxt/app/middleware/auth.ts.hbs +9 -7
  14. package/templates/auth/better-auth/web/nuxt/app/pages/dashboard.vue.hbs +61 -29
  15. package/templates/auth/better-auth/web/nuxt/app/pages/login.vue.hbs +6 -3
  16. package/templates/backend/server/elysia/src/index.ts.hbs +8 -3
  17. package/templates/backend/server/express/src/index.ts.hbs +8 -3
  18. package/templates/backend/server/fastify/src/index.ts.hbs +8 -3
  19. package/templates/backend/server/hono/src/index.ts.hbs +16 -6
  20. package/templates/examples/ai/fullstack/next/src/app/api/ai/route.ts.hbs +8 -3
  21. package/templates/examples/ai/fullstack/tanstack-start/src/routes/api/ai/$.ts.hbs +8 -3
  22. package/templates/examples/ai/native/bare/polyfills.js +14 -11
  23. package/templates/examples/ai/native/uniwind/app/(drawer)/ai.tsx.hbs +104 -124
  24. package/templates/examples/ai/web/nuxt/app/pages/ai.vue.hbs +22 -22
  25. package/templates/examples/todo/native/uniwind/app/(drawer)/todos.tsx.hbs +67 -80
  26. package/templates/examples/todo/web/nuxt/app/pages/todos.vue.hbs +115 -90
  27. package/templates/frontend/native/uniwind/app/(drawer)/index.tsx.hbs +60 -54
  28. package/templates/frontend/native/uniwind/app/+not-found.tsx.hbs +16 -21
  29. package/templates/frontend/native/uniwind/app/modal.tsx.hbs +18 -34
  30. package/templates/frontend/native/uniwind/package.json.hbs +10 -10
  31. package/templates/frontend/nuxt/app/components/Header.vue.hbs +25 -29
  32. package/templates/frontend/nuxt/app/layouts/default.vue.hbs +7 -8
  33. package/templates/frontend/nuxt/app/pages/index.vue.hbs +58 -39
  34. package/templates/frontend/nuxt/package.json.hbs +1 -1
  35. package/templates/frontend/react/tanstack-router/package.json.hbs +1 -1
  36. package/templates/frontend/react/web-base/src/components/ui/skeleton.tsx.hbs +5 -5
  37. package/templates/frontend/nuxt/app/components/Loader.vue.hbs +0 -5
  38. 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.5",
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": "^3.12.5",
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 { appRouter } from "@{{projectName}}/api/routers/index";
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<typeof appRouter>;
76
+ return createORPCClient(link) as RouterClient<AppRouter>;
77
77
  };
78
78
 
79
- export const client: RouterClient<typeof appRouter> = getORPCClient();
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
- ActivityIndicator,
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
- <Card variant="secondary" className="mt-6 p-4">
50
- <Card.Title className="mb-4">Sign In</Card.Title>
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
- <View className="mb-4 p-3 bg-danger/10 rounded-lg">
54
- <Text className="text-danger text-sm">{error}</Text>
55
- </View>
56
- )}
41
+ <ErrorView isInvalid={!!error} className="mb-3">
42
+ {error}
43
+ </ErrorView>
57
44
 
58
- <TextInput
59
- className="mb-3 py-3 px-4 rounded-lg bg-surface text-foreground border border-divider"
60
- placeholder="Email"
61
- value={email}
62
- onChangeText={setEmail}
63
- placeholderTextColor={mutedColor}
64
- keyboardType="email-address"
65
- autoCapitalize="none"
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
- <TextInput
69
- className="mb-4 py-3 px-4 rounded-lg bg-surface text-foreground border border-divider"
70
- placeholder="Password"
71
- value={password}
72
- onChangeText={setPassword}
73
- placeholderTextColor={mutedColor}
74
- secureTextEntry
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
- <Pressable
78
- onPress={handleLogin}
79
- disabled={isLoading}
80
- className="bg-accent p-4 rounded-lg flex-row justify-center items-center active:opacity-70"
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
- ActivityIndicator,
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
- <Card variant="secondary" className="mt-6 p-4">
53
- <Card.Title className="mb-4">Create Account</Card.Title>
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
- <View className="mb-4 p-3 bg-danger/10 rounded-lg">
57
- <Text className="text-danger text-sm">{error}</Text>
58
- </View>
59
- )}
44
+ <ErrorView isInvalid={!!error} className="mb-3">
45
+ {error}
46
+ </ErrorView>
60
47
 
61
- <TextInput
62
- className="mb-3 py-3 px-4 rounded-lg bg-surface text-foreground border border-divider"
63
- placeholder="Name"
64
- value={name}
65
- onChangeText={setName}
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
- <TextInput
70
- className="mb-3 py-3 px-4 rounded-lg bg-surface text-foreground border border-divider"
71
- placeholder="Email"
72
- value={email}
73
- onChangeText={setEmail}
74
- placeholderTextColor={mutedColor}
75
- keyboardType="email-address"
76
- autoCapitalize="none"
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
- <TextInput
80
- className="mb-4 py-3 px-4 rounded-lg bg-surface text-foreground border border-divider"
81
- placeholder="Password"
82
- value={password}
83
- onChangeText={setPassword}
84
- placeholderTextColor={mutedColor}
85
- secureTextEntry
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
- <Pressable
89
- onPress={handleSignUp}
90
- disabled={isLoading}
91
- className="bg-accent p-4 rounded-lg flex-row justify-center items-center active:opacity-70"
92
- >
93
- {isLoading ? (
94
- <ActivityIndicator size="small" color={foregroundColor} />
95
- ) : (
96
- <Text className="text-foreground font-medium">Sign Up</Text>
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
- ActivityIndicator,
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
- <Card variant="secondary" className="mt-6 p-4">
62
- <Card.Title className="mb-4">Sign In</Card.Title>
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
- <View className="mb-4 p-3 bg-danger/10 rounded-lg">
66
- <Text className="text-danger text-sm">{error}</Text>
67
- </View>
68
- ) : null}
53
+ <ErrorView isInvalid={!!error} className="mb-3">
54
+ {error}
55
+ </ErrorView>
69
56
 
70
- <TextInput className="mb-3 py-3 px-4 rounded-lg bg-surface text-foreground border border-divider"
71
- placeholder="Email" value={email} onChangeText={setEmail} placeholderTextColor={mutedColor}
72
- keyboardType="email-address" autoCapitalize="none" />
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
- <TextInput className="mb-4 py-3 px-4 rounded-lg bg-surface text-foreground border border-divider"
75
- placeholder="Password" value={password} onChangeText={setPassword} placeholderTextColor={mutedColor}
76
- secureTextEntry />
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
- <Pressable onPress={handleLogin} disabled={isLoading}
79
- className="bg-accent p-4 rounded-lg flex-row justify-center items-center active:opacity-70">
80
- {isLoading ? (
81
- <ActivityIndicator size="small" color={foregroundColor} />
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
- ActivityIndicator,
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
- <Card variant="secondary" className="mt-6 p-4">
87
- <Card.Title className="mb-4">Create Account</Card.Title>
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
- <View className="mb-4 p-3 bg-danger/10 rounded-lg">
91
- <Text className="text-danger text-sm">{error}</Text>
92
- </View>
93
- )}
87
+ <ErrorView isInvalid={!!error} className="mb-3">
88
+ {error}
89
+ </ErrorView>
94
90
 
95
- <TextInput className="mb-3 py-3 px-4 rounded-lg bg-surface text-foreground border border-divider" placeholder="Name"
96
- value={name} onChangeText={setName} placeholderTextColor={mutedColor} />
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
- <TextInput className="mb-3 py-3 px-4 rounded-lg bg-surface text-foreground border border-divider"
99
- placeholder="Email" value={email} onChangeText={setEmail} placeholderTextColor={mutedColor}
100
- keyboardType="email-address" autoCapitalize="none" />
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
- <TextInput className="mb-4 py-3 px-4 rounded-lg bg-surface text-foreground border border-divider"
103
- placeholder="Password" value={password} onChangeText={setPassword} placeholderTextColor={mutedColor}
104
- secureTextEntry />
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
- <Pressable onPress={handlePress} disabled={isLoading}
107
- className="bg-accent p-4 rounded-lg flex-row justify-center items-center active:opacity-70">
108
- {isLoading ? (
109
- <ActivityIndicator size="small" color={foregroundColor} />
110
- ) : (
111
- <Text className="text-foreground font-medium">Sign Up</Text>
112
- )}
113
- </Pressable>
114
- </Card>
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
- const {$authClient} = useNuxtApp()
4
- import type { FormSubmitEvent } from '#ui/types'
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
- const state = reactive({
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
- toast.add({ title: 'An unexpected error occurred', description: error.message || 'Please try again.' })
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="mx-auto w-full mt-10 max-w-md p-6">
51
- <h1 class="mb-6 text-center text-3xl font-bold">Welcome Back</h1>
52
-
53
- <UForm :schema="schema" :state="state" class="space-y-4" @submit="onSubmit">
54
- <UFormField label="Email" name="email">
55
- <UInput v-model="state.email" type="email" class="w-full" />
56
- </UFormField>
57
-
58
- <UFormField label="Password" name="password">
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
- Need an account? Sign Up
74
- </UButton>
75
- </div>
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>