🚀 Fix: Roles access for Staff/Tax Center and Profile update bug
This commit is contained in:
@@ -17,19 +17,26 @@ import {
|
||||
} from 'lucide-react';
|
||||
import { useAuthStore } from '../../store/authStore';
|
||||
|
||||
const menuItems = [
|
||||
const getMenuItems = (role: string | undefined) => [
|
||||
{ icon: LayoutDashboard, label: 'الرئيسية', path: '/dashboard' },
|
||||
{ icon: Crown, label: 'المركز الضريبي الموحد', path: '/elite-dashboard' },
|
||||
{ icon: AlertTriangle, label: 'مراقبة المخاطر', path: '/risk-monitor' },
|
||||
...(role === 'admin' ? [
|
||||
{ icon: Crown, label: 'المركز الضريبي الموحد', path: '/elite-dashboard' },
|
||||
{ icon: AlertTriangle, label: 'مراقبة المخاطر', path: '/risk-monitor' }
|
||||
] : []),
|
||||
{ icon: FileText, label: 'الفواتير', path: '/invoices' },
|
||||
{ icon: Building2, label: 'الشركات', path: '/companies' },
|
||||
{ icon: Users, label: 'الموظفون', path: '/staff' },
|
||||
...(role === 'admin' ? [
|
||||
{ icon: Users, label: 'الموظفون', path: '/staff' }
|
||||
] : []),
|
||||
{ icon: Settings, label: 'الإعدادات', path: '/settings' },
|
||||
];
|
||||
|
||||
export const Sidebar = () => {
|
||||
const navigate = useNavigate();
|
||||
const user = useAuthStore((state) => state.user);
|
||||
const clearAuth = useAuthStore((state) => state.clearAuth);
|
||||
|
||||
const menuItems = getMenuItems(user?.role);
|
||||
|
||||
const handleLogout = () => {
|
||||
clearAuth();
|
||||
|
||||
@@ -22,6 +22,7 @@ import { useNavigate } from 'react-router-dom';
|
||||
|
||||
export const DashboardPage = () => {
|
||||
const navigate = useNavigate();
|
||||
const user = useAuthStore((state) => state.user);
|
||||
const [stats, setStats] = useState<any>(null);
|
||||
const [isLoading, setIsLoading] = useState(true);
|
||||
|
||||
@@ -210,18 +211,20 @@ export const DashboardPage = () => {
|
||||
</div>
|
||||
</button>
|
||||
|
||||
<button
|
||||
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>
|
||||
<div className="text-right">
|
||||
<div className="font-bold text-white">مراقبة المخاطر</div>
|
||||
<div className="text-xs text-slate-500">لوحة النخبة ودرجات الخطر الضريبي</div>
|
||||
</div>
|
||||
</button>
|
||||
{user?.role === 'admin' && (
|
||||
<button
|
||||
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>
|
||||
<div className="text-right">
|
||||
<div className="font-bold text-white">مراقبة المخاطر</div>
|
||||
<div className="text-xs text-slate-500">لوحة النخبة ودرجات الخطر الضريبي</div>
|
||||
</div>
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { useState, useEffect } from 'react';
|
||||
import { Building2, TrendingUp, AlertTriangle, ChevronDown, Loader2, RefreshCw, Crown } from 'lucide-react';
|
||||
import { motion } from 'framer-motion';
|
||||
import { useAuthStore } from '../../store/authStore';
|
||||
import apiClient from '../../api/client';
|
||||
|
||||
interface CompanyStats {
|
||||
@@ -92,6 +93,7 @@ const Cpu = ({ className }: { className?: string }) => (
|
||||
);
|
||||
|
||||
export const MultiEntityDashboard = () => {
|
||||
const user = useAuthStore((state) => state.user);
|
||||
const [companies, setCompanies] = useState<CompanyStats[]>([]);
|
||||
const [isLoading, setIsLoading] = useState(true);
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
@@ -188,12 +190,14 @@ export const MultiEntityDashboard = () => {
|
||||
className="card-premium p-6 relative overflow-hidden group"
|
||||
>
|
||||
{/* AI Usage Badge */}
|
||||
<div className="absolute top-0 right-0 p-2">
|
||||
<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">
|
||||
<Cpu className="w-3 h-3 text-purple-400" />
|
||||
<span>{company.aiStats?.totalTokens > 1000 ? `${(company.aiStats.totalTokens / 1000).toFixed(1)}k` : company.aiStats?.totalTokens || 0} tokens</span>
|
||||
{user?.role === 'admin' && (
|
||||
<div className="absolute top-4 left-4">
|
||||
<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">
|
||||
<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>
|
||||
)}
|
||||
|
||||
{/* Ambient glow */}
|
||||
<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">
|
||||
<p className="text-sm text-slate-600 dark:text-slate-300">{company.totalInvoices} فاتورة</p>
|
||||
<div className="flex items-center gap-2">
|
||||
{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">
|
||||
{user?.role === 'admin' && company.aiStats?.totalCost > 0 && (
|
||||
<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)}
|
||||
</span>
|
||||
)}
|
||||
|
||||
@@ -64,7 +64,8 @@ export const SettingsPage = () => {
|
||||
const handleSave = async () => {
|
||||
setIsSaving(true);
|
||||
try {
|
||||
await apiClient.post('/users/profile', formData);
|
||||
const { email, ...updateData } = formData;
|
||||
await apiClient.post('/users/profile', updateData);
|
||||
updateUser({ name: formData.name });
|
||||
setShowSuccess(true);
|
||||
setTimeout(() => setShowSuccess(false), 3000);
|
||||
|
||||
@@ -7,9 +7,11 @@
|
||||
import { useState, useEffect } from 'react';
|
||||
import { Users, UserPlus, Search, Shield, Mail, MoreVertical, Trash2, Loader2, X } from 'lucide-react';
|
||||
import { motion, AnimatePresence } from 'framer-motion';
|
||||
import { useAuthStore } from '../../store/authStore';
|
||||
import apiClient from '../../api/client';
|
||||
|
||||
export const StaffPage = () => {
|
||||
const user = useAuthStore((state) => state.user);
|
||||
const [staff, setStaff] = useState<any[]>([]);
|
||||
const [isLoading, setIsLoading] = useState(true);
|
||||
const [searchTerm, setSearchTerm] = useState('');
|
||||
@@ -77,6 +79,7 @@ export const StaffPage = () => {
|
||||
<h2 className="text-3xl font-black text-white">إدارة الموظفين</h2>
|
||||
<p className="text-slate-300 mt-1">إدارة فريق العمل المالي لمكتب المحاسبة الخاص بك.</p>
|
||||
</div>
|
||||
{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 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" />
|
||||
إضافة موظف جديد
|
||||
</button>
|
||||
)}
|
||||
</header>
|
||||
|
||||
{/* ── Search Bar ──────────────────────────────── */}
|
||||
@@ -112,9 +116,13 @@ export const StaffPage = () => {
|
||||
</div>
|
||||
<h3 className="text-xl font-bold text-white mb-2">لا يوجد موظفون مضافون</h3>
|
||||
<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">
|
||||
إضافة أول موظف
|
||||
</button>
|
||||
{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>
|
||||
) : (
|
||||
<p className="text-slate-500 max-w-sm mb-8">ليس لديك صلاحية إضافة موظفين.</p>
|
||||
)}
|
||||
</div>
|
||||
) : (
|
||||
<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 text-center">إجراءات</th>
|
||||
{user?.role === 'admin' && <th className="px-6 py-4 text-xs font-bold text-slate-300 text-center">إجراءات</th>}
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody className="divide-y divide-slate-800/50">
|
||||
@@ -160,6 +168,7 @@ export const StaffPage = () => {
|
||||
{s.role === 'admin' ? 'مدير نظام' : 'محاسب'}
|
||||
</span>
|
||||
</td>
|
||||
{user?.role === 'admin' && (
|
||||
<td className="px-6 py-4 text-center">
|
||||
<div className="flex items-center justify-center gap-2">
|
||||
<button className="p-2 text-slate-500 hover:text-white transition-colors">
|
||||
@@ -173,6 +182,7 @@ export const StaffPage = () => {
|
||||
</button>
|
||||
</div>
|
||||
</td>
|
||||
)}
|
||||
</motion.tr>
|
||||
))}
|
||||
</tbody>
|
||||
|
||||
Reference in New Issue
Block a user