create-better-t-stack 3.6.1 → 3.6.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.d.ts +2 -0
- package/dist/index.js +1 -1
- package/dist/{src-DaTuAN3w.js → src-BN3eh8p0.js} +41 -73
- package/package.json +1 -1
- package/templates/db/prisma/postgres/prisma.config.ts.hbs +0 -2
- package/templates/db/prisma/postgres/src/index.ts.hbs +18 -0
- package/templates/frontend/native/uniwind/app/(drawer)/index.tsx.hbs +98 -102
- package/templates/frontend/native/uniwind/package.json.hbs +7 -7
package/dist/cli.js
CHANGED
package/dist/index.d.ts
CHANGED
|
@@ -126,6 +126,7 @@ declare const TemplateSchema: z.ZodEnum<{
|
|
|
126
126
|
mern: "mern";
|
|
127
127
|
pern: "pern";
|
|
128
128
|
t3: "t3";
|
|
129
|
+
uniwind: "uniwind";
|
|
129
130
|
none: "none";
|
|
130
131
|
}>;
|
|
131
132
|
type Template = z.infer<typeof TemplateSchema>;
|
|
@@ -221,6 +222,7 @@ declare const router: {
|
|
|
221
222
|
mern: "mern";
|
|
222
223
|
pern: "pern";
|
|
223
224
|
t3: "t3";
|
|
225
|
+
uniwind: "uniwind";
|
|
224
226
|
none: "none";
|
|
225
227
|
}>>;
|
|
226
228
|
yes: z$1.ZodDefault<z$1.ZodOptional<z$1.ZodBoolean>>;
|
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-BN3eh8p0.js";
|
|
3
3
|
|
|
4
4
|
export { builder, createBtsCli, docs, init, router, sponsors };
|
|
@@ -88,6 +88,7 @@ const dependencyVersionMap = {
|
|
|
88
88
|
"@prisma/adapter-better-sqlite3": "^7.0.0",
|
|
89
89
|
"@prisma/adapter-pg": "^7.0.0",
|
|
90
90
|
"@prisma/adapter-planetscale": "^7.0.0",
|
|
91
|
+
"@prisma/adapter-ppg": "^7.0.0",
|
|
91
92
|
mongoose: "^8.14.0",
|
|
92
93
|
"vite-plugin-pwa": "^1.0.1",
|
|
93
94
|
"@vite-pwa/assets-generator": "^1.0.0",
|
|
@@ -295,6 +296,7 @@ const TemplateSchema = z.enum([
|
|
|
295
296
|
"mern",
|
|
296
297
|
"pern",
|
|
297
298
|
"t3",
|
|
299
|
+
"uniwind",
|
|
298
300
|
"none"
|
|
299
301
|
]).describe("Predefined project template");
|
|
300
302
|
|
|
@@ -1632,6 +1634,21 @@ const TEMPLATE_PRESETS = {
|
|
|
1632
1634
|
webDeploy: "none",
|
|
1633
1635
|
serverDeploy: "none"
|
|
1634
1636
|
},
|
|
1637
|
+
uniwind: {
|
|
1638
|
+
database: "none",
|
|
1639
|
+
orm: "none",
|
|
1640
|
+
backend: "none",
|
|
1641
|
+
runtime: "none",
|
|
1642
|
+
frontend: ["native-uniwind"],
|
|
1643
|
+
api: "none",
|
|
1644
|
+
auth: "none",
|
|
1645
|
+
payments: "none",
|
|
1646
|
+
addons: ["none"],
|
|
1647
|
+
examples: ["none"],
|
|
1648
|
+
dbSetup: "none",
|
|
1649
|
+
webDeploy: "none",
|
|
1650
|
+
serverDeploy: "none"
|
|
1651
|
+
},
|
|
1635
1652
|
none: null
|
|
1636
1653
|
};
|
|
1637
1654
|
function getTemplateConfig(template) {
|
|
@@ -1645,6 +1662,7 @@ function getTemplateDescription(template) {
|
|
|
1645
1662
|
mern: "MongoDB + Express + React + Node.js - Classic MERN stack",
|
|
1646
1663
|
pern: "PostgreSQL + Express + React + Node.js - Popular PERN stack",
|
|
1647
1664
|
t3: "T3 Stack - Next.js + tRPC + Prisma + PostgreSQL + Better Auth",
|
|
1665
|
+
uniwind: "Expo + Uniwind native app with no backend services",
|
|
1648
1666
|
none: "No template - Full customization"
|
|
1649
1667
|
}[template] || "";
|
|
1650
1668
|
}
|
|
@@ -5216,31 +5234,6 @@ async function setupWithCreateDb(serverDir, packageManager) {
|
|
|
5216
5234
|
return null;
|
|
5217
5235
|
}
|
|
5218
5236
|
}
|
|
5219
|
-
async function initPrismaDatabase(serverDir, packageManager) {
|
|
5220
|
-
try {
|
|
5221
|
-
const prismaDir = path.join(serverDir, "prisma");
|
|
5222
|
-
await fs.ensureDir(prismaDir);
|
|
5223
|
-
log.info("Starting Prisma PostgreSQL setup.");
|
|
5224
|
-
await execa(getPackageExecutionCommand(packageManager, "prisma init --db"), {
|
|
5225
|
-
cwd: serverDir,
|
|
5226
|
-
stdio: "inherit",
|
|
5227
|
-
shell: true
|
|
5228
|
-
});
|
|
5229
|
-
log.info(pc.yellow("Please copy the Prisma Postgres URL.\nIt looks like: postgresql://user:password@host:5432/db?sslmode=require"));
|
|
5230
|
-
const databaseUrl = await text({
|
|
5231
|
-
message: "Paste your Prisma Postgres database URL:",
|
|
5232
|
-
validate(value) {
|
|
5233
|
-
if (!value) return "Please enter a database URL";
|
|
5234
|
-
if (!value.startsWith("postgresql://")) return "URL should start with postgresql://";
|
|
5235
|
-
}
|
|
5236
|
-
});
|
|
5237
|
-
if (isCancel(databaseUrl)) return null;
|
|
5238
|
-
return { databaseUrl };
|
|
5239
|
-
} catch (error) {
|
|
5240
|
-
if (error instanceof Error) consola$1.error(error.message);
|
|
5241
|
-
return null;
|
|
5242
|
-
}
|
|
5243
|
-
}
|
|
5244
5237
|
async function writeEnvFile$1(projectDir, backend, config) {
|
|
5245
5238
|
try {
|
|
5246
5239
|
const targetApp = backend === "self" ? "apps/web" : "apps/server";
|
|
@@ -5260,16 +5253,6 @@ async function writeEnvFile$1(projectDir, backend, config) {
|
|
|
5260
5253
|
consola$1.error("Failed to update environment configuration");
|
|
5261
5254
|
}
|
|
5262
5255
|
}
|
|
5263
|
-
async function addDotenvImportToPrismaConfig(projectDir, backend) {
|
|
5264
|
-
try {
|
|
5265
|
-
const prismaConfigPath = path.join(projectDir, "packages/db/prisma.config.ts");
|
|
5266
|
-
let content = await fs.readFile(prismaConfigPath, "utf8");
|
|
5267
|
-
content = `import dotenv from "dotenv";\ndotenv.config({ path: "${backend === "self" ? "../../apps/web/.env" : "../../apps/server/.env"}" });\n${content}`;
|
|
5268
|
-
await fs.writeFile(prismaConfigPath, content);
|
|
5269
|
-
} catch (_error) {
|
|
5270
|
-
consola$1.error("Failed to update prisma.config.ts");
|
|
5271
|
-
}
|
|
5272
|
-
}
|
|
5273
5256
|
function displayManualSetupInstructions$1(target) {
|
|
5274
5257
|
log.info(`Manual Prisma PostgreSQL Setup Instructions:
|
|
5275
5258
|
|
|
@@ -5281,7 +5264,7 @@ function displayManualSetupInstructions$1(target) {
|
|
|
5281
5264
|
DATABASE_URL="your_database_url"`);
|
|
5282
5265
|
}
|
|
5283
5266
|
async function setupPrismaPostgres(config, cliInput) {
|
|
5284
|
-
const { packageManager, projectDir,
|
|
5267
|
+
const { packageManager, projectDir, backend } = config;
|
|
5285
5268
|
const manualDb = cliInput?.manualDb ?? false;
|
|
5286
5269
|
const dbDir = path.join(projectDir, "packages/db");
|
|
5287
5270
|
try {
|
|
@@ -5291,47 +5274,28 @@ async function setupPrismaPostgres(config, cliInput) {
|
|
|
5291
5274
|
displayManualSetupInstructions$1(backend === "self" ? "apps/web" : "apps/server");
|
|
5292
5275
|
return;
|
|
5293
5276
|
}
|
|
5294
|
-
const
|
|
5277
|
+
const setupMode = await select({
|
|
5295
5278
|
message: "Prisma Postgres setup: choose mode",
|
|
5296
5279
|
options: [{
|
|
5297
|
-
label: "Automatic",
|
|
5280
|
+
label: "Automatic (create-db)",
|
|
5298
5281
|
value: "auto",
|
|
5299
|
-
hint: "
|
|
5282
|
+
hint: "Provision a database via Prisma's create-db CLI"
|
|
5300
5283
|
}, {
|
|
5301
5284
|
label: "Manual",
|
|
5302
5285
|
value: "manual",
|
|
5303
|
-
hint: "
|
|
5286
|
+
hint: "Add your own DATABASE_URL later"
|
|
5304
5287
|
}],
|
|
5305
5288
|
initialValue: "auto"
|
|
5306
5289
|
});
|
|
5307
|
-
if (isCancel(
|
|
5308
|
-
if (
|
|
5290
|
+
if (isCancel(setupMode)) return;
|
|
5291
|
+
if (setupMode === "manual") {
|
|
5309
5292
|
await writeEnvFile$1(projectDir, backend);
|
|
5310
5293
|
displayManualSetupInstructions$1(backend === "self" ? "apps/web" : "apps/server");
|
|
5311
5294
|
return;
|
|
5312
5295
|
}
|
|
5313
|
-
const
|
|
5314
|
-
label: "Quick setup with create-db",
|
|
5315
|
-
value: "create-db",
|
|
5316
|
-
hint: "Fastest, automated database creation (no auth)"
|
|
5317
|
-
}];
|
|
5318
|
-
if (orm === "prisma") setupOptions.push({
|
|
5319
|
-
label: "Custom setup with Prisma Init",
|
|
5320
|
-
value: "custom",
|
|
5321
|
-
hint: "More control (requires auth)"
|
|
5322
|
-
});
|
|
5323
|
-
const setupMethod = await select({
|
|
5324
|
-
message: "Choose your Prisma Postgres setup method:",
|
|
5325
|
-
options: setupOptions,
|
|
5326
|
-
initialValue: "create-db"
|
|
5327
|
-
});
|
|
5328
|
-
if (isCancel(setupMethod)) return exitCancelled("Operation cancelled");
|
|
5329
|
-
let prismaConfig = null;
|
|
5330
|
-
if (setupMethod === "create-db") prismaConfig = await setupWithCreateDb(dbDir, packageManager);
|
|
5331
|
-
else prismaConfig = await initPrismaDatabase(dbDir, packageManager);
|
|
5296
|
+
const prismaConfig = await setupWithCreateDb(dbDir, packageManager);
|
|
5332
5297
|
if (prismaConfig) {
|
|
5333
5298
|
await writeEnvFile$1(projectDir, backend, prismaConfig);
|
|
5334
|
-
if (orm === "prisma") await addDotenvImportToPrismaConfig(projectDir, backend);
|
|
5335
5299
|
log.success(pc.green("Prisma Postgres database configured successfully!"));
|
|
5336
5300
|
if (prismaConfig.claimUrl) log.info(pc.blue(`Claim URL saved to .env: ${prismaConfig.claimUrl}`));
|
|
5337
5301
|
} else {
|
|
@@ -5748,7 +5712,8 @@ async function setupDatabase(config, cliInput) {
|
|
|
5748
5712
|
else if (database === "postgres") if (dbSetup === "neon") {
|
|
5749
5713
|
prismaDependencies.push("@prisma/adapter-neon", "@neondatabase/serverless", "ws");
|
|
5750
5714
|
prismaDevDependencies.push("@types/ws");
|
|
5751
|
-
} else
|
|
5715
|
+
} else if (dbSetup === "prisma-postgres") prismaDependencies.push("@prisma/adapter-ppg");
|
|
5716
|
+
else {
|
|
5752
5717
|
prismaDependencies.push("@prisma/adapter-pg");
|
|
5753
5718
|
prismaDependencies.push("pg");
|
|
5754
5719
|
prismaDevDependencies.push("@types/pg");
|
|
@@ -6326,7 +6291,7 @@ async function displayPostInstallInstructions(config) {
|
|
|
6326
6291
|
const databaseInstructions = !isConvex && database !== "none" ? await getDatabaseInstructions(database, orm, runCmd, runtime, dbSetup, serverDeploy, backend) : "";
|
|
6327
6292
|
const tauriInstructions = addons?.includes("tauri") ? getTauriInstructions(runCmd) : "";
|
|
6328
6293
|
const lintingInstructions = hasHuskyOrBiome ? getLintingInstructions(runCmd) : "";
|
|
6329
|
-
const nativeInstructions = frontend?.includes("native-bare") || frontend?.includes("native-uniwind") || frontend?.includes("native-unistyles") ? getNativeInstructions(isConvex, isBackendSelf, frontend || []) : "";
|
|
6294
|
+
const nativeInstructions = (frontend?.includes("native-bare") || frontend?.includes("native-uniwind") || frontend?.includes("native-unistyles")) && backend !== "none" ? getNativeInstructions(isConvex, isBackendSelf, frontend || []) : "";
|
|
6330
6295
|
const pwaInstructions = addons?.includes("pwa") && frontend?.includes("react-router") ? getPwaInstructions() : "";
|
|
6331
6296
|
const starlightInstructions = addons?.includes("starlight") ? getStarlightInstructions(runCmd) : "";
|
|
6332
6297
|
const clerkInstructions = isConvex && config.auth === "clerk" ? getClerkInstructions() : "";
|
|
@@ -6363,16 +6328,19 @@ async function displayPostInstallInstructions(config) {
|
|
|
6363
6328
|
output += `${pc.cyan(`${stepCounter++}.`)} ${runCmd} dev\n`;
|
|
6364
6329
|
}
|
|
6365
6330
|
}
|
|
6366
|
-
|
|
6367
|
-
if (hasWeb
|
|
6368
|
-
|
|
6369
|
-
|
|
6370
|
-
output += `${pc.
|
|
6371
|
-
if (
|
|
6331
|
+
const hasStandaloneBackend = backend !== "none";
|
|
6332
|
+
if (hasWeb || hasStandaloneBackend || addons?.includes("starlight") || addons?.includes("fumadocs")) {
|
|
6333
|
+
output += `${pc.bold("Your project will be available at:")}\n`;
|
|
6334
|
+
if (hasWeb) output += `${pc.cyan("•")} Frontend: http://localhost:${webPort}\n`;
|
|
6335
|
+
else if (!hasNative && !addons?.includes("starlight")) output += `${pc.yellow("NOTE:")} You are creating a backend-only app\n (no frontend selected)\n`;
|
|
6336
|
+
if (!isConvex && !isBackendSelf && hasStandaloneBackend) {
|
|
6337
|
+
output += `${pc.cyan("•")} Backend API: http://localhost:3000\n`;
|
|
6338
|
+
if (api === "orpc") output += `${pc.cyan("•")} OpenAPI (Scalar UI): http://localhost:3000/api-reference\n`;
|
|
6339
|
+
}
|
|
6340
|
+
if (isBackendSelf && api === "orpc") output += `${pc.cyan("•")} OpenAPI (Scalar UI): http://localhost:${webPort}/api/rpc/api-reference\n`;
|
|
6341
|
+
if (addons?.includes("starlight")) output += `${pc.cyan("•")} Docs: http://localhost:4321\n`;
|
|
6342
|
+
if (addons?.includes("fumadocs")) output += `${pc.cyan("•")} Fumadocs: http://localhost:4000\n`;
|
|
6372
6343
|
}
|
|
6373
|
-
if (isBackendSelf && api === "orpc") output += `${pc.cyan("•")} OpenAPI (Scalar UI): http://localhost:${webPort}/api/rpc/api-reference\n`;
|
|
6374
|
-
if (addons?.includes("starlight")) output += `${pc.cyan("•")} Docs: http://localhost:4321\n`;
|
|
6375
|
-
if (addons?.includes("fumadocs")) output += `${pc.cyan("•")} Fumadocs: http://localhost:4000\n`;
|
|
6376
6344
|
if (nativeInstructions) output += `\n${nativeInstructions.trim()}\n`;
|
|
6377
6345
|
if (databaseInstructions) output += `\n${databaseInstructions.trim()}\n`;
|
|
6378
6346
|
if (tauriInstructions) output += `\n${tauriInstructions.trim()}\n`;
|
package/package.json
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import path from "node:path";
|
|
2
2
|
import { defineConfig, env } from 'prisma/config'
|
|
3
|
-
{{#unless (eq dbSetup "prisma-postgres")}}
|
|
4
3
|
import dotenv from 'dotenv'
|
|
5
4
|
|
|
6
5
|
dotenv.config({
|
|
@@ -10,7 +9,6 @@ dotenv.config({
|
|
|
10
9
|
path: "../../apps/server/.env",
|
|
11
10
|
{{/if}}
|
|
12
11
|
})
|
|
13
|
-
{{/unless}}
|
|
14
12
|
|
|
15
13
|
export default defineConfig({
|
|
16
14
|
schema: path.join("prisma", "schema"),
|
|
@@ -13,6 +13,15 @@ const prisma = new PrismaClient({
|
|
|
13
13
|
}),
|
|
14
14
|
});
|
|
15
15
|
|
|
16
|
+
{{else if (eq dbSetup "prisma-postgres")}}
|
|
17
|
+
import { PrismaPostgresAdapter } from "@prisma/adapter-ppg";
|
|
18
|
+
|
|
19
|
+
const adapter = new PrismaPostgresAdapter({
|
|
20
|
+
connectionString: env.DATABASE_URL || "",
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
const prisma = new PrismaClient({ adapter });
|
|
24
|
+
|
|
16
25
|
{{else}}
|
|
17
26
|
import { PrismaPg } from "@prisma/adapter-pg";
|
|
18
27
|
|
|
@@ -38,6 +47,15 @@ const adapter = new PrismaNeon({
|
|
|
38
47
|
|
|
39
48
|
const prisma = new PrismaClient({ adapter });
|
|
40
49
|
|
|
50
|
+
{{else if (eq dbSetup "prisma-postgres")}}
|
|
51
|
+
import { PrismaPostgresAdapter } from "@prisma/adapter-ppg";
|
|
52
|
+
|
|
53
|
+
const adapter = new PrismaPostgresAdapter({
|
|
54
|
+
connectionString: process.env.DATABASE_URL || "",
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
const prisma = new PrismaClient({ adapter });
|
|
58
|
+
|
|
41
59
|
{{else}}
|
|
42
60
|
import { PrismaPg } from "@prisma/adapter-pg";
|
|
43
61
|
|
|
@@ -24,128 +24,124 @@ import { SignUp } from "@/components/sign-up";
|
|
|
24
24
|
import { useQuery } from "convex/react";
|
|
25
25
|
import { api } from "@{{projectName}}/backend/convex/_generated/api";
|
|
26
26
|
{{/if}}
|
|
27
|
+
{{#unless (eq backend "none")}}
|
|
27
28
|
import { Ionicons } from "@expo/vector-icons";
|
|
29
|
+
{{/unless}}
|
|
28
30
|
import { Card, Chip, useThemeColor } from "heroui-native";
|
|
29
31
|
|
|
30
32
|
export default function Home() {
|
|
31
33
|
{{#if (eq api "orpc")}}
|
|
32
|
-
|
|
34
|
+
const healthCheck = useQuery(orpc.healthCheck.queryOptions());
|
|
33
35
|
{{/if}}
|
|
34
36
|
{{#if (eq api "trpc")}}
|
|
35
|
-
|
|
37
|
+
const healthCheck = useQuery(trpc.healthCheck.queryOptions());
|
|
36
38
|
{{/if}}
|
|
37
39
|
{{#if (and (eq backend "convex") (eq auth "clerk"))}}
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
40
|
+
const { user } = useUser();
|
|
41
|
+
const healthCheck = useQuery(api.healthCheck.get);
|
|
42
|
+
const privateData = useQuery(api.privateData.get);
|
|
41
43
|
{{else if (and (eq backend "convex") (eq auth "better-auth"))}}
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
44
|
+
const healthCheck = useQuery(api.healthCheck.get);
|
|
45
|
+
const { isAuthenticated } = useConvexAuth();
|
|
46
|
+
const user = useQuery(api.auth.getCurrentUser, isAuthenticated ? {} : "skip");
|
|
45
47
|
{{else if (eq backend "convex")}}
|
|
46
|
-
|
|
48
|
+
const healthCheck = useQuery(api.healthCheck.get);
|
|
47
49
|
{{/if}}
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
50
|
+
{{#unless (eq backend "none")}}
|
|
51
|
+
const mutedColor = useThemeColor("muted");
|
|
52
|
+
const successColor = useThemeColor("success");
|
|
53
|
+
const dangerColor = useThemeColor("danger");
|
|
51
54
|
|
|
52
55
|
{{#if (eq backend "convex")}}
|
|
53
|
-
|
|
54
|
-
|
|
56
|
+
const isConnected = healthCheck === "OK";
|
|
57
|
+
const isLoading = healthCheck === undefined;
|
|
55
58
|
{{else}}
|
|
56
59
|
{{#unless (eq api "none")}}
|
|
57
|
-
|
|
58
|
-
|
|
60
|
+
const isConnected = healthCheck?.data === "OK";
|
|
61
|
+
const isLoading = healthCheck?.isLoading;
|
|
59
62
|
{{/unless}}
|
|
60
63
|
{{/if}}
|
|
64
|
+
{{/unless}}
|
|
61
65
|
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
{{#unless (and (eq backend "convex") (eq auth "better-auth"))}}
|
|
71
|
-
<Card variant="secondary" className="p-6">
|
|
72
|
-
<View className="flex-row items-center justify-between mb-4">
|
|
73
|
-
<Card.Title>System Status</Card.Title>
|
|
74
|
-
<Chip
|
|
75
|
-
variant="secondary"
|
|
76
|
-
color={isConnected ? "success" : "danger"}
|
|
77
|
-
size="sm"
|
|
78
|
-
>
|
|
79
|
-
<Chip.Label>
|
|
80
|
-
{isConnected ? "LIVE" : "OFFLINE"}
|
|
81
|
-
</Chip.Label>
|
|
82
|
-
</Chip>
|
|
83
|
-
</View>
|
|
84
|
-
<Card className="p-4">
|
|
85
|
-
<View className="flex-row items-center">
|
|
86
|
-
<View
|
|
87
|
-
className={`w-3 h-3 rounded-full mr-3 ${
|
|
88
|
-
isConnected ? "bg-success" : "bg-muted"
|
|
89
|
-
}`}
|
|
90
|
-
/>
|
|
91
|
-
<View className="flex-1">
|
|
92
|
-
<Text className="text-foreground font-medium mb-1">
|
|
93
|
-
{{#if (eq backend "convex")}}
|
|
94
|
-
Convex Backend
|
|
95
|
-
{{else}}
|
|
96
|
-
{{#unless (eq api "none")}}
|
|
97
|
-
{{#if (eq api "orpc")}}ORPC{{else}}TRPC{{/if}} Backend
|
|
98
|
-
{{/unless}}
|
|
99
|
-
{{/if}}
|
|
100
|
-
</Text>
|
|
101
|
-
<Card.Description>
|
|
102
|
-
{isLoading
|
|
103
|
-
? "Checking connection..."
|
|
104
|
-
: isConnected
|
|
105
|
-
? "Connected to API"
|
|
106
|
-
: "API Disconnected"}
|
|
107
|
-
</Card.Description>
|
|
108
|
-
</View>
|
|
109
|
-
{isLoading && (
|
|
110
|
-
<Ionicons name="hourglass-outline" size={20} color={mutedColor} />
|
|
111
|
-
)}
|
|
112
|
-
{!isLoading && isConnected && (
|
|
113
|
-
<Ionicons name="checkmark-circle" size={20} color={successColor} />
|
|
114
|
-
)}
|
|
115
|
-
{!isLoading && !isConnected && (
|
|
116
|
-
<Ionicons name="close-circle" size={20} color={dangerColor} />
|
|
117
|
-
)}
|
|
118
|
-
</View>
|
|
119
|
-
</Card>
|
|
120
|
-
</Card>
|
|
121
|
-
{{/unless}}
|
|
66
|
+
return (
|
|
67
|
+
<Container className="p-6">
|
|
68
|
+
<View className="py-4 mb-6">
|
|
69
|
+
<Text className="text-4xl font-bold text-foreground mb-2">
|
|
70
|
+
BETTER T STACK
|
|
71
|
+
</Text>
|
|
72
|
+
</View>
|
|
122
73
|
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
74
|
+
{{#unless (or (eq backend "none") (and (eq backend "convex") (eq auth "better-auth")))}}
|
|
75
|
+
<Card variant="secondary" className="p-6">
|
|
76
|
+
<View className="flex-row items-center justify-between mb-4">
|
|
77
|
+
<Card.Title>System Status</Card.Title>
|
|
78
|
+
<Chip variant="secondary" color={isConnected ? "success" : "danger" } size="sm">
|
|
79
|
+
<Chip.Label>
|
|
80
|
+
{isConnected ? "LIVE" : "OFFLINE"}
|
|
81
|
+
</Chip.Label>
|
|
82
|
+
</Chip>
|
|
83
|
+
</View>
|
|
84
|
+
<Card className="p-4">
|
|
85
|
+
<View className="flex-row items-center">
|
|
86
|
+
<View className={`w-3 h-3 rounded-full mr-3 ${ isConnected ? "bg-success" : "bg-muted" }`} />
|
|
87
|
+
<View className="flex-1">
|
|
88
|
+
<Text className="text-foreground font-medium mb-1">
|
|
89
|
+
{{#if (eq backend "convex")}}
|
|
90
|
+
Convex Backend
|
|
91
|
+
{{else}}
|
|
92
|
+
{{#unless (eq api "none")}}
|
|
93
|
+
{{#if (eq api "orpc")}}ORPC{{else}}TRPC{{/if}} Backend
|
|
94
|
+
{{/unless}}
|
|
95
|
+
{{/if}}
|
|
131
96
|
</Text>
|
|
132
|
-
<
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
</Link>
|
|
140
|
-
<Link href="/(auth)/sign-up" asChild>
|
|
141
|
-
<Text className="text-primary font-semibold">Sign up</Text>
|
|
142
|
-
</Link>
|
|
97
|
+
<Card.Description>
|
|
98
|
+
{isLoading
|
|
99
|
+
? "Checking connection..."
|
|
100
|
+
: isConnected
|
|
101
|
+
? "Connected to API"
|
|
102
|
+
: "API Disconnected"}
|
|
103
|
+
</Card.Description>
|
|
143
104
|
</View>
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
105
|
+
{isLoading && (
|
|
106
|
+
<Ionicons name="hourglass-outline" size={20} color={mutedColor} />
|
|
107
|
+
)}
|
|
108
|
+
{!isLoading && isConnected && (
|
|
109
|
+
<Ionicons name="checkmark-circle" size={20} color={successColor} />
|
|
110
|
+
)}
|
|
111
|
+
{!isLoading && !isConnected && (
|
|
112
|
+
<Ionicons name="close-circle" size={20} color={dangerColor} />
|
|
113
|
+
)}
|
|
114
|
+
</View>
|
|
115
|
+
</Card>
|
|
116
|
+
</Card>
|
|
117
|
+
{{/unless}}
|
|
118
|
+
|
|
119
|
+
{{#if (and (eq backend "convex") (eq auth "clerk"))}}
|
|
120
|
+
<Authenticated>
|
|
121
|
+
<Card variant="secondary" className="mt-6 p-4">
|
|
122
|
+
<Text className="text-foreground text-base mb-2">
|
|
123
|
+
Hello {user?.emailAddresses[0].emailAddress}
|
|
124
|
+
</Text>
|
|
125
|
+
<Text className="text-muted text-sm mb-4">
|
|
126
|
+
Private Data: {privateData?.message}
|
|
127
|
+
</Text>
|
|
128
|
+
<SignOutButton />
|
|
129
|
+
</Card>
|
|
130
|
+
</Authenticated>
|
|
131
|
+
<Unauthenticated>
|
|
132
|
+
<View className="mt-6 gap-4">
|
|
133
|
+
<Link href="/(auth)/sign-in" asChild>
|
|
134
|
+
<Text className="text-primary font-semibold">Sign in</Text>
|
|
135
|
+
</Link>
|
|
136
|
+
<Link href="/(auth)/sign-up" asChild>
|
|
137
|
+
<Text className="text-primary font-semibold">Sign up</Text>
|
|
138
|
+
</Link>
|
|
139
|
+
</View>
|
|
140
|
+
</Unauthenticated>
|
|
141
|
+
<AuthLoading>
|
|
142
|
+
<Text className="text-muted">Loading...</Text>
|
|
143
|
+
</AuthLoading>
|
|
144
|
+
{{/if}}
|
|
145
|
+
</Container>
|
|
146
|
+
);
|
|
151
147
|
}
|
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
"dependencies": {
|
|
14
14
|
"@expo/metro-runtime": "~6.1.2",
|
|
15
15
|
"@expo/vector-icons": "^15.0.3",
|
|
16
|
-
"@gorhom/bottom-sheet": "^5",
|
|
16
|
+
"@gorhom/bottom-sheet": "^5.2.6",
|
|
17
17
|
"@react-navigation/drawer": "^7.3.9",
|
|
18
18
|
"@react-navigation/elements": "^2.8.1",
|
|
19
19
|
{{#if (includes examples "ai")}}
|
|
@@ -29,22 +29,22 @@
|
|
|
29
29
|
"expo-router": "~6.0.14",
|
|
30
30
|
"expo-secure-store": "~15.0.7",
|
|
31
31
|
"expo-status-bar": "~3.0.8",
|
|
32
|
-
"heroui-native": "
|
|
32
|
+
"heroui-native": "1.0.0-beta.3",
|
|
33
33
|
"react": "19.1.0",
|
|
34
34
|
"react-dom": "19.1.0",
|
|
35
35
|
"react-native": "0.81.5",
|
|
36
36
|
"react-native-gesture-handler": "~2.28.0",
|
|
37
37
|
"react-native-keyboard-controller": "1.18.5",
|
|
38
|
-
"react-native-reanimated": "~4.1.
|
|
38
|
+
"react-native-reanimated": "~4.1.5",
|
|
39
39
|
"react-native-safe-area-context": "5.6.0",
|
|
40
40
|
"react-native-screens": "~4.16.0",
|
|
41
41
|
"react-native-svg": "15.12.1",
|
|
42
42
|
"react-native-web": "^0.21.0",
|
|
43
43
|
"react-native-worklets": "0.5.1",
|
|
44
|
-
"tailwind-merge": "^3.
|
|
45
|
-
"tailwind-variants": "
|
|
46
|
-
"tailwindcss": "~4.1.
|
|
47
|
-
"uniwind": "^1.0.
|
|
44
|
+
"tailwind-merge": "^3.4.0",
|
|
45
|
+
"tailwind-variants": "3.1.1",
|
|
46
|
+
"tailwindcss": "~4.1.17",
|
|
47
|
+
"uniwind": "^1.0.4"
|
|
48
48
|
},
|
|
49
49
|
"devDependencies": {
|
|
50
50
|
"@types/node": "^24.10.0",
|