🚀 Fix: Roles access for Staff/Tax Center and Profile update bug

This commit is contained in:
Hamza-Ayed
2026-04-22 18:15:18 +03:00
parent bb7afc5629
commit 357274683c
5 changed files with 52 additions and 27 deletions

View File

@@ -17,19 +17,26 @@ import {
} from 'lucide-react'; } from 'lucide-react';
import { useAuthStore } from '../../store/authStore'; import { useAuthStore } from '../../store/authStore';
const menuItems = [ const getMenuItems = (role: string | undefined) => [
{ icon: LayoutDashboard, label: 'الرئيسية', path: '/dashboard' }, { icon: LayoutDashboard, label: 'الرئيسية', path: '/dashboard' },
{ icon: Crown, label: 'المركز الضريبي الموحد', path: '/elite-dashboard' }, ...(role === 'admin' ? [
{ icon: AlertTriangle, label: 'مراقبة المخاطر', path: '/risk-monitor' }, { icon: Crown, label: 'المركز الضريبي الموحد', path: '/elite-dashboard' },
{ icon: AlertTriangle, label: 'مراقبة المخاطر', path: '/risk-monitor' }
] : []),
{ icon: FileText, label: 'الفواتير', path: '/invoices' }, { icon: FileText, label: 'الفواتير', path: '/invoices' },
{ icon: Building2, label: 'الشركات', path: '/companies' }, { icon: Building2, label: 'الشركات', path: '/companies' },
{ icon: Users, label: 'الموظفون', path: '/staff' }, ...(role === 'admin' ? [
{ icon: Users, label: 'الموظفون', path: '/staff' }
] : []),
{ icon: Settings, label: 'الإعدادات', path: '/settings' }, { icon: Settings, label: 'الإعدادات', path: '/settings' },
]; ];
export const Sidebar = () => { export const Sidebar = () => {
const navigate = useNavigate(); const navigate = useNavigate();
const user = useAuthStore((state) => state.user);
const clearAuth = useAuthStore((state) => state.clearAuth); const clearAuth = useAuthStore((state) => state.clearAuth);
const menuItems = getMenuItems(user?.role);
const handleLogout = () => { const handleLogout = () => {
clearAuth(); clearAuth();

View File

@@ -22,6 +22,7 @@ import { useNavigate } from 'react-router-dom';
export const DashboardPage = () => { export const DashboardPage = () => {
const navigate = useNavigate(); const navigate = useNavigate();
const user = useAuthStore((state) => state.user);
const [stats, setStats] = useState<any>(null); const [stats, setStats] = useState<any>(null);
const [isLoading, setIsLoading] = useState(true); const [isLoading, setIsLoading] = useState(true);
@@ -210,18 +211,20 @@ export const DashboardPage = () => {
</div> </div>
</button> </button>
<button {user?.role === 'admin' && (
onClick={() => navigate('/elite-dashboard')} <button
className="flex items-center gap-4 p-4 rounded-2xl bg-slate-900/50 border border-slate-800/60 text-slate-300 hover:border-emerald-500/30 transition-all group" onClick={() => navigate('/elite-dashboard')}
> className="flex items-center gap-4 p-4 rounded-2xl bg-slate-900/50 border border-slate-800/60 text-slate-300 hover:border-emerald-500/30 transition-all group"
<div className="w-10 h-10 rounded-xl bg-slate-800 flex items-center justify-center group-hover:bg-amber-500/10 group-hover:text-amber-400 transition-all"> >
<AlertTriangle className="w-5 h-5" /> <div className="w-10 h-10 rounded-xl bg-slate-800 flex items-center justify-center group-hover:bg-amber-500/10 group-hover:text-amber-400 transition-all">
</div> <AlertTriangle className="w-5 h-5" />
<div className="text-right"> </div>
<div className="font-bold text-white">مراقبة المخاطر</div> <div className="text-right">
<div className="text-xs text-slate-500">لوحة النخبة ودرجات الخطر الضريبي</div> <div className="font-bold text-white">مراقبة المخاطر</div>
</div> <div className="text-xs text-slate-500">لوحة النخبة ودرجات الخطر الضريبي</div>
</button> </div>
</button>
)}
</div> </div>
</div> </div>
</div> </div>

View File

@@ -1,6 +1,7 @@
import { useState, useEffect } from 'react'; import { useState, useEffect } from 'react';
import { Building2, TrendingUp, AlertTriangle, ChevronDown, Loader2, RefreshCw, Crown } from 'lucide-react'; import { Building2, TrendingUp, AlertTriangle, ChevronDown, Loader2, RefreshCw, Crown } from 'lucide-react';
import { motion } from 'framer-motion'; import { motion } from 'framer-motion';
import { useAuthStore } from '../../store/authStore';
import apiClient from '../../api/client'; import apiClient from '../../api/client';
interface CompanyStats { interface CompanyStats {
@@ -92,6 +93,7 @@ const Cpu = ({ className }: { className?: string }) => (
); );
export const MultiEntityDashboard = () => { export const MultiEntityDashboard = () => {
const user = useAuthStore((state) => state.user);
const [companies, setCompanies] = useState<CompanyStats[]>([]); const [companies, setCompanies] = useState<CompanyStats[]>([]);
const [isLoading, setIsLoading] = useState(true); const [isLoading, setIsLoading] = useState(true);
const [error, setError] = useState<string | null>(null); const [error, setError] = useState<string | null>(null);
@@ -188,12 +190,14 @@ export const MultiEntityDashboard = () => {
className="card-premium p-6 relative overflow-hidden group" className="card-premium p-6 relative overflow-hidden group"
> >
{/* AI Usage Badge */} {/* AI Usage Badge */}
<div className="absolute top-0 right-0 p-2"> {user?.role === 'admin' && (
<div className="flex items-center gap-1 bg-slate-100 dark:bg-slate-800 px-2 py-1 rounded-md text-[10px] font-bold text-slate-300 dark:text-slate-200 border border-slate-200 dark:border-slate-700"> <div className="absolute top-4 left-4">
<Cpu className="w-3 h-3 text-purple-400" /> <div className="flex items-center gap-1.5 bg-indigo-500/10 backdrop-blur-md px-3 py-1.5 rounded-lg text-xs font-black text-indigo-400 border border-indigo-500/20 shadow-lg shadow-indigo-500/10">
<span>{company.aiStats?.totalTokens > 1000 ? `${(company.aiStats.totalTokens / 1000).toFixed(1)}k` : company.aiStats?.totalTokens || 0} tokens</span> <Cpu className="w-3.5 h-3.5" />
<span>{company.aiStats?.totalTokens > 1000 ? `${(company.aiStats.totalTokens / 1000).toFixed(1)}k` : company.aiStats?.totalTokens || 0} Tokens</span>
</div> </div>
</div> </div>
)}
{/* Ambient glow */} {/* Ambient glow */}
<div className={`absolute -inset-20 opacity-0 group-hover:opacity-10 blur-3xl transition-opacity duration-500 rounded-full <div className={`absolute -inset-20 opacity-0 group-hover:opacity-10 blur-3xl transition-opacity duration-500 rounded-full
@@ -235,8 +239,8 @@ export const MultiEntityDashboard = () => {
<div className="flex justify-between items-center mb-2"> <div className="flex justify-between items-center mb-2">
<p className="text-sm text-slate-600 dark:text-slate-300">{company.totalInvoices} فاتورة</p> <p className="text-sm text-slate-600 dark:text-slate-300">{company.totalInvoices} فاتورة</p>
<div className="flex items-center gap-2"> <div className="flex items-center gap-2">
{company.aiStats?.totalCost > 0 && ( {user?.role === 'admin' && company.aiStats?.totalCost > 0 && (
<span className="text-[10px] font-black text-purple-400 bg-purple-500/10 px-2 py-0.5 rounded border border-purple-500/20"> <span className="text-[10px] font-black text-indigo-400 bg-indigo-500/10 px-2 py-0.5 rounded border border-indigo-500/20">
${company.aiStats.totalCost.toFixed(3)} ${company.aiStats.totalCost.toFixed(3)}
</span> </span>
)} )}

View File

@@ -64,7 +64,8 @@ export const SettingsPage = () => {
const handleSave = async () => { const handleSave = async () => {
setIsSaving(true); setIsSaving(true);
try { try {
await apiClient.post('/users/profile', formData); const { email, ...updateData } = formData;
await apiClient.post('/users/profile', updateData);
updateUser({ name: formData.name }); updateUser({ name: formData.name });
setShowSuccess(true); setShowSuccess(true);
setTimeout(() => setShowSuccess(false), 3000); setTimeout(() => setShowSuccess(false), 3000);

View File

@@ -7,9 +7,11 @@
import { useState, useEffect } from 'react'; import { useState, useEffect } from 'react';
import { Users, UserPlus, Search, Shield, Mail, MoreVertical, Trash2, Loader2, X } from 'lucide-react'; import { Users, UserPlus, Search, Shield, Mail, MoreVertical, Trash2, Loader2, X } from 'lucide-react';
import { motion, AnimatePresence } from 'framer-motion'; import { motion, AnimatePresence } from 'framer-motion';
import { useAuthStore } from '../../store/authStore';
import apiClient from '../../api/client'; import apiClient from '../../api/client';
export const StaffPage = () => { export const StaffPage = () => {
const user = useAuthStore((state) => state.user);
const [staff, setStaff] = useState<any[]>([]); const [staff, setStaff] = useState<any[]>([]);
const [isLoading, setIsLoading] = useState(true); const [isLoading, setIsLoading] = useState(true);
const [searchTerm, setSearchTerm] = useState(''); const [searchTerm, setSearchTerm] = useState('');
@@ -77,6 +79,7 @@ export const StaffPage = () => {
<h2 className="text-3xl font-black text-white">إدارة الموظفين</h2> <h2 className="text-3xl font-black text-white">إدارة الموظفين</h2>
<p className="text-slate-300 mt-1">إدارة فريق العمل المالي لمكتب المحاسبة الخاص بك.</p> <p className="text-slate-300 mt-1">إدارة فريق العمل المالي لمكتب المحاسبة الخاص بك.</p>
</div> </div>
{user?.role === 'admin' && (
<button <button
onClick={() => setIsAddModalOpen(true)} onClick={() => setIsAddModalOpen(true)}
className="bg-emerald-500 hover:bg-emerald-600 text-slate-950 font-bold py-3 px-8 rounded-xl flex items-center gap-2 shadow-lg shadow-emerald-500/20 transition-all active:scale-95" className="bg-emerald-500 hover:bg-emerald-600 text-slate-950 font-bold py-3 px-8 rounded-xl flex items-center gap-2 shadow-lg shadow-emerald-500/20 transition-all active:scale-95"
@@ -84,6 +87,7 @@ export const StaffPage = () => {
<UserPlus className="w-5 h-5" /> <UserPlus className="w-5 h-5" />
إضافة موظف جديد إضافة موظف جديد
</button> </button>
)}
</header> </header>
{/* ── Search Bar ──────────────────────────────── */} {/* ── Search Bar ──────────────────────────────── */}
@@ -112,9 +116,13 @@ export const StaffPage = () => {
</div> </div>
<h3 className="text-xl font-bold text-white mb-2">لا يوجد موظفون مضافون</h3> <h3 className="text-xl font-bold text-white mb-2">لا يوجد موظفون مضافون</h3>
<p className="text-slate-500 max-w-sm mb-8">يمكنك إضافة موظفين لمساعدتك في إدارة ومعالجة فواتير الشركات.</p> <p className="text-slate-500 max-w-sm mb-8">يمكنك إضافة موظفين لمساعدتك في إدارة ومعالجة فواتير الشركات.</p>
<button onClick={() => setIsAddModalOpen(true)} className="bg-emerald-500 hover:bg-emerald-600 text-slate-950 font-bold py-3 px-8 rounded-xl transition-all"> {user?.role === 'admin' ? (
إضافة أول موظف <button onClick={() => setIsAddModalOpen(true)} className="bg-emerald-500 hover:bg-emerald-600 text-slate-950 font-bold py-3 px-8 rounded-xl transition-all">
</button> إضافة أول موظف
</button>
) : (
<p className="text-slate-500 max-w-sm mb-8">ليس لديك صلاحية إضافة موظفين.</p>
)}
</div> </div>
) : ( ) : (
<div className="overflow-x-auto"> <div className="overflow-x-auto">
@@ -124,7 +132,7 @@ export const StaffPage = () => {
<th className="px-6 py-4 text-xs font-bold text-slate-300">الاسم الكامل</th> <th className="px-6 py-4 text-xs font-bold text-slate-300">الاسم الكامل</th>
<th className="px-6 py-4 text-xs font-bold text-slate-300">البريد الإلكتروني</th> <th className="px-6 py-4 text-xs font-bold text-slate-300">البريد الإلكتروني</th>
<th className="px-6 py-4 text-xs font-bold text-slate-300">الدور الوظيفي</th> <th className="px-6 py-4 text-xs font-bold text-slate-300">الدور الوظيفي</th>
<th className="px-6 py-4 text-xs font-bold text-slate-300 text-center">إجراءات</th> {user?.role === 'admin' && <th className="px-6 py-4 text-xs font-bold text-slate-300 text-center">إجراءات</th>}
</tr> </tr>
</thead> </thead>
<tbody className="divide-y divide-slate-800/50"> <tbody className="divide-y divide-slate-800/50">
@@ -160,6 +168,7 @@ export const StaffPage = () => {
{s.role === 'admin' ? 'مدير نظام' : 'محاسب'} {s.role === 'admin' ? 'مدير نظام' : 'محاسب'}
</span> </span>
</td> </td>
{user?.role === 'admin' && (
<td className="px-6 py-4 text-center"> <td className="px-6 py-4 text-center">
<div className="flex items-center justify-center gap-2"> <div className="flex items-center justify-center gap-2">
<button className="p-2 text-slate-500 hover:text-white transition-colors"> <button className="p-2 text-slate-500 hover:text-white transition-colors">
@@ -173,6 +182,7 @@ export const StaffPage = () => {
</button> </button>
</div> </div>
</td> </td>
)}
</motion.tr> </motion.tr>
))} ))}
</tbody> </tbody>