Files
musadaq-saas/public/shell.php
2026-05-04 18:00:43 +03:00

1028 lines
65 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<!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>
<script src="https://cdn.jsdelivr.net/npm/canvas-confetti@1.6.0/dist/confetti.browser.min.js"></script>
<style>
:root {
--emerald: #10b981;
--emerald-glow: rgba(16, 185, 129, 0.4);
--bg-base: #030712;
--bg-surface: #0f172a;
--bg-surface-elevated: #1e293b;
--border-default: rgba(255,255,255,0.08);
--border-glow: rgba(16, 185, 129, 0.3);
--text-primary: #f8fafc;
--text-secondary: #94a3b8;
--text-muted: #64748b;
--gradient-primary: linear-gradient(135deg, #10b981 0%, #06b6d4 50%, #8b5cf6 100%);
--gradient-glow: radial-gradient(ellipse at center, rgba(16,185,129,0.15) 0%, transparent 70%);
}
* {
scrollbar-width: thin;
scrollbar-color: rgba(16, 185, 129, 0.3) transparent;
}
*::-webkit-scrollbar { width: 6px; height: 6px; }
*::-webkit-scrollbar-track { background: transparent; }
*::-webkit-scrollbar-thumb {
background: rgba(16, 185, 129, 0.3);
border-radius: 3px;
transition: background 0.2s;
}
*::-webkit-scrollbar-thumb:hover { background: rgba(16, 185, 129, 0.5); }
body {
font-family: 'IBM Plex Sans Arabic', sans-serif;
background-color: var(--bg-base);
color: var(--text-primary);
background-image:
radial-gradient(ellipse at 20% 80%, rgba(16,185,129,0.03) 0%, transparent 50%),
radial-gradient(ellipse at 80% 20%, rgba(139,92,246,0.03) 0%, transparent 50%),
radial-gradient(ellipse at 50% 50%, rgba(6,182,212,0.02) 0%, transparent 70%);
overflow-x: hidden;
}
body::before {
content: '';
position: fixed;
top: 0; left: 0; right: 0; bottom: 0;
background:
radial-gradient(circle at 20% 35%, rgba(16,185,129,0.04) 0%, transparent 40%),
radial-gradient(circle at 80% 65%, rgba(139,92,246,0.04) 0%, transparent 40%);
pointer-events: none;
z-index: -1;
animation: pulse 8s ease-in-out infinite;
}
@keyframes pulse {
0%, 100% { opacity: 0.5; }
50% { opacity: 1; }
}
[x-cloak] { display: none !important; }
/* Glassmorphism Pro */
.glass {
background: rgba(15, 23, 42, 0.6);
backdrop-filter: blur(20px);
border: 1px solid var(--border-default);
box-shadow:
0 8px 32px rgba(0, 0, 0, 0.3),
inset 0 1px 0 rgba(255,255,255,0.05);
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
}
.glass:hover {
border-color: var(--border-glow);
box-shadow:
0 12px 40px rgba(0, 0, 0, 0.4),
0 0 0 1px var(--border-glow),
inset 0 1px 0 rgba(255,255,255,0.1);
}
.glass-elevated {
background: rgba(30, 41, 59, 0.7);
backdrop-filter: blur(24px);
border: 1px solid rgba(255,255,255,0.1);
box-shadow:
0 20px 60px rgba(0, 0, 0, 0.4),
0 0 0 1px rgba(16,185,129,0.1),
inset 0 1px 0 rgba(255,255,255,0.05);
}
/* Animated Glow Buttons */
.btn-glow {
position: relative;
overflow: hidden;
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
}
.btn-glow::before {
content: '';
position: absolute;
top: 0; left: -100%;
width: 100%; height: 100%;
background: linear-gradient(90deg, transparent, rgba(255,255,255,0.2), transparent);
transition: left 0.5s;
}
.btn-glow:hover::before { left: 100%; }
.btn-glow:hover {
transform: translateY(-2px);
box-shadow: 0 10px 30px var(--emerald-glow);
}
.btn-glow:active { transform: translateY(0); }
/* Card Animations */
.card-enter {
animation: cardEnter 0.4s cubic-bezier(0.4, 0, 0.2, 1) forwards;
}
@keyframes cardEnter {
from { opacity: 0; transform: translateY(20px) scale(0.98); }
to { opacity: 1; transform: translateY(0) scale(1); }
}
/* Table Row Hover */
.table-row {
transition: all 0.2s ease;
border-bottom: 1px solid var(--border-default);
}
.table-row:hover {
background: rgba(16, 185, 129, 0.03);
transform: translateX(4px);
border-left: 3px solid var(--emerald);
}
/* Status Badges */
.badge {
position: relative;
overflow: hidden;
}
.badge::after {
content: '';
position: absolute;
top: -50%; left: -50%;
width: 200%; height: 200%;
background: radial-gradient(circle, rgba(255,255,255,0.1) 0%, transparent 70%);
opacity: 0;
transition: opacity 0.3s;
pointer-events: none;
}
.badge:hover::after { opacity: 1; }
/* Modal Enhancements */
.modal-enter {
animation: modalEnter 0.3s cubic-bezier(0.4, 0, 0.2, 1) forwards;
}
@keyframes modalEnter {
from { opacity: 0; transform: scale(0.95) translateY(10px); }
to { opacity: 1; transform: scale(1) translateY(0); }
}
/* Sidebar Active State */
.nav-item-active {
background: linear-gradient(135deg, rgba(16,185,129,0.15) 0%, rgba(6,182,212,0.1) 100%);
border-left: 3px solid var(--emerald);
box-shadow: inset 4px 0 20px rgba(16,185,129,0.1);
}
/* Animated Stats Cards */
.stat-card {
position: relative;
overflow: hidden;
}
.stat-card::before {
content: '';
position: absolute;
top: -50%; left: -50%;
width: 200%; height: 200%;
background: var(--gradient-glow);
opacity: 0;
transition: opacity 0.4s;
pointer-events: none;
}
.stat-card:hover::before { opacity: 1; }
/* File Upload Zone */
.upload-zone {
border: 2px dashed var(--border-default);
transition: all 0.3s ease;
background:
radial-gradient(ellipse at center, rgba(16,185,129,0.02) 0%, transparent 70%),
linear-gradient(135deg, rgba(30,41,59,0.5) 0%, rgba(15,23,42,0.8) 100%);
}
.upload-zone:hover {
border-color: var(--emerald);
background:
radial-gradient(ellipse at center, rgba(16,185,129,0.08) 0%, transparent 70%),
linear-gradient(135deg, rgba(30,41,59,0.7) 0%, rgba(15,23,42,0.9) 100%);
transform: scale(1.01);
}
/* Loading Animation */
@keyframes shimmer {
0% { background-position: -200% 0; }
100% { background-position: 200% 0; }
}
.loading-shimmer {
background: linear-gradient(90deg, transparent, rgba(255,255,255,0.1), transparent);
background-size: 200% 100%;
animation: shimmer 1.5s infinite;
}
/* Floating Elements */
.float { animation: float 3s ease-in-out infinite; }
@keyframes float {
0%, 100% { transform: translateY(0); }
50% { transform: translateY(-8px); }
}
/* QR Code Container */
.qr-container {
background: white;
border-radius: 16px;
padding: 12px;
box-shadow:
0 10px 40px rgba(0,0,0,0.3),
0 0 0 1px rgba(255,255,255,0.1),
0 0 30px rgba(16,185,129,0.2);
transition: transform 0.3s ease;
}
.qr-container:hover { transform: scale(1.03); }
/* Toast Notification */
.toast {
animation: toastSlide 0.3s cubic-bezier(0.4, 0, 0.2, 1);
}
@keyframes toastSlide {
from { opacity: 0; transform: translate(-50%, -20px); }
to { opacity: 1; transform: translate(-50%, 0); }
}
/* Gradient Text */
.text-gradient {
background: var(--gradient-primary);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
background-clip: text;
}
/* Focus States */
input:focus, select:focus, textarea:focus {
outline: none;
border-color: var(--emerald);
box-shadow: 0 0 0 3px rgba(16, 185, 129, 0.2);
}
/* Smooth Transitions for Alpine */
[x-transition] { transition: opacity 0.2s ease, transform 0.2s ease; }
</style>
</head>
<body x-data="app" x-init="init()" class="antialiased">
<!-- Global Error Toast -->
<div x-show="globalError" x-cloak
class="fixed top-4 left-1/2 -translate-x-1/2 z-[100] bg-red-500/90 border border-red-400/30 text-white px-6 py-4 rounded-2xl shadow-2xl max-w-lg text-center glass-elevated toast"
x-text="globalError"
x-transition:enter="toast"
@click="globalError = ''"
role="alert">
</div>
<div class="flex h-screen overflow-hidden">
<!-- Sidebar -->
<aside class="w-72 bg-surface border-l border-gray-800/50 flex flex-col glass-elevated">
<div class="p-6 border-b border-gray-800/50">
<div class="flex items-center gap-3">
<div class="w-10 h-10 rounded-xl bg-gradient-to-br from-emerald-500 to-cyan-500 flex items-center justify-center shadow-lg shadow-emerald-500/20">
<span class="text-white font-bold text-lg"></span>
</div>
<div>
<h1 class="text-xl font-bold text-gradient tracking-tight">مُصادَق</h1>
<p class="text-[10px] text-gray-500 mt-0.5 uppercase tracking-widest">Enterprise SaaS</p>
</div>
</div>
</div>
<nav class="flex-1 px-4 py-6 space-y-1.5 overflow-y-auto">
<a href="#" @click="setPage('dashboard')"
class="nav-item flex items-center gap-3 p-3.5 rounded-xl transition-all duration-200 group"
:class="page==='dashboard'?'nav-item-active text-emerald-400':'text-gray-400 hover:bg-gray-800/50 hover:text-gray-200'">
<span class="text-xl transition-transform group-hover:scale-110">📊</span>
<span class="font-medium">لوحة التحكم</span>
<span x-show="page==='dashboard'" class="ml-auto w-1.5 h-1.5 rounded-full bg-emerald-400 shadow-[0_0_8px_rgba(16,185,129,0.8)]"></span>
</a>
<a x-show="user?.role === 'super_admin'" href="#" @click="setPage('tenants')"
class="nav-item flex items-center gap-3 p-3.5 rounded-xl transition-all duration-200 group"
:class="page==='tenants'?'nav-item-active text-emerald-400':'text-gray-400 hover:bg-gray-800/50 hover:text-gray-200'">
<span class="text-xl transition-transform group-hover:scale-110">🏢</span>
<span class="font-medium">المكاتب المحاسبية</span>
</a>
<a x-show="user?.role !== 'viewer'" href="#" @click="setPage('companies')"
class="nav-item flex items-center gap-3 p-3.5 rounded-xl transition-all duration-200 group"
:class="page==='companies'?'nav-item-active text-emerald-400':'text-gray-400 hover:bg-gray-800/50 hover:text-gray-200'">
<span class="text-xl transition-transform group-hover:scale-110">🏭</span>
<span class="font-medium">الشركات</span>
</a>
<a x-show="user?.role !== 'viewer'" href="#" @click="setPage('invoices')"
class="nav-item flex items-center gap-3 p-3.5 rounded-xl transition-all duration-200 group"
:class="page==='invoices'?'nav-item-active text-emerald-400':'text-gray-400 hover:bg-gray-800/50 hover:text-gray-200'">
<span class="text-xl transition-transform group-hover:scale-110">📄</span>
<span class="font-medium">الفواتير</span>
<span x-show="stats.pending > 0" class="ml-auto bg-red-500/20 text-red-400 text-[10px] font-bold px-2 py-0.5 rounded-full border border-red-500/30" x-text="stats.pending"></span>
</a>
<a x-show="user?.role === 'super_admin' || user?.role === 'admin'" href="#" @click="setPage('users')"
class="nav-item flex items-center gap-3 p-3.5 rounded-xl transition-all duration-200 group"
:class="page==='users'?'nav-item-active text-emerald-400':'text-gray-400 hover:bg-gray-800/50 hover:text-gray-200'">
<span class="text-xl transition-transform group-hover:scale-110">👥</span>
<span class="font-medium">المستخدمون</span>
</a>
</nav>
<div class="p-5 border-t border-gray-800/50">
<div class="flex items-center gap-3 mb-4 p-3 rounded-xl bg-gray-900/30 border border-gray-800/50">
<div class="w-9 h-9 rounded-full bg-gradient-to-br from-emerald-500/20 to-cyan-500/20 flex items-center justify-center text-emerald-400 font-bold text-sm border border-emerald-500/20 shadow-inner" x-text="user?.name?.[0]"></div>
<div class="truncate flex-1 min-w-0">
<p class="text-xs font-bold truncate" x-text="user?.name"></p>
<p class="text-[10px] text-gray-500 uppercase tracking-wide" x-text="user?.role"></p>
</div>
<span class="w-2 h-2 rounded-full bg-emerald-400 shadow-[0_0_6px_rgba(16,185,129,0.6)] animate-pulse"></span>
</div>
<button @click="logout()" class="w-full text-right text-red-400/90 hover:text-red-300 text-xs font-bold py-2.5 px-4 rounded-xl hover:bg-red-500/10 transition-all flex items-center gap-2 group">
<span class="transition-transform group-hover:-translate-x-1">🚪</span>
<span>تسجيل الخروج</span>
</button>
</div>
</aside>
<!-- Main Content -->
<main class="flex-1 overflow-y-auto p-8 bg-[#020617] relative">
<!-- Animated Background Elements -->
<div class="absolute inset-0 overflow-hidden pointer-events-none">
<div class="absolute top-1/4 left-1/4 w-96 h-96 bg-emerald-500/5 rounded-full blur-3xl animate-pulse"></div>
<div class="absolute bottom-1/4 right-1/4 w-80 h-80 bg-cyan-500/5 rounded-full blur-3xl animate-pulse" style="animation-delay: 1s;"></div>
</div>
<div class="relative z-10">
<header class="mb-10 flex flex-col sm:flex-row justify-between items-start sm:items-end gap-4">
<div>
<h2 class="text-3xl font-bold tracking-tight text-gradient" x-text="title()"></h2>
<p class="text-gray-500 mt-2 text-sm flex items-center gap-2">
<span class="w-1.5 h-1.5 rounded-full bg-emerald-400 animate-pulse"></span>
<span x-text="subtitle()"></span>
</p>
</div>
<div class="flex flex-wrap items-center gap-3">
<button x-show="page==='tenants'" @click="showAddTenantModal = true" class="btn-glow bg-gradient-to-r from-emerald-600 to-emerald-500 hover:from-emerald-500 hover:to-cyan-500 px-5 py-3 rounded-xl text-sm font-bold shadow-lg shadow-emerald-900/30 transition-all active:scale-95 flex items-center gap-2">
<span></span><span>إضافة مكتب</span>
</button>
<button x-show="page==='users'" @click="showAddModal = true" class="btn-glow bg-gradient-to-r from-emerald-600 to-emerald-500 hover:from-emerald-500 hover:to-cyan-500 px-5 py-3 rounded-xl text-sm font-bold shadow-lg shadow-emerald-900/30 transition-all active:scale-95 flex items-center gap-2">
<span></span><span>إضافة مستخدم</span>
</button>
<button x-show="page==='companies'" @click="showAddCompanyModal = true" class="btn-glow bg-gradient-to-r from-emerald-600 to-emerald-500 hover:from-emerald-500 hover:to-cyan-500 px-5 py-3 rounded-xl text-sm font-bold shadow-lg shadow-emerald-900/30 transition-all active:scale-95 flex items-center gap-2">
<span></span><span>إضافة شركة</span>
</button>
<button x-show="page==='invoices'" @click="showUploadModal = true" class="btn-glow bg-gradient-to-r from-emerald-600 to-emerald-500 hover:from-emerald-500 hover:to-cyan-500 px-5 py-3 rounded-xl text-sm font-bold shadow-lg shadow-emerald-900/30 transition-all active:scale-95 flex items-center gap-2">
<span>📤</span><span>رفع فواتير</span>
</button>
</div>
</header>
<div id="content" x-transition:enter="card-enter">
<!-- Invoices Page -->
<div x-show="page === 'invoices'" class="space-y-6">
<div class="glass rounded-3xl overflow-hidden shadow-2xl border border-gray-800/50">
<div class="overflow-x-auto">
<table class="w-full text-right border-collapse">
<thead class="bg-gray-900/40 border-b border-gray-800/50">
<tr>
<th class="p-5 text-xs font-bold text-gray-500 uppercase tracking-wider">الشركة</th>
<th class="p-5 text-xs font-bold text-gray-500 uppercase tracking-wider">المورد</th>
<th class="p-5 text-xs font-bold text-gray-500 uppercase tracking-wider">التاريخ</th>
<th class="p-5 text-xs font-bold text-gray-500 uppercase tracking-wider">المجموع</th>
<th class="p-5 text-xs font-bold text-gray-500 uppercase tracking-wider">الحالة</th>
<th class="p-5 text-xs font-bold text-gray-500 uppercase tracking-wider">إجراءات</th>
</tr>
</thead>
<tbody class="divide-y divide-gray-800/30">
<tr x-show="invoices.length === 0">
<td colspan="6" class="p-16 text-center">
<div class="flex flex-col items-center gap-4">
<span class="text-5xl opacity-30">📭</span>
<p class="text-gray-500 text-lg">لا توجد فواتير بعد</p>
<button @click="showUploadModal = true" class="text-emerald-400 hover:text-emerald-300 text-sm font-medium transition flex items-center gap-2">
<span></span> ابدأ برفع فاتورة أولى
</button>
</div>
</td>
</tr>
<template x-for="inv in invoices" :key="inv.id">
<tr class="table-row group">
<td class="p-5">
<p class="font-bold text-emerald-400" x-text="inv.company_name"></p>
</td>
<td class="p-5">
<p class="text-sm font-medium text-gray-200" x-text="inv.supplier_name"></p>
<p class="text-[10px] text-gray-600 mt-0.5 font-mono" x-text="inv.supplier_tin"></p>
</td>
<td class="p-5 text-sm text-gray-400 font-mono" x-text="inv.invoice_date || '-'"></td>
<td class="p-5">
<span class="font-mono text-sm font-bold text-gray-200" x-text="parseFloat(inv.grand_total).toLocaleString()"></span>
<span class="text-[10px] text-gray-600 mr-1 bg-gray-800/50 px-1.5 py-0.5 rounded">JOD</span>
</td>
<td class="p-5">
<span class="badge px-3 py-1.5 rounded-full text-[10px] font-bold uppercase tracking-wide border"
:class="inv.status==='extracted'?'bg-blue-500/10 text-blue-400 border-blue-500/30':(inv.status==='approved'?'bg-emerald-500/10 text-emerald-400 border-emerald-500/30':'bg-gray-800/50 text-gray-400 border-gray-700')"
x-text="inv.status"></span>
</td>
<td class="p-5">
<button @click="viewInvoice(inv.id)" class="text-gray-500 hover:text-emerald-400 p-2.5 rounded-xl hover:bg-emerald-400/10 transition-all hover:scale-105 active:scale-95" title="عرض التفاصيل">
👁️
</button>
</td>
</tr>
</template>
</tbody>
</table>
</div>
</div>
</div>
<!-- Dashboard Page -->
<div x-show="page === 'dashboard'">
<div class="grid grid-cols-1 md:grid-cols-3 gap-6">
<div class="stat-card p-7 bg-surface border border-gray-800/50 rounded-3xl shadow-xl relative overflow-hidden group glass">
<div class="absolute top-0 right-0 p-5 opacity-10 group-hover:opacity-20 transition-opacity float">
<span class="text-5xl">📄</span>
</div>
<p class="text-gray-500 text-xs uppercase font-bold tracking-widest mb-3">إجمالي الفواتير</p>
<p class="text-4xl font-bold mt-2 bg-gradient-to-r from-emerald-400 to-cyan-400 bg-clip-text text-transparent" x-text="stats.total || 0"></p>
<div class="mt-4 h-1 bg-gray-800 rounded-full overflow-hidden">
<div class="h-full bg-gradient-to-r from-emerald-500 to-cyan-500 rounded-full" :style="`width: ${Math.min((stats.total || 0) * 2, 100)}%`"></div>
</div>
</div>
<div class="stat-card p-7 bg-surface border border-gray-800/50 rounded-3xl shadow-xl relative overflow-hidden group glass">
<div class="absolute top-0 right-0 p-5 opacity-10 group-hover:opacity-20 transition-opacity float" style="animation-delay: 0.5s">
<span class="text-5xl text-yellow-400"></span>
</div>
<p class="text-gray-500 text-xs uppercase font-bold tracking-widest mb-3">قيد المعالجة</p>
<p class="text-4xl font-bold mt-2 text-yellow-400" x-text="stats.pending || 0"></p>
<div class="mt-4 h-1 bg-gray-800 rounded-full overflow-hidden">
<div class="h-full bg-gradient-to-r from-yellow-500 to-orange-500 rounded-full animate-pulse" :style="`width: ${Math.min((stats.pending || 0) * 3, 100)}%`"></div>
</div>
</div>
<div class="stat-card p-7 bg-surface border border-gray-800/50 rounded-3xl shadow-xl relative overflow-hidden group glass">
<div class="absolute top-0 right-0 p-5 opacity-10 group-hover:opacity-20 transition-opacity float" style="animation-delay: 1s">
<span class="text-5xl text-emerald-400"></span>
</div>
<p class="text-gray-500 text-xs uppercase font-bold tracking-widest mb-3">تم الاعتماد</p>
<p class="text-4xl font-bold mt-2 text-emerald-400" x-text="stats.approved || 0"></p>
<div class="mt-4 h-1 bg-gray-800 rounded-full overflow-hidden">
<div class="h-full bg-gradient-to-r from-emerald-500 to-teal-500 rounded-full" :style="`width: ${Math.min((stats.approved || 0) * 2, 100)}%`"></div>
</div>
</div>
</div>
<!-- Quick Actions -->
<div class="mt-8 p-6 bg-surface border border-gray-800/50 rounded-3xl glass">
<h3 class="text-lg font-bold mb-4 flex items-center gap-2">
<span></span> إجراءات سريعة
</h3>
<div class="grid grid-cols-2 md:grid-cols-4 gap-4">
<button @click="showUploadModal = true" class="p-4 rounded-2xl bg-gray-900/30 border border-gray-800/50 hover:border-emerald-500/30 hover:bg-emerald-500/5 transition-all group text-center">
<span class="text-2xl block mb-2 group-hover:scale-110 transition-transform">📤</span>
<span class="text-sm font-medium">رفع فاتورة</span>
</button>
<button @click="setPage('companies')" class="p-4 rounded-2xl bg-gray-900/30 border border-gray-800/50 hover:border-cyan-500/30 hover:bg-cyan-500/5 transition-all group text-center">
<span class="text-2xl block mb-2 group-hover:scale-110 transition-transform">🏭</span>
<span class="text-sm font-medium">إدارة الشركات</span>
</button>
<button @click="setPage('users')" class="p-4 rounded-2xl bg-gray-900/30 border border-gray-800/50 hover:border-purple-500/30 hover:bg-purple-500/5 transition-all group text-center">
<span class="text-2xl block mb-2 group-hover:scale-110 transition-transform">👥</span>
<span class="text-sm font-medium">المستخدمون</span>
</button>
<button @click="loadAll()" class="p-4 rounded-2xl bg-gray-900/30 border border-gray-800/50 hover:border-emerald-500/30 hover:bg-emerald-500/5 transition-all group text-center">
<span class="text-2xl block mb-2 group-hover:scale-110 transition-transform animate-spin-slow">🔄</span>
<span class="text-sm font-medium">تحديث البيانات</span>
</button>
</div>
</div>
</div>
<!-- Companies Page -->
<div x-show="page === 'companies'">
<div class="glass rounded-3xl overflow-hidden shadow-2xl border border-gray-800/50">
<table class="w-full text-right divide-y divide-gray-800/30">
<thead class="bg-gray-900/40">
<tr>
<th class="p-5 text-xs font-bold text-gray-500 uppercase tracking-wider">الشركة</th>
<th class="p-5 text-xs font-bold text-gray-500 uppercase tracking-wider">الأرقام الرسمية</th>
<th class="p-5 text-xs font-bold text-gray-500 uppercase tracking-wider">العنوان</th>
<th class="p-5 text-xs font-bold text-gray-500 uppercase tracking-wider">المكتب</th>
<th class="p-5 text-xs font-bold text-gray-500 uppercase tracking-wider">إجراءات</th>
</tr>
</thead>
<tbody class="divide-y divide-gray-800/30">
<tr x-show="companies.length === 0">
<td colspan="5" class="p-16 text-center">
<div class="flex flex-col items-center gap-4">
<span class="text-5xl opacity-30">🏢</span>
<p class="text-gray-500 text-lg">لا توجد شركات مسجلة بعد</p>
</div>
</td>
</tr>
<template x-for="c in companies" :key="c.id">
<tr class="table-row group">
<td class="p-5">
<p class="font-bold text-emerald-400" x-text="c.name"></p>
</td>
<td class="p-5">
<p class="text-xs text-gray-400 font-mono">TIN: <span class="text-gray-300" x-text="c.tax_identification_number"></span></p>
<p class="text-xs text-gray-400 font-mono mt-1">CRN: <span class="text-gray-300" x-text="c.commercial_registration_number"></span></p>
</td>
<td class="p-5 text-sm text-gray-500" x-text="c.address"></td>
<td class="p-5 text-xs text-gray-500" x-text="c.tenant_name || '-'"></td>
<td class="p-5 flex gap-2">
<button x-show="user?.role === 'super_admin' || user?.role === 'admin'" @click="confirmDeleteCompany(c)" class="text-gray-500 hover:text-red-400 p-2.5 rounded-xl hover:bg-red-500/10 transition-all hover:scale-105 active:scale-95" title="حذف">
🗑️
</button>
</td>
</tr>
</template>
</tbody>
</table>
</div>
</div>
<!-- Users Page -->
<div x-show="page === 'users'">
<div class="glass rounded-3xl overflow-hidden shadow-2xl border border-gray-800/50">
<table class="w-full text-right divide-y divide-gray-800/30">
<thead class="bg-gray-900/40">
<tr>
<th class="p-5 text-xs font-bold text-gray-500 uppercase tracking-wider">المستخدم</th>
<th class="p-5 text-xs font-bold text-gray-500 uppercase tracking-wider">المكتب</th>
<th class="p-5 text-xs font-bold text-gray-500 uppercase tracking-wider">الدور</th>
<th class="p-5 text-xs font-bold text-gray-500 uppercase tracking-wider">الحالة</th>
<th class="p-5 text-xs font-bold text-gray-500 uppercase tracking-wider">إجراءات</th>
</tr>
</thead>
<tbody class="divide-y divide-gray-800/30">
<tr x-show="users.length === 0">
<td colspan="5" class="p-16 text-center">
<div class="flex flex-col items-center gap-4">
<span class="text-5xl opacity-30">👥</span>
<p class="text-gray-500 text-lg">لا يوجد مستخدمون بعد</p>
</div>
</td>
</tr>
<template x-for="u in users" :key="u.id">
<tr class="table-row group">
<td class="p-5">
<p class="font-bold text-emerald-400" x-text="u.name"></p>
<p class="text-xs text-gray-600 font-mono" x-text="u.email"></p>
</td>
<td class="p-5 text-sm text-gray-400" x-text="u.tenant_name || '-'"></td>
<td class="p-5">
<span class="badge 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-5">
<span class="flex items-center gap-2">
<span class="w-2.5 h-2.5 rounded-full transition-all" :class="u.is_active?'bg-emerald-400 shadow-[0_0_10px_rgba(16,185,129,0.6)]':'bg-red-400 shadow-[0_0_10px_rgba(248,113,113,0.4)]'"></span>
<span class="text-xs text-gray-500" x-text="u.is_active ? 'نشط' : 'غير نشط'"></span>
</span>
</td>
<td class="p-5 flex gap-2">
<button x-show="u.id !== user.id" @click="confirmDeleteUser(u)" class="text-gray-500 hover:text-red-400 p-2.5 rounded-xl hover:bg-red-500/10 transition-all hover:scale-105 active:scale-95" title="حذف">
🗑️
</button>
</td>
</tr>
</template>
</tbody>
</table>
</div>
</div>
</div>
</div>
</main>
</div>
<!-- Full Invoice View Modal - Enhanced -->
<div x-show="showViewModal" x-cloak class="fixed inset-0 bg-black/95 backdrop-blur-xl flex items-center justify-center p-4 z-[100] modal-enter">
<div class="bg-surface border border-gray-800/50 w-full h-full max-w-7xl rounded-3xl shadow-2xl flex overflow-hidden glass-elevated">
<!-- Left: File Preview -->
<div class="w-1/2 bg-black/30 border-l border-gray-800/50 flex flex-col relative">
<div class="p-4 border-b border-gray-800/50 flex justify-between items-center bg-gray-950/40">
<span class="text-xs font-bold text-gray-500 uppercase tracking-widest flex items-center gap-2">
<span>📄</span> معاينة المستند الأصلي
</span>
<a :href="currentInvoice?.file_url + '&token=' + token()" target="_blank" class="text-[10px] bg-gray-800/50 hover:bg-gray-700/50 px-3 py-1.5 rounded-lg transition border border-gray-700 flex items-center gap-1">
📥 تحميل
</a>
</div>
<div class="flex-1 overflow-auto p-4 flex items-start justify-center scrollbar-hide">
<template x-if="currentInvoice?.original_file_path?.toLowerCase().endsWith('.pdf')">
<iframe :src="currentInvoice?.file_url + '&token=' + token()" class="w-full h-full rounded-2xl border border-gray-800/30 shadow-2xl" frameborder="0"></iframe>
</template>
<template x-if="!currentInvoice?.original_file_path?.toLowerCase().endsWith('.pdf')">
<img :src="currentInvoice?.file_url + '&token=' + token()" @error="$el.src='https://placehold.co/600x800/1e293b/94a3b8?text=Error+Loading+Image'" class="max-w-full rounded-2xl shadow-2xl border border-white/5">
</template>
</div>
</div>
<!-- Right: Extracted Data -->
<div class="w-1/2 flex flex-col">
<div class="p-6 border-b border-gray-800/50 flex justify-between items-center bg-gradient-to-r from-emerald-900/20 to-transparent">
<div>
<h3 class="text-xl font-bold flex items-center gap-2">
<span class="text-emerald-400"></span>
تفاصيل الفاتورة المستخرجة
</h3>
<p class="text-[10px] text-emerald-500/70 mt-1 uppercase tracking-tighter flex items-center gap-1">
<span class="w-1.5 h-1.5 rounded-full bg-emerald-400 animate-pulse"></span>
AI-Powered Extraction (Gemini 1.5 Flash)
</p>
</div>
<button @click="showViewModal = false" class="text-gray-500 hover:text-white text-2xl transition hover:scale-110 active:scale-95 p-2 rounded-xl hover:bg-gray-800/50"></button>
</div>
<div class="flex-1 overflow-y-auto p-6 space-y-6 scrollbar-hide">
<!-- Supplier & Invoice Info -->
<div class="grid grid-cols-2 gap-4">
<div class="p-5 bg-gray-950/40 border border-gray-800/50 rounded-2xl glass hover:border-emerald-500/30 transition-all">
<label class="block text-[10px] text-gray-500 font-bold uppercase mb-3 tracking-widest flex items-center gap-1">
<span>🏪</span> المورد
</label>
<p class="text-sm font-bold text-emerald-400" x-text="currentInvoice?.supplier_name"></p>
<p class="text-[10px] text-gray-600 mt-2 font-mono">TIN: <span x-text="currentInvoice?.supplier_tin"></span></p>
<p class="text-[10px] text-gray-600 mt-1" x-text="currentInvoice?.supplier_address"></p>
</div>
<div class="p-5 bg-gray-950/40 border border-gray-800/50 rounded-2xl glass hover:border-cyan-500/30 transition-all">
<label class="block text-[10px] text-gray-500 font-bold uppercase mb-3 tracking-widest flex items-center gap-1">
<span>🔖</span> رقم وتاريخ الفاتورة
</label>
<p class="text-sm font-bold font-mono" x-text="currentInvoice?.invoice_number"></p>
<p class="text-[10px] text-gray-400 mt-2 font-mono" x-text="currentInvoice?.invoice_date"></p>
<p class="text-[10px] text-gray-600 mt-2 uppercase tracking-wide bg-gray-900/30 px-2 py-1 rounded inline-block" x-text="currentInvoice?.invoice_type + ' / ' + currentInvoice?.invoice_category"></p>
</div>
</div>
<!-- Line Items Table -->
<div>
<label class="block text-[10px] text-gray-500 font-bold uppercase mb-4 tracking-widest flex items-center gap-1">
<span>📋</span> بنود الفاتورة التفصيلية
</label>
<div class="bg-gray-950/30 border border-gray-800/50 rounded-2xl overflow-hidden glass">
<table class="w-full text-right text-xs">
<thead class="bg-gray-900/40 border-b border-gray-800/50">
<tr>
<th class="p-3 text-gray-500 font-bold">#</th>
<th class="p-3 text-gray-500 font-bold">الوصف</th>
<th class="p-3 text-gray-500 font-bold">الكمية</th>
<th class="p-3 text-gray-500 font-bold">السعر</th>
<th class="p-3 text-gray-500 font-bold">الضريبة</th>
<th class="p-3 text-gray-500 font-bold text-emerald-400">الإجمالي</th>
</tr>
</thead>
<tbody class="divide-y divide-gray-800/30">
<template x-for="(item, index) in currentInvoice?.items" :key="item.id || index">
<tr class="hover:bg-white/[0.03] transition-colors">
<td class="p-3 text-gray-600 font-mono" x-text="item.line_number"></td>
<td class="p-3 font-medium text-gray-200" x-text="item.description"></td>
<td class="p-3 font-mono" x-text="parseFloat(item.quantity).toLocaleString()"></td>
<td class="p-3 font-mono" x-text="parseFloat(item.unit_price).toLocaleString()"></td>
<td class="p-3 text-gray-500 font-mono" x-text="(item.tax_rate * 100).toFixed(1) + '%'"></td>
<td class="p-3 font-bold text-emerald-400 font-mono" x-text="parseFloat(item.line_total).toLocaleString()"></td>
</tr>
</template>
</tbody>
</table>
</div>
</div>
<!-- Totals Section - Enhanced -->
<div class="flex justify-end">
<div class="w-72 space-y-3 p-5 bg-gradient-to-br from-emerald-500/5 to-cyan-500/5 border border-emerald-500/20 rounded-2xl glass">
<div class="flex justify-between text-xs">
<span class="text-gray-500 font-bold">المجموع الفرعي</span>
<span class="font-mono text-gray-300" x-text="parseFloat(currentInvoice?.subtotal || 0).toLocaleString()"></span>
</div>
<div class="flex justify-between text-xs">
<span class="text-gray-500 font-bold">الخصم الإجمالي</span>
<span class="font-mono text-red-400" x-text="'-' + parseFloat(currentInvoice?.discount_total || 0).toLocaleString()"></span>
</div>
<div class="flex justify-between text-xs">
<span class="text-gray-500 font-bold">ضريبة المبيعات</span>
<span class="font-mono text-gray-300" x-text="parseFloat(currentInvoice?.tax_amount || 0).toLocaleString()"></span>
</div>
<div class="border-t border-emerald-500/30 pt-4 flex justify-between items-center">
<span class="text-sm font-bold text-emerald-400">الإجمالي النهائي</span>
<span class="text-xl font-bold text-emerald-400 font-mono" x-text="parseFloat(currentInvoice?.grand_total || 0).toLocaleString() + ' JOD'"></span>
</div>
</div>
</div>
<!-- JoFotara Integration Badge -->
<template x-if="currentInvoice?.jofotara">
<div class="p-5 bg-gradient-to-r from-emerald-950/30 to-cyan-950/30 border border-emerald-500/30 rounded-2xl flex items-center gap-6 glass">
<div class="qr-container">
<img :src="currentInvoice.jofotara.qr_image_uri" class="w-28 h-28" alt="QR Code">
</div>
<div class="space-y-2 flex-1 min-w-0">
<h4 class="text-emerald-400 font-bold flex items-center gap-2">
<span></span> فاتورة معتمدة رسمياً
</h4>
<p class="text-xs text-gray-400">الرقم الموحد (UUID):</p>
<p class="text-[10px] font-mono bg-gray-900/50 px-3 py-2 rounded-lg select-all text-gray-300 break-all" x-text="currentInvoice.jofotara.uuid"></p>
<p class="text-xs text-gray-500 mt-2">تاريخ الرفع: <span class="text-gray-300" x-text="currentInvoice.jofotara.submitted_at"></span></p>
<div class="pt-3 flex gap-3">
<a :href="'/index.php?route=v1/invoices/download_xml&id=' + currentInvoice.id + '&token=' + token()"
class="inline-flex items-center gap-2 text-[10px] bg-emerald-600/20 hover:bg-emerald-600/30 text-emerald-300 px-4 py-2 rounded-xl transition-all border border-emerald-500/30 hover:shadow-lg hover:shadow-emerald-500/20">
⬇️ تحميل XML الرسمي
</a>
</div>
</div>
</div>
</template>
</div>
<!-- Action Buttons -->
<div class="p-6 bg-gray-950/40 border-t border-gray-800/50 flex gap-4">
<template x-if="currentInvoice?.status === 'extracted'">
<button @click="approveInvoice(currentInvoice.id)"
class="flex-1 bg-gradient-to-r from-emerald-600 to-emerald-500 hover:from-emerald-500 hover:to-cyan-500 py-4 rounded-2xl font-bold transition-all flex items-center justify-center gap-2 disabled:opacity-50 disabled:cursor-not-allowed btn-glow"
:disabled="isApproving">
<span x-show="!isApproving" class="flex items-center gap-2">
<span></span> اعتماد الفاتورة وتوليد QR
</span>
<span x-show="isApproving" class="flex items-center justify-center gap-2">
<span class="animate-spin"></span> جارِ الإرسال إلى جوفوترة...
</span>
</button>
</template>
<template x-if="currentInvoice?.status === 'approved' && !currentInvoice.jofotara">
<div class="flex-1 flex flex-col items-center justify-center bg-gray-900/50 rounded-2xl p-4 border border-emerald-500/20">
<span class="text-xs text-emerald-400 font-bold mb-1 flex items-center gap-1">
<span></span> تم الاعتماد محلياً
</span>
<span class="text-[10px] text-gray-500">في انتظار المزامنة مع جوفوترة</span>
</div>
</template>
<button @click="showViewModal = false" class="px-8 py-4 border border-gray-800/50 rounded-2xl hover:bg-gray-800/50 transition-all text-sm font-medium hover:scale-105 active:scale-95">إغلاق</button>
</div>
</div>
</div>
</div>
<!-- Upload Invoice Modal - Enhanced -->
<div x-show="showUploadModal" x-cloak class="fixed inset-0 bg-black/90 backdrop-blur-xl flex items-center justify-center p-4 z-50 modal-enter">
<div class="bg-surface border border-gray-800/50 w-full max-w-lg p-8 rounded-3xl shadow-2xl glass-elevated" @click.away="showUploadModal = false">
<div class="text-center mb-8">
<div class="w-16 h-16 mx-auto mb-4 rounded-2xl bg-gradient-to-br from-emerald-500/20 to-cyan-500/20 flex items-center justify-center border border-emerald-500/30">
<span class="text-3xl">📤</span>
</div>
<h3 class="text-2xl font-bold">رفع فواتير جديدة</h3>
<p class="text-gray-500 text-sm mt-2">سيقوم النظام باستخراج البيانات آلياً باستخدام الذكاء الاصطناعي المتقدم</p>
</div>
<form @submit.prevent="uploadInvoice" class="space-y-6">
<div>
<label class="block text-xs font-bold text-gray-500 uppercase mb-3 tracking-widest flex items-center gap-1">
<span>🏭</span> اختر الشركة
</label>
<select x-model="uploadData.company_id" class="w-full bg-gray-950/50 border border-gray-800/50 p-4 rounded-2xl outline-none focus:ring-2 focus:ring-emerald-500/30 focus:border-emerald-500/50 transition-all appearance-none cursor-pointer" 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 class="upload-zone border-2 border-dashed rounded-2xl p-10 text-center relative cursor-pointer group">
<input type="file" @change="handleFile" class="absolute inset-0 opacity-0 cursor-pointer" required accept=".pdf,.png,.jpg,.jpeg">
<div class="space-y-3 transition-transform group-hover:scale-105">
<span class="text-5xl block transition-transform group-hover:scale-110">📄</span>
<p class="text-sm font-bold text-gray-300" x-text="selectedFile ? selectedFile.name : 'اسحب الملف هنا أو اضغط للاختيار'"></p>
<p class="text-[10px] text-gray-600 uppercase tracking-wide bg-gray-900/30 px-3 py-1 rounded-full inline-block">PDF, PNG, JPG (Max 5MB)</p>
</div>
</div>
<div class="pt-4 flex gap-4">
<button type="submit" class="flex-1 bg-gradient-to-r from-emerald-600 to-emerald-500 hover:from-emerald-500 hover:to-cyan-500 py-4 rounded-2xl font-bold shadow-lg shadow-emerald-900/30 transition-all active:scale-95 disabled:opacity-50 disabled:cursor-not-allowed btn-glow flex items-center justify-center gap-2" :disabled="isUploading">
<span x-show="!isUploading" class="flex items-center gap-2">
<span>🚀</span> بدء المعالجة الذكية
</span>
<span x-show="isUploading" class="flex items-center justify-center gap-2">
<span class="animate-spin">⚙️</span> جارِ التحليل...
</span>
</button>
<button type="button" @click="showUploadModal = false" class="px-8 py-4 border border-gray-800/50 rounded-2xl hover:bg-gray-800/50 transition-all text-sm font-medium hover:scale-105 active:scale-95">إلغاء</button>
</div>
</form>
</div>
</div>
<!-- Add User Modal - Enhanced -->
<div x-show="showAddModal" x-cloak class="fixed inset-0 bg-black/80 backdrop-blur-xl flex items-center justify-center p-4 z-50 modal-enter">
<div class="bg-surface border border-gray-800/50 w-full max-w-md p-8 rounded-3xl shadow-2xl glass-elevated" @click.away="showAddModal = false">
<div class="text-center mb-8">
<div class="w-14 h-14 mx-auto mb-4 rounded-2xl bg-gradient-to-br from-purple-500/20 to-pink-500/20 flex items-center justify-center border border-purple-500/30">
<span class="text-2xl">👥</span>
</div>
<h3 class="text-xl font-bold">إضافة مستخدم جديد</h3>
</div>
<form @submit.prevent="createUser" class="space-y-4">
<div class="space-y-1">
<label class="text-xs text-gray-500 font-bold uppercase tracking-wide">الاسم الكامل</label>
<input type="text" x-model="newUser.name" placeholder="أدخل الاسم الكامل" class="w-full bg-gray-950/50 border border-gray-800/50 p-3.5 rounded-xl outline-none focus:ring-2 focus:ring-emerald-500/30 transition-all" required>
</div>
<div class="space-y-1">
<label class="text-xs text-gray-500 font-bold uppercase tracking-wide">البريد الإلكتروني</label>
<input type="email" x-model="newUser.email" placeholder="example@company.com" class="w-full bg-gray-950/50 border border-gray-800/50 p-3.5 rounded-xl outline-none focus:ring-2 focus:ring-emerald-500/30 transition-all" required>
</div>
<div class="space-y-1">
<label class="text-xs text-gray-500 font-bold uppercase tracking-wide">كلمة المرور</label>
<input type="password" x-model="newUser.password" placeholder="••••••••" class="w-full bg-gray-950/50 border border-gray-800/50 p-3.5 rounded-xl outline-none focus:ring-2 focus:ring-emerald-500/30 transition-all" required>
</div>
<div class="space-y-1">
<label class="text-xs text-gray-500 font-bold uppercase tracking-wide">الدور الوظيفي</label>
<select x-model="newUser.role" class="w-full bg-gray-950/50 border border-gray-800/50 p-3.5 rounded-xl outline-none focus:ring-2 focus:ring-emerald-500/30 transition-all appearance-none cursor-pointer">
<option value="employee">👤 موظف</option>
<option value="accountant">📊 محاسب</option>
<option value="admin">🔐 مدير مكتب</option>
</select>
</div>
<div class="pt-6 flex gap-3">
<button type="submit" class="flex-1 bg-gradient-to-r from-emerald-600 to-emerald-500 hover:from-emerald-500 hover:to-cyan-500 py-3.5 rounded-xl font-bold transition-all btn-glow">حفظ المستخدم</button>
<button type="button" @click="showAddModal = false" class="px-6 py-3.5 border border-gray-800/50 rounded-xl hover:bg-gray-800/50 transition-all">إلغاء</button>
</div>
</form>
</div>
</div>
<script>
document.addEventListener('alpine:init', () => {
Alpine.data('app', () => ({
user: JSON.parse(localStorage.getItem('user')),
page: 'dashboard',
users: [],
companies: [],
tenants: [],
invoices: [],
stats: { total: 0, pending: 0, approved: 0 },
showAddModal: false,
showAddCompanyModal: false,
showAddTenantModal: false,
showUploadModal: false,
showViewModal: false,
isUploading: false,
isApproving: false,
globalError: '',
newUser: { name: '', email: '', password: '', role: 'employee', tenant_id: '' },
uploadData: { company_id: '' },
selectedFile: null,
currentInvoice: null,
init() {
if (!this.user) { window.location.href = '/login.php'; return; }
this.loadAll();
// Confetti celebration on successful actions (optional enhancement)
if (sessionStorage.getItem('celebrate')) {
confetti({
particleCount: 100,
spread: 70,
origin: { y: 0.6 },
colors: ['#10b981', '#06b6d4', '#8b5cf6']
});
sessionStorage.removeItem('celebrate');
}
},
setPage(p) {
this.page = p;
this.loadAll();
},
title() { return { dashboard: 'نظرة عامة', users: 'إدارة المستخدمين', companies: 'الشركات', invoices: 'الفواتير', tenants: 'المكاتب المحاسبية' }[this.page] || ''; },
subtitle() { return { dashboard: 'تحليلات المنصة الذكية', users: 'إدارة أدوار الوصول والصلاحيات', companies: 'إدارة بيانات الشركات المسجلة', invoices: 'إدارة ومعالجة الفواتير المرفوعة', tenants: 'إدارة المكاتب المحاسبية الشريكة' }[this.page] || ''; },
showError(msg) {
this.globalError = msg;
setTimeout(() => this.globalError = '', 6000);
},
token() { return localStorage.getItem('access_token'); },
async apiRequest(route, method = 'GET', body = null) {
const options = {
method: 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);
// Session Expired Check
if (res.status === 401) {
const json = await res.json();
if (json.code === 'TOKEN_EXPIRED') {
localStorage.clear();
window.location.href = '/login.php';
return null;
}
}
const json = await res.json();
return json.success ? json.data : (this.showError(json.message), null);
},
async apiGet(route) { return this.apiRequest(route); },
async loadAll() {
this.loadStats();
this.loadCompanies();
if (this.page === 'users') this.loadUsers();
if (this.page === 'invoices') this.loadInvoices();
if (this.page === 'tenants') this.loadTenants();
},
async loadUsers() { this.users = await this.apiGet('v1/users') || []; },
async loadCompanies() { this.companies = await this.apiGet('v1/companies') || []; },
async loadInvoices() { this.invoices = await this.apiGet('v1/invoices') || []; },
async loadStats() { this.stats = await this.apiGet('v1/dashboard/stats') || {}; },
async loadTenants() { this.tenants = await this.apiGet('v1/tenants') || []; },
async viewInvoice(id) {
const data = await this.apiGet('v1/invoices/view&id=' + id);
if (data) {
this.currentInvoice = data;
this.showViewModal = true;
}
},
async approveInvoice(id) {
if (!confirm('هل أنت متأكد من اعتماد الفاتورة وإرسالها إلى جوفوترة؟')) return;
this.isApproving = true;
try {
const data = await this.apiRequest('v1/invoices/approve', 'POST', { id: id });
if (data) {
// Trigger celebration
confetti({
particleCount: 150,
spread: 100,
origin: { y: 0.7 },
colors: ['#10b981', '#06b6d4']
});
sessionStorage.setItem('celebrate', 'true');
alert('✅ تم الاعتماد بنجاح!');
this.viewInvoice(id);
this.loadInvoices();
}
} catch (e) {
this.showError('خطأ غير متوقع: ' + e.message);
} finally {
this.isApproving = false;
}
},
generateQRPng(base64Tlv) {
if (!base64Tlv) return '';
const qr = new QRious({
value: base64Tlv,
size: 200,
level: 'M'
});
return qr.toDataURL().split(',')[1];
},
handleFile(e) { this.selectedFile = e.target.files[0]; },
async uploadInvoice() {
if (!this.selectedFile) return alert('الرجاء اختيار ملف');
this.isUploading = true;
const formData = new FormData();
formData.append('company_id', this.uploadData.company_id);
formData.append('invoice', this.selectedFile);
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.loadInvoices();
this.viewInvoice(json.data.id);
} else {
this.showError(json.message);
}
},
logout() { localStorage.clear(); window.location.href = '/login.php'; }
}));
});
</script>
</body>
</html>