diff --git a/next.config.mjs b/next.config.mjs index 01761fd..af59363 100644 --- a/next.config.mjs +++ b/next.config.mjs @@ -2,6 +2,14 @@ const nextConfig = { reactStrictMode: true, output: "standalone", + images: { + remotePatterns: [ + { + protocol: "https", + hostname: "shuzimumin.com", + }, + ], + }, }; export default nextConfig; diff --git a/package.json b/package.json index abb2973..5d0463d 100644 --- a/package.json +++ b/package.json @@ -18,6 +18,7 @@ "dependencies": { "@auth/prisma-adapter": "^2.4.2", "@prisma/client": "^5.19.0", + "@radix-ui/react-avatar": "^1.1.0", "@radix-ui/react-checkbox": "^1.1.1", "@radix-ui/react-dialog": "^1.1.1", "@radix-ui/react-dropdown-menu": "^2.1.1", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 510e14b..3cae1b7 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -14,6 +14,9 @@ importers: '@prisma/client': specifier: ^5.19.0 version: 5.19.0(prisma@5.19.0) + '@radix-ui/react-avatar': + specifier: ^1.1.0 + version: 1.1.0(@types/react-dom@18.3.0)(@types/react@18.3.5)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) '@radix-ui/react-checkbox': specifier: ^1.1.1 version: 1.1.1(@types/react-dom@18.3.0)(@types/react@18.3.5)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) @@ -418,6 +421,19 @@ packages: '@types/react-dom': optional: true + '@radix-ui/react-avatar@1.1.0': + resolution: {integrity: sha512-Q/PbuSMk/vyAd/UoIShVGZ7StHHeRFYU7wXmi5GV+8cLXflZAEpHL/F697H1klrzxKXNtZ97vWiC0q3RKUH8UA==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + '@radix-ui/react-checkbox@1.1.1': resolution: {integrity: sha512-0i/EKJ222Afa1FE0C6pNJxDq1itzcl3HChE9DwskA4th4KRse8ojx8a1nVcOjwJdbpDLcz7uol77yYnQNMHdKw==} peerDependencies: @@ -2849,6 +2865,18 @@ snapshots: '@types/react': 18.3.5 '@types/react-dom': 18.3.0 + '@radix-ui/react-avatar@1.1.0(@types/react-dom@18.3.0)(@types/react@18.3.5)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + dependencies: + '@radix-ui/react-context': 1.1.0(@types/react@18.3.5)(react@18.3.1) + '@radix-ui/react-primitive': 2.0.0(@types/react-dom@18.3.0)(@types/react@18.3.5)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-use-callback-ref': 1.1.0(@types/react@18.3.5)(react@18.3.1) + '@radix-ui/react-use-layout-effect': 1.1.0(@types/react@18.3.5)(react@18.3.1) + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + optionalDependencies: + '@types/react': 18.3.5 + '@types/react-dom': 18.3.0 + '@radix-ui/react-checkbox@1.1.1(@types/react-dom@18.3.0)(@types/react@18.3.5)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': dependencies: '@radix-ui/primitive': 1.1.0 @@ -3706,7 +3734,7 @@ snapshots: eslint: 8.57.0 eslint-import-resolver-node: 0.3.9 eslint-import-resolver-typescript: 3.6.3(@typescript-eslint/parser@7.2.0(eslint@8.57.0)(typescript@5.5.4))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.29.1(eslint@8.57.0))(eslint@8.57.0) - eslint-plugin-import: 2.29.1(@typescript-eslint/parser@7.2.0(eslint@8.57.0)(typescript@5.5.4))(eslint-import-resolver-typescript@3.6.3(@typescript-eslint/parser@7.2.0(eslint@8.57.0)(typescript@5.5.4))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.29.1(eslint@8.57.0))(eslint@8.57.0))(eslint@8.57.0) + eslint-plugin-import: 2.29.1(@typescript-eslint/parser@7.2.0(eslint@8.57.0)(typescript@5.5.4))(eslint-import-resolver-typescript@3.6.3)(eslint@8.57.0) eslint-plugin-jsx-a11y: 6.9.0(eslint@8.57.0) eslint-plugin-react: 7.35.0(eslint@8.57.0) eslint-plugin-react-hooks: 4.6.2(eslint@8.57.0) @@ -3737,7 +3765,7 @@ snapshots: is-bun-module: 1.1.0 is-glob: 4.0.3 optionalDependencies: - eslint-plugin-import: 2.29.1(@typescript-eslint/parser@7.2.0(eslint@8.57.0)(typescript@5.5.4))(eslint-import-resolver-typescript@3.6.3(@typescript-eslint/parser@7.2.0(eslint@8.57.0)(typescript@5.5.4))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.29.1(eslint@8.57.0))(eslint@8.57.0))(eslint@8.57.0) + eslint-plugin-import: 2.29.1(@typescript-eslint/parser@7.2.0(eslint@8.57.0)(typescript@5.5.4))(eslint-import-resolver-typescript@3.6.3)(eslint@8.57.0) transitivePeerDependencies: - '@typescript-eslint/parser' - eslint-import-resolver-node @@ -3755,7 +3783,7 @@ snapshots: transitivePeerDependencies: - supports-color - eslint-plugin-import@2.29.1(@typescript-eslint/parser@7.2.0(eslint@8.57.0)(typescript@5.5.4))(eslint-import-resolver-typescript@3.6.3(@typescript-eslint/parser@7.2.0(eslint@8.57.0)(typescript@5.5.4))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.29.1(eslint@8.57.0))(eslint@8.57.0))(eslint@8.57.0): + eslint-plugin-import@2.29.1(@typescript-eslint/parser@7.2.0(eslint@8.57.0)(typescript@5.5.4))(eslint-import-resolver-typescript@3.6.3)(eslint@8.57.0): dependencies: array-includes: 3.1.8 array.prototype.findlastindex: 1.2.5 diff --git a/public/logo-dark.png b/public/logo-dark.png new file mode 100644 index 0000000..286294a Binary files /dev/null and b/public/logo-dark.png differ diff --git a/public/logo.png b/public/logo.png new file mode 100644 index 0000000..e854eaf Binary files /dev/null and b/public/logo.png differ diff --git a/src/app/()/page.tsx b/src/app/()/page.tsx index b8a68f1..58d5790 100644 --- a/src/app/()/page.tsx +++ b/src/app/()/page.tsx @@ -1,24 +1,62 @@ +import dynamic from "next/dynamic"; +import Link from "next/link"; + import { Button } from "@/components/ui/button"; +import { NavBar } from "@/components/layout/nav-bar"; import { ThemeToggle } from "@/components/theme-toggle"; -export default async function IndexPage() { +// 动态导入 Logo 组件以避免服务器端渲染错误 +const DynamicLogo = dynamic(() => import("@/components/dynamic-logo"), { + ssr: false, +}); + +export default function IndexPage() { return ( - <> -
-
-
-

Next.js

+
+ +
+
+

+ 数字牧民 Connect +

+

+ 数字牧民 Connect 是数字牧民社区基于 Discourse SSO 身份认证的 OAuth + 2.0 服务。通过数字牧民 Connect + 认证服务,将您的数字牧民账号与第三方应用进行安全、便捷的集成。 +

+
+ + + + + +
- -
-
-
-
-

Hello, Next js & Shadcn UI & Next Auth

-
-
- + + ); } diff --git a/src/app/(auth)/authorize/page.tsx b/src/app/(auth)/authorize/page.tsx index 5271b5f..3e7351e 100644 --- a/src/app/(auth)/authorize/page.tsx +++ b/src/app/(auth)/authorize/page.tsx @@ -16,38 +16,36 @@ export const metadata: Metadata = { export default function AuthPage({ searchParams }: Props) { return ( -
-
-
- -
- Welcome to{" "} - 数字牧民社区 -
+
+
+ +
+ Welcome to{" "} + 数字牧民社区
-
- - - -
-

- By clicking continue, you agree to our{" "} - - Terms of Service - {" "} - and{" "} - - Privacy Policy - - . -

+
+ + + +
+

+ By clicking continue, you agree to our{" "} + + Terms of Service + {" "} + and{" "} + + Privacy Policy + + . +

); } diff --git a/src/app/(auth)/layout.tsx b/src/app/(auth)/layout.tsx index deb4b39..eb31bdd 100644 --- a/src/app/(auth)/layout.tsx +++ b/src/app/(auth)/layout.tsx @@ -14,5 +14,11 @@ export default async function AuthLayout({ redirect("/dashboard"); } - return
{children}
; + return ( +
+
+ {children} +
+
+ ); } diff --git a/src/app/(auth)/sign-in/page.tsx b/src/app/(auth)/sign-in/page.tsx index 0c99545..b2fbee7 100644 --- a/src/app/(auth)/sign-in/page.tsx +++ b/src/app/(auth)/sign-in/page.tsx @@ -14,7 +14,7 @@ export const metadata: Metadata = { export default function LoginPage() { return ( -
+ <>
Welcome to{" "} - 数字牧民社区 + + 数字牧民社区 Connect +
@@ -56,6 +58,6 @@ export default function LoginPage() { .

-
+ ); } diff --git a/src/app/providers.tsx b/src/app/providers.tsx index 23fb6cd..5498803 100644 --- a/src/app/providers.tsx +++ b/src/app/providers.tsx @@ -1,19 +1,22 @@ "use client"; import * as React from "react"; +import { SessionProvider } from "next-auth/react"; import { ThemeProvider } from "next-themes"; import { type ThemeProviderProps as ProviderProps } from "next-themes/dist/types"; export function Providers({ children, ...props }: ProviderProps) { return ( - - {children} - + + + {children} + + ); } diff --git a/src/components/dynamic-logo.tsx b/src/components/dynamic-logo.tsx new file mode 100644 index 0000000..2cfecbb --- /dev/null +++ b/src/components/dynamic-logo.tsx @@ -0,0 +1,12 @@ +"use client"; + +import Image from "next/image"; +import { useTheme } from "next-themes"; + +export default function DynamicLogo() { + const { resolvedTheme } = useTheme(); + + const logoSrc = resolvedTheme === "dark" ? "/logo-dark.png" : "/logo.png"; + + return 数字牧民 Logo; +} diff --git a/src/components/layout/nav-bar.tsx b/src/components/layout/nav-bar.tsx new file mode 100644 index 0000000..b67416b --- /dev/null +++ b/src/components/layout/nav-bar.tsx @@ -0,0 +1,79 @@ +"use client"; + +import Image from "next/image"; +import Link from "next/link"; +import { signOut, useSession } from "next-auth/react"; + +import DynamicLogo from "../dynamic-logo"; +import { ThemeToggle } from "../theme-toggle"; +import { Button } from "../ui/button"; +import { + DropdownMenu, + DropdownMenuContent, + DropdownMenuItem, + DropdownMenuLabel, + DropdownMenuSeparator, + DropdownMenuTrigger, +} from "../ui/dropdown-menu"; + +export function NavBar() { + const { data: session } = useSession(); + const user = session?.user; + + return ( + + ); +} diff --git a/src/components/ui/avatar.tsx b/src/components/ui/avatar.tsx new file mode 100644 index 0000000..09cd14d --- /dev/null +++ b/src/components/ui/avatar.tsx @@ -0,0 +1,50 @@ +"use client"; + +import * as React from "react"; +import * as AvatarPrimitive from "@radix-ui/react-avatar"; + +import { cn } from "@/lib/utils"; + +const Avatar = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)); +Avatar.displayName = AvatarPrimitive.Root.displayName; + +const AvatarImage = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)); +AvatarImage.displayName = AvatarPrimitive.Image.displayName; + +const AvatarFallback = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)); +AvatarFallback.displayName = AvatarPrimitive.Fallback.displayName; + +export { Avatar, AvatarImage, AvatarFallback }; diff --git a/src/lib/dto/client.ts b/src/lib/dto/client.ts index f5b1de4..25f0bcf 100644 --- a/src/lib/dto/client.ts +++ b/src/lib/dto/client.ts @@ -1,14 +1,14 @@ -import { Client } from "@prisma/client"; +import { Prisma as PrismaType } from "@prisma/client"; import { prisma } from "@/lib/prisma"; -export async function getClientByClientId( - clientId: string, -): Promise { +export async function getClientByClientId(clientId: string) { return prisma.client.findUnique({ where: { clientId } }); } -export async function createClient(data: Omit): Promise { +export async function createClient( + data: PrismaType.ClientUncheckedCreateInput, +) { return prisma.client.create({ data }); }