Live Dashboard and debug logging

This commit is contained in:
Hamza-Ayed
2026-04-18 01:04:33 +03:00
parent 93591c75e2
commit aad1998e56
2 changed files with 188 additions and 104 deletions

View File

@@ -1,118 +1,199 @@
/** /**
* ════════════════════════════════════════════════════════════ * ════════════════════════════════════════════════════════════
* مُصادَق (Musadaq) — Dashboard Statistics Components * مُصادَق (Musadaq) — Dashboard Page
* ════════════════════════════════════════════════════════════ * ════════════════════════════════════════════════════════════
*/ */
import { motion } from 'framer-motion'; import { useState, useEffect } from 'react';
import { import {
BarChart3,
Users,
FileText, FileText,
CheckCircle2,
AlertCircle,
TrendingUp, TrendingUp,
Wallet, ArrowUpRight,
ArrowUpRight ArrowDownRight,
Clock,
CheckCircle2,
AlertCircle,
Building2
} from 'lucide-react'; } from 'lucide-react';
import { motion } from 'framer-motion';
const stats = [ import apiClient from '../../api/client';
{ label: 'إجمالي الفواتير', value: '1,280', icon: FileText, color: 'text-primary-600', bg: 'bg-primary-50', change: '+12%' },
{ label: 'تمت مصادقتها', value: '1,150', icon: CheckCircle2, color: 'text-emerald-600', bg: 'bg-emerald-50', change: '+18%' },
{ label: 'قيد المراجعة', value: '42', icon: AlertCircle, color: 'text-amber-600', bg: 'bg-amber-50', change: '-5%' },
{ label: 'مجموع الضريبة (JOD)', value: '14,250.000', icon: Wallet, color: 'text-blue-600', bg: 'bg-blue-50', change: '+8%' },
];
export const DashboardPage = () => { export const DashboardPage = () => {
const [stats, setStats] = useState<any>(null);
const [isLoading, setIsLoading] = useState(true);
const fetchStats = async () => {
try {
const { data } = await apiClient.get('/dashboard/stats');
setStats(data);
} catch (error) {
console.error('Failed to fetch dashboard stats', error);
} finally {
setIsLoading(false);
}
};
useEffect(() => {
fetchStats();
}, []);
if (isLoading) {
return ( return (
<div className="space-y-8 animate-in fade-in slide-in-from-bottom-4 duration-700"> <div className="flex-1 flex justify-center items-center">
<header className="flex items-center justify-between"> <div className="animate-spin rounded-full h-12 w-12 border-b-2 border-primary-600"></div>
<div> </div>
<h2 className="text-3xl font-bold text-slate-900">لوحة التحكم</h2>
<p className="text-slate-500 mt-1">نظرة عامة على نشاطك الضريبي هذا الشهر.</p>
</div>
<div className="flex gap-3">
<button className="bg-white border border-slate-200 text-slate-700 font-semibold py-2.5 px-6 rounded-xl shadow-sm hover:bg-slate-50 transition-all flex items-center gap-2">
<TrendingUp className="w-4 h-4 text-primary-500" />
تصدير التقارير
</button>
<button className="btn-primary py-2.5 px-6 rounded-xl flex items-center gap-2 shadow-lg shadow-primary-500/25">
<FileText className="w-5 h-5" />
فاتورة جديدة
</button>
</div>
</header>
{/* ── Stats Grid ────────────────────────────────────────── */}
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-6">
{stats.map((stat, i) => (
<motion.div
key={i}
initial={{ opacity: 0, y: 20 }}
animate={{ opacity: 1, y: 0 }}
transition={{ delay: i * 0.1 }}
className="card-premium p-6 group cursor-pointer"
>
<div className="flex items-start justify-between mb-4">
<div className={`p-3 rounded-2xl ${stat.bg} ${stat.color} transition-transform group-hover:scale-110 duration-300`}>
<stat.icon className="w-6 h-6" />
</div>
<div className={`flex items-center gap-1 text-[12px] font-bold px-2 py-1 rounded-full ${stat.change.startsWith('+') ? 'bg-emerald-50 text-emerald-600' : 'bg-red-50 text-red-600'}`}>
<ArrowUpRight className="w-3 h-3" />
{stat.change}
</div>
</div>
<p className="text-slate-500 text-sm font-medium">{stat.label}</p>
<h3 className="text-2xl font-bold text-slate-900 mt-1">{stat.value}</h3>
</motion.div>
))}
</div>
{/* ── Main Dashboard Content (Placeholder for Charts/Lists) ── */}
<div className="grid grid-cols-1 lg:grid-cols-3 gap-8">
<div className="lg:col-span-2 space-y-6">
<div className="card-premium h-[400px] p-6 flex flex-col">
<div className="flex items-center justify-between mb-8">
<h4 className="font-bold text-lg">تحليلات الفوترة الأسبوعية</h4>
<select className="bg-slate-50 border border-slate-100 rounded-lg py-1.5 px-3 text-sm font-medium outline-none">
<option>آخر 7 أيام</option>
<option>آخر 30 يوم</option>
</select>
</div>
<div className="flex-1 bg-slate-50 rounded-2xl border border-dashed border-slate-200 flex items-center justify-center">
<p className="text-slate-400 text-sm font-medium italic">رسم بياني توضيحي (Chart integration goes here)</p>
</div>
</div>
</div>
<div className="space-y-6">
<div className="card-premium p-6 bg-primary-600 text-white shadow-xl shadow-primary-500/30">
<h4 className="font-bold text-lg mb-2">استهلاك الاشتراك الحالي</h4>
<p className="text-primary-100 text-sm mb-6">لقد استهلكت 65% من حصتك الشهرية من الفواتير.</p>
<div className="w-full h-3 bg-white/20 rounded-full overflow-hidden mb-6">
<div className="w-2/3 h-full bg-white rounded-full shadow-lg" />
</div>
<button className="w-full bg-white text-primary-600 font-bold py-3 rounded-xl hover:bg-primary-50 transition-all">
ترقية الباقة الآن
</button>
</div>
<div className="card-premium p-6">
<h4 className="font-bold text-lg mb-4">آخر النشاطات</h4>
<div className="space-y-4">
{[1, 2, 3].map(i => (
<div key={i} className="flex items-center gap-3 p-2 hover:bg-slate-50 rounded-xl transition-all cursor-pointer">
<div className="w-10 h-10 bg-slate-100 rounded-lg flex items-center justify-center">
<FileText className="w-5 h-5 text-slate-500" />
</div>
<div className="flex-1">
<p className="text-sm font-bold text-slate-800">فاتورة مبيعات #A-2024-001</p>
<p className="text-[12px] text-slate-500">منذ 10 دقائق · تمت المصادقة</p>
</div>
</div>
))}
</div>
</div>
</div>
</div>
</div>
); );
}
const statCards = [
{
title: 'إجمالي الفواتير',
value: stats?.totalInvoices || 0,
icon: FileText,
color: 'bg-blue-500',
trend: '+12%',
isUp: true
},
{
title: 'الفواتير المصدقة',
value: stats?.approvedInvoices || 0,
icon: CheckCircle2,
color: 'bg-emerald-500',
trend: '+8%',
isUp: true
},
{
title: 'إجمالي الشركات',
value: stats?.companiesCount || 0,
icon: Building2,
color: 'bg-purple-500',
trend: '+2',
isUp: true
},
{
title: 'إجمالي الضرائب (JOD)',
value: Number(stats?.totalTax || 0).toLocaleString('ar-JO', { minimumFractionDigits: 3 }),
icon: TrendingUp,
color: 'bg-amber-500',
trend: '+5%',
isUp: true
},
];
return (
<div className="space-y-8 animate-in fade-in duration-700">
<header className="flex justify-between items-end">
<div>
<h2 className="text-3xl font-black text-slate-900 tracking-tight">لوحة التحكم</h2>
<p className="text-slate-500 mt-1 font-medium">مرحباً بك مجدداً! إليك ملخص نشاط مكتبك اليوم.</p>
</div>
<div className="flex gap-3">
<button className="glass border-slate-200 px-5 py-2.5 rounded-2xl text-slate-600 font-bold text-sm hover:bg-white transition-all">
آخر 30 يوم
</button>
</div>
</header>
{/* ── Stats Grid ────────────────────────────────────────── */}
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-6">
{statCards.map((card, idx) => (
<motion.div
key={card.title}
initial={{ opacity: 0, y: 20 }}
animate={{ opacity: 1, y: 0 }}
transition={{ delay: idx * 0.1 }}
className="card-premium p-6 group hover:scale-[1.02] transition-all cursor-default"
>
<div className="flex justify-between items-start mb-4">
<div className={`w-12 h-12 ${card.color} rounded-2xl flex items-center justify-center text-white shadow-lg shadow-inherit/20`}>
<card.icon className="w-6 h-6" />
</div>
<span className={`flex items-center gap-1 text-xs font-bold ${card.isUp ? 'text-emerald-600 bg-emerald-50' : 'text-red-600 bg-red-50'} px-2 py-1 rounded-lg`}>
{card.trend}
{card.isUp ? <ArrowUpRight className="w-3 h-3" /> : <ArrowDownRight className="w-3 h-3" />}
</span>
</div>
<h3 className="text-slate-500 font-bold text-sm mb-1">{card.title}</h3>
<div className="text-2xl font-black text-slate-900 tracking-tight">{card.value}</div>
</motion.div>
))}
</div>
<div className="grid grid-cols-1 lg:grid-cols-3 gap-8">
{/* ── Recent Activities ────────────────────────────────── */}
<div className="lg:col-span-2 space-y-4">
<div className="flex items-center justify-between px-2">
<h3 className="text-xl font-bold text-slate-900 flex items-center gap-2">
<Clock className="w-5 h-5 text-primary-600" />
آخر الفواتير المرفوعة
</h3>
<button className="text-primary-600 text-sm font-bold hover:underline">عرض الكل</button>
</div>
<div className="card-premium overflow-hidden bg-white border border-slate-100">
{(!stats?.recentActivities || stats.recentActivities.length === 0) ? (
<div className="p-12 text-center text-slate-400 font-medium">
لا توجد نشاطات حديثة بعد.
</div>
) : (
<table className="w-full text-right">
<thead className="bg-slate-50 border-b border-slate-100">
<tr>
<th className="px-6 py-4 text-xs font-bold text-slate-500">الفاتورة</th>
<th className="px-6 py-4 text-xs font-bold text-slate-500">الشركة</th>
<th className="px-6 py-4 text-xs font-bold text-slate-500">المبلغ</th>
<th className="px-6 py-4 text-xs font-bold text-slate-500">الحالة</th>
</tr>
</thead>
<tbody className="divide-y divide-slate-100">
{stats.recentActivities.map((inv: any) => (
<tr key={inv.id} className="hover:bg-slate-50/50 transition-colors">
<td className="px-6 py-4 font-bold text-slate-800">{inv.invoice_number || 'OCR_PENDING'}</td>
<td className="px-6 py-4 text-slate-600">{inv.company?.name}</td>
<td className="px-6 py-4 font-mono font-bold text-slate-700">{Number(inv.total_amount || 0).toFixed(3)}</td>
<td className="px-6 py-4">
<span className={`px-2 py-1 rounded-md text-[10px] font-bold ${
inv.status === 'approved' ? 'bg-emerald-50 text-emerald-600' : 'bg-amber-50 text-amber-600'
}`}>
{inv.status === 'approved' ? 'مصدقة' : 'قيد المعالجة'}
</span>
</td>
</tr>
))}
</tbody>
</table>
)}
</div>
</div>
{/* ── Quick Actions ────────────────────────────────────── */}
<div className="space-y-4">
<h3 className="text-xl font-bold text-slate-900 px-2">إجراءات سريعة</h3>
<div className="grid grid-cols-1 gap-4">
<button className="flex items-center gap-4 p-4 rounded-2xl bg-primary-600 text-white shadow-xl shadow-primary-500/25 hover:bg-primary-700 transition-all group">
<div className="w-10 h-10 rounded-xl bg-white/20 flex items-center justify-center group-hover:scale-110 transition-transform">
<FileText className="w-5 h-5" />
</div>
<div className="text-right">
<div className="font-bold">رفع فاتورة جديدة</div>
<div className="text-xs text-white/70">معالجة فورية بالذكاء الاصطناعي</div>
</div>
</button>
<button className="flex items-center gap-4 p-4 rounded-2xl bg-white border border-slate-200 text-slate-800 hover:border-primary-500 transition-all group">
<div className="w-10 h-10 rounded-xl bg-slate-50 flex items-center justify-center group-hover:bg-primary-50 group-hover:text-primary-600 transition-all">
<Building2 className="w-5 h-5" />
</div>
<div className="text-right">
<div className="font-bold">إضافة شركة</div>
<div className="text-xs text-slate-500">تسجيل عميل جديد في المكتب</div>
</div>
</button>
</div>
</div>
</div>
</div>
);
}; };

View File

@@ -37,10 +37,13 @@ export const InvoicesPage = () => {
const fetchData = async () => { const fetchData = async () => {
try { try {
setIsLoading(true);
const [invRes, compRes] = await Promise.all([ const [invRes, compRes] = await Promise.all([
apiClient.get('/invoices'), apiClient.get('/invoices'),
apiClient.get('/companies') apiClient.get('/companies')
]); ]);
console.log('Fetched Invoices:', invRes.data);
console.log('Fetched Companies:', compRes.data);
setInvoices(invRes.data); setInvoices(invRes.data);
setCompanies(compRes.data); setCompanies(compRes.data);
} catch (error) { } catch (error) {