create-better-t-stack 1.12.4 → 1.13.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.
- package/README.md +1 -2
- package/dist/index.js +73 -73
- package/package.json +1 -1
- package/template/base/apps/server/_gitignore +17 -2
- package/template/base/apps/web-base/_gitignore +39 -13
- package/template/base/apps/web-base/src/index.css +1 -1
- package/template/base/apps/web-next/next-env.d.ts +5 -0
- package/template/base/apps/web-next/next.config.ts +7 -0
- package/template/base/apps/web-next/package.json +41 -0
- package/template/base/apps/web-next/postcss.config.mjs +5 -0
- package/template/base/apps/web-next/src/app/favicon.ico +0 -0
- package/template/base/apps/web-next/src/app/layout.tsx +41 -0
- package/template/base/apps/web-next/src/app/page.tsx +83 -0
- package/template/base/apps/web-next/src/components/header.tsx +30 -0
- package/template/base/apps/web-next/src/components/mode-toggle.tsx +39 -0
- package/template/base/apps/web-next/src/components/providers.tsx +25 -0
- package/template/base/apps/web-next/src/components/theme-provider.tsx +11 -0
- package/template/base/apps/web-next/src/utils/trpc.ts +33 -0
- package/template/base/apps/web-next/tsconfig.json +28 -0
- package/template/with-auth/apps/server/src/lib/with-next-context.ts +14 -0
- package/template/with-auth/apps/server/src/with-next-app/api/auth/[...all]/route.ts +4 -0
- package/template/with-auth/apps/web-next/src/app/dashboard/page.tsx +31 -0
- package/template/with-auth/apps/web-next/src/app/login/page.tsx +16 -0
- package/template/with-auth/apps/web-next/src/components/header.tsx +33 -0
- package/template/with-auth/apps/web-next/src/components/sign-in-form.tsx +135 -0
- package/template/with-auth/apps/web-next/src/components/sign-up-form.tsx +160 -0
- package/template/with-auth/apps/web-next/src/components/theme-provider.tsx +11 -0
- package/template/with-auth/apps/web-next/src/components/user-menu.tsx +60 -0
- package/template/with-auth/apps/web-next/src/lib/auth-client.ts +5 -0
- package/template/with-auth/apps/web-next/src/utils/trpc.ts +39 -0
- package/template/with-next/apps/server/next-env.d.ts +5 -0
- package/template/with-next/apps/server/next.config.ts +7 -0
- package/template/with-next/apps/server/package.json +20 -0
- package/template/with-next/apps/server/src/app/api/trpc/[trpc]/route.ts +14 -0
- package/template/with-next/apps/server/src/app/route.ts +5 -0
- package/template/with-next/apps/server/src/lib/context.ts +9 -0
- package/template/with-next/apps/server/src/middleware.ts +19 -0
- package/template/with-next/apps/server/tsconfig.json +27 -0
- package/template/base/apps/web-tanstack-start/src/index.css +0 -135
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "create-better-t-stack",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.13.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
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -1,22 +1,32 @@
|
|
|
1
1
|
# prod
|
|
2
2
|
dist/
|
|
3
|
+
/build
|
|
4
|
+
/out/
|
|
3
5
|
|
|
4
6
|
# dev
|
|
5
7
|
.yarn/
|
|
8
|
+
!.yarn/patches
|
|
9
|
+
!.yarn/plugins
|
|
6
10
|
!.yarn/releases
|
|
11
|
+
!.yarn/versions
|
|
7
12
|
.vscode/*
|
|
8
13
|
!.vscode/launch.json
|
|
9
14
|
!.vscode/*.code-snippets
|
|
10
15
|
.idea/workspace.xml
|
|
11
16
|
.idea/usage.statistics.xml
|
|
12
17
|
.idea/shelf
|
|
18
|
+
.wrangler
|
|
19
|
+
/.next/
|
|
20
|
+
.vercel
|
|
13
21
|
|
|
14
22
|
# deps
|
|
15
23
|
node_modules/
|
|
16
|
-
|
|
24
|
+
/node_modules
|
|
25
|
+
/.pnp
|
|
26
|
+
.pnp.*
|
|
17
27
|
|
|
18
28
|
# env
|
|
19
|
-
.env
|
|
29
|
+
.env*
|
|
20
30
|
.env.production
|
|
21
31
|
.dev.vars
|
|
22
32
|
|
|
@@ -31,6 +41,11 @@ lerna-debug.log*
|
|
|
31
41
|
|
|
32
42
|
# misc
|
|
33
43
|
.DS_Store
|
|
44
|
+
*.pem
|
|
34
45
|
|
|
35
46
|
# local db
|
|
36
47
|
*.db*
|
|
48
|
+
|
|
49
|
+
# typescript
|
|
50
|
+
*.tsbuildinfo
|
|
51
|
+
next-env.d.ts
|
|
@@ -1,26 +1,52 @@
|
|
|
1
|
-
#
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
1
|
+
# Dependencies
|
|
2
|
+
/node_modules
|
|
3
|
+
/.pnp
|
|
4
|
+
.pnp.*
|
|
5
|
+
.yarn/*
|
|
6
|
+
!.yarn/patches
|
|
7
|
+
!.yarn/plugins
|
|
8
|
+
!.yarn/releases
|
|
9
|
+
!.yarn/versions
|
|
5
10
|
|
|
6
|
-
#
|
|
7
|
-
|
|
8
|
-
|
|
11
|
+
# Testing
|
|
12
|
+
/coverage
|
|
13
|
+
|
|
14
|
+
# Build outputs
|
|
15
|
+
/.next/
|
|
16
|
+
/out/
|
|
17
|
+
/build/
|
|
18
|
+
/dist/
|
|
9
19
|
.vinxi
|
|
10
20
|
.output
|
|
21
|
+
.react-router/
|
|
22
|
+
|
|
23
|
+
# Deployment
|
|
11
24
|
.vercel
|
|
12
25
|
.netlify
|
|
13
26
|
.wrangler
|
|
14
27
|
|
|
28
|
+
# Environment & local files
|
|
29
|
+
.env*
|
|
30
|
+
!.env.example
|
|
31
|
+
.DS_Store
|
|
32
|
+
*.pem
|
|
33
|
+
*.local
|
|
34
|
+
|
|
35
|
+
# Logs
|
|
36
|
+
npm-debug.log*
|
|
37
|
+
yarn-debug.log*
|
|
38
|
+
yarn-error.log*
|
|
39
|
+
.pnpm-debug.log*
|
|
40
|
+
*.log*
|
|
41
|
+
|
|
42
|
+
# TypeScript
|
|
43
|
+
*.tsbuildinfo
|
|
44
|
+
next-env.d.ts
|
|
45
|
+
|
|
15
46
|
# IDE
|
|
16
47
|
.vscode/*
|
|
17
48
|
!.vscode/extensions.json
|
|
18
49
|
.idea
|
|
19
50
|
|
|
20
|
-
|
|
21
|
-
!.env.example
|
|
22
|
-
|
|
51
|
+
# Other
|
|
23
52
|
dev-dist
|
|
24
|
-
|
|
25
|
-
/.react-router/
|
|
26
|
-
/build/
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
@custom-variant dark (&:where(.dark, .dark *));
|
|
5
5
|
|
|
6
6
|
@theme {
|
|
7
|
-
--font-sans: "Inter", ui-sans-serif, system-ui, sans-serif,
|
|
7
|
+
--font-sans: "Inter", "Geist", ui-sans-serif, system-ui, sans-serif,
|
|
8
8
|
"Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";
|
|
9
9
|
}
|
|
10
10
|
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "web",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"private": true,
|
|
5
|
+
"scripts": {
|
|
6
|
+
"dev": "next dev --turbopack --port=3001",
|
|
7
|
+
"build": "next build",
|
|
8
|
+
"start": "next start",
|
|
9
|
+
"lint": "next lint"
|
|
10
|
+
},
|
|
11
|
+
"dependencies": {
|
|
12
|
+
"@radix-ui/react-checkbox": "^1.1.5",
|
|
13
|
+
"@radix-ui/react-dropdown-menu": "^2.1.7",
|
|
14
|
+
"@radix-ui/react-label": "^2.1.3",
|
|
15
|
+
"@radix-ui/react-slot": "^1.2.0",
|
|
16
|
+
"@tanstack/react-form": "^1.3.2",
|
|
17
|
+
"@tanstack/react-query": "^5.72.2",
|
|
18
|
+
"@trpc/client": "^11.1.0",
|
|
19
|
+
"@trpc/tanstack-react-query": "^11.1.0",
|
|
20
|
+
"class-variance-authority": "^0.7.1",
|
|
21
|
+
"clsx": "^2.1.1",
|
|
22
|
+
"lucide-react": "^0.487.0",
|
|
23
|
+
"next": "15.3.0",
|
|
24
|
+
"next-themes": "^0.4.6",
|
|
25
|
+
"react": "^19.0.0",
|
|
26
|
+
"react-dom": "^19.0.0",
|
|
27
|
+
"sonner": "^2.0.3",
|
|
28
|
+
"tailwind-merge": "^3.2.0",
|
|
29
|
+
"tw-animate-css": "^1.2.5",
|
|
30
|
+
"zod": "^3.24.2"
|
|
31
|
+
},
|
|
32
|
+
"devDependencies": {
|
|
33
|
+
"@tailwindcss/postcss": "^4",
|
|
34
|
+
"@tanstack/react-query-devtools": "^5.72.2",
|
|
35
|
+
"@types/node": "^20",
|
|
36
|
+
"@types/react": "^19",
|
|
37
|
+
"@types/react-dom": "^19",
|
|
38
|
+
"tailwindcss": "^4",
|
|
39
|
+
"typescript": "^5"
|
|
40
|
+
}
|
|
41
|
+
}
|
|
Binary file
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import type { Metadata } from "next";
|
|
2
|
+
import { Geist, Geist_Mono } from "next/font/google";
|
|
3
|
+
import "../index.css";
|
|
4
|
+
import Providers from "@/components/providers";
|
|
5
|
+
import Header from "@/components/header";
|
|
6
|
+
|
|
7
|
+
const geistSans = Geist({
|
|
8
|
+
variable: "--font-geist-sans",
|
|
9
|
+
subsets: ["latin"],
|
|
10
|
+
});
|
|
11
|
+
|
|
12
|
+
const geistMono = Geist_Mono({
|
|
13
|
+
variable: "--font-geist-mono",
|
|
14
|
+
subsets: ["latin"],
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
export const metadata: Metadata = {
|
|
18
|
+
title: "Create Next App",
|
|
19
|
+
description: "Generated by create next app",
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
export default function RootLayout({
|
|
23
|
+
children,
|
|
24
|
+
}: Readonly<{
|
|
25
|
+
children: React.ReactNode;
|
|
26
|
+
}>) {
|
|
27
|
+
return (
|
|
28
|
+
<html lang="en" suppressHydrationWarning>
|
|
29
|
+
<body
|
|
30
|
+
className={`${geistSans.variable} ${geistMono.variable} antialiased`}
|
|
31
|
+
>
|
|
32
|
+
<Providers>
|
|
33
|
+
<div className="grid grid-rows-[auto_1fr] h-svh">
|
|
34
|
+
<Header />
|
|
35
|
+
{children}
|
|
36
|
+
</div>
|
|
37
|
+
</Providers>
|
|
38
|
+
</body>
|
|
39
|
+
</html>
|
|
40
|
+
);
|
|
41
|
+
}
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
"use client"
|
|
2
|
+
import { trpc } from "@/utils/trpc";
|
|
3
|
+
import { useQuery } from "@tanstack/react-query";
|
|
4
|
+
|
|
5
|
+
const TITLE_TEXT = `
|
|
6
|
+
██████╗ ███████╗████████╗████████╗███████╗██████╗
|
|
7
|
+
██╔══██╗██╔════╝╚══██╔══╝╚══██╔══╝██╔════╝██╔══██╗
|
|
8
|
+
██████╔╝█████╗ ██║ ██║ █████╗ ██████╔╝
|
|
9
|
+
██╔══██╗██╔══╝ ██║ ██║ ██╔══╝ ██╔══██╗
|
|
10
|
+
██████╔╝███████╗ ██║ ██║ ███████╗██║ ██║
|
|
11
|
+
╚═════╝ ╚══════╝ ╚═╝ ╚═╝ ╚══════╝╚═╝ ╚═╝
|
|
12
|
+
|
|
13
|
+
████████╗ ███████╗████████╗ █████╗ ██████╗██╗ ██╗
|
|
14
|
+
╚══██╔══╝ ██╔════╝╚══██╔══╝██╔══██╗██╔════╝██║ ██╔╝
|
|
15
|
+
██║ ███████╗ ██║ ███████║██║ █████╔╝
|
|
16
|
+
██║ ╚════██║ ██║ ██╔══██║██║ ██╔═██╗
|
|
17
|
+
██║ ███████║ ██║ ██║ ██║╚██████╗██║ ██╗
|
|
18
|
+
╚═╝ ╚══════╝ ╚═╝ ╚═╝ ╚═╝ ╚═════╝╚═╝ ╚═╝
|
|
19
|
+
`;
|
|
20
|
+
|
|
21
|
+
export default function Home() {
|
|
22
|
+
const healthCheck = useQuery(trpc.healthCheck.queryOptions());
|
|
23
|
+
|
|
24
|
+
return (
|
|
25
|
+
<div className="container mx-auto max-w-3xl px-4 py-2">
|
|
26
|
+
<pre className="overflow-x-auto font-mono text-sm">{TITLE_TEXT}</pre>
|
|
27
|
+
<div className="grid gap-6">
|
|
28
|
+
<section className="rounded-lg border p-4">
|
|
29
|
+
<h2 className="mb-2 font-medium">API Status</h2>
|
|
30
|
+
<div className="flex items-center gap-2">
|
|
31
|
+
<div
|
|
32
|
+
className={`h-2 w-2 rounded-full ${healthCheck.data ? "bg-green-500" : "bg-red-500"}`}
|
|
33
|
+
/>
|
|
34
|
+
<span className="text-sm text-muted-foreground">
|
|
35
|
+
{healthCheck.isLoading
|
|
36
|
+
? "Checking..."
|
|
37
|
+
: healthCheck.data
|
|
38
|
+
? "Connected"
|
|
39
|
+
: "Disconnected"}
|
|
40
|
+
</span>
|
|
41
|
+
</div>
|
|
42
|
+
</section>
|
|
43
|
+
|
|
44
|
+
<section>
|
|
45
|
+
<h2 className="mb-3 font-medium">Core Features</h2>
|
|
46
|
+
<ul className="grid grid-cols-2 gap-3">
|
|
47
|
+
<FeatureItem
|
|
48
|
+
title="Type-Safe API"
|
|
49
|
+
description="End-to-end type safety with tRPC"
|
|
50
|
+
/>
|
|
51
|
+
<FeatureItem
|
|
52
|
+
title="Modern React"
|
|
53
|
+
description="TanStack Router + TanStack Query"
|
|
54
|
+
/>
|
|
55
|
+
<FeatureItem
|
|
56
|
+
title="Fast Backend"
|
|
57
|
+
description="Lightweight Hono server"
|
|
58
|
+
/>
|
|
59
|
+
<FeatureItem
|
|
60
|
+
title="Beautiful UI"
|
|
61
|
+
description="TailwindCSS + shadcn/ui components"
|
|
62
|
+
/>
|
|
63
|
+
</ul>
|
|
64
|
+
</section>
|
|
65
|
+
</div>
|
|
66
|
+
</div>
|
|
67
|
+
);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
function FeatureItem({
|
|
71
|
+
title,
|
|
72
|
+
description,
|
|
73
|
+
}: {
|
|
74
|
+
title: string;
|
|
75
|
+
description: string;
|
|
76
|
+
}) {
|
|
77
|
+
return (
|
|
78
|
+
<li className="border-l-2 border-primary py-1 pl-3">
|
|
79
|
+
<h3 className="font-medium">{title}</h3>
|
|
80
|
+
<p className="text-sm text-muted-foreground">{description}</p>
|
|
81
|
+
</li>
|
|
82
|
+
);
|
|
83
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
"use client"
|
|
2
|
+
import Link from "next/link";
|
|
3
|
+
import { ModeToggle } from "./mode-toggle";
|
|
4
|
+
|
|
5
|
+
export default function Header() {
|
|
6
|
+
const links = [
|
|
7
|
+
{ to: "/", label: "Home" },
|
|
8
|
+
];
|
|
9
|
+
|
|
10
|
+
return (
|
|
11
|
+
<div>
|
|
12
|
+
<div className="flex flex-row items-center justify-between px-2 py-1">
|
|
13
|
+
<nav className="flex gap-4 text-lg">
|
|
14
|
+
{links.map(({ to, label }) => (
|
|
15
|
+
<Link
|
|
16
|
+
key={to}
|
|
17
|
+
href={to}
|
|
18
|
+
>
|
|
19
|
+
{label}
|
|
20
|
+
</Link>
|
|
21
|
+
))}
|
|
22
|
+
</nav>
|
|
23
|
+
<div className="flex items-center gap-2">
|
|
24
|
+
<ModeToggle />
|
|
25
|
+
</div>
|
|
26
|
+
</div>
|
|
27
|
+
<hr />
|
|
28
|
+
</div>
|
|
29
|
+
);
|
|
30
|
+
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
"use client"
|
|
2
|
+
|
|
3
|
+
import * as React from "react"
|
|
4
|
+
import { Moon, Sun } from "lucide-react"
|
|
5
|
+
import { useTheme } from "next-themes"
|
|
6
|
+
import { Button } from "@/components/ui/button"
|
|
7
|
+
import {
|
|
8
|
+
DropdownMenu,
|
|
9
|
+
DropdownMenuContent,
|
|
10
|
+
DropdownMenuItem,
|
|
11
|
+
DropdownMenuTrigger,
|
|
12
|
+
} from "@/components/ui/dropdown-menu"
|
|
13
|
+
|
|
14
|
+
export function ModeToggle() {
|
|
15
|
+
const { setTheme } = useTheme()
|
|
16
|
+
|
|
17
|
+
return (
|
|
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>
|
|
25
|
+
</DropdownMenuTrigger>
|
|
26
|
+
<DropdownMenuContent align="end">
|
|
27
|
+
<DropdownMenuItem onClick={() => setTheme("light")}>
|
|
28
|
+
Light
|
|
29
|
+
</DropdownMenuItem>
|
|
30
|
+
<DropdownMenuItem onClick={() => setTheme("dark")}>
|
|
31
|
+
Dark
|
|
32
|
+
</DropdownMenuItem>
|
|
33
|
+
<DropdownMenuItem onClick={() => setTheme("system")}>
|
|
34
|
+
System
|
|
35
|
+
</DropdownMenuItem>
|
|
36
|
+
</DropdownMenuContent>
|
|
37
|
+
</DropdownMenu>
|
|
38
|
+
)
|
|
39
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
"use client"
|
|
2
|
+
import { QueryClientProvider } from "@tanstack/react-query";
|
|
3
|
+
import { queryClient } from "@/utils/trpc";
|
|
4
|
+
import { ThemeProvider } from "./theme-provider";
|
|
5
|
+
import { Toaster } from "./ui/sonner";
|
|
6
|
+
|
|
7
|
+
export default function Providers({
|
|
8
|
+
children,
|
|
9
|
+
}: {
|
|
10
|
+
children: React.ReactNode
|
|
11
|
+
}) {
|
|
12
|
+
return (
|
|
13
|
+
<ThemeProvider
|
|
14
|
+
attribute="class"
|
|
15
|
+
defaultTheme="system"
|
|
16
|
+
enableSystem
|
|
17
|
+
disableTransitionOnChange
|
|
18
|
+
>
|
|
19
|
+
<QueryClientProvider client={queryClient}>
|
|
20
|
+
{children}
|
|
21
|
+
</QueryClientProvider>
|
|
22
|
+
<Toaster richColors />
|
|
23
|
+
</ThemeProvider>
|
|
24
|
+
)
|
|
25
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
"use client"
|
|
2
|
+
|
|
3
|
+
import * as React from "react"
|
|
4
|
+
import { ThemeProvider as NextThemesProvider } from "next-themes"
|
|
5
|
+
|
|
6
|
+
export function ThemeProvider({
|
|
7
|
+
children,
|
|
8
|
+
...props
|
|
9
|
+
}: React.ComponentProps<typeof NextThemesProvider>) {
|
|
10
|
+
return <NextThemesProvider {...props}>{children}</NextThemesProvider>
|
|
11
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { QueryCache, QueryClient } from '@tanstack/react-query';
|
|
2
|
+
import { createTRPCClient, httpBatchLink } from '@trpc/client';
|
|
3
|
+
import { createTRPCOptionsProxy } from '@trpc/tanstack-react-query';
|
|
4
|
+
import type { AppRouter } from '../../../server/src/routers';
|
|
5
|
+
import { toast } from 'sonner';
|
|
6
|
+
|
|
7
|
+
export const queryClient = new QueryClient({
|
|
8
|
+
queryCache: new QueryCache({
|
|
9
|
+
onError: (error) => {
|
|
10
|
+
toast.error(error.message, {
|
|
11
|
+
action: {
|
|
12
|
+
label: "retry",
|
|
13
|
+
onClick: () => {
|
|
14
|
+
queryClient.invalidateQueries();
|
|
15
|
+
},
|
|
16
|
+
},
|
|
17
|
+
});
|
|
18
|
+
},
|
|
19
|
+
}),
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
const trpcClient = createTRPCClient<AppRouter>({
|
|
23
|
+
links: [
|
|
24
|
+
httpBatchLink({
|
|
25
|
+
url: `${process.env.NEXT_PUBLIC_SERVER_URL}/api/trpc`,
|
|
26
|
+
}),
|
|
27
|
+
],
|
|
28
|
+
})
|
|
29
|
+
|
|
30
|
+
export const trpc = createTRPCOptionsProxy<AppRouter>({
|
|
31
|
+
client: trpcClient,
|
|
32
|
+
queryClient,
|
|
33
|
+
});
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"target": "ES2017",
|
|
4
|
+
"lib": ["dom", "dom.iterable", "esnext"],
|
|
5
|
+
"allowJs": true,
|
|
6
|
+
"skipLibCheck": true,
|
|
7
|
+
"strict": true,
|
|
8
|
+
"noEmit": true,
|
|
9
|
+
"esModuleInterop": true,
|
|
10
|
+
"module": "esnext",
|
|
11
|
+
"moduleResolution": "bundler",
|
|
12
|
+
"resolveJsonModule": true,
|
|
13
|
+
"isolatedModules": true,
|
|
14
|
+
"verbatimModuleSyntax": true,
|
|
15
|
+
"jsx": "preserve",
|
|
16
|
+
"incremental": true,
|
|
17
|
+
"plugins": [
|
|
18
|
+
{
|
|
19
|
+
"name": "next"
|
|
20
|
+
}
|
|
21
|
+
],
|
|
22
|
+
"paths": {
|
|
23
|
+
"@/*": ["./src/*"]
|
|
24
|
+
}
|
|
25
|
+
},
|
|
26
|
+
"include": ["./next-env.d.ts", "./**/*.ts", "./**/*.tsx", "./.next/types/**/*.ts"],
|
|
27
|
+
"exclude": ["./node_modules"]
|
|
28
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { NextRequest } from "next/server";
|
|
2
|
+
import { auth } from "./auth";
|
|
3
|
+
|
|
4
|
+
export async function createContext(req: NextRequest) {
|
|
5
|
+
const session = await auth.api.getSession({
|
|
6
|
+
headers: req.headers,
|
|
7
|
+
});
|
|
8
|
+
|
|
9
|
+
return {
|
|
10
|
+
session,
|
|
11
|
+
};
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export type Context = Awaited<ReturnType<typeof createContext>>;
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
"use client"
|
|
2
|
+
import { authClient } from "@/lib/auth-client";
|
|
3
|
+
import { trpc } from "@/utils/trpc";
|
|
4
|
+
import { useQuery } from "@tanstack/react-query";
|
|
5
|
+
import { useRouter } from "next/navigation";
|
|
6
|
+
import { useEffect } from "react";
|
|
7
|
+
|
|
8
|
+
export default function Dashboard() {
|
|
9
|
+
const router = useRouter()
|
|
10
|
+
const { data: session, isPending } = authClient.useSession();
|
|
11
|
+
|
|
12
|
+
const privateData = useQuery(trpc.privateData.queryOptions());
|
|
13
|
+
|
|
14
|
+
useEffect(() => {
|
|
15
|
+
if (!session && !isPending) {
|
|
16
|
+
router.push("/login");
|
|
17
|
+
}
|
|
18
|
+
}, [session, isPending]);
|
|
19
|
+
|
|
20
|
+
if (isPending) {
|
|
21
|
+
return <div>Loading...</div>;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
return (
|
|
25
|
+
<div>
|
|
26
|
+
<h1>Dashboard</h1>
|
|
27
|
+
<p>Welcome {session?.user.name}</p>
|
|
28
|
+
<p>privateData: {privateData.data?.message}</p>
|
|
29
|
+
</div>
|
|
30
|
+
);
|
|
31
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
"use client"
|
|
2
|
+
|
|
3
|
+
import SignInForm from "@/components/sign-in-form";
|
|
4
|
+
import SignUpForm from "@/components/sign-up-form";
|
|
5
|
+
import { useState } from "react";
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
export default function LoginPage() {
|
|
9
|
+
const [showSignIn, setShowSignIn] = useState(false);
|
|
10
|
+
|
|
11
|
+
return showSignIn ? (
|
|
12
|
+
<SignInForm onSwitchToSignUp={() => setShowSignIn(false)} />
|
|
13
|
+
) : (
|
|
14
|
+
<SignUpForm onSwitchToSignIn={() => setShowSignIn(true)} />
|
|
15
|
+
);
|
|
16
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
"use client"
|
|
2
|
+
import Link from "next/link";
|
|
3
|
+
import { ModeToggle } from "./mode-toggle";
|
|
4
|
+
import UserMenu from "./user-menu";
|
|
5
|
+
|
|
6
|
+
export default function Header() {
|
|
7
|
+
const links = [
|
|
8
|
+
{ to: "/", label: "Home" },
|
|
9
|
+
{ to: "/dashboard", label: "Dashboard" },
|
|
10
|
+
];
|
|
11
|
+
|
|
12
|
+
return (
|
|
13
|
+
<div>
|
|
14
|
+
<div className="flex flex-row items-center justify-between px-2 py-1">
|
|
15
|
+
<nav className="flex gap-4 text-lg">
|
|
16
|
+
{links.map(({ to, label }) => (
|
|
17
|
+
<Link
|
|
18
|
+
key={to}
|
|
19
|
+
href={to}
|
|
20
|
+
>
|
|
21
|
+
{label}
|
|
22
|
+
</Link>
|
|
23
|
+
))}
|
|
24
|
+
</nav>
|
|
25
|
+
<div className="flex items-center gap-2">
|
|
26
|
+
<ModeToggle />
|
|
27
|
+
<UserMenu />
|
|
28
|
+
</div>
|
|
29
|
+
</div>
|
|
30
|
+
<hr />
|
|
31
|
+
</div>
|
|
32
|
+
);
|
|
33
|
+
}
|