create-better-t-stack 3.12.5 → 3.12.6
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.mjs +1 -1
- package/dist/{src-D3EHRs6z.mjs → src-Ci2sRCrb.mjs} +31 -20
- package/package.json +2 -2
- 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/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/dist/cli.mjs
CHANGED
package/dist/index.mjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { a as router, i as docs, n as create, o as sponsors, r as createBtsCli, t as builder } from "./src-
|
|
2
|
+
import { a as router, i as docs, n as create, o as sponsors, r as createBtsCli, t as builder } from "./src-Ci2sRCrb.mjs";
|
|
3
3
|
|
|
4
4
|
export { builder, create, createBtsCli, docs, router, sponsors };
|
|
@@ -116,13 +116,14 @@ const dependencyVersionMap = {
|
|
|
116
116
|
fastify: "^5.3.3",
|
|
117
117
|
"@fastify/cors": "^11.0.1",
|
|
118
118
|
turbo: "^2.6.3",
|
|
119
|
-
ai: "^
|
|
120
|
-
"@ai-sdk/google": "^
|
|
121
|
-
"@ai-sdk/vue": "^
|
|
122
|
-
"@ai-sdk/svelte": "^
|
|
123
|
-
"@ai-sdk/react": "^
|
|
119
|
+
ai: "^6.0.3",
|
|
120
|
+
"@ai-sdk/google": "^3.0.1",
|
|
121
|
+
"@ai-sdk/vue": "^3.0.3",
|
|
122
|
+
"@ai-sdk/svelte": "^4.0.3",
|
|
123
|
+
"@ai-sdk/react": "^3.0.3",
|
|
124
|
+
"@ai-sdk/devtools": "^0.0.2",
|
|
124
125
|
streamdown: "^1.6.10",
|
|
125
|
-
shiki: "^3.
|
|
126
|
+
shiki: "^3.20.0",
|
|
126
127
|
"@orpc/server": "^1.12.2",
|
|
127
128
|
"@orpc/client": "^1.12.2",
|
|
128
129
|
"@orpc/openapi": "^1.12.2",
|
|
@@ -292,8 +293,10 @@ function allowedApisForFrontends(frontends = []) {
|
|
|
292
293
|
if (includesNuxt || includesSvelte || includesSolid) return ["orpc", "none"];
|
|
293
294
|
return base;
|
|
294
295
|
}
|
|
295
|
-
function isExampleTodoAllowed(backend, database) {
|
|
296
|
-
|
|
296
|
+
function isExampleTodoAllowed(backend, database, api) {
|
|
297
|
+
if (backend === "convex") return true;
|
|
298
|
+
if (database === "none" || api === "none") return false;
|
|
299
|
+
return true;
|
|
297
300
|
}
|
|
298
301
|
function isExampleAIAllowed(backend, frontends = []) {
|
|
299
302
|
if (frontends.includes("solid")) return false;
|
|
@@ -343,10 +346,13 @@ function validatePaymentsCompatibility(payments, auth, _backend, frontends = [])
|
|
|
343
346
|
if (web.length === 0 && frontends.length > 0) exitWithError("Polar payments requires a web frontend or no frontend. Please select a web frontend or choose a different payments provider.");
|
|
344
347
|
}
|
|
345
348
|
}
|
|
346
|
-
function validateExamplesCompatibility(examples, backend, database, frontend) {
|
|
349
|
+
function validateExamplesCompatibility(examples, backend, database, frontend, api) {
|
|
347
350
|
const examplesArr = examples ?? [];
|
|
348
351
|
if (examplesArr.length === 0 || examplesArr.includes("none")) return;
|
|
349
|
-
if (examplesArr.includes("todo") && backend !== "convex"
|
|
352
|
+
if (examplesArr.includes("todo") && backend !== "convex") {
|
|
353
|
+
if (database === "none") exitWithError("The 'todo' example requires a database. Cannot use --examples todo when database is 'none'.");
|
|
354
|
+
if (api === "none") exitWithError("The 'todo' example requires an API layer (tRPC or oRPC). Cannot use --examples todo when api is 'none'.");
|
|
355
|
+
}
|
|
350
356
|
if (examplesArr.includes("ai") && (frontend ?? []).includes("solid")) exitWithError("The 'ai' example is not compatible with the Solid frontend.");
|
|
351
357
|
if (examplesArr.includes("ai") && backend === "convex") {
|
|
352
358
|
const frontendArr = frontend ?? [];
|
|
@@ -578,6 +584,7 @@ async function getAuthChoice(auth, backend, frontend) {
|
|
|
578
584
|
label: "Clerk",
|
|
579
585
|
hint: "More than auth, Complete User Management"
|
|
580
586
|
});
|
|
587
|
+
if (options.length === 0) return "none";
|
|
581
588
|
options.push({
|
|
582
589
|
value: "none",
|
|
583
590
|
label: "None",
|
|
@@ -802,13 +809,9 @@ async function getDBSetupChoice(databaseType, dbSetup, _orm, backend, runtime) {
|
|
|
802
809
|
async function getExamplesChoice(examples, database, frontends, backend, api) {
|
|
803
810
|
if (examples !== void 0) return examples;
|
|
804
811
|
if (backend === "none") return [];
|
|
805
|
-
if (backend !== "convex") {
|
|
806
|
-
if (api === "none") return [];
|
|
807
|
-
if (database === "none") return [];
|
|
808
|
-
}
|
|
809
812
|
let response = [];
|
|
810
813
|
const options = [];
|
|
811
|
-
if (isExampleTodoAllowed(backend, database)) options.push({
|
|
814
|
+
if (isExampleTodoAllowed(backend, database, api)) options.push({
|
|
812
815
|
value: "todo",
|
|
813
816
|
label: "Todo App",
|
|
814
817
|
hint: "A simple CRUD example app"
|
|
@@ -1758,7 +1761,7 @@ function validateFrontendConstraints(config, providedFlags) {
|
|
|
1758
1761
|
}
|
|
1759
1762
|
function validateApiConstraints(config, options) {
|
|
1760
1763
|
if (config.api === "none") {
|
|
1761
|
-
if (options.examples
|
|
1764
|
+
if (options.examples?.includes("todo") && options.backend !== "convex" && options.backend !== "none") exitWithError("Cannot use '--examples todo' when '--api' is set to 'none'. The todo example requires an API layer. Please remove 'todo' from --examples or choose an API type.");
|
|
1762
1765
|
}
|
|
1763
1766
|
}
|
|
1764
1767
|
function validateFullConfig(config, providedFlags, options) {
|
|
@@ -1779,7 +1782,7 @@ function validateFullConfig(config, providedFlags, options) {
|
|
|
1779
1782
|
validateAddonsAgainstFrontends(config.addons, config.frontend, config.auth);
|
|
1780
1783
|
config.addons = [...new Set(config.addons)];
|
|
1781
1784
|
}
|
|
1782
|
-
validateExamplesCompatibility(config.examples ?? [], config.backend, config.database, config.frontend ?? []);
|
|
1785
|
+
validateExamplesCompatibility(config.examples ?? [], config.backend, config.database, config.frontend ?? [], config.api);
|
|
1783
1786
|
validatePaymentsCompatibility(config.payments, config.auth, config.backend, config.frontend ?? []);
|
|
1784
1787
|
}
|
|
1785
1788
|
function validateConfigForProgrammaticUse(config) {
|
|
@@ -1789,7 +1792,7 @@ function validateConfigForProgrammaticUse(config) {
|
|
|
1789
1792
|
validateApiFrontendCompatibility(config.api, config.frontend);
|
|
1790
1793
|
validatePaymentsCompatibility(config.payments, config.auth, config.backend, config.frontend);
|
|
1791
1794
|
if (config.addons && config.addons.length > 0) validateAddonsAgainstFrontends(config.addons, config.frontend, config.auth);
|
|
1792
|
-
validateExamplesCompatibility(config.examples ?? [], config.backend, config.database, config.frontend ?? []);
|
|
1795
|
+
validateExamplesCompatibility(config.examples ?? [], config.backend, config.database, config.frontend ?? [], config.api);
|
|
1793
1796
|
} catch (error) {
|
|
1794
1797
|
if (error instanceof Error) throw error;
|
|
1795
1798
|
throw new Error(String(error));
|
|
@@ -4007,11 +4010,19 @@ async function setupAIDependencies(config) {
|
|
|
4007
4010
|
projectDir: convexBackendDir
|
|
4008
4011
|
});
|
|
4009
4012
|
else if (backend === "self" && webClientDirExists) await addPackageDependency({
|
|
4010
|
-
dependencies: [
|
|
4013
|
+
dependencies: [
|
|
4014
|
+
"ai",
|
|
4015
|
+
"@ai-sdk/google",
|
|
4016
|
+
"@ai-sdk/devtools"
|
|
4017
|
+
],
|
|
4011
4018
|
projectDir: webClientDir
|
|
4012
4019
|
});
|
|
4013
4020
|
else if (serverDirExists && backend !== "none") await addPackageDependency({
|
|
4014
|
-
dependencies: [
|
|
4021
|
+
dependencies: [
|
|
4022
|
+
"ai",
|
|
4023
|
+
"@ai-sdk/google",
|
|
4024
|
+
"@ai-sdk/devtools"
|
|
4025
|
+
],
|
|
4015
4026
|
projectDir: serverDir
|
|
4016
4027
|
});
|
|
4017
4028
|
if (webClientDirExists) {
|
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.6",
|
|
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,7 @@
|
|
|
67
67
|
"prepublishOnly": "npm run build"
|
|
68
68
|
},
|
|
69
69
|
"dependencies": {
|
|
70
|
-
"@better-t-stack/types": "^3.12.
|
|
70
|
+
"@better-t-stack/types": "^3.12.6",
|
|
71
71
|
"@clack/prompts": "^1.0.0-alpha.8",
|
|
72
72
|
"@orpc/server": "^1.13.0",
|
|
73
73
|
"consola": "^3.4.2",
|
|
@@ -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
|
}
|