Update: 2026-05-09 21:27:35

This commit is contained in:
Hamza-Ayed
2026-05-09 21:27:35 +03:00
parent 3a9a3be04f
commit 72a00bb308

View File

@@ -1647,7 +1647,76 @@
<span>📊</span> استيراد Excel (Bulk)
</button>
</div>
<table class="data-table">
<!-- Invoice Filters & Tabs -->
<div class="px-6 py-4 border-b border-gray-100 bg-gray-50/50">
<div class="flex flex-wrap items-center justify-between gap-4">
<!-- Tabs -->
<div class="flex flex-wrap bg-white p-1 rounded-xl border border-gray-200 shadow-sm gap-1">
<button @click="activeInvoiceTab = 'all'"
:class="activeInvoiceTab === 'all' ? 'bg-indigo-600 text-white shadow-md' : 'text-gray-600 hover:bg-gray-100'"
class="px-4 py-2 rounded-lg text-xs font-bold transition-all duration-200">
الكل
</button>
<button @click="activeInvoiceTab = 'companies'"
:class="activeInvoiceTab === 'companies' ? 'bg-indigo-600 text-white shadow-md' : 'text-gray-600 hover:bg-gray-100'"
class="px-4 py-2 rounded-lg text-xs font-bold transition-all duration-200">
حسب الشركات
</button>
<button @click="activeInvoiceTab = 'approved'"
:class="activeInvoiceTab === 'approved' ? 'bg-indigo-600 text-white shadow-md' : 'text-gray-600 hover:bg-gray-100'"
class="px-4 py-2 rounded-lg text-xs font-bold transition-all duration-200">
المعتمدة
</button>
<button @click="activeInvoiceTab = 'pending'"
:class="activeInvoiceTab === 'pending' ? 'bg-indigo-600 text-white shadow-md' : 'text-gray-600 hover:bg-gray-100'"
class="px-4 py-2 rounded-lg text-xs font-bold transition-all duration-200">
المعلقة
</button>
<button @click="activeInvoiceTab = 'rejected'"
:class="activeInvoiceTab === 'rejected' ? 'bg-indigo-600 text-white shadow-md' : 'text-gray-600 hover:bg-gray-100'"
class="px-4 py-2 rounded-lg text-xs font-bold transition-all duration-200">
المرفوضة
</button>
<button @click="activeInvoiceTab = 'jofotara'"
:class="activeInvoiceTab === 'jofotara' ? 'bg-indigo-600 text-white shadow-md' : 'text-gray-600 hover:bg-gray-100'"
class="px-4 py-2 rounded-lg text-xs font-bold transition-all duration-200">
جوفوترا
</button>
</div>
<!-- Company Filter -->
<div class="flex items-center gap-2">
<span class="text-xs font-bold text-gray-500">تصفية حسب الشركة:</span>
<select x-model="invoiceCompanyFilter" class="text-xs font-bold border border-gray-200 rounded-lg px-3 py-2 bg-white outline-none focus:border-indigo-500 transition-colors">
<option value="">جميع الشركات</option>
<template x-for="c in companies" :key="c.id">
<option :value="c.id" x-text="c.name"></option>
</template>
</select>
</div>
</div>
</div>
<!-- Companies Selection View -->
<div x-show="activeInvoiceTab === 'companies'" class="p-6 bg-gray-50 border-b border-gray-100">
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
<template x-for="c in companies" :key="c.id">
<div @click="invoiceCompanyFilter = c.id; activeInvoiceTab = 'all'"
class="bg-white p-4 rounded-xl border border-gray-200 shadow-sm cursor-pointer hover:border-indigo-500 hover:shadow-md transition-all group">
<div class="flex items-center gap-3">
<div class="w-10 h-10 rounded-lg bg-indigo-50 text-indigo-600 flex items-center justify-center font-bold group-hover:bg-indigo-600 group-hover:text-white transition-colors" x-text="c.name[0]"></div>
<div>
<div class="font-bold text-gray-800" x-text="c.name"></div>
<div class="text-xs text-gray-500 num-font" x-text="c.tax_identification_number"></div>
</div>
</div>
</div>
</template>
</div>
</div>
<table class="data-table" x-show="activeInvoiceTab !== 'companies'">
<thead>
<tr>
<th>الشركة / المورد</th>
@@ -1658,15 +1727,15 @@
</tr>
</thead>
<tbody>
<tr x-show="invoices.length === 0">
<tr x-show="filteredInvoices.length === 0">
<td colspan="5">
<div class="empty-row">
<span class="empty-icon">📄</span>
<p class="empty-msg">لا توجد فواتير مرفوعة بعد</p>
<p class="empty-msg">لا توجد فواتير تطابق التصفية لهذا الشهر</p>
</div>
</td>
</tr>
<template x-for="inv in invoices" :key="inv.id">
<template x-for="inv in filteredInvoices" :key="inv.id">
<tr>
<td>
<div style="font-weight:700; color:var(--violet-mid); font-size:14px;"
@@ -1682,8 +1751,8 @@
x-text="parseFloat(inv.grand_total).toLocaleString() + ' JOD'"></td>
<td style="text-align:center;">
<span class="badge"
:class="inv.status==='extracted' ? 'badge-blue' : (inv.status==='approved' ? 'badge-teal' : 'badge-gray')"
x-text="inv.status === 'approved' ? '✓ مدققة' : (inv.status === 'extracted' ? 'جاهزة للتدقيق' : inv.status)">
:class="inv.status==='extracted' ? 'badge-blue' : (inv.status==='approved' ? 'badge-teal' : (inv.status==='rejected'||inv.status==='failed'?'badge-red':'badge-gray'))"
x-text="inv.status === 'approved' ? '✓ مدققة' : (inv.status === 'extracted' ? 'جاهزة للتدقيق' : (inv.status === 'rejected' ? 'مرفوضة' : inv.status))">
</span>
</td>
<td style="text-align:center;">
@@ -1790,18 +1859,18 @@
<span style="font-weight:700; color:var(--text-1); font-size:14px;">📄 الفواتير
الشهرية</span>
<span class="num-font" style="font-weight:700; color:var(--green-mid);"
x-text="(subscription?.invoices?.used || 0) + ' / ' + (subscription?.invoices?.limit || 0)"></span>
x-text="(subscription?.invoices?.used || 0) + ' من ' + (subscription?.invoices?.limit || 0)"></span>
</div>
<div class="usage-bar-bg">
<div class="usage-bar-fill"
style="background:linear-gradient(to left, var(--green-bright), var(--green-mid));"
:style="'width:' + (subscription?.invoices?.percent || 0) + '%'"></div>
style="background:linear-gradient(to left, var(--green-bright), var(--green-mid)); min-width: 2px;"
:style="'width:' + Math.min(100, (subscription?.invoices?.percent || 0)) + '%'"></div>
</div>
<div
style="display:flex; justify-content:space-between; font-size:12px; color:var(--text-3);">
<span
x-text="'يتم التصفير في: ' + (subscription?.period_end?.split(' ')[0] || '—')"></span>
<span class="num-font" x-text="(subscription?.invoices?.percent || 0) + '%'"></span>
<span class="num-font font-bold" x-text="(subscription?.invoices?.percent || 0) + '%'"></span>
</div>
</div>
@@ -1811,14 +1880,17 @@
<span style="font-weight:700; color:var(--text-1); font-size:14px;">🏭 الشركات
المدارة</span>
<span class="num-font" style="font-weight:700; color:var(--violet-mid);"
x-text="(subscription?.companies?.used || 0) + ' / ' + (subscription?.companies?.limit || 0)"></span>
x-text="(subscription?.companies?.used || 0) + ' من ' + (subscription?.companies?.limit || 0)"></span>
</div>
<div class="usage-bar-bg">
<div class="usage-bar-fill"
style="background:linear-gradient(to left, var(--sb-accent), var(--violet-mid));"
:style="'width:' + (subscription?.companies?.percent || 0) + '%'"></div>
style="background:linear-gradient(to left, var(--sb-accent), var(--violet-mid)); min-width: 2px;"
:style="'width:' + Math.min(100, (subscription?.companies?.percent || 0)) + '%'"></div>
</div>
<div style="display:flex; justify-content:space-between; font-size:12px; color:var(--text-3);">
<span>إجمالي الشركات المسموح بها</span>
<span class="num-font font-bold" x-text="(subscription?.companies?.percent || 0) + '%'"></span>
</div>
<div style="font-size:12px; color:var(--text-3);">إجمالي الشركات المسموح بها</div>
</div>
<!-- Team -->
@@ -1826,14 +1898,17 @@
<div style="display:flex; justify-content:space-between; margin-bottom:6px;">
<span style="font-weight:700; color:var(--text-1); font-size:14px;">👥 فريق العمل</span>
<span class="num-font" style="font-weight:700; color:var(--amber-mid);"
x-text="(subscription?.users?.used || 0) + ' / ' + (subscription?.users?.limit || 0)"></span>
x-text="(subscription?.users?.used || 0) + ' من ' + (subscription?.users?.limit || 0)"></span>
</div>
<div class="usage-bar-bg">
<div class="usage-bar-fill"
style="background:linear-gradient(to left, var(--amber-bright), var(--amber-mid));"
:style="'width:' + (subscription?.users?.percent || 0) + '%'"></div>
style="background:linear-gradient(to left, var(--amber-bright), var(--amber-mid)); min-width: 2px;"
:style="'width:' + Math.min(100, (subscription?.users?.percent || 0)) + '%'"></div>
</div>
<div style="display:flex; justify-content:space-between; font-size:12px; color:var(--text-3);">
<span>مستخدمين نشطين في النظام</span>
<span class="num-font font-bold" x-text="(subscription?.users?.percent || 0) + '%'"></span>
</div>
<div style="font-size:12px; color:var(--text-3);">مستخدمين نشطين في النظام</div>
</div>
</div>
@@ -2746,6 +2821,8 @@
user: JSON.parse(localStorage.getItem('user')),
page: 'dashboard',
users: [], companies: [], invoices: [], tenants: [], subscription: null, plans: [],
activeInvoiceTab: 'all', // all, approved, pending, rejected, jofotara
invoiceCompanyFilter: '',
showPaymentModal: false,
paymentData: { cliq_alias: '', amount: 0, plan_name: '', reference: '', payment_id: '' },
stats: { total: 0, pending: 0, approved: 0 },
@@ -2773,6 +2850,29 @@
setPage(p) { this.page = p; this.loadAll(); },
title() { return { dashboard: 'الرئيسية', users: 'فريق العمل', companies: 'الشركات', invoices: 'إدارة الفواتير', tenants: 'المكاتب المحاسبية', subscription: 'إدارة الاشتراك' }[this.page] || ''; },
subtitle() { return { dashboard: 'نظرة شاملة على نشاط النظام', users: 'إدارة المستخدمين والصلاحيات', companies: 'إدارة الشركات والربط بالفوترة الحكومية', invoices: 'رفع ومعالجة الفواتير الضريبية', tenants: 'إدارة المكاتب المحاسبية المشتركة', subscription: 'تفاصيل باقتك الحالية واستهلاك الموارد' }[this.page] || ''; },
get filteredInvoices() {
const now = new Date();
const startOfMonth = new Date(now.getFullYear(), now.getMonth(), 1);
return this.invoices.filter(inv => {
// 1. Time Filter (Current Month)
const invDate = new Date(inv.invoice_date || inv.created_at);
if (invDate < startOfMonth) return false;
// 2. Company Filter
if (this.invoiceCompanyFilter && inv.company_id != this.invoiceCompanyFilter) return false;
// 3. Tab Filter
if (this.activeInvoiceTab === 'approved' && inv.status !== 'approved') return false;
if (this.activeInvoiceTab === 'pending' && inv.status !== 'extracted' && inv.status !== 'pending') return false;
if (this.activeInvoiceTab === 'rejected' && inv.status !== 'rejected' && inv.status !== 'failed') return false;
if (this.activeInvoiceTab === 'jofotara' && !inv.jofotara_uuid && !inv.jofotara) return false;
return true;
});
},
token() { return localStorage.getItem('access_token'); },
showError(msg) { this.globalError = msg; setTimeout(() => this.globalError = '', 8000); },