Files
juwan-frontend/store/auth.ts
T

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,
})
},
}))