fix: sync verification state with auth guards
This commit is contained in:
@@ -16,9 +16,14 @@ import {
|
|||||||
} from "@/components/ui/select"
|
} from "@/components/ui/select"
|
||||||
import { Separator } from "@/components/ui/separator"
|
import { Separator } from "@/components/ui/separator"
|
||||||
import { Textarea } from "@/components/ui/textarea"
|
import { Textarea } from "@/components/ui/textarea"
|
||||||
|
import type { UserRole } from "@/lib/types"
|
||||||
|
import { useAuthStore } from "@/store/auth"
|
||||||
|
|
||||||
export default function VerifyPage() {
|
export default function VerifyPage() {
|
||||||
const [submitted, setSubmitted] = useState(false)
|
const [verifyRole, setVerifyRole] = useState<UserRole | "">("")
|
||||||
|
const verificationStatus = useAuthStore((state) => state.verificationStatus)
|
||||||
|
const submitVerification = useAuthStore((state) => state.submitVerification)
|
||||||
|
const submitted = Object.values(verificationStatus).includes("pending")
|
||||||
|
|
||||||
if (submitted) {
|
if (submitted) {
|
||||||
return (
|
return (
|
||||||
@@ -68,7 +73,7 @@ export default function VerifyPage() {
|
|||||||
<CardContent className="space-y-6">
|
<CardContent className="space-y-6">
|
||||||
<div className="space-y-2">
|
<div className="space-y-2">
|
||||||
<Label>认证类型</Label>
|
<Label>认证类型</Label>
|
||||||
<Select>
|
<Select value={verifyRole} onValueChange={(value) => setVerifyRole(value as UserRole)}>
|
||||||
<SelectTrigger>
|
<SelectTrigger>
|
||||||
<SelectValue placeholder="选择认证类型" />
|
<SelectValue placeholder="选择认证类型" />
|
||||||
</SelectTrigger>
|
</SelectTrigger>
|
||||||
@@ -115,7 +120,14 @@ export default function VerifyPage() {
|
|||||||
<p className="text-xs text-muted-foreground">支持 JPG、PNG 格式,单张不超过 5MB</p>
|
<p className="text-xs text-muted-foreground">支持 JPG、PNG 格式,单张不超过 5MB</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<Button className="w-full" onClick={() => setSubmitted(true)}>
|
<Button
|
||||||
|
className="w-full"
|
||||||
|
disabled={!verifyRole}
|
||||||
|
onClick={() => {
|
||||||
|
if (!verifyRole) return
|
||||||
|
submitVerification(verifyRole)
|
||||||
|
}}
|
||||||
|
>
|
||||||
提交认证申请
|
提交认证申请
|
||||||
</Button>
|
</Button>
|
||||||
</CardContent>
|
</CardContent>
|
||||||
|
|||||||
@@ -1,8 +1,16 @@
|
|||||||
|
"use client"
|
||||||
|
|
||||||
|
import Link from "next/link"
|
||||||
import { AuthGuard } from "@/components/auth-guard"
|
import { AuthGuard } from "@/components/auth-guard"
|
||||||
import { DashboardSidebar } from "@/components/dashboard-sidebar"
|
import { DashboardSidebar } from "@/components/dashboard-sidebar"
|
||||||
import { Header } from "@/components/header"
|
import { Header } from "@/components/header"
|
||||||
|
import { Button } from "@/components/ui/button"
|
||||||
|
import { useAuthStore } from "@/store/auth"
|
||||||
|
|
||||||
export default function DashboardLayout({ children }: { children: React.ReactNode }) {
|
export default function DashboardLayout({ children }: { children: React.ReactNode }) {
|
||||||
|
const isAuthenticated = useAuthStore((state) => state.isAuthenticated)
|
||||||
|
const currentRole = useAuthStore((state) => state.currentRole)
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="flex min-h-screen flex-col">
|
<div className="flex min-h-screen flex-col">
|
||||||
<Header />
|
<Header />
|
||||||
@@ -11,7 +19,17 @@ export default function DashboardLayout({ children }: { children: React.ReactNod
|
|||||||
<DashboardSidebar />
|
<DashboardSidebar />
|
||||||
</div>
|
</div>
|
||||||
<main className="flex-1 p-6">
|
<main className="flex-1 p-6">
|
||||||
<AuthGuard>{children}</AuthGuard>
|
<AuthGuard>
|
||||||
|
{isAuthenticated && currentRole === "consumer" ? (
|
||||||
|
<div className="flex min-h-[50vh] items-center justify-center">
|
||||||
|
<Button asChild>
|
||||||
|
<Link href="/">首页</Link>
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
children
|
||||||
|
)}
|
||||||
|
</AuthGuard>
|
||||||
</main>
|
</main>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
"use client"
|
"use client"
|
||||||
|
|
||||||
import { useEffect } from "react"
|
import { useEffect, useRef } from "react"
|
||||||
import { Button } from "@/components/ui/button"
|
import { Button } from "@/components/ui/button"
|
||||||
import { useAuthStore } from "@/store/auth"
|
import { useAuthStore } from "@/store/auth"
|
||||||
import { useLoginDialogStore } from "@/store/login-dialog"
|
import { useLoginDialogStore } from "@/store/login-dialog"
|
||||||
@@ -12,9 +12,15 @@ interface AuthGuardProps {
|
|||||||
export function AuthGuard({ children }: AuthGuardProps) {
|
export function AuthGuard({ children }: AuthGuardProps) {
|
||||||
const isAuthenticated = useAuthStore((s) => s.isAuthenticated)
|
const isAuthenticated = useAuthStore((s) => s.isAuthenticated)
|
||||||
const openLoginDialog = useLoginDialogStore((s) => s.openLoginDialog)
|
const openLoginDialog = useLoginDialogStore((s) => s.openLoginDialog)
|
||||||
|
const hasTriggered = useRef(false)
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!isAuthenticated) {
|
if (isAuthenticated) {
|
||||||
|
hasTriggered.current = false
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if (!hasTriggered.current) {
|
||||||
|
hasTriggered.current = true
|
||||||
openLoginDialog()
|
openLoginDialog()
|
||||||
}
|
}
|
||||||
}, [isAuthenticated, openLoginDialog])
|
}, [isAuthenticated, openLoginDialog])
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
export type UserRole = "consumer" | "player" | "owner"
|
export type UserRole = "consumer" | "player" | "owner"
|
||||||
|
export type VerificationStatus = "pending" | "approved" | "rejected"
|
||||||
|
|
||||||
export interface User {
|
export interface User {
|
||||||
id: string
|
id: string
|
||||||
@@ -6,6 +7,8 @@ export interface User {
|
|||||||
nickname: string
|
nickname: string
|
||||||
avatar: string
|
avatar: string
|
||||||
role: UserRole
|
role: UserRole
|
||||||
|
verifiedRoles?: UserRole[]
|
||||||
|
verificationStatus?: Partial<Record<UserRole, VerificationStatus>>
|
||||||
phone?: string
|
phone?: string
|
||||||
bio?: string
|
bio?: string
|
||||||
createdAt: string
|
createdAt: string
|
||||||
|
|||||||
+25
-1
@@ -1,12 +1,14 @@
|
|||||||
import { create } from "zustand"
|
import { create } from "zustand"
|
||||||
import type { User, UserRole } from "@/lib/types"
|
import type { User, UserRole, VerificationStatus } from "@/lib/types"
|
||||||
|
|
||||||
interface AuthState {
|
interface AuthState {
|
||||||
isAuthenticated: boolean
|
isAuthenticated: boolean
|
||||||
currentRole: UserRole
|
currentRole: UserRole
|
||||||
verifiedRoles: UserRole[]
|
verifiedRoles: UserRole[]
|
||||||
|
verificationStatus: Partial<Record<UserRole, VerificationStatus>>
|
||||||
user: User | null
|
user: User | null
|
||||||
switchRole: (role: UserRole) => void
|
switchRole: (role: UserRole) => void
|
||||||
|
submitVerification: (role: UserRole) => void
|
||||||
login: (user: User, verifiedRoles?: UserRole[]) => void
|
login: (user: User, verifiedRoles?: UserRole[]) => void
|
||||||
logout: () => void
|
logout: () => void
|
||||||
}
|
}
|
||||||
@@ -15,6 +17,7 @@ export const useAuthStore = create<AuthState>((set, get) => ({
|
|||||||
isAuthenticated: false,
|
isAuthenticated: false,
|
||||||
currentRole: "consumer",
|
currentRole: "consumer",
|
||||||
verifiedRoles: ["consumer"],
|
verifiedRoles: ["consumer"],
|
||||||
|
verificationStatus: { consumer: "approved" },
|
||||||
user: null,
|
user: null,
|
||||||
switchRole: (role) => {
|
switchRole: (role) => {
|
||||||
const { verifiedRoles } = get()
|
const { verifiedRoles } = get()
|
||||||
@@ -22,18 +25,39 @@ export const useAuthStore = create<AuthState>((set, get) => ({
|
|||||||
set({ currentRole: role })
|
set({ currentRole: role })
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
submitVerification: (role) =>
|
||||||
|
set((state) => {
|
||||||
|
if (state.verifiedRoles.includes(role)) {
|
||||||
|
return state
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
verificationStatus: {
|
||||||
|
...state.verificationStatus,
|
||||||
|
[role]: "pending",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}),
|
||||||
login: (user, verifiedRoles = ["consumer"]) =>
|
login: (user, verifiedRoles = ["consumer"]) =>
|
||||||
set({
|
set({
|
||||||
isAuthenticated: true,
|
isAuthenticated: true,
|
||||||
user,
|
user,
|
||||||
currentRole: user.role,
|
currentRole: user.role,
|
||||||
verifiedRoles,
|
verifiedRoles,
|
||||||
|
verificationStatus: verifiedRoles.reduce<Partial<Record<UserRole, VerificationStatus>>>(
|
||||||
|
(acc, role) => {
|
||||||
|
acc[role] = "approved"
|
||||||
|
return acc
|
||||||
|
},
|
||||||
|
{},
|
||||||
|
),
|
||||||
}),
|
}),
|
||||||
logout: () =>
|
logout: () =>
|
||||||
set({
|
set({
|
||||||
isAuthenticated: false,
|
isAuthenticated: false,
|
||||||
currentRole: "consumer",
|
currentRole: "consumer",
|
||||||
verifiedRoles: ["consumer"],
|
verifiedRoles: ["consumer"],
|
||||||
|
verificationStatus: { consumer: "approved" },
|
||||||
user: null,
|
user: null,
|
||||||
}),
|
}),
|
||||||
}))
|
}))
|
||||||
|
|||||||
Reference in New Issue
Block a user