806 lines
57 KiB
PHP
806 lines
57 KiB
PHP
<!DOCTYPE html>
|
||
<html lang="ar" dir="rtl" data-theme="dark">
|
||
<head>
|
||
<meta charset="UTF-8">
|
||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||
<title>مُصادَق | لوحة التحكم الذكية</title>
|
||
<link href="https://fonts.googleapis.com/css2?family=IBM+Plex+Sans+Arabic:wght@300;400;500;600;700&family=IBM+Plex+Mono:wght@400;500&display=swap" rel="stylesheet">
|
||
<script src="https://cdn.tailwindcss.com"></script>
|
||
<script defer src="https://unpkg.com/alpinejs@3.x.x/dist/cdn.min.js"></script>
|
||
<script src="https://cdnjs.cloudflare.com/ajax/libs/qrious/4.0.2/qrious.min.js"></script>
|
||
<style>
|
||
:root {
|
||
--emerald: #10b981;
|
||
--emerald-glow: rgba(16, 185, 129, 0.4);
|
||
--bg-base: #030712;
|
||
--bg-surface: #0f172a;
|
||
--border-default: rgba(255,255,255,0.08);
|
||
--text-primary: #f8fafc;
|
||
--text-secondary: #94a3b8;
|
||
}
|
||
body {
|
||
font-family: 'IBM Plex Sans Arabic', sans-serif;
|
||
background-color: var(--bg-base);
|
||
color: var(--text-primary);
|
||
background-image: radial-gradient(circle at top right, rgba(16,185,129,0.05), transparent 400px);
|
||
}
|
||
[x-cloak] { display: none !important; }
|
||
.glass { background: rgba(15, 23, 42, 0.7); backdrop-filter: blur(16px); border: 1px solid var(--border-default); }
|
||
.glass-elevated { background: rgba(30, 41, 59, 0.8); backdrop-filter: blur(24px); border: 1px solid rgba(255,255,255,0.1); }
|
||
.btn-glow:hover { box-shadow: 0 0 20px var(--emerald-glow); transform: translateY(-1px); }
|
||
.scrollbar-hide::-webkit-scrollbar { display: none; }
|
||
.nav-active { background: rgba(16,185,129,0.1); border-right: 4px solid var(--emerald); color: var(--emerald); }
|
||
</style>
|
||
</head>
|
||
<body x-data="app" x-init="init()">
|
||
|
||
<!-- Global Error Toast -->
|
||
<div x-show="globalError" x-cloak
|
||
class="fixed top-6 left-1/2 -translate-x-1/2 z-[100] bg-red-900/90 border border-red-500 text-white px-8 py-4 rounded-2xl shadow-2xl max-w-lg text-center glass"
|
||
x-text="globalError"
|
||
x-transition
|
||
@click="globalError = ''">
|
||
</div>
|
||
|
||
<div class="flex h-screen overflow-hidden">
|
||
<!-- Sidebar -->
|
||
<aside class="w-64 bg-surface border-l border-gray-800 flex flex-col glass">
|
||
<div class="p-8">
|
||
<div class="flex items-center gap-3">
|
||
<div class="w-8 h-8 rounded-lg bg-emerald-500 flex items-center justify-center text-white font-bold">✓</div>
|
||
<h1 class="text-2xl font-bold tracking-tight">مُصادَق</h1>
|
||
</div>
|
||
<p class="text-[10px] text-gray-500 mt-2 uppercase tracking-widest">Enterprise SaaS</p>
|
||
</div>
|
||
|
||
<nav class="flex-1 px-4 space-y-2 mt-4">
|
||
<button @click="setPage('dashboard')" class="w-full flex items-center gap-3 p-3.5 rounded-xl transition-all" :class="page==='dashboard'?'nav-active':'text-gray-400 hover:bg-gray-800/50'">
|
||
<span class="text-xl">📊</span><span class="font-medium">لوحة التحكم</span>
|
||
</button>
|
||
<button x-show="user?.role === 'super_admin'" @click="setPage('tenants')" class="w-full flex items-center gap-3 p-3.5 rounded-xl transition-all" :class="page==='tenants'?'nav-active':'text-gray-400 hover:bg-gray-800/50'">
|
||
<span class="text-xl">🏢</span><span class="font-medium">المكاتب المحاسبية</span>
|
||
</button>
|
||
<button x-show="user?.role !== 'viewer'" @click="setPage('companies')" class="w-full flex items-center gap-3 p-3.5 rounded-xl transition-all" :class="page==='companies'?'nav-active':'text-gray-400 hover:bg-gray-800/50'">
|
||
<span class="text-xl">🏭</span><span class="font-medium">الشركات</span>
|
||
</button>
|
||
<button x-show="user?.role !== 'viewer'" @click="setPage('invoices')" class="w-full flex items-center gap-3 p-3.5 rounded-xl transition-all" :class="page==='invoices'?'nav-active':'text-gray-400 hover:bg-gray-800/50'">
|
||
<span class="text-xl">📄</span><span class="font-medium">الفواتير</span>
|
||
</button>
|
||
<button x-show="user?.role === 'super_admin' || user?.role === 'admin'" @click="setPage('users')" class="w-full flex items-center gap-3 p-3.5 rounded-xl transition-all" :class="page==='users'?'nav-active':'text-gray-400 hover:bg-gray-800/50'">
|
||
<span class="text-xl">👥</span><span class="font-medium">المستخدمون</span>
|
||
</button>
|
||
</nav>
|
||
|
||
<div class="p-6 border-t border-gray-800">
|
||
<div class="flex items-center gap-3 mb-4 p-3 rounded-xl bg-gray-950/50 border border-gray-800">
|
||
<div class="w-10 h-10 rounded-full bg-emerald-500/20 flex items-center justify-center text-emerald-500 font-bold" x-text="user?.name?.[0]"></div>
|
||
<div class="truncate">
|
||
<p class="text-xs font-bold truncate" x-text="user?.name"></p>
|
||
<p class="text-[10px] text-gray-500 uppercase" x-text="user?.role"></p>
|
||
</div>
|
||
</div>
|
||
<button @click="logout()" class="w-full text-right text-red-400 text-xs font-bold hover:text-red-300 transition-colors">🚪 تسجيل الخروج</button>
|
||
</div>
|
||
</aside>
|
||
|
||
<!-- Main -->
|
||
<main class="flex-1 overflow-y-auto p-10 bg-[#060910]">
|
||
<header class="mb-12 flex justify-between items-end">
|
||
<div>
|
||
<h2 class="text-4xl font-bold tracking-tight" x-text="title()"></h2>
|
||
<p class="text-gray-500 mt-2 text-sm" x-text="subtitle()"></p>
|
||
</div>
|
||
<div class="flex items-center gap-4">
|
||
<button x-show="page==='tenants' && user?.role === 'super_admin'" @click="showAddTenantModal = true" class="bg-emerald-600 hover:bg-emerald-500 px-6 py-3 rounded-xl text-sm font-bold shadow-lg transition-all active:scale-95 btn-glow">➕ إضافة مكتب محاسبي</button>
|
||
<button x-show="page==='users'" @click="showAddUserModal = true" class="bg-emerald-600 hover:bg-emerald-500 px-6 py-3 rounded-xl text-sm font-bold shadow-lg transition-all active:scale-95 btn-glow">➕ إضافة مستخدم</button>
|
||
<button x-show="page==='companies'" @click="showAddCompanyModal = true" class="bg-emerald-600 hover:bg-emerald-500 px-6 py-3 rounded-xl text-sm font-bold shadow-lg transition-all active:scale-95 btn-glow">➕ إضافة شركة</button>
|
||
<button x-show="page==='invoices'" @click="showUploadModal = true" class="bg-emerald-600 hover:bg-emerald-500 px-6 py-3 rounded-xl text-sm font-bold shadow-lg transition-all active:scale-95 btn-glow">📤 رفع فواتير</button>
|
||
</div>
|
||
</header>
|
||
|
||
<div id="content" x-transition>
|
||
<!-- Dashboard -->
|
||
<div x-show="page === 'dashboard'" class="space-y-8">
|
||
<div class="grid grid-cols-1 md:grid-cols-3 gap-8">
|
||
<div class="p-8 bg-surface border border-gray-800 rounded-3xl shadow-xl relative overflow-hidden group glass">
|
||
<p class="text-gray-500 text-xs uppercase font-bold tracking-widest">إجمالي الفواتير</p>
|
||
<p class="text-5xl font-bold mt-4" x-text="stats.total || 0"></p>
|
||
<div class="absolute top-0 right-0 p-6 opacity-5 group-hover:opacity-10 transition-opacity"><span class="text-6xl">📄</span></div>
|
||
</div>
|
||
<div class="p-8 bg-surface border border-gray-800 rounded-3xl shadow-xl relative overflow-hidden group glass">
|
||
<p class="text-gray-500 text-xs uppercase font-bold tracking-widest text-yellow-500">بانتظار الاعتماد</p>
|
||
<p class="text-5xl font-bold mt-4 text-yellow-500" x-text="stats.pending || 0"></p>
|
||
<div class="absolute top-0 right-0 p-6 opacity-5 group-hover:opacity-10 transition-opacity"><span class="text-6xl">⏳</span></div>
|
||
</div>
|
||
<div class="p-8 bg-surface border border-gray-800 rounded-3xl shadow-xl relative overflow-hidden group glass">
|
||
<p class="text-gray-500 text-xs uppercase font-bold tracking-widest text-emerald-500">تم الاعتماد</p>
|
||
<p class="text-5xl font-bold mt-4 text-emerald-500" x-text="stats.approved || 0"></p>
|
||
<div class="absolute top-0 right-0 p-6 opacity-5 group-hover:opacity-10 transition-opacity"><span class="text-6xl">✅</span></div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Companies Table -->
|
||
<div x-show="page === 'companies'">
|
||
<div class="bg-surface border border-gray-800 rounded-3xl overflow-hidden shadow-2xl glass">
|
||
<table class="w-full text-right divide-y divide-gray-800">
|
||
<thead class="bg-gray-950/50">
|
||
<tr>
|
||
<th class="p-6 text-xs font-bold text-gray-500 uppercase">الشركة</th>
|
||
<th class="p-6 text-xs font-bold text-gray-500 uppercase">الأرقام الرسمية</th>
|
||
<th class="p-6 text-xs font-bold text-gray-500 uppercase">الفوترة الحكومية</th>
|
||
<th class="p-6 text-xs font-bold text-gray-500 uppercase">الإحصائيات</th>
|
||
<th class="p-6 text-xs font-bold text-gray-500 uppercase">إجراءات</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody class="divide-y divide-gray-800/50">
|
||
<tr x-show="companies.length === 0"><td colspan="5" class="p-20 text-center text-gray-600 text-lg">لا توجد شركات مسجلة</td></tr>
|
||
<template x-for="c in companies" :key="c.id">
|
||
<tr class="hover:bg-white/[0.02] transition-colors group">
|
||
<td class="p-6">
|
||
<p class="font-bold text-emerald-500 text-lg" x-text="c.name"></p>
|
||
<p class="text-xs text-gray-500" x-text="c.address"></p>
|
||
</td>
|
||
<td class="p-6 text-xs font-mono">
|
||
<p>TIN: <span x-text="c.tax_identification_number"></span></p>
|
||
<p>CRN: <span x-text="c.commercial_registration_number || '-'"></span></p>
|
||
</td>
|
||
<td class="p-6">
|
||
<div class="flex items-center gap-2">
|
||
<span class="w-2 h-2 rounded-full" :class="c.jofotara_client_id_encrypted ? 'bg-emerald-500 shadow-[0_0_8px_rgba(16,185,129,0.5)]' : 'bg-gray-700'"></span>
|
||
<button @click="openConnectModal(c)" class="text-xs font-bold hover:underline" :class="c.jofotara_client_id_encrypted ? 'text-emerald-400' : 'text-gray-400'">
|
||
<span x-text="c.jofotara_client_id_encrypted ? '✅ تم الربط (تعديل)' : '🔗 ربط الفوترة'"></span>
|
||
</button>
|
||
</div>
|
||
</td>
|
||
<td class="p-6">
|
||
<button @click="showCompanyStats(c.id)" class="text-xs bg-emerald-500/10 hover:bg-emerald-500/20 text-emerald-400 px-4 py-2 rounded-xl border border-emerald-500/20 transition-all font-bold">📊 عرض التقارير</button>
|
||
</td>
|
||
<td class="p-6">
|
||
<button @click="confirmDeleteCompany(c)" class="text-gray-500 hover:text-red-500 p-2.5 rounded-xl hover:bg-red-500/10 transition">🗑️</button>
|
||
</td>
|
||
</tr>
|
||
</template>
|
||
</tbody>
|
||
</table>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Users Table -->
|
||
<div x-show="page === 'users'">
|
||
<div class="bg-surface border border-gray-800 rounded-3xl overflow-hidden shadow-2xl glass">
|
||
<table class="w-full text-right divide-y divide-gray-800">
|
||
<thead class="bg-gray-950/50">
|
||
<tr>
|
||
<th class="p-6 text-xs font-bold text-gray-500 uppercase">المستخدم</th>
|
||
<th class="p-6 text-xs font-bold text-gray-500 uppercase">المكتب</th>
|
||
<th class="p-6 text-xs font-bold text-gray-500 uppercase">الدور</th>
|
||
<th class="p-6 text-xs font-bold text-gray-500 uppercase">الحالة</th>
|
||
<th class="p-6 text-xs font-bold text-gray-500 uppercase">إجراءات</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody class="divide-y divide-gray-800/50">
|
||
<tr x-show="users.length === 0"><td colspan="5" class="p-20 text-center text-gray-600 text-lg">لا يوجد مستخدمون مسجلون</td></tr>
|
||
<template x-for="u in users" :key="u.id">
|
||
<tr class="hover:bg-white/[0.02] transition-colors">
|
||
<td class="p-6">
|
||
<p class="font-bold text-emerald-500" x-text="u.name"></p>
|
||
<p class="text-xs text-gray-500 font-mono" x-text="u.email"></p>
|
||
</td>
|
||
<td class="p-6 text-sm text-gray-400" x-text="u.tenant_name || '-'"></td>
|
||
<td class="p-6">
|
||
<span class="px-3 py-1 bg-gray-800/50 border border-gray-700 rounded-full text-[10px] font-bold uppercase tracking-wide" x-text="u.role"></span>
|
||
</td>
|
||
<td class="p-6">
|
||
<span class="w-2.5 h-2.5 rounded-full inline-block" :class="u.is_active?'bg-emerald-400':'bg-red-400'"></span>
|
||
</td>
|
||
<td class="p-6">
|
||
<button x-show="u.id !== user.id" @click="confirmDeleteUser(u)" class="text-gray-500 hover:text-red-500 p-2.5 transition">🗑️</button>
|
||
</td>
|
||
</tr>
|
||
</template>
|
||
</tbody>
|
||
</table>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Invoices Table -->
|
||
<div x-show="page === 'invoices'">
|
||
<div class="bg-surface border border-gray-800 rounded-3xl overflow-hidden shadow-2xl glass">
|
||
<table class="w-full text-right border-collapse">
|
||
<thead class="bg-gray-950/50 border-b border-gray-800">
|
||
<tr>
|
||
<th class="p-6 text-xs font-bold text-gray-500 uppercase">الشركة / المورد</th>
|
||
<th class="p-6 text-xs font-bold text-gray-500 uppercase">التاريخ</th>
|
||
<th class="p-6 text-xs font-bold text-gray-500 uppercase">المجموع</th>
|
||
<th class="p-6 text-xs font-bold text-gray-500 uppercase">الحالة</th>
|
||
<th class="p-6 text-xs font-bold text-gray-500 uppercase">إجراءات</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody class="divide-y divide-gray-800/50">
|
||
<tr x-show="invoices.length === 0"><td colspan="5" class="p-20 text-center text-gray-600 text-lg">لا توجد فواتير مرفوعة</td></tr>
|
||
<template x-for="inv in invoices" :key="inv.id">
|
||
<tr class="hover:bg-white/[0.02] transition-all">
|
||
<td class="p-6">
|
||
<p class="font-bold text-emerald-500" x-text="inv.company_name"></p>
|
||
<p class="text-sm font-medium text-gray-200 mt-1" x-text="inv.supplier_name"></p>
|
||
<p class="text-[10px] text-gray-500 mt-0.5" x-text="inv.supplier_tin"></p>
|
||
</td>
|
||
<td class="p-6 text-sm text-gray-400 font-mono" x-text="inv.invoice_date || '-'"></td>
|
||
<td class="p-6 font-mono font-bold" x-text="parseFloat(inv.grand_total).toLocaleString() + ' JOD'"></td>
|
||
<td class="p-6">
|
||
<span class="px-3 py-1.5 rounded-full text-[10px] font-bold uppercase tracking-wider"
|
||
:class="inv.status==='extracted'?'bg-blue-900/40 text-blue-400':(inv.status==='approved'?'bg-emerald-900/40 text-emerald-400':'bg-gray-800 text-gray-400')"
|
||
x-text="inv.status === 'approved' ? 'مدققة' : inv.status"></span>
|
||
</td>
|
||
<td class="p-6">
|
||
<button @click="viewInvoice(inv.id)" class="text-gray-500 hover:text-emerald-400 p-3 rounded-2xl hover:bg-emerald-400/10 transition-all">👁️ عرض</button>
|
||
</td>
|
||
</tr>
|
||
</template>
|
||
</tbody>
|
||
</table>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Tenants Table (Super Admin Only) -->
|
||
<div x-show="page === 'tenants' && user?.role === 'super_admin'">
|
||
<div class="bg-surface border border-gray-800 rounded-3xl overflow-hidden shadow-2xl glass">
|
||
<table class="w-full text-right divide-y divide-gray-800">
|
||
<thead class="bg-gray-950/50">
|
||
<tr>
|
||
<th class="p-6 text-xs font-bold text-gray-500 uppercase">المكتب المحاسبي</th>
|
||
<th class="p-6 text-xs font-bold text-gray-500 uppercase">التواصل</th>
|
||
<th class="p-6 text-xs font-bold text-gray-500 uppercase">الحالة</th>
|
||
<th class="p-6 text-xs font-bold text-gray-500 uppercase">تاريخ الانضمام</th>
|
||
<th class="p-6 text-xs font-bold text-gray-500 uppercase">إجراءات</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody class="divide-y divide-gray-800/50">
|
||
<tr x-show="tenants.length === 0"><td colspan="5" class="p-20 text-center text-gray-600 text-lg">لا توجد مكاتب مسجلة</td></tr>
|
||
<template x-for="t in tenants" :key="t.id">
|
||
<tr class="hover:bg-white/[0.02] transition-colors">
|
||
<td class="p-6">
|
||
<p class="font-bold text-emerald-500" x-text="t.name"></p>
|
||
</td>
|
||
<td class="p-6 text-sm">
|
||
<p class="text-gray-200" x-text="t.email"></p>
|
||
<p class="text-gray-500 font-mono" x-text="t.phone || '-'"></p>
|
||
</td>
|
||
<td class="p-6">
|
||
<span class="px-3 py-1 rounded-full text-[10px] font-bold uppercase tracking-wide"
|
||
:class="t.status==='active'?'bg-emerald-900/40 text-emerald-400':'bg-yellow-900/40 text-yellow-400'"
|
||
x-text="t.status"></span>
|
||
</td>
|
||
<td class="p-6 text-xs text-gray-500 font-mono" x-text="t.created_at"></td>
|
||
<td class="p-6">
|
||
<button @click="openEditTenantModal(t)" class="text-gray-500 hover:text-emerald-400 p-2 rounded-lg hover:bg-emerald-400/10 transition-all">⚙️</button>
|
||
</td>
|
||
</tr>
|
||
</template>
|
||
</tbody>
|
||
</table>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</main>
|
||
|
||
<!-- Company Stats Modal -->
|
||
<div x-show="showCompanyStatsModal" x-cloak class="fixed inset-0 bg-black/90 backdrop-blur-md flex items-center justify-center p-6 z-[110]">
|
||
<div class="bg-surface border border-gray-800 w-full max-w-4xl p-10 rounded-[40px] shadow-2xl glass-elevated" @click.away="showCompanyStatsModal = false">
|
||
<div class="flex justify-between items-start mb-12">
|
||
<div>
|
||
<h3 class="text-3xl font-bold text-emerald-400" x-text="companyStats?.company?.name"></h3>
|
||
<p class="text-xs text-gray-500 mt-2 uppercase tracking-widest font-mono" x-text="'TIN: ' + companyStats?.company?.tax_identification_number"></p>
|
||
</div>
|
||
<button @click="showCompanyStatsModal = false" class="text-gray-500 hover:text-white text-3xl transition">✕</button>
|
||
</div>
|
||
|
||
<div class="grid grid-cols-1 md:grid-cols-4 gap-6 mb-12">
|
||
<div class="p-8 bg-gray-950/50 border border-gray-800 rounded-3xl">
|
||
<p class="text-[10px] text-gray-500 font-bold uppercase mb-3">إجمالي الفواتير</p>
|
||
<p class="text-3xl font-bold" x-text="companyStats?.totals?.total_invoices || 0"></p>
|
||
</div>
|
||
<div class="p-8 bg-gray-950/50 border border-gray-800 rounded-3xl">
|
||
<p class="text-[10px] text-gray-500 font-bold uppercase mb-3">المجموع (JOD)</p>
|
||
<p class="text-3xl font-bold text-emerald-400 font-mono" x-text="parseFloat(companyStats?.totals?.total_amount || 0).toLocaleString()"></p>
|
||
</div>
|
||
<div class="p-8 bg-gray-950/50 border border-gray-800 rounded-3xl">
|
||
<p class="text-[10px] text-gray-500 font-bold uppercase mb-3">إجمالي الضريبة</p>
|
||
<p class="text-3xl font-bold text-yellow-500 font-mono" x-text="parseFloat(companyStats?.totals?.total_tax || 0).toLocaleString()"></p>
|
||
</div>
|
||
<div class="p-8 bg-gray-950/50 border border-gray-800 rounded-3xl">
|
||
<p class="text-[10px] text-gray-500 font-bold uppercase mb-3">المعتمدة</p>
|
||
<p class="text-3xl font-bold text-blue-400" x-text="companyStats?.totals?.approved_count || 0"></p>
|
||
</div>
|
||
</div>
|
||
|
||
<h4 class="text-sm font-bold text-gray-500 uppercase mb-6 tracking-widest">التحليل الشهري للفواتير والضرائب</h4>
|
||
<div class="bg-gray-950/30 border border-gray-800 rounded-3xl overflow-hidden max-h-64 overflow-y-auto">
|
||
<table class="w-full text-right text-xs">
|
||
<thead class="bg-gray-900/50 sticky top-0">
|
||
<tr>
|
||
<th class="p-5 text-gray-500 font-bold">الشهر</th>
|
||
<th class="p-5 text-gray-500 font-bold">عدد الفواتير</th>
|
||
<th class="p-5 text-gray-500 font-bold">الضريبة المستحقة</th>
|
||
<th class="p-5 text-gray-500 font-bold text-emerald-500">الإجمالي النهائي</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody class="divide-y divide-gray-800">
|
||
<template x-for="m in companyStats?.monthly" :key="m.month">
|
||
<tr>
|
||
<td class="p-5 font-mono text-gray-200" x-text="m.month"></td>
|
||
<td class="p-5" x-text="m.total_invoices"></td>
|
||
<td class="p-5 text-yellow-500 font-mono" x-text="parseFloat(m.total_tax || 0).toLocaleString() + ' JOD'"></td>
|
||
<td class="p-5 font-bold text-emerald-500 font-mono" x-text="parseFloat(m.total_amount || 0).toLocaleString() + ' JOD'"></td>
|
||
</tr>
|
||
</template>
|
||
</tbody>
|
||
</table>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Add User Modal -->
|
||
<div x-show="showAddUserModal" x-cloak class="fixed inset-0 bg-black/90 flex items-center justify-center p-6 z-[120]">
|
||
<div class="bg-surface border border-gray-800 w-full max-w-lg p-10 rounded-[40px] shadow-2xl glass-elevated">
|
||
<h3 class="text-2xl font-bold mb-8">إضافة مستخدم جديد</h3>
|
||
<form @submit.prevent="createUser" class="space-y-6">
|
||
<input type="text" x-model="newUser.name" placeholder="الاسم الكامل" class="w-full bg-gray-950 border border-gray-800 p-4 rounded-2xl outline-none" required>
|
||
<input type="email" x-model="newUser.email" placeholder="البريد الإلكتروني" class="w-full bg-gray-950 border border-gray-800 p-4 rounded-2xl outline-none" required>
|
||
<input type="password" x-model="newUser.password" placeholder="كلمة المرور" class="w-full bg-gray-950 border border-gray-800 p-4 rounded-2xl outline-none" required>
|
||
<select x-model="newUser.role" class="w-full bg-gray-950 border border-gray-800 p-4 rounded-2xl outline-none" required>
|
||
<option value="accountant">محاسب</option>
|
||
<option value="viewer">مشاهد فقط</option>
|
||
<option x-show="user?.role === 'super_admin'" value="admin">مدير مكتب</option>
|
||
</select>
|
||
<div class="flex gap-4 pt-4">
|
||
<button type="submit" class="flex-1 bg-emerald-600 hover:bg-emerald-500 py-4 rounded-2xl font-bold transition-all btn-glow" :disabled="isBusy">حفظ المستخدم</button>
|
||
<button type="button" @click="showAddUserModal = false" class="px-8 py-4 border border-gray-800 rounded-2xl">إلغاء</button>
|
||
</div>
|
||
</form>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Add Company Modal -->
|
||
<div x-show="showAddCompanyModal" x-cloak class="fixed inset-0 bg-black/90 flex items-center justify-center p-6 z-[120]">
|
||
<div class="bg-surface border border-gray-800 w-full max-w-lg p-10 rounded-[40px] shadow-2xl glass-elevated">
|
||
<h3 class="text-2xl font-bold mb-8">إضافة شركة جديدة</h3>
|
||
<form @submit.prevent="createCompany" class="space-y-6">
|
||
<input type="text" x-model="newCompany.name" placeholder="اسم الشركة (بالعربي)" class="w-full bg-gray-950 border border-gray-800 p-4 rounded-2xl outline-none" required>
|
||
<input type="text" x-model="newCompany.tax_identification_number" placeholder="الرقم الضريبي (TIN)" class="w-full bg-gray-950 border border-gray-800 p-4 rounded-2xl outline-none" required>
|
||
<input type="text" x-model="newCompany.commercial_registration_number" placeholder="رقم السجل التجاري" class="w-full bg-gray-950 border border-gray-800 p-4 rounded-2xl outline-none">
|
||
<input type="text" x-model="newCompany.address" placeholder="العنوان" class="w-full bg-gray-950 border border-gray-800 p-4 rounded-2xl outline-none">
|
||
<div class="flex gap-4 pt-4">
|
||
<button type="submit" class="flex-1 bg-emerald-600 hover:bg-emerald-500 py-4 rounded-2xl font-bold transition-all btn-glow" :disabled="isBusy">إضافة الشركة</button>
|
||
<button type="button" @click="showAddCompanyModal = false" class="px-8 py-4 border border-gray-800 rounded-2xl">إلغاء</button>
|
||
</div>
|
||
</form>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- JoFotara Connect Modal -->
|
||
<div x-show="showConnectModal" x-cloak class="fixed inset-0 bg-black/90 backdrop-blur-sm flex items-center justify-center p-6 z-[130]">
|
||
<div class="bg-surface border border-gray-800 w-full max-w-lg p-10 rounded-[40px] shadow-2xl glass-elevated">
|
||
<h3 class="text-2xl font-bold mb-4">ربط نظام الفوترة الحكومي</h3>
|
||
<p class="text-gray-500 text-sm mb-8" x-text="'الشركة: ' + currentCompany?.name"></p>
|
||
<form @submit.prevent="connectJoFotara" class="space-y-6">
|
||
<div>
|
||
<label class="block text-xs font-bold text-gray-500 uppercase mb-2">Client ID</label>
|
||
<input type="text" x-model="connectData.client_id" placeholder="أدخل Client ID" class="w-full bg-gray-950 border border-gray-800 p-4 rounded-2xl outline-none font-mono text-sm" required>
|
||
</div>
|
||
<div>
|
||
<label class="block text-xs font-bold text-gray-500 uppercase mb-2">Secret Key</label>
|
||
<input type="password" x-model="connectData.secret_key" placeholder="أدخل Secret Key" class="w-full bg-gray-950 border border-gray-800 p-4 rounded-2xl outline-none font-mono text-sm" required>
|
||
</div>
|
||
<div>
|
||
<label class="block text-xs font-bold text-gray-500 uppercase mb-2">تسلسل مصدر الدخل</label>
|
||
<input type="text" x-model="connectData.income_source_sequence" placeholder="مثال: 1" class="w-full bg-gray-950 border border-gray-800 p-4 rounded-2xl outline-none font-mono text-sm">
|
||
</div>
|
||
<div class="flex gap-4 pt-4">
|
||
<button type="submit" class="flex-1 bg-emerald-600 hover:bg-emerald-500 py-4 rounded-2xl font-bold transition-all btn-glow" :disabled="isBusy">حفظ وتفعيل الربط</button>
|
||
<button type="button" @click="showConnectModal = false" class="px-8 py-4 border border-gray-800 rounded-2xl">إلغاء</button>
|
||
</div>
|
||
</form>
|
||
</div>
|
||
</div>
|
||
<!-- Upload Invoice Modal -->
|
||
<div x-show="showUploadModal" x-cloak class="fixed inset-0 bg-black/90 flex items-center justify-center p-6 z-[120]">
|
||
<div class="bg-surface border border-gray-800 w-full max-w-lg p-10 rounded-[40px] shadow-2xl glass-elevated">
|
||
<h3 class="text-2xl font-bold mb-8">رفع فاتورة جديدة</h3>
|
||
<form @submit.prevent="uploadInvoice" class="space-y-6">
|
||
<div>
|
||
<label class="block text-xs font-bold text-gray-500 uppercase mb-2">اختر الشركة</label>
|
||
<select x-model="uploadData.company_id" class="w-full bg-gray-950 border border-gray-800 p-4 rounded-2xl outline-none" required>
|
||
<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>
|
||
<label class="block text-xs font-bold text-gray-500 uppercase mb-2">ملف الفاتورة (صورة أو PDF)</label>
|
||
<input type="file" @change="selectedFile = $event.target.files[0]" class="w-full bg-gray-950 border border-gray-800 p-4 rounded-2xl outline-none" required>
|
||
</div>
|
||
<div class="flex gap-4 pt-4">
|
||
<button type="submit" class="flex-1 bg-emerald-600 hover:bg-emerald-500 py-4 rounded-2xl font-bold transition-all btn-glow" :disabled="isUploading">
|
||
<span x-show="!isUploading">📤 رفع وتحليل</span>
|
||
<span x-show="isUploading">⏳ جاري التحليل...</span>
|
||
</button>
|
||
<button type="button" @click="showUploadModal = false" class="px-8 py-4 border border-gray-800 rounded-2xl">إلغاء</button>
|
||
</div>
|
||
</form>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- View Invoice Modal -->
|
||
<div x-show="showViewModal" x-cloak class="fixed inset-0 bg-black/90 backdrop-blur-md flex items-center justify-center p-6 z-[110]">
|
||
<div class="bg-surface border border-gray-800 w-full max-w-6xl h-[90vh] p-8 rounded-[40px] shadow-2xl glass-elevated flex flex-col md:flex-row gap-8" @click.away="showViewModal = false">
|
||
<!-- Left: Document Preview -->
|
||
<div class="flex-1 bg-gray-950 rounded-3xl overflow-hidden border border-gray-800 relative">
|
||
<template x-if="currentInvoice?.file_url">
|
||
<iframe :src="currentInvoice.file_url + '&token=' + token()" class="w-full h-full border-0"></iframe>
|
||
</template>
|
||
<div x-show="!currentInvoice?.file_url" class="absolute inset-0 flex items-center justify-center text-gray-600">لا يوجد ملف مرفق</div>
|
||
</div>
|
||
|
||
<!-- Right: Invoice Data -->
|
||
<div class="w-full md:w-96 flex flex-col h-full overflow-y-auto pr-4 scrollbar-hide">
|
||
<div class="flex justify-between items-start mb-6">
|
||
<h3 class="text-2xl font-bold text-emerald-400">تفاصيل الفاتورة</h3>
|
||
<button @click="showViewModal = false" class="text-gray-500 hover:text-white text-3xl transition">✕</button>
|
||
</div>
|
||
|
||
<div class="space-y-4 flex-1">
|
||
<div class="p-4 bg-gray-900/50 rounded-2xl border border-gray-800">
|
||
<p class="text-[10px] text-gray-500 uppercase tracking-widest mb-1">المورد</p>
|
||
<p class="font-bold text-lg text-emerald-300" x-text="currentInvoice?.supplier_name || 'غير متوفر'"></p>
|
||
<p class="text-xs text-gray-400 font-mono mt-1" x-text="'TIN: ' + (currentInvoice?.supplier_tin || '-')"></p>
|
||
</div>
|
||
|
||
<div class="p-4 bg-gray-900/50 rounded-2xl border border-gray-800">
|
||
<p class="text-[10px] text-gray-500 uppercase tracking-widest mb-1">الرقم والتاريخ</p>
|
||
<p class="font-bold" x-text="currentInvoice?.invoice_number || '-'"></p>
|
||
<p class="text-sm text-gray-400 font-mono" x-text="currentInvoice?.invoice_date || '-'"></p>
|
||
</div>
|
||
|
||
<div class="p-4 bg-gray-900/50 rounded-2xl border border-gray-800">
|
||
<p class="text-[10px] text-gray-500 uppercase tracking-widest mb-1">المجموع الكلي</p>
|
||
<p class="text-3xl font-bold font-mono text-emerald-400" x-text="parseFloat(currentInvoice?.grand_total || 0).toLocaleString() + ' JOD'"></p>
|
||
<p class="text-xs text-yellow-500 mt-1 font-mono" x-text="'الضريبة: ' + parseFloat(currentInvoice?.tax_amount || 0).toLocaleString()"></p>
|
||
</div>
|
||
|
||
<!-- Items -->
|
||
<div x-show="currentInvoice?.items?.length > 0" class="mt-4 border border-gray-800 rounded-2xl overflow-hidden">
|
||
<table class="w-full text-xs text-right">
|
||
<thead class="bg-gray-900">
|
||
<tr><th class="p-2">البند</th><th class="p-2">الكمية</th><th class="p-2">السعر</th></tr>
|
||
</thead>
|
||
<tbody class="divide-y divide-gray-800 bg-gray-900/30">
|
||
<template x-for="item in currentInvoice?.items" :key="item.id">
|
||
<tr>
|
||
<td class="p-2" x-text="item.description"></td>
|
||
<td class="p-2 font-mono text-gray-400" x-text="item.quantity"></td>
|
||
<td class="p-2 font-mono text-emerald-400" x-text="item.unit_price"></td>
|
||
</tr>
|
||
</template>
|
||
</tbody>
|
||
</table>
|
||
</div>
|
||
|
||
<!-- QR Code -->
|
||
<div x-show="currentInvoice?.jofotara?.qr_image_uri || currentInvoice?.qr_code" class="mt-6 p-4 bg-white rounded-2xl flex justify-center items-center shadow-inner">
|
||
<img :src="getQrSrc(currentInvoice)" class="w-40 h-40 object-contain" alt="QR Code">
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Actions -->
|
||
<div class="mt-6 pt-6 border-t border-gray-800 space-y-3">
|
||
<button x-show="currentInvoice?.status === 'extracted'" @click="approveInvoice" class="w-full bg-emerald-600 hover:bg-emerald-500 py-4 rounded-2xl font-bold shadow-lg shadow-emerald-900/50 transition-all active:scale-95 btn-glow" :disabled="isBusy">
|
||
<span x-show="!isBusy">✔️ تدقيق واعتماد محلي</span>
|
||
<span x-show="isBusy">⏳ جاري التدقيق...</span>
|
||
</button>
|
||
<button x-show="currentInvoice?.status === 'extracted'" class="w-full bg-red-900/30 text-red-400 hover:bg-red-900/50 py-3 rounded-2xl font-bold transition-all">❌ رفض</button>
|
||
<div x-show="currentInvoice?.status === 'approved'" class="w-full bg-emerald-900/30 text-emerald-400 py-3 rounded-2xl font-bold text-center border border-emerald-900/50">
|
||
✅ مدققة وموثقة محلياً
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Edit Tenant Modal (Super Admin Only) -->
|
||
<div x-show="showEditTenantModal" x-cloak class="fixed inset-0 bg-black/90 flex items-center justify-center p-6 z-[140]">
|
||
<div class="bg-surface border border-gray-800 w-full max-w-xl p-10 rounded-[40px] shadow-2xl glass-elevated overflow-y-auto max-h-[90vh]">
|
||
<h3 class="text-2xl font-bold mb-8 text-emerald-400">تعديل بيانات المكتب</h3>
|
||
<form @submit.prevent="updateTenant" class="space-y-6">
|
||
<div>
|
||
<label class="block text-xs font-bold text-gray-500 uppercase mb-2">اسم المكتب</label>
|
||
<input type="text" x-model="currentTenant.name" class="w-full bg-gray-950 border border-gray-800 p-4 rounded-2xl outline-none focus:border-emerald-500" required>
|
||
</div>
|
||
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
|
||
<div>
|
||
<label class="block text-xs font-bold text-gray-500 uppercase mb-2">البريد الرسمي</label>
|
||
<input type="email" x-model="currentTenant.email" class="w-full bg-gray-950 border border-gray-800 p-4 rounded-2xl outline-none focus:border-emerald-500" required>
|
||
</div>
|
||
<div>
|
||
<label class="block text-xs font-bold text-gray-500 uppercase mb-2">رقم الهاتف</label>
|
||
<input type="text" x-model="currentTenant.phone" class="w-full bg-gray-950 border border-gray-800 p-4 rounded-2xl outline-none focus:border-emerald-500">
|
||
</div>
|
||
</div>
|
||
<div>
|
||
<label class="block text-xs font-bold text-gray-500 uppercase mb-2">حالة المكتب</label>
|
||
<select x-model="currentTenant.status" class="w-full bg-gray-950 border border-gray-800 p-4 rounded-2xl outline-none focus:border-emerald-500">
|
||
<option value="active">نشط (Active)</option>
|
||
<option value="suspended">معلق (Suspended)</option>
|
||
<option value="trial">تجريبي (Trial)</option>
|
||
</select>
|
||
</div>
|
||
|
||
<div class="flex gap-4 pt-6 border-t border-gray-800">
|
||
<button type="submit" class="flex-1 bg-emerald-600 hover:bg-emerald-500 py-4 rounded-2xl font-bold transition-all btn-glow" :disabled="isBusy">
|
||
<span x-show="!isBusy">💾 حفظ التعديلات</span>
|
||
<span x-show="isBusy">⏳ جاري الحفظ...</span>
|
||
</button>
|
||
<button type="button" @click="showEditTenantModal = false" class="px-8 py-4 border border-gray-800 rounded-2xl hover:bg-gray-800 transition-colors">إلغاء</button>
|
||
</div>
|
||
</form>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Add Tenant Modal (Super Admin Only) -->
|
||
<div x-show="showAddTenantModal" x-cloak class="fixed inset-0 bg-black/90 flex items-center justify-center p-6 z-[140]">
|
||
<div class="bg-surface border border-gray-800 w-full max-w-2xl p-10 rounded-[40px] shadow-2xl glass-elevated overflow-y-auto max-h-[90vh]">
|
||
<h3 class="text-2xl font-bold mb-8 text-emerald-400">إضافة مكتب محاسبي جديد</h3>
|
||
<form @submit.prevent="createTenant" class="space-y-8">
|
||
<!-- Office Details -->
|
||
<div class="space-y-4">
|
||
<p class="text-xs font-bold text-gray-500 uppercase tracking-widest border-b border-gray-800 pb-2">بيانات المكتب</p>
|
||
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
|
||
<input type="text" x-model="newTenant.name" placeholder="اسم المكتب" class="w-full bg-gray-950 border border-gray-800 p-4 rounded-2xl outline-none focus:border-emerald-500 transition-colors" required>
|
||
<input type="email" x-model="newTenant.email" placeholder="بريد المكتب الرسمي" class="w-full bg-gray-950 border border-gray-800 p-4 rounded-2xl outline-none focus:border-emerald-500 transition-colors" required>
|
||
<input type="text" x-model="newTenant.phone" placeholder="رقم الهاتف" class="w-full bg-gray-950 border border-gray-800 p-4 rounded-2xl outline-none focus:border-emerald-500 transition-colors">
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Manager Details -->
|
||
<div class="space-y-4">
|
||
<p class="text-xs font-bold text-gray-500 uppercase tracking-widest border-b border-gray-800 pb-2">بيانات مدير المكتب (المسؤول)</p>
|
||
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
|
||
<input type="text" x-model="newTenant.manager_name" placeholder="اسم المدير الكامل" class="w-full bg-gray-950 border border-gray-800 p-4 rounded-2xl outline-none focus:border-emerald-500 transition-colors" required>
|
||
<input type="email" x-model="newTenant.manager_email" placeholder="بريد المدير الخاص" class="w-full bg-gray-950 border border-gray-800 p-4 rounded-2xl outline-none focus:border-emerald-500 transition-colors" required>
|
||
<input type="password" x-model="newTenant.manager_password" placeholder="كلمة مرور المدير" class="w-full bg-gray-950 border border-gray-800 p-4 rounded-2xl outline-none focus:border-emerald-500 transition-colors" required>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="flex gap-4 pt-4 border-t border-gray-800">
|
||
<button type="submit" class="flex-1 bg-emerald-600 hover:bg-emerald-500 py-4 rounded-2xl font-bold transition-all btn-glow" :disabled="isBusy">
|
||
<span x-show="!isBusy">🏗️ إنشاء المكتب والمدير</span>
|
||
<span x-show="isBusy">⏳ جاري الإنشاء...</span>
|
||
</button>
|
||
<button type="button" @click="showAddTenantModal = false" class="px-8 py-4 border border-gray-800 rounded-2xl hover:bg-gray-800 transition-colors">إلغاء</button>
|
||
</div>
|
||
</form>
|
||
</div>
|
||
</div>
|
||
|
||
<script>
|
||
document.addEventListener('alpine:init', () => {
|
||
Alpine.data('app', () => ({
|
||
user: JSON.parse(localStorage.getItem('user')),
|
||
page: 'dashboard',
|
||
users: [], companies: [], invoices: [], tenants: [],
|
||
stats: { total: 0, pending: 0, approved: 0 },
|
||
|
||
showAddUserModal: false, showAddCompanyModal: false, showConnectModal: false,
|
||
showUploadModal: false, showViewModal: false, showCompanyStatsModal: false,
|
||
showAddTenantModal: false, showEditTenantModal: false,
|
||
isBusy: false, globalError: '',
|
||
|
||
newUser: { name: '', email: '', password: '', role: 'accountant' },
|
||
newCompany: { name: '', tax_identification_number: '', commercial_registration_number: '', address: '' },
|
||
newTenant: { name: '', email: '', phone: '', manager_name: '', manager_email: '', manager_password: '' },
|
||
connectData: { client_id: '', secret_key: '', income_source_sequence: '1' },
|
||
uploadData: { company_id: '' },
|
||
currentCompany: null, currentInvoice: null, companyStats: null, currentTenant: { name: '', email: '', phone: '', status: '' },
|
||
|
||
init() {
|
||
if (!this.user) { window.location.href = '/login.php'; return; }
|
||
this.loadAll();
|
||
},
|
||
setPage(p) { this.page = p; this.loadAll(); },
|
||
title() { return { dashboard: 'لوحة التحكم', users: 'المستخدمون', companies: 'الشركات', invoices: 'إدارة الفواتير' }[this.page] || ''; },
|
||
subtitle() { return { dashboard: 'نظرة شاملة', users: 'إدارة الفريق', companies: 'إدارة المؤسسات والربط الضريبي', invoices: 'معالجة الفواتير' }[this.page] || ''; },
|
||
token() { return localStorage.getItem('access_token'); },
|
||
showError(msg) { this.globalError = msg; setTimeout(() => this.globalError = '', 8000); },
|
||
|
||
async apiRequest(route, method = 'GET', body = null) {
|
||
try {
|
||
const options = {
|
||
method,
|
||
headers: { 'Authorization': 'Bearer ' + this.token(), 'Content-Type': 'application/json' }
|
||
};
|
||
if (body) options.body = JSON.stringify(body);
|
||
const res = await fetch('/index.php?route=' + route, options);
|
||
const json = await res.json();
|
||
if (res.status === 401) { localStorage.clear(); window.location.href = '/login.php'; return null; }
|
||
return json.success ? json.data : (this.showError(json.message), null);
|
||
} catch (e) { this.showError('فشل الاتصال بالخادم'); return null; }
|
||
},
|
||
|
||
async loadAll() {
|
||
this.stats = await this.apiRequest('v1/dashboard/stats') || { total: 0, pending: 0, approved: 0 };
|
||
this.companies = await this.apiRequest('v1/companies') || [];
|
||
if (this.page === 'users') this.users = await this.apiRequest('v1/users') || [];
|
||
if (this.page === 'invoices') this.invoices = await this.apiRequest('v1/invoices') || [];
|
||
if (this.page === 'tenants' && this.user.role === 'super_admin') this.tenants = await this.apiRequest('v1/tenants') || [];
|
||
},
|
||
|
||
getQrSrc(inv) {
|
||
if (!inv) return '';
|
||
if (inv.jofotara?.qr_image_uri) return inv.jofotara.qr_image_uri;
|
||
if (inv.qr_code) {
|
||
// Check if it's already a data URI
|
||
if (inv.qr_code.startsWith('data:')) return inv.qr_code;
|
||
|
||
// Otherwise, it's raw TLV, generate it using QRious
|
||
try {
|
||
const qr = new QRious({
|
||
value: inv.qr_code,
|
||
size: 250
|
||
});
|
||
return qr.toDataURL();
|
||
} catch (e) { return ''; }
|
||
}
|
||
return '';
|
||
},
|
||
|
||
async createUser() {
|
||
this.isBusy = true;
|
||
const res = await this.apiRequest('v1/users/create', 'POST', this.newUser);
|
||
if (res) { this.showAddUserModal = false; this.loadAll(); alert('تم إضافة المستخدم بنجاح'); }
|
||
this.isBusy = false;
|
||
},
|
||
|
||
async createCompany() {
|
||
this.isBusy = true;
|
||
const res = await this.apiRequest('v1/companies/create', 'POST', this.newCompany);
|
||
if (res) { this.showAddCompanyModal = false; this.loadAll(); alert('تم إضافة الشركة بنجاح'); }
|
||
this.isBusy = false;
|
||
},
|
||
|
||
async createTenant() {
|
||
this.isBusy = true;
|
||
const res = await this.apiRequest('v1/tenants/create', 'POST', this.newTenant);
|
||
if (res) {
|
||
this.showAddTenantModal = false;
|
||
this.newTenant = { name: '', email: '', phone: '', manager_name: '', manager_email: '', manager_password: '' };
|
||
this.loadAll();
|
||
alert('تم إضافة المكتب المحاسبي والمدير المسؤول بنجاح');
|
||
}
|
||
this.isBusy = false;
|
||
},
|
||
|
||
openEditTenantModal(t) {
|
||
this.currentTenant = JSON.parse(JSON.stringify(t));
|
||
this.showEditTenantModal = true;
|
||
},
|
||
|
||
async updateTenant() {
|
||
this.isBusy = true;
|
||
const res = await this.apiRequest('v1/tenants/update', 'POST', this.currentTenant);
|
||
if (res) {
|
||
this.showEditTenantModal = false;
|
||
this.loadAll();
|
||
alert('تم تحديث بيانات المكتب بنجاح');
|
||
}
|
||
this.isBusy = false;
|
||
},
|
||
|
||
openConnectModal(company) {
|
||
this.currentCompany = company;
|
||
this.connectData.client_id = '';
|
||
this.connectData.secret_key = '';
|
||
this.showConnectModal = true;
|
||
},
|
||
|
||
async connectJoFotara() {
|
||
this.isBusy = true;
|
||
const res = await this.apiRequest('v1/companies/connect', 'POST', {
|
||
id: this.currentCompany.id,
|
||
...this.connectData
|
||
});
|
||
if (res) { this.showConnectModal = false; this.loadAll(); alert('تم تفعيل الربط الضريبي بنجاح'); }
|
||
this.isBusy = false;
|
||
},
|
||
|
||
async showCompanyStats(id) {
|
||
this.companyStats = await this.apiRequest('v1/companies/stats&id=' + id);
|
||
if (this.companyStats) this.showCompanyStatsModal = true;
|
||
},
|
||
|
||
async viewInvoice(id) {
|
||
this.currentInvoice = await this.apiRequest('v1/invoices/view&id=' + id);
|
||
if (this.currentInvoice) {
|
||
this.showViewModal = true;
|
||
|
||
// Fallback QR code generation if missing from DB but we have invoice data
|
||
if (!this.currentInvoice.jofotara?.qr_image_uri && !this.currentInvoice.qr_code && this.currentInvoice.status === 'approved') {
|
||
try {
|
||
const qr = new QRious({
|
||
value: 'Invoice: ' + this.currentInvoice.invoice_number + '\nSeller: ' + this.currentInvoice.supplier_name + '\nTotal: ' + this.currentInvoice.grand_total,
|
||
size: 250
|
||
});
|
||
this.currentInvoice.qr_code = qr.toDataURL();
|
||
} catch (e) {}
|
||
}
|
||
}
|
||
},
|
||
|
||
selectedFile: null,
|
||
isUploading: false,
|
||
|
||
async uploadInvoice() {
|
||
if (!this.selectedFile) return alert('الرجاء اختيار ملف');
|
||
if (!this.uploadData.company_id) return alert('الرجاء اختيار الشركة');
|
||
|
||
this.isUploading = true;
|
||
const formData = new FormData();
|
||
formData.append('company_id', this.uploadData.company_id);
|
||
formData.append('invoice', this.selectedFile);
|
||
|
||
try {
|
||
const res = await fetch('/index.php?route=v1/invoices/upload', {
|
||
method: 'POST',
|
||
headers: { 'Authorization': 'Bearer ' + this.token() },
|
||
body: formData
|
||
});
|
||
const json = await res.json();
|
||
this.isUploading = false;
|
||
|
||
if (json.success) {
|
||
this.showUploadModal = false;
|
||
this.selectedFile = null;
|
||
await this.loadAll();
|
||
this.viewInvoice(json.data.id);
|
||
} else {
|
||
this.showError(json.message);
|
||
}
|
||
} catch (e) {
|
||
this.isUploading = false;
|
||
this.showError('فشل الاتصال بالخادم أثناء الرفع');
|
||
}
|
||
},
|
||
|
||
async approveInvoice() {
|
||
if (!this.currentInvoice || this.isBusy) return;
|
||
this.isBusy = true;
|
||
try {
|
||
const res = await fetch('/index.php?route=v1/invoices/approve', {
|
||
method: 'POST',
|
||
headers: { 'Authorization': 'Bearer ' + this.token(), 'Content-Type': 'application/json' },
|
||
body: JSON.stringify({ id: this.currentInvoice.id })
|
||
});
|
||
const json = await res.json();
|
||
this.isBusy = false;
|
||
|
||
if (json.success) {
|
||
alert('تم اعتماد الفاتورة بنجاح!');
|
||
this.showViewModal = false;
|
||
this.loadAll();
|
||
} else {
|
||
this.showError(json.message);
|
||
}
|
||
} catch (e) {
|
||
this.isBusy = false;
|
||
this.showError('حدث خطأ أثناء الاعتماد');
|
||
}
|
||
},
|
||
|
||
logout() { localStorage.clear(); window.location.href = '/login.php'; }
|
||
}));
|
||
});
|
||
</script>
|
||
</body>
|
||
</html> |