From 3e85d0828b00b51e6b02c411b3b2d2d24654b735 Mon Sep 17 00:00:00 2001 From: Tuluobo Date: Thu, 26 Sep 2024 23:07:25 +0800 Subject: [PATCH] feat: design home page ui --- next.config.mjs | 8 +++ package.json | 1 + pnpm-lock.yaml | 34 +++++++++++-- public/logo-dark.png | Bin 0 -> 6291 bytes public/logo.png | Bin 0 -> 2919 bytes src/app/()/page.tsx | 68 +++++++++++++++++++------ src/app/(auth)/authorize/page.tsx | 58 +++++++++++----------- src/app/(auth)/layout.tsx | 8 ++- src/app/(auth)/sign-in/page.tsx | 8 +-- src/app/providers.tsx | 21 ++++---- src/components/dynamic-logo.tsx | 12 +++++ src/components/layout/nav-bar.tsx | 79 ++++++++++++++++++++++++++++++ src/components/ui/avatar.tsx | 50 +++++++++++++++++++ src/lib/dto/client.ts | 10 ++-- 14 files changed, 291 insertions(+), 66 deletions(-) create mode 100644 public/logo-dark.png create mode 100644 public/logo.png create mode 100644 src/components/dynamic-logo.tsx create mode 100644 src/components/layout/nav-bar.tsx create mode 100644 src/components/ui/avatar.tsx 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 0000000000000000000000000000000000000000..286294a9fed82c49f1a5f7332801cf5430dfb5c6 GIT binary patch literal 6291 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 literal 0 HcmV?d00001 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 }); }