feat: support merchant team roles and permissions

This commit is contained in:
yml2213
2026-08-13 11:41:34 +08:00
parent 6c98fa557e
commit a4791decb0
21 changed files with 969 additions and 201 deletions
+57 -3
View File
@@ -2,12 +2,13 @@ import {
createContext,
useCallback,
useContext,
useEffect,
useMemo,
useState,
type ReactNode,
} from 'react'
import type { User } from '../types'
import { authApi } from '../api'
import type { Merchant, User } from '../types'
import { authApi, merchantApi } from '../api'
interface AuthState {
token: string | null
@@ -15,6 +16,11 @@ interface AuthState {
login: (username: string, password: string) => Promise<void>
logout: () => void
refreshProfile: () => Promise<void>
merchant: Merchant | null
merchantRole: string | null
merchantPermissions: string[]
merchantLoading: boolean
refreshMerchantContext: () => Promise<void>
isAdmin: boolean
}
@@ -32,20 +38,63 @@ function loadUser(): User | null {
export function AuthProvider({ children }: { children: ReactNode }) {
const [token, setToken] = useState<string | null>(() => localStorage.getItem('token'))
const [user, setUser] = useState<User | null>(() => loadUser())
const [merchant, setMerchant] = useState<Merchant | null>(null)
const [merchantRole, setMerchantRole] = useState<string | null>(null)
const [merchantPermissions, setMerchantPermissions] = useState<string[]>([])
const [merchantLoading, setMerchantLoading] = useState(() => Boolean(localStorage.getItem('token')))
const refreshMerchantContext = useCallback(async () => {
if (!token) {
setMerchant(null)
setMerchantRole(null)
setMerchantPermissions([])
setMerchantLoading(false)
return
}
setMerchantLoading(true)
try {
const data = await merchantApi.current()
setMerchant(data.merchant)
setMerchantRole(data.role)
const nextPermissions = [...new Set(data.permissions || [])].sort()
setMerchantPermissions((current) => (
current.length === nextPermissions.length && current.every((value, index) => value === nextPermissions[index])
? current
: nextPermissions
))
} catch {
setMerchant(null)
setMerchantRole(null)
setMerchantPermissions([])
} finally {
setMerchantLoading(false)
}
}, [token])
useEffect(() => {
void refreshMerchantContext()
}, [refreshMerchantContext])
const login = useCallback(async (username: string, password: string) => {
const result = await authApi.login(username, password)
localStorage.removeItem('merchant_id')
localStorage.setItem('token', result.token)
localStorage.setItem('user', JSON.stringify(result.user))
setToken(result.token)
setUser(result.user)
setMerchantLoading(true)
}, [])
const logout = useCallback(() => {
localStorage.removeItem('token')
localStorage.removeItem('user')
localStorage.removeItem('merchant_id')
setToken(null)
setUser(null)
setMerchant(null)
setMerchantRole(null)
setMerchantPermissions([])
setMerchantLoading(false)
}, [])
const refreshProfile = useCallback(async () => {
@@ -61,9 +110,14 @@ export function AuthProvider({ children }: { children: ReactNode }) {
login,
logout,
refreshProfile,
merchant,
merchantRole,
merchantPermissions,
merchantLoading,
refreshMerchantContext,
isAdmin: user?.role === 'admin',
}),
[token, user, login, logout, refreshProfile],
[token, user, login, logout, refreshProfile, merchant, merchantRole, merchantPermissions, merchantLoading, refreshMerchantContext],
)
return <AuthContext.Provider value={value}>{children}</AuthContext.Provider>