🚀 Phase 3 Complete: Fix staff list, PDF preview, and functional Settings/Profile
This commit is contained in:
@@ -46,4 +46,9 @@ export class UsersController {
|
||||
async remove(@CurrentUser() user: any, @Param('id') id: string) {
|
||||
return this.usersService.remove(user.tenantId, id, user.id);
|
||||
}
|
||||
|
||||
@Post('profile')
|
||||
async updateProfile(@CurrentUser() user: any, @Body() dto: any) {
|
||||
return this.usersService.update(user.id, dto);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -71,4 +71,21 @@ export class UsersService {
|
||||
const user = await this.findOne(tenantId, id);
|
||||
await this.userRepository.update(id, { is_active: false });
|
||||
}
|
||||
|
||||
/**
|
||||
* تحديث بيانات مستخدم
|
||||
*/
|
||||
async update(id: string, dto: any): Promise<User> {
|
||||
const user = await this.userRepository.findOne({ where: { id } });
|
||||
if (!user) throw new NotFoundException('User not found');
|
||||
|
||||
// Hash password if provided
|
||||
if (dto.password) {
|
||||
dto.password_hash = await bcrypt.hash(dto.password, 12);
|
||||
delete dto.password;
|
||||
}
|
||||
|
||||
Object.assign(user, dto);
|
||||
return this.userRepository.save(user);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -373,25 +373,17 @@ export const InvoicesPage = () => {
|
||||
</button>
|
||||
</header>
|
||||
|
||||
<div className="flex-1 overflow-auto bg-slate-950 p-8 flex justify-center items-start">
|
||||
<div className="bg-white rounded-lg overflow-hidden shadow-2xl">
|
||||
<img
|
||||
src={`${apiClient.defaults.baseURL}/invoices/${viewingInvoice.id}/file?token=${localStorage.getItem('access_token')}`}
|
||||
alt="Invoice"
|
||||
className="max-w-full h-auto"
|
||||
onError={(e) => {
|
||||
e.currentTarget.style.display = 'none';
|
||||
const token = localStorage.getItem('access_token');
|
||||
e.currentTarget.parentElement!.innerHTML = `
|
||||
<div class="p-20 text-center bg-slate-900 text-slate-300 w-[600px]">
|
||||
<FileText class="w-16 h-16 text-slate-700 mx-auto mb-4" />
|
||||
<h4 class="text-xl font-bold text-white mb-2">تعذر عرض الملف</h4>
|
||||
<p class="text-slate-500 mb-8">قد يكون الملف PDF أو حدث خطأ في التحميل.</p>
|
||||
<a href="${apiClient.defaults.baseURL}/invoices/${viewingInvoice.id}/file?token=${token}" target="_blank" class="px-8 py-3 bg-emerald-500 text-slate-950 font-bold rounded-xl inline-block">تحميل الملف لفتحه</a>
|
||||
</div>
|
||||
`;
|
||||
}}
|
||||
<div className="flex-1 overflow-auto bg-slate-950 p-8 flex justify-center items-center">
|
||||
<div className="w-full h-full max-w-4xl bg-white rounded-xl overflow-hidden shadow-2xl relative">
|
||||
<iframe
|
||||
src={`${apiClient.defaults.baseURL}/invoices/${viewingInvoice.id}/file?token=${localStorage.getItem('access_token')}#toolbar=0`}
|
||||
className="w-full h-full border-none"
|
||||
title="Invoice Preview"
|
||||
/>
|
||||
{/* Fallback overlay in case of loading issues */}
|
||||
<div className="absolute inset-0 pointer-events-none flex items-center justify-center bg-slate-900/10 backdrop-blur-[2px] opacity-0 hover:opacity-100 transition-opacity">
|
||||
<p className="bg-slate-900/80 text-white px-4 py-2 rounded-lg text-xs">جاري عرض الفاتورة...</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -4,25 +4,75 @@
|
||||
* ════════════════════════════════════════════════════════════
|
||||
*/
|
||||
|
||||
import { useState } from 'react';
|
||||
import { useState, useEffect } from 'react';
|
||||
import {
|
||||
Settings,
|
||||
User,
|
||||
User as UserIcon,
|
||||
Lock,
|
||||
Bell,
|
||||
Shield,
|
||||
CreditCard,
|
||||
Save,
|
||||
Palette,
|
||||
Moon
|
||||
Moon,
|
||||
Camera,
|
||||
Loader2,
|
||||
CheckCircle2
|
||||
} from 'lucide-react';
|
||||
import { motion } from 'framer-motion';
|
||||
import { motion, AnimatePresence } from 'framer-motion';
|
||||
import apiClient from '../../api/client';
|
||||
|
||||
export const SettingsPage = () => {
|
||||
const [activeTab, setActiveTab] = useState('profile');
|
||||
const [user, setUser] = useState<any>(null);
|
||||
const [isLoading, setIsLoading] = useState(true);
|
||||
const [isSaving, setIsSaving] = useState(false);
|
||||
const [showSuccess, setShowSuccess] = useState(false);
|
||||
|
||||
// Form State
|
||||
const [formData, setFormData] = useState({
|
||||
name: '',
|
||||
email: '',
|
||||
phone: '',
|
||||
language: 'العربية'
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
const fetchProfile = async () => {
|
||||
try {
|
||||
// Get current user from auth state or fetch again
|
||||
const { data } = await apiClient.get('/auth/me');
|
||||
setUser(data);
|
||||
setFormData({
|
||||
name: data.name || '',
|
||||
email: data.email || '',
|
||||
phone: data.phone || '',
|
||||
language: data.language || 'العربية'
|
||||
});
|
||||
} catch (err) {
|
||||
console.error('Failed to fetch profile', err);
|
||||
} finally {
|
||||
setIsLoading(false);
|
||||
}
|
||||
};
|
||||
fetchProfile();
|
||||
}, []);
|
||||
|
||||
const handleSave = async () => {
|
||||
setIsSaving(true);
|
||||
try {
|
||||
await apiClient.post('/users/profile', formData);
|
||||
setShowSuccess(true);
|
||||
setTimeout(() => setShowSuccess(false), 3000);
|
||||
} catch (err) {
|
||||
alert('حدث خطأ أثناء حفظ التغييرات');
|
||||
} finally {
|
||||
setIsSaving(false);
|
||||
}
|
||||
};
|
||||
|
||||
const tabs = [
|
||||
{ id: 'profile', label: 'الملف الشخصي', icon: User },
|
||||
{ id: 'profile', label: 'الملف الشخصي', icon: UserIcon },
|
||||
{ id: 'security', label: 'الأمان والخصوصية', icon: Lock },
|
||||
{ id: 'office', label: 'إعدادات المكتب', icon: Settings },
|
||||
{ id: 'notifications', label: 'التنبيهات', icon: Bell },
|
||||
@@ -30,16 +80,39 @@ export const SettingsPage = () => {
|
||||
{ id: 'subscription', label: 'الاشتراك والدفع', icon: CreditCard },
|
||||
];
|
||||
|
||||
if (isLoading) {
|
||||
return (
|
||||
<div className="space-y-8 animate-in fade-in duration-700">
|
||||
<header>
|
||||
<h2 className="text-3xl font-black text-white">إعدادات النظام</h2>
|
||||
<p className="text-slate-400 mt-1">تخصيص حسابك وتفضيلات مكتب المحاسبة الخاص بك.</p>
|
||||
<div className="flex-1 flex flex-col items-center justify-center">
|
||||
<Loader2 className="w-10 h-10 text-emerald-500 animate-spin" />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="space-y-8 animate-in fade-in duration-700 max-w-7xl mx-auto">
|
||||
<header className="flex items-end justify-between">
|
||||
<div>
|
||||
<h2 className="text-4xl font-black text-white tracking-tight">إعدادات النظام</h2>
|
||||
<p className="text-slate-400 mt-2 text-lg font-medium">إدارة حسابك الشخصي وتخصيص تجربة "مُصادَق" الخاصة بك.</p>
|
||||
</div>
|
||||
<AnimatePresence>
|
||||
{showSuccess && (
|
||||
<motion.div
|
||||
initial={{ opacity: 0, y: 10 }}
|
||||
animate={{ opacity: 1, y: 0 }}
|
||||
exit={{ opacity: 0 }}
|
||||
className="bg-emerald-500/10 border border-emerald-500/20 text-emerald-400 px-6 py-3 rounded-2xl flex items-center gap-3 font-bold"
|
||||
>
|
||||
<CheckCircle2 className="w-5 h-5" />
|
||||
تم حفظ التغييرات بنجاح
|
||||
</motion.div>
|
||||
)}
|
||||
</AnimatePresence>
|
||||
</header>
|
||||
|
||||
<div className="flex flex-col lg:flex-row gap-8">
|
||||
<div className="flex flex-col lg:flex-row gap-10">
|
||||
{/* ── Tabs Sidebar ─────────────────────────── */}
|
||||
<aside className="w-full lg:w-72 space-y-2">
|
||||
<aside className="w-full lg:w-80 space-y-3">
|
||||
{tabs.map((tab) => {
|
||||
const Icon = tab.icon;
|
||||
const isActive = activeTab === tab.id;
|
||||
@@ -47,91 +120,149 @@ export const SettingsPage = () => {
|
||||
<button
|
||||
key={tab.id}
|
||||
onClick={() => setActiveTab(tab.id)}
|
||||
className={`w-full flex items-center gap-3 px-6 py-4 rounded-xl font-bold transition-all ${
|
||||
className={`w-full flex items-center gap-4 px-7 py-5 rounded-[20px] font-bold transition-all relative group ${
|
||||
isActive
|
||||
? 'bg-emerald-500 text-slate-950 shadow-lg shadow-emerald-500/20'
|
||||
: 'text-slate-400 hover:bg-slate-800/50 hover:text-white'
|
||||
? 'bg-emerald-500 text-slate-950 shadow-2xl shadow-emerald-500/30 scale-[1.02]'
|
||||
: 'text-slate-500 hover:bg-slate-800/40 hover:text-slate-200'
|
||||
}`}
|
||||
>
|
||||
<Icon className={`w-5 h-5 ${isActive ? 'text-slate-950' : 'text-slate-500'}`} />
|
||||
{tab.label}
|
||||
<Icon className={`w-6 h-6 ${isActive ? 'text-slate-950' : 'text-slate-600 group-hover:text-slate-400'}`} />
|
||||
<span className="text-lg">{tab.label}</span>
|
||||
{isActive && (
|
||||
<motion.div
|
||||
layoutId="activeTab"
|
||||
className="absolute right-0 w-1.5 h-8 bg-slate-950 rounded-l-full"
|
||||
/>
|
||||
)}
|
||||
</button>
|
||||
);
|
||||
})}
|
||||
</aside>
|
||||
|
||||
{/* ── Content Area ─────────────────────────── */}
|
||||
<main className="flex-1 bg-slate-900/50 backdrop-blur-xl border border-slate-800/60 rounded-[32px] overflow-hidden flex flex-col min-h-[600px]">
|
||||
<div className="p-10 flex-1">
|
||||
<main className="flex-1 bg-slate-900/40 backdrop-blur-3xl border border-slate-800/50 rounded-[40px] overflow-hidden flex flex-col min-h-[650px] shadow-2xl shadow-black/50">
|
||||
<div className="p-12 flex-1">
|
||||
{activeTab === 'profile' && (
|
||||
<motion.div initial={{ opacity: 0, x: 20 }} animate={{ opacity: 1, x: 0 }} className="space-y-8">
|
||||
<div className="flex items-center gap-6 pb-8 border-b border-slate-800/60">
|
||||
<div className="w-24 h-24 rounded-3xl bg-gradient-to-br from-emerald-500 to-emerald-600 flex items-center justify-center text-4xl font-black text-slate-950 shadow-xl shadow-emerald-500/20">
|
||||
H
|
||||
<motion.div initial={{ opacity: 0, y: 20 }} animate={{ opacity: 1, y: 0 }} className="space-y-12">
|
||||
{/* Profile Header */}
|
||||
<div className="flex flex-col md:flex-row items-center gap-10 pb-12 border-b border-slate-800/50">
|
||||
<div className="relative group">
|
||||
<div className="w-32 h-32 rounded-[32px] bg-gradient-to-br from-emerald-400 via-emerald-500 to-emerald-600 flex items-center justify-center text-5xl font-black text-slate-950 shadow-2xl shadow-emerald-500/20 group-hover:scale-105 transition-transform duration-500">
|
||||
{formData.name[0] || 'U'}
|
||||
</div>
|
||||
<div>
|
||||
<h3 className="text-2xl font-bold text-white mb-2">حمزة الغويريين</h3>
|
||||
<p className="text-slate-500 font-medium">مدير مكتب • حساب احترافي</p>
|
||||
</div>
|
||||
<button className="mr-auto bg-slate-800 hover:bg-slate-700 text-white px-6 py-2 rounded-xl text-sm font-bold transition-all">
|
||||
تغيير الصورة
|
||||
<button className="absolute -bottom-2 -right-2 w-12 h-12 bg-slate-900 border border-slate-700 rounded-2xl flex items-center justify-center text-slate-400 hover:text-emerald-400 hover:border-emerald-500/50 transition-all shadow-xl">
|
||||
<Camera className="w-6 h-6" />
|
||||
</button>
|
||||
</div>
|
||||
<div className="text-center md:text-right">
|
||||
<h3 className="text-3xl font-black text-white mb-2">{formData.name || 'مستخدم جديد'}</h3>
|
||||
<div className="flex flex-wrap items-center gap-3 justify-center md:justify-start">
|
||||
<span className="bg-emerald-500/10 text-emerald-400 text-xs font-black px-4 py-1.5 rounded-full border border-emerald-500/20 uppercase tracking-widest">
|
||||
{user?.role === 'admin' ? 'مدير مكتب' : 'محاسب'}
|
||||
</span>
|
||||
<span className="text-slate-500 font-bold text-sm">•</span>
|
||||
<span className="text-slate-500 font-bold text-sm">{formData.email}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-6">
|
||||
<div className="space-y-2">
|
||||
<label className="text-sm font-bold text-slate-400 mr-1">الاسم الكامل</label>
|
||||
<input type="text" defaultValue="حمزة الغويريين" className="w-full bg-slate-800 border border-slate-700 rounded-xl px-5 py-3 text-white outline-none focus:border-emerald-500/50 transition-all" />
|
||||
{/* Form Grid */}
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-x-10 gap-y-8">
|
||||
<div className="space-y-3">
|
||||
<label className="text-sm font-black text-slate-500 uppercase tracking-widest ml-1">الاسم الكامل</label>
|
||||
<input
|
||||
type="text"
|
||||
value={formData.name}
|
||||
onChange={e => setFormData({...formData, name: e.target.value})}
|
||||
className="w-full bg-slate-900/60 border border-slate-800 rounded-2xl px-6 py-4 text-white font-bold outline-none focus:border-emerald-500/50 focus:bg-slate-900 transition-all shadow-inner"
|
||||
/>
|
||||
</div>
|
||||
<div className="space-y-2">
|
||||
<label className="text-sm font-bold text-slate-400 mr-1">البريد الإلكتروني</label>
|
||||
<input type="email" defaultValue="hamza@musadaq.jo" className="w-full bg-slate-800 border border-slate-700 rounded-xl px-5 py-3 text-white outline-none focus:border-emerald-500/50 transition-all" />
|
||||
<div className="space-y-3">
|
||||
<label className="text-sm font-black text-slate-500 uppercase tracking-widest ml-1">البريد الإلكتروني</label>
|
||||
<input
|
||||
type="email"
|
||||
value={formData.email}
|
||||
onChange={e => setFormData({...formData, email: e.target.value})}
|
||||
className="w-full bg-slate-900/60 border border-slate-800 rounded-2xl px-6 py-4 text-slate-400 font-bold outline-none cursor-not-allowed opacity-70"
|
||||
disabled
|
||||
/>
|
||||
</div>
|
||||
<div className="space-y-2">
|
||||
<label className="text-sm font-bold text-slate-400 mr-1">رقم الهاتف</label>
|
||||
<input type="text" defaultValue="+962 79 000 0000" className="w-full bg-slate-800 border border-slate-700 rounded-xl px-5 py-3 text-white outline-none focus:border-emerald-500/50 transition-all" />
|
||||
<div className="space-y-3">
|
||||
<label className="text-sm font-black text-slate-500 uppercase tracking-widest ml-1">رقم الهاتف</label>
|
||||
<input
|
||||
type="text"
|
||||
value={formData.phone}
|
||||
onChange={e => setFormData({...formData, phone: e.target.value})}
|
||||
className="w-full bg-slate-900/60 border border-slate-800 rounded-2xl px-6 py-4 text-white font-bold outline-none focus:border-emerald-500/50 focus:bg-slate-900 transition-all shadow-inner"
|
||||
/>
|
||||
</div>
|
||||
<div className="space-y-2">
|
||||
<label className="text-sm font-bold text-slate-400 mr-1">اللغة المفضلة</label>
|
||||
<select className="w-full bg-slate-800 border border-slate-700 rounded-xl px-5 py-3 text-white outline-none focus:border-emerald-500/50 transition-all appearance-none cursor-pointer">
|
||||
<div className="space-y-3">
|
||||
<label className="text-sm font-black text-slate-500 uppercase tracking-widest ml-1">اللغة المفضلة</label>
|
||||
<div className="relative">
|
||||
<select
|
||||
value={formData.language}
|
||||
onChange={e => setFormData({...formData, language: e.target.value})}
|
||||
className="w-full bg-slate-900/60 border border-slate-800 rounded-2xl px-6 py-4 text-white font-bold outline-none focus:border-emerald-500/50 focus:bg-slate-900 transition-all appearance-none cursor-pointer"
|
||||
>
|
||||
<option>العربية</option>
|
||||
<option>English</option>
|
||||
</select>
|
||||
<div className="absolute left-6 top-1/2 -translate-y-1/2 pointer-events-none text-slate-600">
|
||||
<Palette className="w-5 h-5" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</motion.div>
|
||||
)}
|
||||
|
||||
{activeTab === 'appearance' && (
|
||||
<motion.div initial={{ opacity: 0, x: 20 }} animate={{ opacity: 1, x: 0 }} className="space-y-8">
|
||||
<h3 className="text-2xl font-bold text-white mb-6">المظهر والنظام</h3>
|
||||
<div className="grid grid-cols-2 gap-6">
|
||||
<div className="p-6 rounded-2xl bg-emerald-500/10 border border-emerald-500/20 flex flex-col items-center gap-4 cursor-pointer">
|
||||
<Moon className="w-12 h-12 text-emerald-500" />
|
||||
<span className="font-bold text-white">الوضع الداكن (مفعل)</span>
|
||||
<motion.div initial={{ opacity: 0, y: 20 }} animate={{ opacity: 1, y: 0 }} className="space-y-10">
|
||||
<h3 className="text-3xl font-black text-white">المظهر والنظام</h3>
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-8">
|
||||
<div className="p-8 rounded-3xl bg-emerald-500/10 border-2 border-emerald-500/40 flex flex-col items-center gap-6 cursor-pointer shadow-2xl shadow-emerald-500/10 transition-all hover:scale-[1.02]">
|
||||
<div className="w-20 h-20 bg-slate-900 rounded-3xl flex items-center justify-center border border-emerald-500/30">
|
||||
<Moon className="w-10 h-10 text-emerald-500" />
|
||||
</div>
|
||||
<div className="text-center">
|
||||
<span className="font-black text-white text-xl block">الوضع الداكن (Premium)</span>
|
||||
<span className="text-emerald-500/60 text-sm font-bold">الوضع الافتراضي مفعل الآن</span>
|
||||
</div>
|
||||
</div>
|
||||
<div className="p-8 rounded-3xl bg-slate-800/20 border-2 border-slate-800 flex flex-col items-center gap-6 opacity-40 cursor-not-allowed grayscale">
|
||||
<div className="w-20 h-20 bg-white rounded-3xl flex items-center justify-center border border-slate-700" />
|
||||
<div className="text-center">
|
||||
<span className="font-black text-slate-500 text-xl block">الوضع الفاتح</span>
|
||||
<span className="text-slate-600 text-sm font-bold">غير متوفر في نسخة النخبة</span>
|
||||
</div>
|
||||
<div className="p-6 rounded-2xl bg-slate-800/30 border border-slate-700 flex flex-col items-center gap-4 opacity-50 cursor-not-allowed">
|
||||
<div className="w-12 h-12 bg-white rounded-full" />
|
||||
<span className="font-bold text-slate-500">الوضع الفاتح</span>
|
||||
</div>
|
||||
</div>
|
||||
</motion.div>
|
||||
)}
|
||||
|
||||
{(activeTab !== 'profile' && activeTab !== 'appearance') && (
|
||||
<div className="flex flex-col items-center justify-center h-full text-center">
|
||||
<Shield className="w-16 h-16 text-slate-800 mb-6" />
|
||||
<h3 className="text-xl font-bold text-slate-500">هذه الصفحة قيد التطوير</h3>
|
||||
<p className="text-slate-600 max-w-xs mt-2 text-sm">نحن نعمل على توفير المزيد من خيارات التخصيص والأمان قريباً.</p>
|
||||
<div className="flex flex-col items-center justify-center h-full text-center py-20">
|
||||
<div className="w-24 h-24 bg-slate-800/50 rounded-full flex items-center justify-center mb-8 border border-slate-700/50">
|
||||
<Shield className="w-12 h-12 text-slate-700" />
|
||||
</div>
|
||||
<h3 className="text-2xl font-black text-slate-400">هذه الخيارات قيد التطوير</h3>
|
||||
<p className="text-slate-600 max-w-sm mt-4 text-lg font-medium">نحن نعمل على بناء أقوى أدوات التحكم والأمان لتناسب احتياجات المحاسب المتميز.</p>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<footer className="px-10 py-6 border-t border-slate-800/60 bg-slate-900/30 flex items-center justify-between">
|
||||
<p className="text-xs text-slate-500">آخر تحديث: 22 أبريل 2026</p>
|
||||
<button className="bg-emerald-500 hover:bg-emerald-600 text-slate-950 font-black px-10 py-3 rounded-xl flex items-center gap-2 shadow-lg shadow-emerald-500/20 transition-all active:scale-95">
|
||||
<Save className="w-5 h-5" />
|
||||
حفظ التغييرات
|
||||
<footer className="px-12 py-8 border-t border-slate-800/50 bg-slate-900/60 flex items-center justify-between">
|
||||
<div className="flex items-center gap-3">
|
||||
<div className="w-2 h-2 rounded-full bg-emerald-500 animate-pulse" />
|
||||
<p className="text-sm text-slate-500 font-bold uppercase tracking-widest">تعديل البيانات متاح حالياً</p>
|
||||
</div>
|
||||
<button
|
||||
onClick={handleSave}
|
||||
disabled={isSaving}
|
||||
className="bg-emerald-500 hover:bg-emerald-600 text-slate-950 font-black px-12 py-4 rounded-2xl flex items-center gap-3 shadow-2xl shadow-emerald-500/30 transition-all active:scale-95 disabled:opacity-50"
|
||||
>
|
||||
{isSaving ? <Loader2 className="w-6 h-6 animate-spin" /> : <Save className="w-6 h-6" />}
|
||||
<span className="text-lg">حفظ التغييرات</span>
|
||||
</button>
|
||||
</footer>
|
||||
</main>
|
||||
|
||||
@@ -24,7 +24,7 @@ export const StaffPage = () => {
|
||||
|
||||
const fetchStaff = async () => {
|
||||
try {
|
||||
const { data } = await apiClient.get('/staff');
|
||||
const { data } = await apiClient.get('/users');
|
||||
setStaff(data);
|
||||
} catch (error) {
|
||||
console.error('Failed to fetch staff', error);
|
||||
@@ -41,7 +41,7 @@ export const StaffPage = () => {
|
||||
e.preventDefault();
|
||||
setIsSubmitting(true);
|
||||
try {
|
||||
await apiClient.post('/staff', { name, email, password, role });
|
||||
await apiClient.post('/users', { name, email, password, role });
|
||||
setIsAddModalOpen(false);
|
||||
setName('');
|
||||
setEmail('');
|
||||
@@ -58,7 +58,7 @@ export const StaffPage = () => {
|
||||
const handleDelete = async (id: string) => {
|
||||
if (!confirm('هل أنت متأكد من حذف هذا الموظف؟')) return;
|
||||
try {
|
||||
await apiClient.post(`/staff/${id}/delete`);
|
||||
await apiClient.delete(`/users/${id}`);
|
||||
fetchStaff();
|
||||
} catch (error) {
|
||||
alert('فشل حذف الموظف');
|
||||
|
||||
Reference in New Issue
Block a user