create-better-t-stack 3.9.0-pr730.0ee9844 → 3.10.0

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 (20) hide show
  1. package/dist/cli.mjs +1 -1
  2. package/dist/index.mjs +1 -1
  3. package/dist/{src-DW15ZRe9.mjs → src-QkFdHtZE.mjs} +14 -10
  4. package/package.json +2 -2
  5. package/templates/auth/better-auth/convex/backend/convex/auth.ts.hbs +4 -6
  6. package/templates/auth/better-auth/convex/backend/convex/http.ts.hbs +1 -5
  7. package/templates/auth/better-auth/convex/native/base/lib/auth-client.ts.hbs +6 -11
  8. package/templates/auth/better-auth/convex/web/react/next/src/components/user-menu.tsx.hbs +14 -17
  9. package/templates/auth/better-auth/convex/web/react/tanstack-router/src/components/user-menu.tsx.hbs +20 -21
  10. package/templates/auth/better-auth/convex/web/react/tanstack-start/src/components/user-menu.tsx.hbs +16 -18
  11. package/templates/auth/better-auth/fullstack/tanstack-start/src/routes/api/auth/$.ts.hbs +10 -6
  12. package/templates/auth/better-auth/web/react/next/src/components/user-menu.tsx.hbs +21 -22
  13. package/templates/auth/better-auth/web/react/react-router/src/components/user-menu.tsx.hbs +20 -22
  14. package/templates/auth/better-auth/web/react/tanstack-router/src/components/user-menu.tsx.hbs +22 -24
  15. package/templates/auth/better-auth/web/react/tanstack-start/src/components/user-menu.tsx.hbs +22 -24
  16. package/templates/backend/convex/packages/backend/convex/README.md +4 -4
  17. package/templates/backend/convex/packages/backend/convex/tsconfig.json.hbs +1 -1
  18. package/templates/frontend/react/next/src/components/mode-toggle.tsx.hbs +4 -6
  19. package/templates/frontend/react/react-router/src/components/mode-toggle.tsx.hbs +4 -6
  20. package/templates/frontend/react/tanstack-router/src/components/mode-toggle.tsx.hbs +4 -6
package/dist/cli.mjs CHANGED
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env node
2
- import { n as createBtsCli } from "./src-DW15ZRe9.mjs";
2
+ import { n as createBtsCli } from "./src-QkFdHtZE.mjs";
3
3
 
4
4
  //#region src/cli.ts
5
5
  createBtsCli().run();
package/dist/index.mjs 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-DW15ZRe9.mjs";
2
+ import { a as router, i as init, n as createBtsCli, o as sponsors, r as docs, t as builder } from "./src-QkFdHtZE.mjs";
3
3
 
4
4
  export { builder, createBtsCli, docs, init, router, sponsors };
@@ -1234,7 +1234,7 @@ const getLatestCLIVersion = () => {
1234
1234
  */
1235
1235
  function isTelemetryEnabled() {
1236
1236
  const BTS_TELEMETRY_DISABLED = process.env.BTS_TELEMETRY_DISABLED;
1237
- const BTS_TELEMETRY = "0";
1237
+ const BTS_TELEMETRY = "1";
1238
1238
  if (BTS_TELEMETRY_DISABLED !== void 0) return BTS_TELEMETRY_DISABLED !== "1";
1239
1239
  if (BTS_TELEMETRY !== void 0) return BTS_TELEMETRY === "1";
1240
1240
  return true;
@@ -1242,7 +1242,16 @@ function isTelemetryEnabled() {
1242
1242
 
1243
1243
  //#endregion
1244
1244
  //#region src/utils/analytics.ts
1245
- async function sendConvexEvent(payload) {}
1245
+ const CONVEX_INGEST_URL = "https://striped-seahorse-863.convex.site/api/analytics/ingest";
1246
+ async function sendConvexEvent(payload) {
1247
+ try {
1248
+ await fetch(CONVEX_INGEST_URL, {
1249
+ method: "POST",
1250
+ headers: { "Content-Type": "application/json" },
1251
+ body: JSON.stringify(payload)
1252
+ });
1253
+ } catch {}
1254
+ }
1246
1255
  async function trackProjectCreation(config, disableAnalytics = false) {
1247
1256
  if (!isTelemetryEnabled() || disableAnalytics) return;
1248
1257
  const { projectName: _projectName, projectDir: _projectDir, relativePath: _relativePath, ...safeConfig } = config;
@@ -4479,7 +4488,6 @@ async function setupEnvironmentVariables(config) {
4479
4488
  if (!await fs.pathExists(envLocalPath) || !(await fs.readFile(envLocalPath, "utf8")).includes("npx convex env set")) {
4480
4489
  let siteUrlComments = "";
4481
4490
  if (hasWeb) siteUrlComments += "# npx convex env set SITE_URL http://localhost:3001\n";
4482
- if (hasNative) siteUrlComments += "# npx convex env set NATIVE_SITE_URL http://localhost:8081 # For Expo Web\n";
4483
4491
  const convexCommands = `# Set Convex environment variables
4484
4492
  # npx convex env set BETTER_AUTH_SECRET=$(openssl rand -base64 32)
4485
4493
  ${siteUrlComments}`;
@@ -4491,11 +4499,6 @@ ${siteUrlComments}`;
4491
4499
  value: "",
4492
4500
  condition: true,
4493
4501
  comment: "Same as CONVEX_URL but ends in .site"
4494
- }, {
4495
- key: "NATIVE_SITE_URL",
4496
- value: "http://localhost:8081",
4497
- condition: true,
4498
- comment: "Expo Web URL for authentication"
4499
4502
  });
4500
4503
  if (hasWeb) convexBackendVars.push({
4501
4504
  key: hasNextJs ? "NEXT_PUBLIC_CONVEX_SITE_URL" : "VITE_CONVEX_SITE_URL",
@@ -6147,7 +6150,7 @@ async function displayPostInstallInstructions(config) {
6147
6150
  const tauriInstructions = addons?.includes("tauri") ? getTauriInstructions(runCmd) : "";
6148
6151
  const huskyInstructions = hasHusky ? getHuskyInstructions(runCmd) : "";
6149
6152
  const lintingInstructions = hasLinting ? getLintingInstructions(runCmd) : "";
6150
- const nativeInstructions = (frontend?.includes("native-bare") || frontend?.includes("native-uniwind") || frontend?.includes("native-unistyles")) && backend !== "none" ? getNativeInstructions(isConvex, isBackendSelf, frontend || []) : "";
6153
+ const nativeInstructions = (frontend?.includes("native-bare") || frontend?.includes("native-uniwind") || frontend?.includes("native-unistyles")) && backend !== "none" ? getNativeInstructions(isConvex, isBackendSelf, frontend || [], runCmd) : "";
6151
6154
  const pwaInstructions = addons?.includes("pwa") && frontend?.includes("react-router") ? getPwaInstructions() : "";
6152
6155
  const starlightInstructions = addons?.includes("starlight") ? getStarlightInstructions(runCmd) : "";
6153
6156
  const clerkInstructions = isConvex && config.auth === "clerk" ? getClerkInstructions() : "";
@@ -6213,13 +6216,14 @@ async function displayPostInstallInstructions(config) {
6213
6216
  output += pc.cyan("https://github.com/AmanVarshney01/create-better-t-stack");
6214
6217
  consola$1.box(output);
6215
6218
  }
6216
- function getNativeInstructions(isConvex, isBackendSelf, _frontend) {
6219
+ function getNativeInstructions(isConvex, isBackendSelf, frontend, runCmd) {
6217
6220
  const envVar = isConvex ? "EXPO_PUBLIC_CONVEX_URL" : "EXPO_PUBLIC_SERVER_URL";
6218
6221
  const exampleUrl = isConvex ? "https://<YOUR_CONVEX_URL>" : isBackendSelf ? "http://<YOUR_LOCAL_IP>:3001" : "http://<YOUR_LOCAL_IP>:3000";
6219
6222
  const envFileName = ".env";
6220
6223
  const ipNote = isConvex ? "your Convex deployment URL (find after running 'dev:setup')" : "your local IP address";
6221
6224
  let instructions = `${pc.yellow("NOTE:")} For Expo connectivity issues, update\n apps/native/${envFileName} with ${ipNote}:\n ${`${envVar}=${exampleUrl}`}\n`;
6222
6225
  if (isConvex) instructions += `\n${pc.yellow("IMPORTANT:")} When using local development with Convex and native apps,\n ensure you use your local IP address instead of localhost or 127.0.0.1\n for proper connectivity.\n`;
6226
+ if (frontend.includes("native-unistyles")) instructions += `\n${pc.yellow("NOTE:")} Unistyles requires a development build.\n cd apps/native and run ${runCmd} android or ${runCmd} ios\n`;
6223
6227
  return instructions;
6224
6228
  }
6225
6229
  function getHuskyInstructions(runCmd) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-better-t-stack",
3
- "version": "3.9.0-pr730.0ee9844",
3
+ "version": "3.10.0",
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.9.0-pr730.0ee9844",
70
+ "@better-t-stack/types": "^3.10.0",
71
71
  "@clack/prompts": "^1.0.0-alpha.8",
72
72
  "@orpc/server": "^1.12.2",
73
73
  "consola": "^3.4.2",
@@ -1,6 +1,6 @@
1
1
  import { createClient, type GenericCtx } from "@convex-dev/better-auth";
2
2
  {{#if (or (includes frontend "native-bare") (includes frontend "native-uniwind") (includes frontend "native-unistyles"))}}
3
- import { convex, crossDomain } from "@convex-dev/better-auth/plugins";
3
+ import { convex } from "@convex-dev/better-auth/plugins";
4
4
  import { expo } from "@better-auth/expo";
5
5
  {{else if (or (includes frontend "tanstack-router") (includes frontend "react-router") (includes frontend "nuxt") (includes frontend "svelte") (includes frontend "solid"))}}
6
6
  import { convex, crossDomain } from "@convex-dev/better-auth/plugins";
@@ -21,7 +21,6 @@ const siteUrl = process.env.SITE_URL!;
21
21
  {{/if}}
22
22
  {{#if (or (includes frontend "native-bare") (includes frontend "native-uniwind") (includes frontend "native-unistyles"))}}
23
23
  const nativeAppUrl = process.env.NATIVE_APP_URL || "mybettertapp://";
24
- const nativeSiteUrl = process.env.NATIVE_SITE_URL || "http://localhost:8081";
25
24
  {{/if}}
26
25
 
27
26
  export const authComponent = createClient<DataModel>(components.betterAuth);
@@ -30,11 +29,11 @@ function createAuth(ctx: GenericCtx<DataModel>) {
30
29
  return betterAuth({
31
30
  {{#if (and (or (includes frontend "native-bare") (includes frontend "native-uniwind") (includes frontend "native-unistyles")) (or (includes frontend "tanstack-start") (includes frontend "next")))}}
32
31
  baseURL: siteUrl,
33
- trustedOrigins: [siteUrl, nativeSiteUrl, nativeAppUrl],
32
+ trustedOrigins: [siteUrl, nativeAppUrl],
34
33
  {{else if (and (or (includes frontend "native-bare") (includes frontend "native-uniwind") (includes frontend "native-unistyles")) (or (includes frontend "tanstack-router") (includes frontend "react-router") (includes frontend "nuxt") (includes frontend "svelte") (includes frontend "solid")))}}
35
- trustedOrigins: [siteUrl, nativeSiteUrl, nativeAppUrl],
34
+ trustedOrigins: [siteUrl, nativeAppUrl],
36
35
  {{else if (or (includes frontend "native-bare") (includes frontend "native-uniwind") (includes frontend "native-unistyles"))}}
37
- trustedOrigins: [nativeSiteUrl, nativeAppUrl],
36
+ trustedOrigins: [nativeAppUrl],
38
37
  {{else if (or (includes frontend "tanstack-start") (includes frontend "next"))}}
39
38
  baseURL: siteUrl,
40
39
  trustedOrigins: [siteUrl],
@@ -49,7 +48,6 @@ function createAuth(ctx: GenericCtx<DataModel>) {
49
48
  plugins: [
50
49
  {{#if (or (includes frontend "native-bare") (includes frontend "native-uniwind") (includes frontend "native-unistyles"))}}
51
50
  expo(),
52
- crossDomain({ siteUrl: nativeSiteUrl }),
53
51
  {{else if (or (includes frontend "tanstack-router") (includes frontend "react-router") (includes frontend "nuxt") (includes frontend "svelte") (includes frontend "solid"))}}
54
52
  crossDomain({ siteUrl }),
55
53
  {{/if}}
@@ -3,14 +3,10 @@ import { authComponent, createAuth } from "./auth";
3
3
 
4
4
  const http = httpRouter();
5
5
 
6
- {{#if (or (includes frontend "tanstack-start") (includes frontend "next"))}}
7
- {{#if (or (includes frontend "native-bare") (includes frontend "native-uniwind") (includes frontend "native-unistyles"))}}
6
+ {{#if (or (includes frontend "tanstack-router") (includes frontend "react-router") (includes frontend "nuxt") (includes frontend "svelte") (includes frontend "solid"))}}
8
7
  authComponent.registerRoutes(http, createAuth, { cors: true });
9
8
  {{else}}
10
9
  authComponent.registerRoutes(http, createAuth);
11
10
  {{/if}}
12
- {{else}}
13
- authComponent.registerRoutes(http, createAuth, { cors: true });
14
- {{/if}}
15
11
 
16
12
  export default http;
@@ -1,22 +1,17 @@
1
1
  import { createAuthClient } from "better-auth/react";
2
- import { convexClient, crossDomainClient } from "@convex-dev/better-auth/client/plugins";
2
+ import { convexClient } from "@convex-dev/better-auth/client/plugins";
3
3
  import { expoClient } from "@better-auth/expo/client";
4
4
  import Constants from "expo-constants";
5
5
  import * as SecureStore from "expo-secure-store";
6
- import { Platform } from "react-native";
7
6
 
8
7
  export const authClient = createAuthClient({
9
8
  baseURL: process.env.EXPO_PUBLIC_CONVEX_SITE_URL,
10
9
  plugins: [
10
+ expoClient({
11
+ scheme: Constants.expoConfig?.scheme as string,
12
+ storagePrefix: Constants.expoConfig?.scheme as string,
13
+ storage: SecureStore,
14
+ }),
11
15
  convexClient(),
12
- ...(Platform.OS === "web"
13
- ? [crossDomainClient()]
14
- : [
15
- expoClient({
16
- scheme: Constants.expoConfig?.scheme as string,
17
- storagePrefix: Constants.expoConfig?.scheme as string,
18
- storage: SecureStore,
19
- }),
20
- ]),
21
16
  ],
22
17
  });
@@ -19,30 +19,27 @@ export default function UserMenu() {
19
19
 
20
20
  return (
21
21
  <DropdownMenu>
22
- <DropdownMenuTrigger>
23
- <Button variant="outline">{user?.name}</Button>
22
+ <DropdownMenuTrigger render={<Button variant="outline" />}>
23
+ {user?.name}
24
24
  </DropdownMenuTrigger>
25
25
  <DropdownMenuContent className="bg-card">
26
26
  <DropdownMenuGroup>
27
27
  <DropdownMenuLabel>My Account</DropdownMenuLabel>
28
28
  <DropdownMenuSeparator />
29
29
  <DropdownMenuItem>{user?.email}</DropdownMenuItem>
30
- <DropdownMenuItem>
31
- <Button
32
- variant="destructive"
33
- className="w-full"
34
- onClick={() => {
35
- authClient.signOut({
36
- fetchOptions: {
37
- onSuccess: () => {
38
- router.push("/dashboard");
39
- },
30
+ <DropdownMenuItem
31
+ variant="destructive"
32
+ onClick={() => {
33
+ authClient.signOut({
34
+ fetchOptions: {
35
+ onSuccess: () => {
36
+ router.push("/dashboard");
40
37
  },
41
- });
42
- }}
43
- >
44
- Sign Out
45
- </Button>
38
+ },
39
+ });
40
+ }}
41
+ >
42
+ Sign Out
46
43
  </DropdownMenuItem>
47
44
  </DropdownMenuGroup>
48
45
  </DropdownMenuContent>
@@ -1,3 +1,5 @@
1
+ import { useNavigate } from "@tanstack/react-router";
2
+
1
3
  import {
2
4
  DropdownMenu,
3
5
  DropdownMenuContent,
@@ -8,43 +10,40 @@ import {
8
10
  DropdownMenuTrigger,
9
11
  } from "@/components/ui/dropdown-menu";
10
12
  import { authClient } from "@/lib/auth-client";
11
- import { useNavigate } from "@tanstack/react-router";
12
- import { Button } from "./ui/button";
13
13
  import { useQuery } from "convex/react";
14
14
  import { api } from "@{{projectName}}/backend/convex/_generated/api";
15
15
 
16
+ import { Button } from "./ui/button";
17
+
16
18
  export default function UserMenu() {
17
19
  const navigate = useNavigate();
18
20
  const user = useQuery(api.auth.getCurrentUser)
19
21
 
20
22
  return (
21
23
  <DropdownMenu>
22
- <DropdownMenuTrigger>
23
- <Button variant="outline">{user?.name}</Button>
24
+ <DropdownMenuTrigger render={<Button variant="outline" />}>
25
+ {user?.name}
24
26
  </DropdownMenuTrigger>
25
27
  <DropdownMenuContent className="bg-card">
26
28
  <DropdownMenuGroup>
27
29
  <DropdownMenuLabel>My Account</DropdownMenuLabel>
28
30
  <DropdownMenuSeparator />
29
31
  <DropdownMenuItem>{user?.email}</DropdownMenuItem>
30
- <DropdownMenuItem>
31
- <Button
32
- variant="destructive"
33
- className="w-full"
34
- onClick={() => {
35
- authClient.signOut({
36
- fetchOptions: {
37
- onSuccess: () => {
38
- navigate({
39
- to: "/dashboard",
40
- });
41
- },
32
+ <DropdownMenuItem
33
+ variant="destructive"
34
+ onClick={() => {
35
+ authClient.signOut({
36
+ fetchOptions: {
37
+ onSuccess: () => {
38
+ navigate({
39
+ to: "/dashboard",
40
+ });
42
41
  },
43
- });
44
- }}
45
- >
46
- Sign Out
47
- </Button>
42
+ },
43
+ });
44
+ }}
45
+ >
46
+ Sign Out
48
47
  </DropdownMenuItem>
49
48
  </DropdownMenuGroup>
50
49
  </DropdownMenuContent>
@@ -8,39 +8,37 @@ import {
8
8
  DropdownMenuTrigger,
9
9
  } from "@/components/ui/dropdown-menu";
10
10
  import { authClient } from "@/lib/auth-client";
11
- import { Button } from "./ui/button";
12
11
  import { useQuery } from "convex/react";
13
12
  import { api } from "@{{projectName}}/backend/convex/_generated/api";
14
13
 
14
+ import { Button } from "./ui/button";
15
+
15
16
  export default function UserMenu() {
16
17
  const user = useQuery(api.auth.getCurrentUser)
17
18
 
18
19
  return (
19
20
  <DropdownMenu>
20
- <DropdownMenuTrigger>
21
- <Button variant="outline">{user?.name}</Button>
21
+ <DropdownMenuTrigger render={<Button variant="outline" />}>
22
+ {user?.name}
22
23
  </DropdownMenuTrigger>
23
24
  <DropdownMenuContent className="bg-card">
24
25
  <DropdownMenuGroup>
25
26
  <DropdownMenuLabel>My Account</DropdownMenuLabel>
26
27
  <DropdownMenuSeparator />
27
28
  <DropdownMenuItem>{user?.email}</DropdownMenuItem>
28
- <DropdownMenuItem>
29
- <Button
30
- variant="destructive"
31
- className="w-full"
32
- onClick={() => {
33
- authClient.signOut({
34
- fetchOptions: {
35
- onSuccess: () => {
36
- location.reload();
37
- },
29
+ <DropdownMenuItem
30
+ variant="destructive"
31
+ onClick={() => {
32
+ authClient.signOut({
33
+ fetchOptions: {
34
+ onSuccess: () => {
35
+ location.reload();
38
36
  },
39
- });
40
- }}
41
- >
42
- Sign Out
43
- </Button>
37
+ },
38
+ });
39
+ }}
40
+ >
41
+ Sign Out
44
42
  </DropdownMenuItem>
45
43
  </DropdownMenuGroup>
46
44
  </DropdownMenuContent>
@@ -1,11 +1,15 @@
1
- import { createFileRoute } from "@tanstack/react-router";
2
- import { handler } from "@/lib/auth-server";
1
+ import { auth } from '@{{projectName}}/auth'
2
+ import { createFileRoute } from '@tanstack/react-router'
3
3
 
4
- export const Route = createFileRoute("/api/auth/$")({
4
+ export const Route = createFileRoute('/api/auth/$')({
5
5
  server: {
6
6
  handlers: {
7
- GET: ({ request }) => handler(request),
8
- POST: ({ request }) => handler(request),
7
+ GET: ({ request }) => {
8
+ return auth.handler(request)
9
+ },
10
+ POST: ({ request }) => {
11
+ return auth.handler(request)
12
+ },
9
13
  },
10
14
  },
11
- });
15
+ })
@@ -1,3 +1,6 @@
1
+ import Link from "next/link";
2
+ import { useRouter } from "next/navigation";
3
+
1
4
  import {
2
5
  DropdownMenu,
3
6
  DropdownMenuContent,
@@ -8,10 +11,9 @@ import {
8
11
  DropdownMenuTrigger,
9
12
  } from "@/components/ui/dropdown-menu";
10
13
  import { authClient } from "@/lib/auth-client";
14
+
11
15
  import { Button } from "./ui/button";
12
16
  import { Skeleton } from "./ui/skeleton";
13
- import { useRouter } from "next/navigation";
14
- import Link from "next/link";
15
17
 
16
18
  export default function UserMenu() {
17
19
  const router = useRouter();
@@ -23,38 +25,35 @@ export default function UserMenu() {
23
25
 
24
26
  if (!session) {
25
27
  return (
26
- <Button variant="outline">
27
- <Link href="/login">Sign In</Link>
28
- </Button>
28
+ <Link href="/login">
29
+ <Button variant="outline">Sign In</Button>
30
+ </Link>
29
31
  );
30
32
  }
31
33
 
32
34
  return (
33
35
  <DropdownMenu>
34
- <DropdownMenuTrigger>
35
- <Button variant="outline">{session.user.name}</Button>
36
+ <DropdownMenuTrigger render={<Button variant="outline" />}>
37
+ {session.user.name}
36
38
  </DropdownMenuTrigger>
37
39
  <DropdownMenuContent className="bg-card">
38
40
  <DropdownMenuGroup>
39
41
  <DropdownMenuLabel>My Account</DropdownMenuLabel>
40
42
  <DropdownMenuSeparator />
41
43
  <DropdownMenuItem>{session.user.email}</DropdownMenuItem>
42
- <DropdownMenuItem>
43
- <Button
44
- variant="destructive"
45
- className="w-full"
46
- onClick={() => {
47
- authClient.signOut({
48
- fetchOptions: {
49
- onSuccess: () => {
50
- router.push("/");
51
- },
44
+ <DropdownMenuItem
45
+ variant="destructive"
46
+ onClick={() => {
47
+ authClient.signOut({
48
+ fetchOptions: {
49
+ onSuccess: () => {
50
+ router.push("/");
52
51
  },
53
- });
54
- }}
55
- >
56
- Sign Out
57
- </Button>
52
+ },
53
+ });
54
+ }}
55
+ >
56
+ Sign Out
58
57
  </DropdownMenuItem>
59
58
  </DropdownMenuGroup>
60
59
  </DropdownMenuContent>
@@ -1,3 +1,5 @@
1
+ import { Link, useNavigate } from "react-router";
2
+
1
3
  import {
2
4
  DropdownMenu,
3
5
  DropdownMenuContent,
@@ -8,10 +10,9 @@ import {
8
10
  DropdownMenuTrigger,
9
11
  } from "@/components/ui/dropdown-menu";
10
12
  import { authClient } from "@/lib/auth-client";
11
- import { useNavigate } from "react-router";
13
+
12
14
  import { Button } from "./ui/button";
13
15
  import { Skeleton } from "./ui/skeleton";
14
- import { Link } from "react-router";
15
16
 
16
17
  export default function UserMenu() {
17
18
  const navigate = useNavigate();
@@ -23,38 +24,35 @@ export default function UserMenu() {
23
24
 
24
25
  if (!session) {
25
26
  return (
26
- <Button variant="outline">
27
- <Link to="/login">Sign In</Link>
28
- </Button>
27
+ <Link to="/login">
28
+ <Button variant="outline">Sign In</Button>
29
+ </Link>
29
30
  );
30
31
  }
31
32
 
32
33
  return (
33
34
  <DropdownMenu>
34
- <DropdownMenuTrigger>
35
- <Button variant="outline">{session.user.name}</Button>
35
+ <DropdownMenuTrigger render={<Button variant="outline" />}>
36
+ {session.user.name}
36
37
  </DropdownMenuTrigger>
37
38
  <DropdownMenuContent className="bg-card">
38
39
  <DropdownMenuGroup>
39
40
  <DropdownMenuLabel>My Account</DropdownMenuLabel>
40
41
  <DropdownMenuSeparator />
41
42
  <DropdownMenuItem>{session.user.email}</DropdownMenuItem>
42
- <DropdownMenuItem>
43
- <Button
44
- variant="destructive"
45
- className="w-full"
46
- onClick={() => {
47
- authClient.signOut({
48
- fetchOptions: {
49
- onSuccess: () => {
50
- navigate("/");
51
- },
43
+ <DropdownMenuItem
44
+ variant="destructive"
45
+ onClick={() => {
46
+ authClient.signOut({
47
+ fetchOptions: {
48
+ onSuccess: () => {
49
+ navigate("/");
52
50
  },
53
- });
54
- }}
55
- >
56
- Sign Out
57
- </Button>
51
+ },
52
+ });
53
+ }}
54
+ >
55
+ Sign Out
58
56
  </DropdownMenuItem>
59
57
  </DropdownMenuGroup>
60
58
  </DropdownMenuContent>
@@ -1,3 +1,5 @@
1
+ import { Link, useNavigate } from "@tanstack/react-router";
2
+
1
3
  import {
2
4
  DropdownMenu,
3
5
  DropdownMenuContent,
@@ -8,10 +10,9 @@ import {
8
10
  DropdownMenuTrigger,
9
11
  } from "@/components/ui/dropdown-menu";
10
12
  import { authClient } from "@/lib/auth-client";
11
- import { useNavigate } from "@tanstack/react-router";
13
+
12
14
  import { Button } from "./ui/button";
13
15
  import { Skeleton } from "./ui/skeleton";
14
- import { Link } from "@tanstack/react-router";
15
16
 
16
17
  export default function UserMenu() {
17
18
  const navigate = useNavigate();
@@ -23,40 +24,37 @@ export default function UserMenu() {
23
24
 
24
25
  if (!session) {
25
26
  return (
26
- <Button variant="outline">
27
- <Link to="/login">Sign In</Link>
28
- </Button>
27
+ <Link to="/login">
28
+ <Button variant="outline">Sign In</Button>
29
+ </Link>
29
30
  );
30
31
  }
31
32
 
32
33
  return (
33
34
  <DropdownMenu>
34
- <DropdownMenuTrigger>
35
- <Button variant="outline">{session.user.name}</Button>
35
+ <DropdownMenuTrigger render={<Button variant="outline" />}>
36
+ {session.user.name}
36
37
  </DropdownMenuTrigger>
37
38
  <DropdownMenuContent className="bg-card">
38
39
  <DropdownMenuGroup>
39
40
  <DropdownMenuLabel>My Account</DropdownMenuLabel>
40
41
  <DropdownMenuSeparator />
41
42
  <DropdownMenuItem>{session.user.email}</DropdownMenuItem>
42
- <DropdownMenuItem>
43
- <Button
44
- variant="destructive"
45
- className="w-full"
46
- onClick={() => {
47
- authClient.signOut({
48
- fetchOptions: {
49
- onSuccess: () => {
50
- navigate({
51
- to: "/",
52
- });
53
- },
43
+ <DropdownMenuItem
44
+ variant="destructive"
45
+ onClick={() => {
46
+ authClient.signOut({
47
+ fetchOptions: {
48
+ onSuccess: () => {
49
+ navigate({
50
+ to: "/",
51
+ });
54
52
  },
55
- });
56
- }}
57
- >
58
- Sign Out
59
- </Button>
53
+ },
54
+ });
55
+ }}
56
+ >
57
+ Sign Out
60
58
  </DropdownMenuItem>
61
59
  </DropdownMenuGroup>
62
60
  </DropdownMenuContent>
@@ -1,3 +1,5 @@
1
+ import { Link, useNavigate } from "@tanstack/react-router";
2
+
1
3
  import {
2
4
  DropdownMenu,
3
5
  DropdownMenuContent,
@@ -8,10 +10,9 @@ import {
8
10
  DropdownMenuTrigger,
9
11
  } from "@/components/ui/dropdown-menu";
10
12
  import { authClient } from "@/lib/auth-client";
11
- import { useNavigate } from "@tanstack/react-router";
13
+
12
14
  import { Button } from "./ui/button";
13
15
  import { Skeleton } from "./ui/skeleton";
14
- import { Link } from "@tanstack/react-router";
15
16
 
16
17
  export default function UserMenu() {
17
18
  const navigate = useNavigate();
@@ -23,40 +24,37 @@ export default function UserMenu() {
23
24
 
24
25
  if (!session) {
25
26
  return (
26
- <Button variant="outline">
27
- <Link to="/login">Sign In</Link>
28
- </Button>
27
+ <Link to="/login">
28
+ <Button variant="outline">Sign In</Button>
29
+ </Link>
29
30
  );
30
31
  }
31
32
 
32
33
  return (
33
34
  <DropdownMenu>
34
- <DropdownMenuTrigger>
35
- <Button variant="outline">{session.user.name}</Button>
35
+ <DropdownMenuTrigger render={<Button variant="outline" />}>
36
+ {session.user.name}
36
37
  </DropdownMenuTrigger>
37
38
  <DropdownMenuContent className="bg-card">
38
39
  <DropdownMenuGroup>
39
40
  <DropdownMenuLabel>My Account</DropdownMenuLabel>
40
41
  <DropdownMenuSeparator />
41
42
  <DropdownMenuItem>{session.user.email}</DropdownMenuItem>
42
- <DropdownMenuItem>
43
- <Button
44
- variant="destructive"
45
- className="w-full"
46
- onClick={() => {
47
- authClient.signOut({
48
- fetchOptions: {
49
- onSuccess: () => {
50
- navigate({
51
- to: "/",
52
- });
53
- },
43
+ <DropdownMenuItem
44
+ variant="destructive"
45
+ onClick={() => {
46
+ authClient.signOut({
47
+ fetchOptions: {
48
+ onSuccess: () => {
49
+ navigate({
50
+ to: "/",
51
+ });
54
52
  },
55
- });
56
- }}
57
- >
58
- Sign Out
59
- </Button>
53
+ },
54
+ });
55
+ }}
56
+ >
57
+ Sign Out
60
58
  </DropdownMenuItem>
61
59
  </DropdownMenuGroup>
62
60
  </DropdownMenuContent>
@@ -6,7 +6,7 @@ See https://docs.convex.dev/functions for more.
6
6
  A query function that takes two arguments looks like:
7
7
 
8
8
  ```ts
9
- // functions.js
9
+ // convex/myFunctions.ts
10
10
  import { query } from "./_generated/server";
11
11
  import { v } from "convex/values";
12
12
 
@@ -36,7 +36,7 @@ export const myQueryFunction = query({
36
36
  Using this query function in a React component looks like:
37
37
 
38
38
  ```ts
39
- const data = useQuery(api.functions.myQueryFunction, {
39
+ const data = useQuery(api.myFunctions.myQueryFunction, {
40
40
  first: 10,
41
41
  second: "hello",
42
42
  });
@@ -45,7 +45,7 @@ const data = useQuery(api.functions.myQueryFunction, {
45
45
  A mutation function looks like:
46
46
 
47
47
  ```ts
48
- // functions.js
48
+ // convex/myFunctions.ts
49
49
  import { mutation } from "./_generated/server";
50
50
  import { v } from "convex/values";
51
51
 
@@ -73,7 +73,7 @@ export const myMutationFunction = mutation({
73
73
  Using this mutation function in a React component looks like:
74
74
 
75
75
  ```ts
76
- const mutation = useMutation(api.functions.myMutationFunction);
76
+ const mutation = useMutation(api.myFunctions.myMutationFunction);
77
77
  function handleButtonPress() {
78
78
  // fire and forget, the most common way to use mutations
79
79
  mutation({ first: "Hello!", second: "me" });
@@ -1,7 +1,7 @@
1
1
  {
2
2
  /* This TypeScript project config describes the environment that
3
3
  * Convex functions run in and is used to typecheck them.
4
- * You can modify it, but some settings required to use Convex.
4
+ * You can modify it, but some settings are required to use Convex.
5
5
  */
6
6
  "compilerOptions": {
7
7
  /* These settings are not required by Convex and can be modified. */
@@ -16,12 +16,10 @@ export function ModeToggle() {
16
16
 
17
17
  return (
18
18
  <DropdownMenu>
19
- <DropdownMenuTrigger asChild>
20
- <Button variant="outline" size="icon">
21
- <Sun className="h-[1.2rem] w-[1.2rem] rotate-0 scale-100 transition-all dark:-rotate-90 dark:scale-0" />
22
- <Moon className="absolute h-[1.2rem] w-[1.2rem] rotate-90 scale-0 transition-all dark:rotate-0 dark:scale-100" />
23
- <span className="sr-only">Toggle theme</span>
24
- </Button>
19
+ <DropdownMenuTrigger render={<Button variant="outline" size="icon" />}>
20
+ <Sun className="h-[1.2rem] w-[1.2rem] rotate-0 scale-100 transition-all dark:-rotate-90 dark:scale-0" />
21
+ <Moon className="absolute h-[1.2rem] w-[1.2rem] rotate-90 scale-0 transition-all dark:rotate-0 dark:scale-100" />
22
+ <span className="sr-only">Toggle theme</span>
25
23
  </DropdownMenuTrigger>
26
24
  <DropdownMenuContent align="end">
27
25
  <DropdownMenuItem onClick={() => setTheme("light")}>
@@ -14,12 +14,10 @@ export function ModeToggle() {
14
14
 
15
15
  return (
16
16
  <DropdownMenu>
17
- <DropdownMenuTrigger>
18
- <Button variant="outline" size="icon">
19
- <Sun className="h-[1.2rem] w-[1.2rem] scale-100 rotate-0 transition-all dark:scale-0 dark:-rotate-90" />
20
- <Moon className="absolute h-[1.2rem] w-[1.2rem] scale-0 rotate-90 transition-all dark:scale-100 dark:rotate-0" />
21
- <span className="sr-only">Toggle theme</span>
22
- </Button>
17
+ <DropdownMenuTrigger render={<Button variant="outline" size="icon" />}>
18
+ <Sun className="h-[1.2rem] w-[1.2rem] scale-100 rotate-0 transition-all dark:scale-0 dark:-rotate-90" />
19
+ <Moon className="absolute h-[1.2rem] w-[1.2rem] scale-0 rotate-90 transition-all dark:scale-100 dark:rotate-0" />
20
+ <span className="sr-only">Toggle theme</span>
23
21
  </DropdownMenuTrigger>
24
22
  <DropdownMenuContent align="end">
25
23
  <DropdownMenuItem onClick={() => setTheme("light")}>Light</DropdownMenuItem>
@@ -14,12 +14,10 @@ export function ModeToggle() {
14
14
 
15
15
  return (
16
16
  <DropdownMenu>
17
- <DropdownMenuTrigger>
18
- <Button variant="outline" size="icon">
19
- <Sun className="h-[1.2rem] w-[1.2rem] scale-100 rotate-0 transition-all dark:scale-0 dark:-rotate-90" />
20
- <Moon className="absolute h-[1.2rem] w-[1.2rem] scale-0 rotate-90 transition-all dark:scale-100 dark:rotate-0" />
21
- <span className="sr-only">Toggle theme</span>
22
- </Button>
17
+ <DropdownMenuTrigger render={<Button variant="outline" size="icon" />}>
18
+ <Sun className="h-[1.2rem] w-[1.2rem] scale-100 rotate-0 transition-all dark:scale-0 dark:-rotate-90" />
19
+ <Moon className="absolute h-[1.2rem] w-[1.2rem] scale-0 rotate-90 transition-all dark:scale-100 dark:rotate-0" />
20
+ <span className="sr-only">Toggle theme</span>
23
21
  </DropdownMenuTrigger>
24
22
  <DropdownMenuContent align="end">
25
23
  <DropdownMenuItem onClick={() => setTheme("light")}>Light</DropdownMenuItem>