/** * ════════════════════════════════════════════════════════════ * مُصادَق (Musadaq) — Invoices Management Page (Premium Dark) * ════════════════════════════════════════════════════════════ */ import { useState, useEffect } from 'react'; import { motion, AnimatePresence } from 'framer-motion'; import { Upload, Search, Filter, Eye, CheckCircle2, Clock, AlertCircle, ChevronLeft, ChevronRight, Building2, FileText, Send, Download, Trash2, Loader2, X } from 'lucide-react'; import apiClient from '../../api/client'; export const InvoicesPage = () => { const [invoices, setInvoices] = useState([]); const [companies, setCompanies] = useState([]); const [isLoading, setIsLoading] = useState(true); const [searchTerm, setSearchTerm] = useState(''); const [isUploadModalOpen, setIsUploadModalOpen] = useState(false); // Upload Form State const [selectedCompanyId, setSelectedCompanyId] = useState(''); const [selectedFile, setSelectedFile] = useState(null); const [isUploading, setIsUploading] = useState(false); // View Modal State const [viewingInvoice, setViewingInvoice] = useState(null); const [isViewModalOpen, setIsViewModalOpen] = useState(false); const [deleteLoading, setDeleteLoading] = useState(null); const [submitLoading, setSubmitLoading] = useState(null); const fetchData = async () => { setIsLoading(true); try { const [compRes, invRes] = await Promise.all([ apiClient.get('/companies').catch(() => ({ data: [] })), apiClient.get('/invoices').catch(() => ({ data: [] })) ]); setCompanies(compRes.data); setInvoices(invRes.data); } finally { setIsLoading(false); } }; useEffect(() => { fetchData(); }, []); const handleUpload = async (e: React.FormEvent) => { e.preventDefault(); if (!selectedCompanyId || !selectedFile) return; setIsUploading(true); const formData = new FormData(); formData.append('file', selectedFile); try { await apiClient.post(`/invoices/upload/${selectedCompanyId}`, formData, { headers: { 'Content-Type': 'multipart/form-data' } }); setIsUploadModalOpen(false); setSelectedFile(null); setSelectedCompanyId(''); fetchData(); } catch (error) { console.error('Upload failed', error); alert('حدث خطأ أثناء رفع الفاتورة'); } finally { setIsUploading(false); } }; const handleDelete = async (id: string) => { if (!confirm('هل أنت متأكد من حذف هذه الفاتورة نهائياً؟')) return; setDeleteLoading(id); try { await apiClient.post(`/invoices/${id}/delete`); fetchData(); } catch (error) { alert('فشل حذف الفاتورة'); } finally { setDeleteLoading(null); } }; const handleSubmitToJoFotara = async (inv: any) => { setSubmitLoading(inv.id); try { await apiClient.post(`/invoices/${inv.id}/submit`); alert('تم الإرسال لـ جو فوترة بنجاح! 🎉'); fetchData(); } catch (err) { alert('فشل الإرسال لـ جو فوترة. تأكد من إعدادات الشركة وصحة البيانات.'); } finally { setSubmitLoading(null); } }; const handleFileChange = (e: React.ChangeEvent) => { if (e.target.files && e.target.files[0]) { setSelectedFile(e.target.files[0]); } }; const filteredInvoices = invoices.filter(inv => inv.invoice_number?.includes(searchTerm) || inv.company?.name?.toLowerCase().includes(searchTerm.toLowerCase()) ); const StatusBadge = ({ invoice }: { invoice: any }) => { const status = invoice.status; const config: any = { approved: { color: 'text-emerald-400 bg-emerald-500/10 border-emerald-500/20', icon: CheckCircle2, label: 'تم التصديق' }, validated: { color: 'text-blue-400 bg-blue-500/10 border-blue-500/20', icon: CheckCircle2, label: 'جاهز للإرسال' }, extracted: { color: 'text-indigo-400 bg-indigo-500/10 border-indigo-500/20', icon: CheckCircle2, label: 'تم الاستخراج' }, uploaded: { color: 'text-amber-400 bg-amber-500/10 border-amber-500/20', icon: Clock, label: 'قيد المعالجة AI' }, extracting: { color: 'text-amber-400 bg-amber-500/10 border-amber-500/20', icon: Clock, label: 'قيد الاستخراج' }, validation_failed: { color: 'text-red-400 bg-red-500/10 border-red-500/20', icon: AlertCircle, label: 'خطأ في التحقق' }, }; const { color, icon: Icon, label } = config[status] || { color: 'text-slate-400 bg-slate-800', icon: Clock, label: status }; return ( {label} ); }; return (

إدارة الفواتير

عرض، معالجة، وإرسال الفواتير الضريبية لبوابة الضريبة.

{/* ── Filter & Search Bar ──────────────────────────────── */}
setSearchTerm(e.target.value)} />
{/* ── Invoices Table ───────────────────────────────────── */}
{isLoading ? (

جاري جلب الفواتير...

) : filteredInvoices.length === 0 ? (

لا توجد فواتير بعد

ابدأ برفع أول فاتورة ليقوم محرك الذكاء الاصطناعي باستخراج بياناتها ومصادقتها ضريبياً.

) : (
{filteredInvoices.map((inv, idx) => ( { setViewingInvoice(inv); setIsViewModalOpen(true); }} > ))}
رقم الفاتورة الشركة التاريخ المجموع (JOD) الحالة إجراءات
{inv.invoice_number || '---'}
{inv.company?.name || '---'}
{inv.invoice_date ? new Date(inv.invoice_date).toLocaleDateString('ar-JO') : '---'} {Number(inv.grand_total).toLocaleString('en-US', { minimumFractionDigits: 3 })}
e.stopPropagation()}>
)} {/* ── Pagination ───────────────────────────────────────── */} {!isLoading && filteredInvoices.length > 0 && (

عرض {filteredInvoices.length} فواتير

)}
{/* ── Upload Modal ─────────────────────────────────────── */} {isUploadModalOpen && (

رفع فاتورة جديدة

اختر الشركة وملف الفاتورة (PDF أو صورة) وسيقوم الذكاء الاصطناعي بالباقي.

{selectedFile ? selectedFile.name : 'اسحب الملف هنا أو انقر للاختيار'}

PDF, JPG, PNG (حد أقصى 10MB)

)}
{/* ── View Invoice Modal ─────────────────────────────────── */} {isViewModalOpen && viewingInvoice && (

معاينة الفاتورة

رقم: {viewingInvoice.invoice_number || '---'} • {viewingInvoice.company?.name}