create-better-t-stack 3.7.1 → 3.7.3
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.js +1 -1
- package/dist/index.js +1 -1
- package/dist/{src-BmyCNYPk.js → src-b1TtTCMt.js} +29 -36
- package/package.json +2 -3
- package/templates/auth/better-auth/convex/native/base/lib/auth-client.ts.hbs +0 -2
- package/templates/auth/better-auth/web/react/base/src/lib/auth-client.ts.hbs +6 -2
- package/templates/auth/better-auth/web/react/tanstack-router/src/components/sign-in-form.tsx +117 -121
- package/templates/auth/better-auth/web/react/tanstack-router/src/components/sign-up-form.tsx +141 -145
- package/templates/auth/better-auth/web/react/tanstack-start/src/components/sign-in-form.tsx +117 -121
- package/templates/auth/better-auth/web/react/tanstack-start/src/components/sign-up-form.tsx +141 -145
- package/templates/auth/better-auth/web/solid/src/components/sign-in-form.tsx +3 -11
- package/templates/auth/better-auth/web/solid/src/components/sign-up-form.tsx +4 -14
- package/templates/backend/convex/packages/backend/convex/healthCheck.ts +2 -2
- package/templates/examples/ai/native/bare/polyfills.js +3 -7
- package/templates/examples/ai/native/unistyles/polyfills.js +3 -6
- package/templates/examples/ai/native/uniwind/polyfills.js +3 -7
- package/templates/examples/todo/server/drizzle/postgres/src/schema/todo.ts +1 -1
- package/templates/frontend/nuxt/package.json.hbs +1 -2
- package/templates/frontend/react/next/package.json.hbs +0 -1
- package/templates/frontend/react/react-router/package.json.hbs +1 -2
- package/templates/frontend/react/react-router/src/components/mode-toggle.tsx +3 -9
- package/templates/frontend/react/tanstack-router/package.json.hbs +1 -2
- package/templates/frontend/react/tanstack-router/src/components/mode-toggle.tsx +3 -9
- package/templates/frontend/react/tanstack-start/package.json.hbs +1 -2
- package/templates/frontend/react/web-base/src/components/ui/button.tsx +13 -16
- package/templates/frontend/react/web-base/src/components/ui/card.tsx +13 -30
- package/templates/frontend/react/web-base/src/components/ui/checkbox.tsx +8 -11
- package/templates/frontend/react/web-base/src/components/ui/dropdown-menu.tsx +37 -66
- package/templates/frontend/react/web-base/src/components/ui/input.tsx +5 -5
- package/templates/frontend/react/web-base/src/components/ui/label.tsx +7 -10
- package/templates/frontend/react/web-base/src/components/ui/skeleton.tsx +3 -3
- package/templates/frontend/react/web-base/src/lib/utils.ts +3 -3
- package/templates/frontend/solid/package.json.hbs +1 -2
- package/templates/frontend/svelte/package.json.hbs +1 -2
- package/templates/frontend/svelte/src/app.d.ts +7 -7
- package/templates/frontend/svelte/src/lib/index.ts +1 -0
- package/templates/frontend/svelte/vite.config.ts +4 -4
package/dist/cli.js
CHANGED
package/dist/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { a as router, i as init, n as createBtsCli, o as sponsors, r as docs, t as builder } from "./src-
|
|
2
|
+
import { a as router, i as init, n as createBtsCli, o as sponsors, r as docs, t as builder } from "./src-b1TtTCMt.js";
|
|
3
3
|
|
|
4
4
|
export { builder, createBtsCli, docs, init, router, sponsors };
|
|
@@ -68,14 +68,14 @@ const dependencyVersionMap = {
|
|
|
68
68
|
"@clerk/clerk-react": "^5.45.0",
|
|
69
69
|
"@clerk/tanstack-react-start": "^0.26.3",
|
|
70
70
|
"@clerk/clerk-expo": "^2.14.25",
|
|
71
|
-
"drizzle-orm": "^0.
|
|
72
|
-
"drizzle-kit": "^0.31.
|
|
71
|
+
"drizzle-orm": "^0.45.0",
|
|
72
|
+
"drizzle-kit": "^0.31.8",
|
|
73
73
|
"@planetscale/database": "^1.19.0",
|
|
74
74
|
"@libsql/client": "^0.14.0",
|
|
75
75
|
libsql: "^0.5.22",
|
|
76
76
|
"@neondatabase/serverless": "^1.0.2",
|
|
77
|
-
pg: "^8.
|
|
78
|
-
"@types/pg": "^8.
|
|
77
|
+
pg: "^8.16.3",
|
|
78
|
+
"@types/pg": "^8.15.6",
|
|
79
79
|
"@types/ws": "^8.18.1",
|
|
80
80
|
ws: "^8.18.3",
|
|
81
81
|
mysql2: "^3.14.0",
|
|
@@ -128,7 +128,7 @@ const dependencyVersionMap = {
|
|
|
128
128
|
"@trpc/tanstack-react-query": "^11.5.0",
|
|
129
129
|
"@trpc/server": "^11.5.0",
|
|
130
130
|
"@trpc/client": "^11.5.0",
|
|
131
|
-
next: "
|
|
131
|
+
next: "^16.0.7",
|
|
132
132
|
convex: "^1.29.3",
|
|
133
133
|
"@convex-dev/react-query": "^0.1.0",
|
|
134
134
|
"convex-svelte": "^0.0.12",
|
|
@@ -153,7 +153,7 @@ const dependencyVersionMap = {
|
|
|
153
153
|
alchemy: "^0.77.0",
|
|
154
154
|
dotenv: "^17.2.2",
|
|
155
155
|
tsdown: "^0.16.5",
|
|
156
|
-
zod: "^4.1.
|
|
156
|
+
zod: "^4.1.13",
|
|
157
157
|
srvx: "0.8.15",
|
|
158
158
|
"@polar-sh/better-auth": "^1.1.3",
|
|
159
159
|
"@polar-sh/sdk": "^0.34.16"
|
|
@@ -1045,7 +1045,7 @@ async function getORMChoice(orm, hasDatabase, database, backend, runtime) {
|
|
|
1045
1045
|
if (orm !== void 0) return orm;
|
|
1046
1046
|
const response = await select({
|
|
1047
1047
|
message: "Select ORM",
|
|
1048
|
-
options:
|
|
1048
|
+
options: database === "mongodb" ? [ormOptions.prisma, ormOptions.mongoose] : [ormOptions.drizzle, ormOptions.prisma],
|
|
1049
1049
|
initialValue: database === "mongodb" ? "prisma" : runtime === "workers" ? "drizzle" : DEFAULT_CONFIG.orm
|
|
1050
1050
|
});
|
|
1051
1051
|
if (isCancel(response)) return exitCancelled("Operation cancelled");
|
|
@@ -1165,7 +1165,6 @@ async function getServerDeploymentToAdd(runtime, existingDeployment, backend) {
|
|
|
1165
1165
|
}
|
|
1166
1166
|
}
|
|
1167
1167
|
if (existingDeployment && existingDeployment !== "none") return "none";
|
|
1168
|
-
if (options.length > 0) {}
|
|
1169
1168
|
if (options.length === 0) return "none";
|
|
1170
1169
|
const response = await select({
|
|
1171
1170
|
message: "Select server deployment",
|
|
@@ -1363,20 +1362,19 @@ async function sendConvexEvent(payload) {
|
|
|
1363
1362
|
headers: { "Content-Type": "application/json" },
|
|
1364
1363
|
body: JSON.stringify(payload)
|
|
1365
1364
|
});
|
|
1366
|
-
} catch
|
|
1365
|
+
} catch {}
|
|
1367
1366
|
}
|
|
1368
1367
|
async function trackProjectCreation(config, disableAnalytics = false) {
|
|
1369
1368
|
if (!isTelemetryEnabled() || disableAnalytics) return;
|
|
1370
|
-
const { projectName, projectDir, relativePath,...safeConfig } = config;
|
|
1369
|
+
const { projectName: _projectName, projectDir: _projectDir, relativePath: _relativePath,...safeConfig } = config;
|
|
1371
1370
|
try {
|
|
1372
1371
|
await sendConvexEvent({
|
|
1373
|
-
event: "project_created",
|
|
1374
1372
|
...safeConfig,
|
|
1375
1373
|
cli_version: getLatestCLIVersion(),
|
|
1376
1374
|
node_version: typeof process !== "undefined" ? process.version : "",
|
|
1377
1375
|
platform: typeof process !== "undefined" ? process.platform : ""
|
|
1378
1376
|
});
|
|
1379
|
-
} catch
|
|
1377
|
+
} catch {}
|
|
1380
1378
|
}
|
|
1381
1379
|
|
|
1382
1380
|
//#endregion
|
|
@@ -1980,6 +1978,7 @@ async function writeBtsConfig(projectConfig) {
|
|
|
1980
1978
|
addons: btsConfig.addons,
|
|
1981
1979
|
examples: btsConfig.examples,
|
|
1982
1980
|
auth: btsConfig.auth,
|
|
1981
|
+
payments: btsConfig.payments,
|
|
1983
1982
|
packageManager: btsConfig.packageManager,
|
|
1984
1983
|
dbSetup: btsConfig.dbSetup,
|
|
1985
1984
|
api: btsConfig.api,
|
|
@@ -2015,7 +2014,7 @@ async function readBtsConfig(projectDir) {
|
|
|
2015
2014
|
return null;
|
|
2016
2015
|
}
|
|
2017
2016
|
return config;
|
|
2018
|
-
} catch
|
|
2017
|
+
} catch {
|
|
2019
2018
|
return null;
|
|
2020
2019
|
}
|
|
2021
2020
|
}
|
|
@@ -2033,7 +2032,7 @@ async function updateBtsConfig(projectDir, updates) {
|
|
|
2033
2032
|
modifiedContent = JSONC.applyEdits(modifiedContent, editResult);
|
|
2034
2033
|
}
|
|
2035
2034
|
await fs.writeFile(configPath, modifiedContent, "utf-8");
|
|
2036
|
-
} catch
|
|
2035
|
+
} catch {}
|
|
2037
2036
|
}
|
|
2038
2037
|
|
|
2039
2038
|
//#endregion
|
|
@@ -2212,7 +2211,7 @@ async function setupRuler(config) {
|
|
|
2212
2211
|
shell: true
|
|
2213
2212
|
});
|
|
2214
2213
|
s.stop("Applied rules with Ruler");
|
|
2215
|
-
} catch
|
|
2214
|
+
} catch {
|
|
2216
2215
|
s.stop(pc.red("Failed to apply rules"));
|
|
2217
2216
|
}
|
|
2218
2217
|
} catch (error) {
|
|
@@ -2636,14 +2635,14 @@ async function detectProjectConfig(projectDir) {
|
|
|
2636
2635
|
serverDeploy: btsConfig.serverDeploy
|
|
2637
2636
|
};
|
|
2638
2637
|
return null;
|
|
2639
|
-
} catch
|
|
2638
|
+
} catch {
|
|
2640
2639
|
return null;
|
|
2641
2640
|
}
|
|
2642
2641
|
}
|
|
2643
2642
|
async function isBetterTStackProject(projectDir) {
|
|
2644
2643
|
try {
|
|
2645
2644
|
return await fs.pathExists(path.join(projectDir, "bts.jsonc"));
|
|
2646
|
-
} catch
|
|
2645
|
+
} catch {
|
|
2647
2646
|
return false;
|
|
2648
2647
|
}
|
|
2649
2648
|
}
|
|
@@ -2686,7 +2685,7 @@ function initializeBiome() {
|
|
|
2686
2685
|
biome,
|
|
2687
2686
|
projectKey
|
|
2688
2687
|
};
|
|
2689
|
-
} catch
|
|
2688
|
+
} catch {
|
|
2690
2689
|
return null;
|
|
2691
2690
|
}
|
|
2692
2691
|
}
|
|
@@ -2721,7 +2720,7 @@ function formatFileWithBiome(filePath, content) {
|
|
|
2721
2720
|
const result = biome.formatContent(projectKey, content, { filePath: path.basename(filePath) });
|
|
2722
2721
|
if (result.diagnostics && result.diagnostics.length > 0) consola.debug(`Biome formatting diagnostics for ${filePath}:`, result.diagnostics);
|
|
2723
2722
|
return result.content;
|
|
2724
|
-
} catch
|
|
2723
|
+
} catch {
|
|
2725
2724
|
return null;
|
|
2726
2725
|
}
|
|
2727
2726
|
}
|
|
@@ -3976,7 +3975,7 @@ async function setupExamples(config) {
|
|
|
3976
3975
|
const apiDir = path.join(projectDir, "packages/api");
|
|
3977
3976
|
if (await fs.pathExists(apiDir) && backend !== "none") {
|
|
3978
3977
|
if (orm === "drizzle") await addPackageDependency({
|
|
3979
|
-
dependencies: ["drizzle-orm"],
|
|
3978
|
+
dependencies: ["drizzle-orm", "@types/pg"],
|
|
3980
3979
|
projectDir: apiDir
|
|
3981
3980
|
});
|
|
3982
3981
|
else if (orm === "prisma") await addPackageDependency({
|
|
@@ -4035,7 +4034,7 @@ async function addBackendWorkspaceDependency(projectDir, backendPackageName, wor
|
|
|
4035
4034
|
if (!pkgJson.dependencies) pkgJson.dependencies = {};
|
|
4036
4035
|
pkgJson.dependencies[backendPackageName] = workspaceVersion;
|
|
4037
4036
|
await fs.writeJson(pkgJsonPath, pkgJson, { spaces: 2 });
|
|
4038
|
-
} catch
|
|
4037
|
+
} catch {}
|
|
4039
4038
|
}
|
|
4040
4039
|
function getFrontendType(frontend) {
|
|
4041
4040
|
const reactBasedFrontends = [
|
|
@@ -4754,15 +4753,11 @@ async function setupCloudflareD1(config) {
|
|
|
4754
4753
|
const { projectDir, serverDeploy, orm, backend } = config;
|
|
4755
4754
|
if (serverDeploy === "alchemy" && orm === "prisma") {
|
|
4756
4755
|
const targetApp2 = backend === "self" ? "apps/web" : "apps/server";
|
|
4757
|
-
|
|
4758
|
-
const variables = [{
|
|
4756
|
+
await addEnvVariablesToFile(path.join(projectDir, targetApp2, ".env"), [{
|
|
4759
4757
|
key: "DATABASE_URL",
|
|
4760
4758
|
value: `file:${path.join(projectDir, "apps/server", "local.db")}`,
|
|
4761
4759
|
condition: true
|
|
4762
|
-
}];
|
|
4763
|
-
try {
|
|
4764
|
-
await addEnvVariablesToFile(envPath, variables);
|
|
4765
|
-
} catch (_err) {}
|
|
4760
|
+
}]);
|
|
4766
4761
|
await addPackageDependency({
|
|
4767
4762
|
dependencies: ["@prisma/adapter-d1"],
|
|
4768
4763
|
projectDir: path.join(projectDir, backend === "self" ? "apps/web" : "apps/server")
|
|
@@ -4817,7 +4812,7 @@ async function checkAtlasCLI() {
|
|
|
4817
4812
|
if (exists) log.info("MongoDB Atlas CLI found");
|
|
4818
4813
|
else log.warn(pc.yellow("MongoDB Atlas CLI not found"));
|
|
4819
4814
|
return exists;
|
|
4820
|
-
} catch
|
|
4815
|
+
} catch {
|
|
4821
4816
|
log.error(pc.red("Error checking MongoDB Atlas CLI"));
|
|
4822
4817
|
return false;
|
|
4823
4818
|
}
|
|
@@ -4862,7 +4857,7 @@ async function writeEnvFile$3(projectDir, backend, config) {
|
|
|
4862
4857
|
value: config?.connectionString ?? "mongodb://localhost:27017/mydb",
|
|
4863
4858
|
condition: true
|
|
4864
4859
|
}]);
|
|
4865
|
-
} catch
|
|
4860
|
+
} catch {
|
|
4866
4861
|
consola.error("Failed to update environment configuration");
|
|
4867
4862
|
}
|
|
4868
4863
|
}
|
|
@@ -4999,7 +4994,7 @@ async function createNeonProject(projectName, regionId, packageManager) {
|
|
|
4999
4994
|
}
|
|
5000
4995
|
consola$1.error(pc.red("Failed to extract connection information from response"));
|
|
5001
4996
|
return null;
|
|
5002
|
-
} catch
|
|
4997
|
+
} catch {
|
|
5003
4998
|
consola$1.error(pc.red("Failed to create Neon project"));
|
|
5004
4999
|
}
|
|
5005
5000
|
}
|
|
@@ -5247,7 +5242,7 @@ async function writeEnvFile$1(projectDir, backend, config) {
|
|
|
5247
5242
|
condition: true
|
|
5248
5243
|
});
|
|
5249
5244
|
await addEnvVariablesToFile(envPath, variables);
|
|
5250
|
-
} catch
|
|
5245
|
+
} catch {
|
|
5251
5246
|
consola$1.error("Failed to update environment configuration");
|
|
5252
5247
|
}
|
|
5253
5248
|
}
|
|
@@ -5474,7 +5469,7 @@ async function loginToTurso() {
|
|
|
5474
5469
|
await $`turso auth login`;
|
|
5475
5470
|
s.stop("Logged into Turso");
|
|
5476
5471
|
return true;
|
|
5477
|
-
} catch
|
|
5472
|
+
} catch {
|
|
5478
5473
|
s.stop(pc.red("Failed to log in to Turso"));
|
|
5479
5474
|
}
|
|
5480
5475
|
}
|
|
@@ -5562,7 +5557,7 @@ async function createTursoDatabase(dbName, groupName) {
|
|
|
5562
5557
|
dbUrl: dbUrl.trim(),
|
|
5563
5558
|
authToken: authToken.trim()
|
|
5564
5559
|
};
|
|
5565
|
-
} catch
|
|
5560
|
+
} catch {
|
|
5566
5561
|
s.stop(pc.red("Failed to retrieve database connection details"));
|
|
5567
5562
|
}
|
|
5568
5563
|
}
|
|
@@ -5688,7 +5683,6 @@ async function setupDatabase(config, cliInput) {
|
|
|
5688
5683
|
}
|
|
5689
5684
|
return;
|
|
5690
5685
|
}
|
|
5691
|
-
const s = spinner();
|
|
5692
5686
|
const dbPackageDir = path.join(projectDir, "packages/db");
|
|
5693
5687
|
const webDir = path.join(projectDir, "apps/web");
|
|
5694
5688
|
const webDirExists = await fs.pathExists(webDir);
|
|
@@ -5791,7 +5785,6 @@ async function setupDatabase(config, cliInput) {
|
|
|
5791
5785
|
if (dbSetup === "planetscale") await setupPlanetScale(config);
|
|
5792
5786
|
} else if (database === "mongodb" && dbSetup === "mongodb-atlas") await setupMongoDBAtlas(config, cliInput);
|
|
5793
5787
|
} catch (error) {
|
|
5794
|
-
s.stop(pc.red("Failed to set up database"));
|
|
5795
5788
|
if (error instanceof Error) consola.error(pc.red(error.message));
|
|
5796
5789
|
}
|
|
5797
5790
|
}
|
|
@@ -6668,7 +6661,7 @@ async function updateRootPackageJson(projectDir, options) {
|
|
|
6668
6661
|
try {
|
|
6669
6662
|
const { stdout } = await execa(options.packageManager, ["-v"], { cwd: projectDir });
|
|
6670
6663
|
packageJson.packageManager = `${options.packageManager}@${stdout.trim()}`;
|
|
6671
|
-
} catch
|
|
6664
|
+
} catch {
|
|
6672
6665
|
log.warn(`Could not determine ${options.packageManager} version.`);
|
|
6673
6666
|
}
|
|
6674
6667
|
if (!packageJson.workspaces) packageJson.workspaces = [];
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "create-better-t-stack",
|
|
3
|
-
"version": "3.7.
|
|
3
|
+
"version": "3.7.3",
|
|
4
4
|
"description": "A modern CLI tool for scaffolding end-to-end type-safe TypeScript projects with best practices and customizable configurations",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -51,7 +51,6 @@
|
|
|
51
51
|
"build": "tsdown --publint",
|
|
52
52
|
"dev": "tsdown --watch",
|
|
53
53
|
"check-types": "tsc --noEmit",
|
|
54
|
-
"check": "biome check --write .",
|
|
55
54
|
"test": "bun run build && vitest run; rm -rf .smoke || true",
|
|
56
55
|
"test:ui": "bun run build && vitest --ui",
|
|
57
56
|
"prepublishOnly": "npm run build"
|
|
@@ -81,7 +80,7 @@
|
|
|
81
80
|
"trpc-cli": "^0.12.0",
|
|
82
81
|
"ts-morph": "^27.0.2",
|
|
83
82
|
"yaml": "^2.8.1",
|
|
84
|
-
"zod": "^4.1.
|
|
83
|
+
"zod": "^4.1.13"
|
|
85
84
|
},
|
|
86
85
|
"devDependencies": {
|
|
87
86
|
"@types/fs-extra": "^11.0.4",
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { createAuthClient } from "better-auth/react";
|
|
2
|
-
import { anonymousClient } from "better-auth/client/plugins";
|
|
3
2
|
import { convexClient } from "@convex-dev/better-auth/client/plugins";
|
|
4
3
|
import { expoClient } from "@better-auth/expo/client";
|
|
5
4
|
import Constants from "expo-constants";
|
|
@@ -8,7 +7,6 @@ import * as SecureStore from "expo-secure-store";
|
|
|
8
7
|
export const authClient = createAuthClient({
|
|
9
8
|
baseURL: process.env.EXPO_PUBLIC_CONVEX_SITE_URL,
|
|
10
9
|
plugins: [
|
|
11
|
-
anonymousClient(),
|
|
12
10
|
expoClient({
|
|
13
11
|
scheme: Constants.expoConfig?.scheme as string,
|
|
14
12
|
storagePrefix: Constants.expoConfig?.scheme as string,
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import type { auth } from "@{{projectName}}/auth";
|
|
2
2
|
import { createAuthClient } from "better-auth/react";
|
|
3
|
-
|
|
3
|
+
{{#if (eq payments "polar")}}
|
|
4
|
+
import { polarClient } from "@polar-sh/better-auth";
|
|
5
|
+
{{/if}}
|
|
4
6
|
|
|
5
7
|
export const authClient = createAuthClient({
|
|
6
8
|
{{#unless (eq backend "self")}}
|
|
@@ -11,5 +13,7 @@ export const authClient = createAuthClient({
|
|
|
11
13
|
import.meta.env.VITE_SERVER_URL,
|
|
12
14
|
{{/if}}
|
|
13
15
|
{{/unless}}
|
|
14
|
-
|
|
16
|
+
{{#if (eq payments "polar")}}
|
|
17
|
+
plugins: [polarClient()]
|
|
18
|
+
{{/if}}
|
|
15
19
|
});
|
package/templates/auth/better-auth/web/react/tanstack-router/src/components/sign-in-form.tsx
CHANGED
|
@@ -8,132 +8,128 @@ import { Button } from "./ui/button";
|
|
|
8
8
|
import { Input } from "./ui/input";
|
|
9
9
|
import { Label } from "./ui/label";
|
|
10
10
|
|
|
11
|
-
export default function SignInForm({
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
})
|
|
16
|
-
const navigate = useNavigate({
|
|
17
|
-
from: "/",
|
|
18
|
-
});
|
|
19
|
-
const { isPending } = authClient.useSession();
|
|
11
|
+
export default function SignInForm({ onSwitchToSignUp }: { onSwitchToSignUp: () => void }) {
|
|
12
|
+
const navigate = useNavigate({
|
|
13
|
+
from: "/",
|
|
14
|
+
});
|
|
15
|
+
const { isPending } = authClient.useSession();
|
|
20
16
|
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
17
|
+
const form = useForm({
|
|
18
|
+
defaultValues: {
|
|
19
|
+
email: "",
|
|
20
|
+
password: "",
|
|
21
|
+
},
|
|
22
|
+
onSubmit: async ({ value }) => {
|
|
23
|
+
await authClient.signIn.email(
|
|
24
|
+
{
|
|
25
|
+
email: value.email,
|
|
26
|
+
password: value.password,
|
|
27
|
+
},
|
|
28
|
+
{
|
|
29
|
+
onSuccess: () => {
|
|
30
|
+
navigate({
|
|
31
|
+
to: "/dashboard",
|
|
32
|
+
});
|
|
33
|
+
toast.success("Sign in successful");
|
|
34
|
+
},
|
|
35
|
+
onError: (error) => {
|
|
36
|
+
toast.error(error.error.message || error.error.statusText);
|
|
37
|
+
},
|
|
38
|
+
},
|
|
39
|
+
);
|
|
40
|
+
},
|
|
41
|
+
validators: {
|
|
42
|
+
onSubmit: z.object({
|
|
43
|
+
email: z.email("Invalid email address"),
|
|
44
|
+
password: z.string().min(8, "Password must be at least 8 characters"),
|
|
45
|
+
}),
|
|
46
|
+
},
|
|
47
|
+
});
|
|
52
48
|
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
49
|
+
if (isPending) {
|
|
50
|
+
return <Loader />;
|
|
51
|
+
}
|
|
56
52
|
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
53
|
+
return (
|
|
54
|
+
<div className="mx-auto w-full mt-10 max-w-md p-6">
|
|
55
|
+
<h1 className="mb-6 text-center text-3xl font-bold">Welcome Back</h1>
|
|
60
56
|
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
57
|
+
<form
|
|
58
|
+
onSubmit={(e) => {
|
|
59
|
+
e.preventDefault();
|
|
60
|
+
e.stopPropagation();
|
|
61
|
+
form.handleSubmit();
|
|
62
|
+
}}
|
|
63
|
+
className="space-y-4"
|
|
64
|
+
>
|
|
65
|
+
<div>
|
|
66
|
+
<form.Field name="email">
|
|
67
|
+
{(field) => (
|
|
68
|
+
<div className="space-y-2">
|
|
69
|
+
<Label htmlFor={field.name}>Email</Label>
|
|
70
|
+
<Input
|
|
71
|
+
id={field.name}
|
|
72
|
+
name={field.name}
|
|
73
|
+
type="email"
|
|
74
|
+
value={field.state.value}
|
|
75
|
+
onBlur={field.handleBlur}
|
|
76
|
+
onChange={(e) => field.handleChange(e.target.value)}
|
|
77
|
+
/>
|
|
78
|
+
{field.state.meta.errors.map((error) => (
|
|
79
|
+
<p key={error?.message} className="text-red-500">
|
|
80
|
+
{error?.message}
|
|
81
|
+
</p>
|
|
82
|
+
))}
|
|
83
|
+
</div>
|
|
84
|
+
)}
|
|
85
|
+
</form.Field>
|
|
86
|
+
</div>
|
|
91
87
|
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
88
|
+
<div>
|
|
89
|
+
<form.Field name="password">
|
|
90
|
+
{(field) => (
|
|
91
|
+
<div className="space-y-2">
|
|
92
|
+
<Label htmlFor={field.name}>Password</Label>
|
|
93
|
+
<Input
|
|
94
|
+
id={field.name}
|
|
95
|
+
name={field.name}
|
|
96
|
+
type="password"
|
|
97
|
+
value={field.state.value}
|
|
98
|
+
onBlur={field.handleBlur}
|
|
99
|
+
onChange={(e) => field.handleChange(e.target.value)}
|
|
100
|
+
/>
|
|
101
|
+
{field.state.meta.errors.map((error) => (
|
|
102
|
+
<p key={error?.message} className="text-red-500">
|
|
103
|
+
{error?.message}
|
|
104
|
+
</p>
|
|
105
|
+
))}
|
|
106
|
+
</div>
|
|
107
|
+
)}
|
|
108
|
+
</form.Field>
|
|
109
|
+
</div>
|
|
114
110
|
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
111
|
+
<form.Subscribe>
|
|
112
|
+
{(state) => (
|
|
113
|
+
<Button
|
|
114
|
+
type="submit"
|
|
115
|
+
className="w-full"
|
|
116
|
+
disabled={!state.canSubmit || state.isSubmitting}
|
|
117
|
+
>
|
|
118
|
+
{state.isSubmitting ? "Submitting..." : "Sign In"}
|
|
119
|
+
</Button>
|
|
120
|
+
)}
|
|
121
|
+
</form.Subscribe>
|
|
122
|
+
</form>
|
|
127
123
|
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
124
|
+
<div className="mt-4 text-center">
|
|
125
|
+
<Button
|
|
126
|
+
variant="link"
|
|
127
|
+
onClick={onSwitchToSignUp}
|
|
128
|
+
className="text-indigo-600 hover:text-indigo-800"
|
|
129
|
+
>
|
|
130
|
+
Need an account? Sign Up
|
|
131
|
+
</Button>
|
|
132
|
+
</div>
|
|
133
|
+
</div>
|
|
134
|
+
);
|
|
139
135
|
}
|