174 lines
4.8 KiB
TypeScript
174 lines
4.8 KiB
TypeScript
import type { User, UserRole, VerificationStatus } from "@/lib/types"
|
|
import { create } from "zustand"
|
|
|
|
interface NotificationPrefs {
|
|
order: boolean
|
|
community: boolean
|
|
system: boolean
|
|
}
|
|
|
|
const defaultNotificationPrefs: NotificationPrefs = {
|
|
order: true,
|
|
community: true,
|
|
system: false,
|
|
}
|
|
|
|
export type ThemePreference = "light" | "dark" | "system"
|
|
|
|
const STORAGE_KEY = "juwan-auth"
|
|
|
|
interface PersistedAuth {
|
|
user: User
|
|
currentRole: UserRole
|
|
verifiedRoles: UserRole[]
|
|
verificationStatus: Partial<Record<UserRole, VerificationStatus>>
|
|
notificationPrefs: NotificationPrefs
|
|
themePreference: ThemePreference
|
|
}
|
|
|
|
function loadPersisted(): PersistedAuth | null {
|
|
try {
|
|
const raw = localStorage.getItem(STORAGE_KEY)
|
|
if (!raw) return null
|
|
const parsed = JSON.parse(raw) as PersistedAuth
|
|
if (!parsed.user?.id) return null
|
|
return parsed
|
|
} catch {
|
|
return null
|
|
}
|
|
}
|
|
|
|
function persist(state: PersistedAuth) {
|
|
try {
|
|
localStorage.setItem(STORAGE_KEY, JSON.stringify(state))
|
|
} catch {}
|
|
}
|
|
|
|
function persistCurrent(state: AuthState) {
|
|
if (!state.user) return
|
|
persist({
|
|
user: state.user,
|
|
currentRole: state.currentRole,
|
|
verifiedRoles: state.verifiedRoles,
|
|
verificationStatus: state.verificationStatus,
|
|
notificationPrefs: state.notificationPrefs,
|
|
themePreference: state.themePreference,
|
|
})
|
|
}
|
|
|
|
function clearPersisted() {
|
|
try {
|
|
localStorage.removeItem(STORAGE_KEY)
|
|
} catch {}
|
|
}
|
|
|
|
const persisted = loadPersisted()
|
|
|
|
interface AuthState {
|
|
isAuthenticated: boolean
|
|
currentRole: UserRole
|
|
verifiedRoles: UserRole[]
|
|
verificationStatus: Partial<Record<UserRole, VerificationStatus>>
|
|
verificationReasons: Partial<Record<UserRole, string>>
|
|
notificationPrefs: NotificationPrefs
|
|
themePreference: ThemePreference
|
|
user: User | null
|
|
switchRole: (role: UserRole) => void
|
|
setNotificationPref: (type: keyof NotificationPrefs, enabled: boolean) => void
|
|
setThemePreference: (theme: ThemePreference) => void
|
|
updateProfile: (patch: { nickname?: string; bio?: string; avatar?: string }) => void
|
|
login: (user: User, verifiedRoles?: UserRole[], themePreference?: ThemePreference) => void
|
|
logout: () => void
|
|
}
|
|
|
|
export const useAuthStore = create<AuthState>((set, get) => ({
|
|
isAuthenticated: persisted !== null,
|
|
currentRole: persisted?.currentRole ?? "consumer",
|
|
verifiedRoles: persisted?.verifiedRoles ?? ["consumer"],
|
|
verificationStatus: persisted?.verificationStatus ?? { consumer: "approved" },
|
|
verificationReasons: {},
|
|
notificationPrefs: defaultNotificationPrefs,
|
|
themePreference: persisted?.themePreference ?? "system",
|
|
user: persisted?.user ?? null,
|
|
switchRole: (role) => {
|
|
const { verifiedRoles } = get()
|
|
if (verifiedRoles.includes(role)) {
|
|
set({ currentRole: role })
|
|
persistCurrent(get())
|
|
}
|
|
},
|
|
setNotificationPref: (type, enabled) => {
|
|
set((state) => ({
|
|
notificationPrefs: {
|
|
...state.notificationPrefs,
|
|
[type]: enabled,
|
|
},
|
|
}))
|
|
persistCurrent(get())
|
|
},
|
|
setThemePreference: (theme) => {
|
|
set({ themePreference: theme })
|
|
persistCurrent(get())
|
|
},
|
|
updateProfile: (patch) =>
|
|
set((state) => {
|
|
if (!state.user) return state
|
|
const next = {
|
|
...state,
|
|
user: {
|
|
...state.user,
|
|
nickname: patch.nickname ?? state.user.nickname,
|
|
bio: patch.bio ?? state.user.bio,
|
|
avatar: patch.avatar ?? state.user.avatar,
|
|
},
|
|
}
|
|
persistCurrent(next)
|
|
return next
|
|
}),
|
|
login: (user, verifiedRoles, themePreference) =>
|
|
set((state) => {
|
|
const nextVerifiedRoles = verifiedRoles ?? user.verifiedRoles ?? [user.role]
|
|
const nextVerificationStatus =
|
|
user.verificationStatus ??
|
|
nextVerifiedRoles.reduce<Partial<Record<UserRole, VerificationStatus>>>((acc, role) => {
|
|
acc[role] = "approved"
|
|
return acc
|
|
}, {})
|
|
|
|
const nextTheme = themePreference ?? state.themePreference
|
|
|
|
persist({
|
|
user,
|
|
currentRole: user.role,
|
|
verifiedRoles: nextVerifiedRoles,
|
|
verificationStatus: nextVerificationStatus,
|
|
notificationPrefs: state.notificationPrefs,
|
|
themePreference: nextTheme,
|
|
})
|
|
|
|
return {
|
|
isAuthenticated: true,
|
|
user,
|
|
currentRole: user.role,
|
|
verifiedRoles: nextVerifiedRoles,
|
|
verificationStatus: nextVerificationStatus,
|
|
verificationReasons: {},
|
|
notificationPrefs: state.notificationPrefs,
|
|
themePreference: nextTheme,
|
|
}
|
|
}),
|
|
logout: () => {
|
|
clearPersisted()
|
|
set({
|
|
isAuthenticated: false,
|
|
currentRole: "consumer",
|
|
verifiedRoles: ["consumer"],
|
|
verificationStatus: { consumer: "approved" },
|
|
verificationReasons: {},
|
|
notificationPrefs: persisted?.notificationPrefs ?? defaultNotificationPrefs,
|
|
themePreference: "system",
|
|
user: null,
|
|
})
|
|
},
|
|
}))
|