Files
zerp/frontend/src/contexts/AuthContext.tsx
2026-02-22 14:49:27 +04:00

191 lines
5.2 KiB
TypeScript

'use client'
import React, { createContext, useContext, useState, useEffect } from 'react'
import { useRouter } from 'next/navigation'
const API_URL = process.env.NEXT_PUBLIC_API_URL || 'http://localhost:5001/api/v1'
interface User {
id: string
employeeId: string
username: string
email: string
isActive: boolean
role?: {
id: string
name: string
nameEn: string
permissions: Permission[]
}
}
interface Permission {
id: string
module: string
actions?: string[]
canView?: boolean
canCreate?: boolean
canEdit?: boolean
canDelete?: boolean
canExport?: boolean
canApprove?: boolean
}
interface AuthContextType {
user: User | null
login: (email: string, password: string) => Promise<void>
logout: () => void
isLoading: boolean
isAuthenticated: boolean
hasPermission: (module: string, action: 'view' | 'create' | 'edit' | 'delete' | 'export' | 'approve') => boolean
}
const AuthContext = createContext<AuthContextType | undefined>(undefined)
export function AuthProvider({ children }: { children: React.ReactNode }) {
const [user, setUser] = useState<User | null>(null)
const [isLoading, setIsLoading] = useState(true)
const router = useRouter()
// Check for existing token on mount
useEffect(() => {
const token = localStorage.getItem('accessToken')
if (token) {
// Verify token and get user data
fetchUserData(token)
} else {
setIsLoading(false)
}
}, [])
// Transform backend permissions format to frontend format
// Backend uses actions: ['*'] or ['read','create',...] - wildcard grants all
const transformPermissions = (permissions: any[]): Permission[] => {
const hasWildcard = (actions: string[] | any) => {
const arr = Array.isArray(actions) ? actions : []
return arr.includes('*') || arr.includes('all')
}
return permissions.map(p => {
const wildcard = hasWildcard(p.actions)
return {
id: p.id,
module: p.module,
actions: p.actions,
canView: wildcard || p.actions?.includes('read') || false,
canCreate: wildcard || p.actions?.includes('create') || false,
canEdit: wildcard || p.actions?.includes('update') || false,
canDelete: wildcard || p.actions?.includes('delete') || false,
canExport: wildcard || p.actions?.includes('export') || false,
canApprove: wildcard || p.actions?.includes('approve') || false,
}
})
}
const fetchUserData = async (token: string) => {
try {
const response = await fetch(`${API_URL}/auth/me`, {
headers: {
'Authorization': `Bearer ${token}`
}
})
if (response.ok) {
const userData = await response.json()
const user = userData.data
if (user.role?.permissions) {
user.role.permissions = transformPermissions(user.role.permissions)
}
setUser(user)
} else {
localStorage.removeItem('accessToken')
}
} catch (error) {
console.error('Failed to fetch user data:', error)
localStorage.removeItem('accessToken')
} finally {
setIsLoading(false)
}
}
const login = async (email: string, password: string) => {
try {
const response = await fetch(`${API_URL}/auth/login`, {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({ email, password })
})
const data = await response.json()
if (!response.ok) {
throw new Error(data.message || 'فشل تسجيل الدخول')
}
// Store token
localStorage.setItem('accessToken', data.data.accessToken)
localStorage.setItem('refreshToken', data.data.refreshToken)
// Transform permissions and set user data
const userData = data.data.user
if (userData.role?.permissions) {
userData.role.permissions = transformPermissions(userData.role.permissions)
}
setUser(userData)
// Redirect to dashboard
router.push('/dashboard')
} catch (error: any) {
throw new Error(error.message || 'فشل تسجيل الدخول')
}
}
const logout = () => {
localStorage.removeItem('accessToken')
localStorage.removeItem('refreshToken')
setUser(null)
router.push('/')
}
const hasPermission = (module: string, action: 'view' | 'create' | 'edit' | 'delete' | 'export' | 'approve'): boolean => {
if (!user?.role?.permissions) return false
const permission = user.role.permissions.find(p => p.module.toLowerCase() === module.toLowerCase())
if (!permission) return false
const actionMap = {
view: 'canView',
create: 'canCreate',
edit: 'canEdit',
delete: 'canDelete',
export: 'canExport',
approve: 'canApprove'
}
return permission[actionMap[action] as keyof Permission] as boolean
}
return (
<AuthContext.Provider value={{
user,
login,
logout,
isLoading,
isAuthenticated: !!user,
hasPermission
}}>
{children}
</AuthContext.Provider>
)
}
export function useAuth() {
const context = useContext(AuthContext)
if (context === undefined) {
throw new Error('useAuth must be used within an AuthProvider')
}
return context
}