1028 lines
65 KiB
PHP
1028 lines
65 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>
|
||
<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> |