🚀 Final: Fix stats, staff list, settings profile, and logout redirect
This commit is contained in:
@@ -62,6 +62,16 @@ export class AuthController {
|
|||||||
return this.authService.logout(user.id);
|
return this.authService.logout(user.id);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* الملف الشخصي الحالي والبيانات الأساسية
|
||||||
|
*/
|
||||||
|
@UseGuards(JwtAuthGuard)
|
||||||
|
@Get('me')
|
||||||
|
@HttpCode(HttpStatus.OK)
|
||||||
|
async me(@CurrentUser() user: any) {
|
||||||
|
return this.authService.getMe(user.id);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* الملف الشخصي
|
* الملف الشخصي
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -179,6 +179,29 @@ export class AuthService {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* الحصول على بيانات المستخدم والاشتراك الحالي
|
||||||
|
*/
|
||||||
|
async getMe(userId: string) {
|
||||||
|
const user = await this.dataSource.getRepository(User).findOne({
|
||||||
|
where: { id: userId },
|
||||||
|
relations: ['tenant'],
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!user) throw new UnauthorizedException();
|
||||||
|
|
||||||
|
return {
|
||||||
|
user: {
|
||||||
|
id: user.id,
|
||||||
|
name: user.name,
|
||||||
|
email: user.email,
|
||||||
|
role: user.role,
|
||||||
|
tenantId: user.tenant_id,
|
||||||
|
},
|
||||||
|
tenant: user.tenant,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* تسجيل خروج
|
* تسجيل خروج
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -22,13 +22,6 @@ export class DashboardService {
|
|||||||
where: { tenant_id: tenantId, status: InvoiceStatus.APPROVED },
|
where: { tenant_id: tenantId, status: InvoiceStatus.APPROVED },
|
||||||
});
|
});
|
||||||
|
|
||||||
const pendingInvoices = await this.invoiceRepository.count({
|
|
||||||
where: {
|
|
||||||
tenant_id: tenantId,
|
|
||||||
status: Buffer.from('approved').toString() === InvoiceStatus.APPROVED ? InvoiceStatus.UPLOADED : InvoiceStatus.UPLOADED // wait, using In operator is better
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
// Using QueryBuilder for better control
|
// Using QueryBuilder for better control
|
||||||
const statuses = await this.invoiceRepository
|
const statuses = await this.invoiceRepository
|
||||||
.createQueryBuilder('invoice')
|
.createQueryBuilder('invoice')
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
* ════════════════════════════════════════════════════════════
|
* ════════════════════════════════════════════════════════════
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { NavLink } from 'react-router-dom';
|
import { NavLink, useNavigate } from 'react-router-dom';
|
||||||
import {
|
import {
|
||||||
LayoutDashboard,
|
LayoutDashboard,
|
||||||
FileText,
|
FileText,
|
||||||
@@ -24,8 +24,14 @@ const menuItems = [
|
|||||||
];
|
];
|
||||||
|
|
||||||
export const Sidebar = () => {
|
export const Sidebar = () => {
|
||||||
|
const navigate = useNavigate();
|
||||||
const clearAuth = useAuthStore((state) => state.clearAuth);
|
const clearAuth = useAuthStore((state) => state.clearAuth);
|
||||||
|
|
||||||
|
const handleLogout = () => {
|
||||||
|
clearAuth();
|
||||||
|
navigate('/login');
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<aside className="w-64 h-screen glass border-l border-slate-200 sticky top-0 flex flex-col p-4">
|
<aside className="w-64 h-screen glass border-l border-slate-200 sticky top-0 flex flex-col p-4">
|
||||||
<div className="flex items-center gap-3 px-2 py-6">
|
<div className="flex items-center gap-3 px-2 py-6">
|
||||||
@@ -58,7 +64,7 @@ export const Sidebar = () => {
|
|||||||
|
|
||||||
<div className="pt-4 border-t border-slate-100">
|
<div className="pt-4 border-t border-slate-100">
|
||||||
<button
|
<button
|
||||||
onClick={clearAuth}
|
onClick={handleLogout}
|
||||||
className="flex items-center gap-3 px-4 py-3 w-full rounded-xl text-red-500 hover:bg-red-50 transition-all group"
|
className="flex items-center gap-3 px-4 py-3 w-full rounded-xl text-red-500 hover:bg-red-50 transition-all group"
|
||||||
>
|
>
|
||||||
<LogOut className="w-5 h-5 group-hover:-translate-x-1 transition-transform" />
|
<LogOut className="w-5 h-5 group-hover:-translate-x-1 transition-transform" />
|
||||||
|
|||||||
@@ -40,7 +40,7 @@ export const StaffPage = () => {
|
|||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
try {
|
try {
|
||||||
await apiClient.post('/users', {
|
await apiClient.post('/users', {
|
||||||
full_name: fullName,
|
name: fullName,
|
||||||
email,
|
email,
|
||||||
password,
|
password,
|
||||||
role
|
role
|
||||||
@@ -108,7 +108,7 @@ export const StaffPage = () => {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<h3 className="text-xl font-bold text-slate-900 mb-1">{member.full_name}</h3>
|
<h3 className="text-xl font-bold text-slate-900 mb-1">{member.name}</h3>
|
||||||
<div className="flex items-center gap-2 mb-6">
|
<div className="flex items-center gap-2 mb-6">
|
||||||
<span className={`text-[10px] font-black uppercase tracking-widest px-2 py-0.5 rounded-md ${
|
<span className={`text-[10px] font-black uppercase tracking-widest px-2 py-0.5 rounded-md ${
|
||||||
member.role === 'admin' ? 'bg-indigo-50 text-indigo-600' : 'bg-slate-50 text-slate-600'
|
member.role === 'admin' ? 'bg-indigo-50 text-indigo-600' : 'bg-slate-50 text-slate-600'
|
||||||
|
|||||||
Reference in New Issue
Block a user