3255 lines
155 KiB
PHP
3255 lines
155 KiB
PHP
<!DOCTYPE html>
|
||
<html lang="ar" dir="rtl" data-theme="light">
|
||
|
||
<head>
|
||
<meta charset="UTF-8">
|
||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||
<title>مُصادَق | لوحة التحكم الذكية</title>
|
||
<link rel="icon" type="image/jpeg" href="assets/img/logo.jpg">
|
||
<link
|
||
href="https://fonts.googleapis.com/css2?family=Tajawal:wght@300;400;500;700;800;900&family=Inter:wght@400;500;600;700&display=swap"
|
||
rel="stylesheet">
|
||
<script src="https://cdn.tailwindcss.com"></script>
|
||
<script defer src="https://unpkg.com/alpinejs@3.x.x/dist/cdn.min.js"></script>
|
||
<script src="https://cdnjs.cloudflare.com/ajax/libs/qrious/4.0.2/qrious.min.js"></script>
|
||
<style>
|
||
/* ═══════════════════════════════════════════════
|
||
MUSADDAQ 2.0 — MODERN PROFESSIONAL DESIGN
|
||
═══════════════════════════════════════════════ */
|
||
:root {
|
||
--primary: #6366f1;
|
||
--primary-dark: #4f46e5;
|
||
--primary-light: #818cf8;
|
||
--primary-ghost: #eef2ff;
|
||
--accent: #f59e0b;
|
||
--accent-light: #fffbeb;
|
||
--success: #10b981;
|
||
--success-light: #d1fae5;
|
||
--danger: #ef4444;
|
||
--danger-light: #fee2e2;
|
||
--warning: #f59e0b;
|
||
--info: #3b82f6;
|
||
--info-light: #dbeafe;
|
||
|
||
--bg: #f8fafc;
|
||
--surface: #ffffff;
|
||
--surface-hover: #f8fafc;
|
||
--border: #e2e8f0;
|
||
--border-light: #f1f5f9;
|
||
|
||
--text-1: #0f172a;
|
||
--text-2: #475569;
|
||
--text-3: #94a3b8;
|
||
|
||
--shadow-sm: 0 1px 2px 0 rgb(0 0 0 / 0.05);
|
||
--shadow: 0 4px 6px -1px rgb(0 0 0 / 0.1), 0 2px 4px -2px rgb(0 0 0 / 0.1);
|
||
--shadow-md: 0 10px 15px -3px rgb(0 0 0 / 0.1), 0 4px 6px -4px rgb(0 0 0 / 0.1);
|
||
--shadow-lg: 0 20px 25px -5px rgb(0 0 0 / 0.1), 0 8px 10px -6px rgb(0 0 0 / 0.1);
|
||
--shadow-glow: 0 0 20px rgba(99, 102, 241, 0.15);
|
||
|
||
--radius-sm: 8px;
|
||
--radius: 12px;
|
||
--radius-lg: 16px;
|
||
--radius-xl: 20px;
|
||
--radius-2xl: 24px;
|
||
}
|
||
|
||
* {
|
||
box-sizing: border-box;
|
||
margin: 0;
|
||
padding: 0;
|
||
}
|
||
|
||
body {
|
||
font-family: 'Tajawal', 'Inter', -apple-system, BlinkMacSystemFont, sans-serif;
|
||
background: var(--bg);
|
||
color: var(--text-1);
|
||
-webkit-font-smoothing: antialiased;
|
||
font-size: 15px;
|
||
line-height: 1.6;
|
||
}
|
||
|
||
h1,
|
||
h2,
|
||
h3,
|
||
h4,
|
||
h5,
|
||
h6 {
|
||
font-family: 'Tajawal', sans-serif;
|
||
font-weight: 800;
|
||
letter-spacing: -0.02em;
|
||
}
|
||
|
||
[x-cloak] {
|
||
display: none !important;
|
||
}
|
||
|
||
/* ── SCROLLBAR ─────────────────────────────── */
|
||
::-webkit-scrollbar {
|
||
width: 6px;
|
||
height: 6px;
|
||
}
|
||
|
||
::-webkit-scrollbar-track {
|
||
background: transparent;
|
||
}
|
||
|
||
::-webkit-scrollbar-thumb {
|
||
background: #cbd5e1;
|
||
border-radius: 10px;
|
||
}
|
||
|
||
::-webkit-scrollbar-thumb:hover {
|
||
background: #94a3b8;
|
||
}
|
||
|
||
/* ── SIDEBAR ───────────────────────────────── */
|
||
.sidebar {
|
||
width: 270px;
|
||
min-width: 270px;
|
||
background: linear-gradient(180deg, #1e1b4b 0%, #312e81 50%, #1e1b4b 100%);
|
||
display: flex;
|
||
flex-direction: column;
|
||
height: 100vh;
|
||
position: sticky;
|
||
top: 0;
|
||
z-index: 50;
|
||
border-left: 1px solid rgba(255, 255, 255, 0.06);
|
||
}
|
||
|
||
.sb-logo-area {
|
||
padding: 32px 24px 24px;
|
||
}
|
||
|
||
.sb-logo-mark {
|
||
width: 44px;
|
||
height: 44px;
|
||
background: linear-gradient(135deg, var(--accent) 0%, #fbbf24 100%);
|
||
border-radius: 14px;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
font-size: 20px;
|
||
color: #1e1b4b;
|
||
flex-shrink: 0;
|
||
box-shadow: 0 8px 16px rgba(245, 158, 11, 0.3);
|
||
}
|
||
|
||
.sb-brand-name {
|
||
font-family: 'Tajawal', sans-serif;
|
||
font-size: 24px;
|
||
font-weight: 900;
|
||
color: white;
|
||
letter-spacing: 0;
|
||
}
|
||
|
||
.sb-brand-sub {
|
||
font-size: 11px;
|
||
color: rgba(255, 255, 255, 0.4);
|
||
font-weight: 500;
|
||
margin-top: 4px;
|
||
}
|
||
|
||
.sb-nav {
|
||
flex: 1;
|
||
padding: 16px 14px;
|
||
display: flex;
|
||
flex-direction: column;
|
||
gap: 4px;
|
||
overflow-y: auto;
|
||
}
|
||
|
||
.sb-section-label {
|
||
font-size: 10px;
|
||
font-weight: 800;
|
||
color: rgba(255, 255, 255, 0.25);
|
||
letter-spacing: 0.1em;
|
||
text-transform: uppercase;
|
||
padding: 16px 12px 8px;
|
||
margin-top: 4px;
|
||
}
|
||
|
||
.nav-btn {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 12px;
|
||
padding: 11px 14px;
|
||
border-radius: 14px;
|
||
cursor: pointer;
|
||
border: none;
|
||
background: transparent;
|
||
color: rgba(255, 255, 255, 0.55);
|
||
font-family: 'Tajawal', sans-serif;
|
||
font-size: 15px;
|
||
font-weight: 600;
|
||
width: 100%;
|
||
text-align: right;
|
||
transition: all 0.2s cubic-bezier(0.4, 0, 0.2, 1);
|
||
position: relative;
|
||
}
|
||
|
||
.nav-btn:hover {
|
||
background: rgba(255, 255, 255, 0.08);
|
||
color: white;
|
||
transform: translateX(-2px);
|
||
}
|
||
|
||
.nav-btn.active {
|
||
background: rgba(99, 102, 241, 0.25);
|
||
color: white;
|
||
font-weight: 700;
|
||
box-shadow: 0 4px 15px rgba(99, 102, 241, 0.2);
|
||
}
|
||
|
||
.nav-btn.active::before {
|
||
content: '';
|
||
position: absolute;
|
||
right: 0;
|
||
top: 25%;
|
||
bottom: 25%;
|
||
width: 3px;
|
||
background: var(--accent);
|
||
border-radius: 3px 0 0 3px;
|
||
}
|
||
|
||
.nav-icon {
|
||
width: 36px;
|
||
height: 36px;
|
||
border-radius: 10px;
|
||
background: rgba(255, 255, 255, 0.06);
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
font-size: 16px;
|
||
flex-shrink: 0;
|
||
transition: all 0.2s;
|
||
}
|
||
|
||
.nav-btn.active .nav-icon {
|
||
background: var(--primary);
|
||
box-shadow: 0 4px 12px rgba(99, 102, 241, 0.4);
|
||
}
|
||
|
||
.nav-btn:hover .nav-icon {
|
||
background: rgba(255, 255, 255, 0.12);
|
||
}
|
||
|
||
.sb-user-area {
|
||
padding: 16px;
|
||
border-top: 1px solid rgba(255, 255, 255, 0.06);
|
||
margin-top: auto;
|
||
}
|
||
|
||
.sb-user-card {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 12px;
|
||
padding: 12px;
|
||
border-radius: 16px;
|
||
background: rgba(255, 255, 255, 0.05);
|
||
margin-bottom: 10px;
|
||
border: 1px solid rgba(255, 255, 255, 0.05);
|
||
transition: all 0.2s;
|
||
}
|
||
|
||
.sb-user-card:hover {
|
||
background: rgba(255, 255, 255, 0.08);
|
||
}
|
||
|
||
.user-avatar-sb {
|
||
width: 38px;
|
||
height: 38px;
|
||
border-radius: 10px;
|
||
background: linear-gradient(135deg, var(--accent), #fbbf24);
|
||
color: #1e1b4b;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
font-weight: 800;
|
||
font-size: 16px;
|
||
flex-shrink: 0;
|
||
}
|
||
|
||
.role-pill {
|
||
display: inline-block;
|
||
padding: 2px 10px;
|
||
border-radius: 20px;
|
||
font-size: 10px;
|
||
font-weight: 700;
|
||
background: rgba(255, 255, 255, 0.1);
|
||
color: rgba(255, 255, 255, 0.5);
|
||
text-transform: uppercase;
|
||
letter-spacing: 0.04em;
|
||
}
|
||
|
||
.logout-btn {
|
||
width: 100%;
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 8px;
|
||
padding: 10px 12px;
|
||
border-radius: 12px;
|
||
background: transparent;
|
||
border: none;
|
||
color: rgba(255, 255, 255, 0.35);
|
||
cursor: pointer;
|
||
font-family: 'Tajawal', sans-serif;
|
||
font-size: 14px;
|
||
font-weight: 600;
|
||
transition: all 0.2s;
|
||
text-align: right;
|
||
}
|
||
|
||
.logout-btn:hover {
|
||
background: rgba(239, 68, 68, 0.15);
|
||
color: #fca5a5;
|
||
}
|
||
|
||
/* ── MAIN LAYOUT ───────────────────────────── */
|
||
.main-area {
|
||
flex: 1;
|
||
overflow-y: auto;
|
||
display: flex;
|
||
flex-direction: column;
|
||
min-width: 0;
|
||
background: var(--bg);
|
||
}
|
||
|
||
.page-header {
|
||
padding: 24px 32px;
|
||
background: rgba(255, 255, 255, 0.8);
|
||
backdrop-filter: blur(12px);
|
||
border-bottom: 1px solid var(--border-light);
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: space-between;
|
||
gap: 16px;
|
||
position: sticky;
|
||
top: 0;
|
||
z-index: 40;
|
||
}
|
||
|
||
.page-title {
|
||
font-size: 26px;
|
||
font-weight: 900;
|
||
color: var(--text-1);
|
||
line-height: 1.2;
|
||
}
|
||
|
||
.page-subtitle {
|
||
font-size: 14px;
|
||
color: var(--text-3);
|
||
margin-top: 4px;
|
||
font-weight: 500;
|
||
}
|
||
|
||
.page-content {
|
||
padding: 32px;
|
||
flex: 1;
|
||
}
|
||
|
||
/* ── STAT CARDS ────────────────────────────── */
|
||
.stats-grid {
|
||
display: grid;
|
||
grid-template-columns: repeat(4, 1fr);
|
||
gap: 20px;
|
||
margin-bottom: 32px;
|
||
}
|
||
|
||
.stat-card {
|
||
background: var(--surface);
|
||
border-radius: var(--radius-xl);
|
||
padding: 24px;
|
||
border: 1px solid var(--border-light);
|
||
box-shadow: var(--shadow-sm);
|
||
position: relative;
|
||
overflow: hidden;
|
||
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
|
||
}
|
||
|
||
.stat-card:hover {
|
||
transform: translateY(-4px);
|
||
box-shadow: var(--shadow-md);
|
||
border-color: var(--border);
|
||
}
|
||
|
||
.stat-card::after {
|
||
content: '';
|
||
position: absolute;
|
||
top: 0;
|
||
right: 0;
|
||
left: 0;
|
||
height: 4px;
|
||
border-radius: 4px 4px 0 0;
|
||
}
|
||
|
||
.stat-primary::after {
|
||
background: linear-gradient(90deg, var(--primary), var(--primary-light));
|
||
}
|
||
|
||
.stat-accent::after {
|
||
background: linear-gradient(90deg, var(--accent), #fbbf24);
|
||
}
|
||
|
||
.stat-success::after {
|
||
background: linear-gradient(90deg, var(--success), #34d399);
|
||
}
|
||
|
||
.stat-danger::after {
|
||
background: linear-gradient(90deg, var(--danger), #f87171);
|
||
}
|
||
|
||
.stat-header {
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: space-between;
|
||
margin-bottom: 16px;
|
||
}
|
||
|
||
.stat-icon-box {
|
||
width: 48px;
|
||
height: 48px;
|
||
border-radius: 14px;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
font-size: 24px;
|
||
}
|
||
|
||
.stat-primary .stat-icon-box {
|
||
background: var(--primary-ghost);
|
||
}
|
||
|
||
.stat-accent .stat-icon-box {
|
||
background: var(--accent-light);
|
||
}
|
||
|
||
.stat-success .stat-icon-box {
|
||
background: var(--success-light);
|
||
}
|
||
|
||
.stat-danger .stat-icon-box {
|
||
background: var(--danger-light);
|
||
}
|
||
|
||
.stat-label {
|
||
font-size: 13px;
|
||
font-weight: 700;
|
||
color: var(--text-3);
|
||
text-transform: uppercase;
|
||
letter-spacing: 0.04em;
|
||
}
|
||
|
||
.stat-value {
|
||
font-size: 36px;
|
||
font-weight: 900;
|
||
margin-top: 4px;
|
||
line-height: 1;
|
||
font-family: 'Tajawal', sans-serif;
|
||
color: var(--text-1);
|
||
}
|
||
|
||
.stat-primary .stat-value {
|
||
color: var(--primary-dark);
|
||
}
|
||
|
||
.stat-accent .stat-value {
|
||
color: #b45309;
|
||
}
|
||
|
||
.stat-success .stat-value {
|
||
color: #047857;
|
||
}
|
||
|
||
.stat-danger .stat-value {
|
||
color: #b91c1c;
|
||
}
|
||
|
||
/* ── QUICK ACTIONS ─────────────────────────── */
|
||
.quick-actions-grid {
|
||
display: grid;
|
||
grid-template-columns: repeat(auto-fill, minmax(220px, 1fr));
|
||
gap: 16px;
|
||
}
|
||
|
||
.quick-action-card {
|
||
background: var(--surface);
|
||
border: 1px solid var(--border-light);
|
||
border-radius: var(--radius-lg);
|
||
padding: 20px;
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 16px;
|
||
cursor: pointer;
|
||
transition: all 0.25s cubic-bezier(0.4, 0, 0.2, 1);
|
||
text-align: right;
|
||
box-shadow: var(--shadow-sm);
|
||
}
|
||
|
||
.quick-action-card:hover {
|
||
border-color: var(--primary-light);
|
||
box-shadow: var(--shadow-md), var(--shadow-glow);
|
||
transform: translateY(-2px) scale(1.01);
|
||
}
|
||
|
||
.qa-icon {
|
||
width: 48px;
|
||
height: 48px;
|
||
border-radius: 14px;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
font-size: 22px;
|
||
flex-shrink: 0;
|
||
transition: transform 0.2s;
|
||
}
|
||
|
||
.quick-action-card:hover .qa-icon {
|
||
transform: scale(1.1) rotate(-5deg);
|
||
}
|
||
|
||
/* ── DATA TABLE ────────────────────────────── */
|
||
.table-container {
|
||
background: var(--surface);
|
||
border-radius: var(--radius-xl);
|
||
border: 1px solid var(--border-light);
|
||
box-shadow: var(--shadow-sm);
|
||
overflow: hidden;
|
||
}
|
||
|
||
.table-top-bar {
|
||
padding: 20px 24px;
|
||
border-bottom: 1px solid var(--border-light);
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 12px;
|
||
flex-wrap: wrap;
|
||
}
|
||
|
||
.table-top-bar h3 {
|
||
font-size: 18px;
|
||
font-weight: 800;
|
||
color: var(--text-1);
|
||
}
|
||
|
||
.table-count {
|
||
padding: 3px 12px;
|
||
background: var(--bg);
|
||
border: 1px solid var(--border);
|
||
border-radius: 20px;
|
||
font-size: 12px;
|
||
font-weight: 700;
|
||
color: var(--text-3);
|
||
}
|
||
|
||
.data-table {
|
||
width: 100%;
|
||
border-collapse: separate;
|
||
border-spacing: 0;
|
||
}
|
||
|
||
.data-table th {
|
||
padding: 14px 20px;
|
||
text-align: right;
|
||
font-size: 11px;
|
||
font-weight: 800;
|
||
color: var(--text-3);
|
||
text-transform: uppercase;
|
||
letter-spacing: 0.06em;
|
||
border-bottom: 1px solid var(--border-light);
|
||
background: #f8fafc;
|
||
white-space: nowrap;
|
||
}
|
||
|
||
.data-table td {
|
||
padding: 16px 20px;
|
||
border-bottom: 1px solid var(--border-light);
|
||
vertical-align: middle;
|
||
transition: background 0.15s;
|
||
}
|
||
|
||
.data-table tbody tr:last-child td {
|
||
border-bottom: none;
|
||
}
|
||
|
||
.data-table tbody tr {
|
||
transition: all 0.15s;
|
||
}
|
||
|
||
.data-table tbody tr:hover td {
|
||
background: #f8fafc;
|
||
}
|
||
|
||
.empty-row {
|
||
text-align: center;
|
||
padding: 64px 24px;
|
||
}
|
||
|
||
.empty-icon {
|
||
font-size: 56px;
|
||
opacity: 0.2;
|
||
display: block;
|
||
margin-bottom: 16px;
|
||
filter: grayscale(100%);
|
||
}
|
||
|
||
.empty-msg {
|
||
font-size: 16px;
|
||
color: var(--text-3);
|
||
font-weight: 500;
|
||
}
|
||
|
||
/* ── BADGES ────────────────────────────────── */
|
||
.badge {
|
||
display: inline-flex;
|
||
align-items: center;
|
||
gap: 6px;
|
||
padding: 5px 12px;
|
||
border-radius: 20px;
|
||
font-size: 12px;
|
||
font-weight: 700;
|
||
white-space: nowrap;
|
||
border: 1px solid transparent;
|
||
}
|
||
|
||
.badge-success {
|
||
background: var(--success-light);
|
||
color: #047857;
|
||
border-color: rgba(16, 185, 129, 0.2);
|
||
}
|
||
|
||
.badge-warning {
|
||
background: var(--accent-light);
|
||
color: #92400e;
|
||
border-color: rgba(245, 158, 11, 0.2);
|
||
}
|
||
|
||
.badge-info {
|
||
background: var(--info-light);
|
||
color: #1d4ed8;
|
||
border-color: rgba(59, 130, 246, 0.2);
|
||
}
|
||
|
||
.badge-gray {
|
||
background: #f1f5f9;
|
||
color: #64748b;
|
||
border-color: #e2e8f0;
|
||
}
|
||
|
||
.badge-primary {
|
||
background: var(--primary-ghost);
|
||
color: var(--primary-dark);
|
||
border-color: rgba(99, 102, 241, 0.2);
|
||
}
|
||
|
||
.badge-danger {
|
||
background: var(--danger-light);
|
||
color: #991b1b;
|
||
border-color: rgba(239, 68, 68, 0.2);
|
||
}
|
||
|
||
.badge-purple {
|
||
background: #f3e8ff;
|
||
color: #7e22ce;
|
||
border-color: rgba(168, 85, 247, 0.2);
|
||
}
|
||
|
||
.badge-dot {
|
||
width: 7px;
|
||
height: 7px;
|
||
border-radius: 50%;
|
||
display: inline-block;
|
||
flex-shrink: 0;
|
||
}
|
||
|
||
/* ── BUTTONS ───────────────────────────────── */
|
||
.btn {
|
||
display: inline-flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
gap: 8px;
|
||
padding: 10px 20px;
|
||
border-radius: 12px;
|
||
font-family: 'Tajawal', sans-serif;
|
||
font-size: 14px;
|
||
font-weight: 700;
|
||
cursor: pointer;
|
||
border: none;
|
||
transition: all 0.2s cubic-bezier(0.4, 0, 0.2, 1);
|
||
white-space: nowrap;
|
||
line-height: 1.5;
|
||
}
|
||
|
||
.btn:disabled {
|
||
opacity: 0.5;
|
||
cursor: not-allowed;
|
||
transform: none !important;
|
||
}
|
||
|
||
.btn-primary {
|
||
background: linear-gradient(135deg, var(--primary) 0%, var(--primary-dark) 100%);
|
||
color: white;
|
||
box-shadow: 0 4px 12px rgba(99, 102, 241, 0.25), 0 1px 3px rgba(99, 102, 241, 0.1);
|
||
}
|
||
|
||
.btn-primary:hover:not(:disabled) {
|
||
transform: translateY(-2px);
|
||
box-shadow: 0 8px 20px rgba(99, 102, 241, 0.35), 0 2px 4px rgba(99, 102, 241, 0.1);
|
||
}
|
||
|
||
.btn-success {
|
||
background: linear-gradient(135deg, var(--success) 0%, #059669 100%);
|
||
color: white;
|
||
box-shadow: 0 4px 12px rgba(16, 185, 129, 0.25);
|
||
}
|
||
|
||
.btn-success:hover:not(:disabled) {
|
||
transform: translateY(-2px);
|
||
box-shadow: 0 8px 20px rgba(16, 185, 129, 0.35);
|
||
}
|
||
|
||
.btn-ghost {
|
||
background: var(--surface);
|
||
color: var(--text-2);
|
||
border: 1.5px solid var(--border);
|
||
font-weight: 600;
|
||
}
|
||
|
||
.btn-ghost:hover {
|
||
background: var(--bg);
|
||
border-color: var(--text-3);
|
||
color: var(--text-1);
|
||
}
|
||
|
||
.btn-danger-ghost {
|
||
background: transparent;
|
||
color: var(--danger);
|
||
border: none;
|
||
padding: 8px 12px;
|
||
border-radius: 10px;
|
||
font-weight: 600;
|
||
}
|
||
|
||
.btn-danger-ghost:hover {
|
||
background: var(--danger-light);
|
||
}
|
||
|
||
.btn-sm {
|
||
padding: 7px 14px;
|
||
font-size: 13px;
|
||
}
|
||
|
||
.btn-icon {
|
||
padding: 8px;
|
||
width: 36px;
|
||
height: 36px;
|
||
}
|
||
|
||
.btn-table-action {
|
||
display: inline-flex;
|
||
align-items: center;
|
||
gap: 6px;
|
||
padding: 7px 14px;
|
||
border-radius: 10px;
|
||
font-family: inherit;
|
||
font-size: 12px;
|
||
font-weight: 700;
|
||
cursor: pointer;
|
||
border: none;
|
||
transition: all 0.2s;
|
||
}
|
||
|
||
.btn-ta-primary {
|
||
background: var(--primary-ghost);
|
||
color: var(--primary-dark);
|
||
}
|
||
|
||
.btn-ta-primary:hover {
|
||
background: var(--primary);
|
||
color: white;
|
||
box-shadow: 0 4px 12px rgba(99, 102, 241, 0.25);
|
||
}
|
||
|
||
.btn-ta-success {
|
||
background: var(--success-light);
|
||
color: #047857;
|
||
}
|
||
|
||
.btn-ta-success:hover {
|
||
background: var(--success);
|
||
color: white;
|
||
}
|
||
|
||
.btn-ta-danger {
|
||
background: transparent;
|
||
color: #94a3b8;
|
||
border-radius: 10px;
|
||
padding: 7px 10px;
|
||
}
|
||
|
||
.btn-ta-danger:hover {
|
||
background: var(--danger-light);
|
||
color: var(--danger);
|
||
}
|
||
|
||
/* ── FORM ELEMENTS ─────────────────────────── */
|
||
.form-group {
|
||
display: flex;
|
||
flex-direction: column;
|
||
gap: 8px;
|
||
}
|
||
|
||
.form-label {
|
||
font-size: 13px;
|
||
font-weight: 700;
|
||
color: var(--text-2);
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 6px;
|
||
}
|
||
|
||
.form-label-sub {
|
||
font-size: 11px;
|
||
color: var(--text-3);
|
||
font-weight: 500;
|
||
}
|
||
|
||
.form-input {
|
||
width: 100%;
|
||
padding: 12px 16px;
|
||
border: 1.5px solid var(--border);
|
||
border-radius: 12px;
|
||
font-family: 'Tajawal', sans-serif;
|
||
font-size: 15px;
|
||
color: var(--text-1);
|
||
background: var(--surface);
|
||
outline: none;
|
||
transition: all 0.2s;
|
||
direction: rtl;
|
||
}
|
||
|
||
.form-input:focus {
|
||
border-color: var(--primary);
|
||
box-shadow: 0 0 0 4px rgba(99, 102, 241, 0.1);
|
||
}
|
||
|
||
.form-input::placeholder {
|
||
color: var(--text-3);
|
||
font-weight: 400;
|
||
}
|
||
|
||
.form-input.mono {
|
||
font-family: 'Inter', monospace;
|
||
font-weight: 500;
|
||
}
|
||
|
||
.form-section-title {
|
||
font-size: 11px;
|
||
font-weight: 800;
|
||
color: var(--text-3);
|
||
text-transform: uppercase;
|
||
letter-spacing: 0.08em;
|
||
padding-bottom: 12px;
|
||
border-bottom: 1px solid var(--border-light);
|
||
margin-bottom: 8px;
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 8px;
|
||
}
|
||
|
||
/* ── MODALS ────────────────────────────────── */
|
||
.modal-backdrop {
|
||
position: fixed;
|
||
inset: 0;
|
||
background: rgba(15, 23, 42, 0.5);
|
||
backdrop-filter: blur(8px);
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
padding: 24px;
|
||
z-index: 120;
|
||
animation: fadeIn 0.2s ease;
|
||
}
|
||
|
||
@keyframes fadeIn {
|
||
from {
|
||
opacity: 0;
|
||
}
|
||
|
||
to {
|
||
opacity: 1;
|
||
}
|
||
}
|
||
|
||
.modal-box {
|
||
background: var(--surface);
|
||
border-radius: var(--radius-2xl);
|
||
box-shadow: var(--shadow-lg);
|
||
width: 100%;
|
||
max-width: 520px;
|
||
max-height: 92vh;
|
||
overflow-y: auto;
|
||
animation: slideUp 0.3s cubic-bezier(0.4, 0, 0.2, 1);
|
||
border: 1px solid var(--border-light);
|
||
}
|
||
|
||
.modal-box-lg {
|
||
max-width: 680px;
|
||
}
|
||
|
||
@keyframes slideUp {
|
||
from {
|
||
opacity: 0;
|
||
transform: translateY(20px) scale(0.98);
|
||
}
|
||
|
||
to {
|
||
opacity: 1;
|
||
transform: translateY(0) scale(1);
|
||
}
|
||
}
|
||
|
||
.modal-head {
|
||
padding: 24px 28px 0;
|
||
display: flex;
|
||
align-items: flex-start;
|
||
gap: 14px;
|
||
}
|
||
|
||
.modal-head-icon {
|
||
width: 48px;
|
||
height: 48px;
|
||
border-radius: 14px;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
font-size: 22px;
|
||
flex-shrink: 0;
|
||
}
|
||
|
||
.modal-head-icon.primary {
|
||
background: var(--primary-ghost);
|
||
}
|
||
|
||
.modal-head-icon.accent {
|
||
background: var(--accent-light);
|
||
}
|
||
|
||
.modal-head-icon.success {
|
||
background: var(--success-light);
|
||
}
|
||
|
||
.modal-title {
|
||
font-size: 20px;
|
||
font-weight: 800;
|
||
color: var(--text-1);
|
||
margin-bottom: 4px;
|
||
}
|
||
|
||
.modal-subtitle {
|
||
font-size: 14px;
|
||
color: var(--text-3);
|
||
font-weight: 500;
|
||
}
|
||
|
||
.modal-body {
|
||
padding: 24px 28px;
|
||
}
|
||
|
||
.modal-footer {
|
||
padding: 20px 28px 24px;
|
||
display: flex;
|
||
gap: 10px;
|
||
}
|
||
|
||
.modal-divider {
|
||
height: 1px;
|
||
background: var(--border-light);
|
||
margin: 0 28px;
|
||
}
|
||
|
||
.modal-close-btn {
|
||
margin-right: auto;
|
||
background: var(--bg);
|
||
border: none;
|
||
color: var(--text-3);
|
||
font-size: 18px;
|
||
cursor: pointer;
|
||
padding: 6px;
|
||
width: 32px;
|
||
height: 32px;
|
||
border-radius: 10px;
|
||
line-height: 1;
|
||
transition: all 0.2s;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
}
|
||
|
||
.modal-close-btn:hover {
|
||
background: var(--danger-light);
|
||
color: var(--danger);
|
||
}
|
||
|
||
/* ── TOAST ─────────────────────────────────── */
|
||
.toast-error {
|
||
position: fixed;
|
||
top: 24px;
|
||
left: 50%;
|
||
transform: translateX(-50%);
|
||
z-index: 300;
|
||
background: white;
|
||
border-right: 4px solid var(--danger);
|
||
border-radius: 16px;
|
||
padding: 16px 28px;
|
||
box-shadow: var(--shadow-lg);
|
||
color: var(--danger);
|
||
font-weight: 700;
|
||
font-size: 14px;
|
||
max-width: 480px;
|
||
width: 90%;
|
||
text-align: center;
|
||
cursor: pointer;
|
||
animation: slideDown 0.3s ease;
|
||
}
|
||
|
||
@keyframes slideDown {
|
||
from {
|
||
opacity: 0;
|
||
transform: translate(-50%, -20px);
|
||
}
|
||
|
||
to {
|
||
opacity: 1;
|
||
transform: translate(-50%, 0);
|
||
}
|
||
}
|
||
|
||
/* ── COMPANY / JOFOTARA STATUS ─────────────── */
|
||
.jofotara-connected {
|
||
display: inline-flex;
|
||
align-items: center;
|
||
gap: 8px;
|
||
cursor: pointer;
|
||
font-size: 13px;
|
||
font-weight: 700;
|
||
color: var(--success);
|
||
padding: 6px 12px;
|
||
background: var(--success-light);
|
||
border-radius: 20px;
|
||
border: 1px solid rgba(16, 185, 129, 0.2);
|
||
transition: all 0.2s;
|
||
}
|
||
|
||
.jofotara-connected:hover {
|
||
background: #a7f3d0;
|
||
transform: scale(1.05);
|
||
}
|
||
|
||
.jofotara-connect {
|
||
display: inline-flex;
|
||
align-items: center;
|
||
gap: 8px;
|
||
cursor: pointer;
|
||
font-size: 13px;
|
||
font-weight: 700;
|
||
color: var(--text-3);
|
||
padding: 6px 12px;
|
||
background: var(--bg);
|
||
border-radius: 20px;
|
||
border: 1px solid var(--border);
|
||
transition: all 0.2s;
|
||
}
|
||
|
||
.jofotara-connect:hover {
|
||
color: var(--primary);
|
||
border-color: var(--primary-light);
|
||
background: var(--primary-ghost);
|
||
}
|
||
|
||
.conn-dot {
|
||
width: 8px;
|
||
height: 8px;
|
||
border-radius: 50%;
|
||
flex-shrink: 0;
|
||
}
|
||
|
||
.conn-dot.on {
|
||
background: var(--success);
|
||
box-shadow: 0 0 0 3px rgba(16, 185, 129, 0.2);
|
||
}
|
||
|
||
.conn-dot.off {
|
||
background: #cbd5e1;
|
||
}
|
||
|
||
/* ── INVOICE VIEW PANEL ────────────────────── */
|
||
.invoice-field-card {
|
||
background: var(--bg);
|
||
border: 1px solid var(--border-light);
|
||
border-radius: 14px;
|
||
padding: 16px;
|
||
transition: all 0.2s;
|
||
}
|
||
|
||
.invoice-field-card:hover {
|
||
border-color: var(--border);
|
||
box-shadow: var(--shadow-sm);
|
||
}
|
||
|
||
.invoice-field-label {
|
||
font-size: 11px;
|
||
font-weight: 800;
|
||
color: var(--text-3);
|
||
text-transform: uppercase;
|
||
letter-spacing: 0.06em;
|
||
margin-bottom: 6px;
|
||
}
|
||
|
||
.invoice-field-value {
|
||
font-size: 16px;
|
||
font-weight: 700;
|
||
color: var(--text-1);
|
||
}
|
||
|
||
/* ── STATS MODAL TABLE ─────────────────────── */
|
||
.stats-table {
|
||
width: 100%;
|
||
border-collapse: separate;
|
||
border-spacing: 0;
|
||
font-size: 14px;
|
||
}
|
||
|
||
.stats-table th {
|
||
padding: 12px 16px;
|
||
text-align: right;
|
||
font-weight: 800;
|
||
color: var(--text-3);
|
||
font-size: 11px;
|
||
text-transform: uppercase;
|
||
background: #f8fafc;
|
||
border-bottom: 1px solid var(--border-light);
|
||
letter-spacing: 0.05em;
|
||
}
|
||
|
||
.stats-table td {
|
||
padding: 14px 16px;
|
||
border-bottom: 1px solid var(--border-light);
|
||
}
|
||
|
||
.stats-table tbody tr:last-child td {
|
||
border-bottom: none;
|
||
}
|
||
|
||
.stats-table tbody tr:hover {
|
||
background: #f8fafc;
|
||
}
|
||
|
||
.mini-stat {
|
||
background: var(--surface);
|
||
border: 1px solid var(--border-light);
|
||
border-radius: var(--radius-lg);
|
||
padding: 20px;
|
||
transition: all 0.2s;
|
||
}
|
||
|
||
.mini-stat:hover {
|
||
box-shadow: var(--shadow);
|
||
border-color: var(--border);
|
||
}
|
||
|
||
.mini-stat-label {
|
||
font-size: 11px;
|
||
font-weight: 800;
|
||
color: var(--text-3);
|
||
text-transform: uppercase;
|
||
letter-spacing: 0.05em;
|
||
margin-bottom: 10px;
|
||
}
|
||
|
||
.mini-stat-val {
|
||
font-size: 28px;
|
||
font-weight: 900;
|
||
font-family: 'Tajawal', sans-serif;
|
||
color: var(--text-1);
|
||
}
|
||
|
||
/* ── SUBSCRIPTION PAGE ─────────────────────── */
|
||
.plan-card {
|
||
background: var(--surface);
|
||
border: 2px solid var(--border-light);
|
||
border-radius: var(--radius-2xl);
|
||
padding: 40px 32px;
|
||
display: flex;
|
||
flex-direction: column;
|
||
gap: 24px;
|
||
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
|
||
position: relative;
|
||
}
|
||
|
||
.plan-card:hover {
|
||
border-color: var(--border);
|
||
box-shadow: var(--shadow-md);
|
||
transform: translateY(-4px);
|
||
}
|
||
|
||
.plan-card.active {
|
||
border-color: var(--accent);
|
||
box-shadow: var(--shadow-lg), 0 0 30px rgba(245, 158, 11, 0.15);
|
||
transform: scale(1.02);
|
||
}
|
||
|
||
.plan-badge {
|
||
position: absolute;
|
||
top: -12px;
|
||
left: 50%;
|
||
transform: translateX(-50%);
|
||
background: linear-gradient(135deg, var(--accent), #fbbf24);
|
||
color: white;
|
||
padding: 5px 18px;
|
||
border-radius: 20px;
|
||
font-size: 12px;
|
||
font-weight: 800;
|
||
box-shadow: 0 4px 12px rgba(245, 158, 11, 0.3);
|
||
}
|
||
|
||
.plan-price {
|
||
font-size: 52px;
|
||
font-weight: 900;
|
||
color: var(--primary-dark);
|
||
font-family: 'Tajawal', sans-serif;
|
||
line-height: 1;
|
||
}
|
||
|
||
.plan-features li {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 12px;
|
||
font-size: 15px;
|
||
color: var(--text-2);
|
||
font-weight: 500;
|
||
}
|
||
|
||
.plan-features .feature-check {
|
||
width: 22px;
|
||
height: 22px;
|
||
background: var(--success-light);
|
||
color: var(--success);
|
||
border-radius: 50%;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
font-size: 11px;
|
||
flex-shrink: 0;
|
||
}
|
||
|
||
/* ── PROGRESS BAR ──────────────────────────── */
|
||
.progress-bar-bg {
|
||
height: 10px;
|
||
background: #f1f5f9;
|
||
border-radius: 10px;
|
||
overflow: hidden;
|
||
}
|
||
|
||
.progress-bar-fill {
|
||
height: 100%;
|
||
background: linear-gradient(90deg, var(--success), #34d399);
|
||
border-radius: 10px;
|
||
transition: width 1s ease;
|
||
}
|
||
|
||
/* ── RESPONSIVE ────────────────────────────── */
|
||
@media (max-width: 1280px) {
|
||
.stats-grid {
|
||
grid-template-columns: repeat(2, 1fr);
|
||
}
|
||
}
|
||
|
||
@media (max-width: 768px) {
|
||
.sidebar {
|
||
display: none;
|
||
}
|
||
|
||
.stats-grid {
|
||
grid-template-columns: 1fr;
|
||
}
|
||
|
||
.page-content {
|
||
padding: 20px;
|
||
}
|
||
|
||
.page-header {
|
||
padding: 16px 20px;
|
||
}
|
||
}
|
||
|
||
/* ── UTILITIES ─────────────────────────────── */
|
||
.flex-1 {
|
||
flex: 1;
|
||
}
|
||
|
||
.text-right {
|
||
text-align: right;
|
||
}
|
||
|
||
.text-center {
|
||
text-align: center;
|
||
}
|
||
|
||
.w-full {
|
||
width: 100%;
|
||
}
|
||
|
||
.justify-center {
|
||
justify-content: center;
|
||
}
|
||
</style>
|
||
</head>
|
||
|
||
<body x-data="app" x-init="init()">
|
||
|
||
<!-- ═══ TOAST ERROR ═══════════════════════════════════ -->
|
||
<div x-show="globalError" x-cloak class="toast-error" x-text="globalError" x-transition @click="globalError = ''">
|
||
</div>
|
||
|
||
<!-- ═══ MAIN LAYOUT ══════════════════════════════════ -->
|
||
<div style="display:flex; height:100vh; overflow:hidden;">
|
||
|
||
<!-- ── SIDEBAR ──────────────────────────────────── -->
|
||
<aside class="sidebar">
|
||
<!-- Logo -->
|
||
<div class="sb-logo-area">
|
||
<div style="display:flex; align-items:center; gap:14px;">
|
||
<div class="sb-logo-mark" style="overflow:hidden;">
|
||
<img src="assets/img/logo.jpg" alt="Musadaq Logo"
|
||
style="width:100%; height:100%; object-fit:cover;">
|
||
</div>
|
||
<div>
|
||
<div class="sb-brand-name">مُصادَق</div>
|
||
<div class="sb-brand-sub">نظام إدارة المحاسبة الضريبية</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Navigation -->
|
||
<nav class="sb-nav">
|
||
<div class="sb-section-label">القائمة الرئيسية</div>
|
||
|
||
<button @click="setPage('dashboard')" class="nav-btn" :class="page==='dashboard' ? 'active' : ''">
|
||
<span class="nav-icon">🏠</span>
|
||
<span>الرئيسية</span>
|
||
</button>
|
||
|
||
<button x-show="user?.role !== 'viewer'" @click="setPage('companies')" class="nav-btn"
|
||
:class="page==='companies' ? 'active' : ''">
|
||
<span class="nav-icon">🏭</span>
|
||
<span>الشركات</span>
|
||
</button>
|
||
|
||
<button x-show="user?.role !== 'viewer'" @click="setPage('invoices')" class="nav-btn"
|
||
:class="page==='invoices' ? 'active' : ''">
|
||
<span class="nav-icon">📄</span>
|
||
<span>الفواتير</span>
|
||
</button>
|
||
|
||
<button x-show="user?.role === 'super_admin' || user?.role === 'admin'" @click="setPage('users')"
|
||
class="nav-btn" :class="page==='users' ? 'active' : ''">
|
||
<span class="nav-icon">👥</span>
|
||
<span>فريق العمل</span>
|
||
</button>
|
||
|
||
<div x-show="user?.role === 'super_admin' || user?.role === 'admin'">
|
||
<div class="sb-section-label">الإدارة العليا</div>
|
||
<button x-show="user?.role === 'super_admin'" @click="setPage('tenants')" class="nav-btn"
|
||
:class="page==='tenants' ? 'active' : ''">
|
||
<span class="nav-icon">🏢</span>
|
||
<span>المكاتب المحاسبية</span>
|
||
</button>
|
||
<button @click="setPage('subscription')" class="nav-btn"
|
||
:class="page==='subscription' ? 'active' : ''">
|
||
<span class="nav-icon">💎</span>
|
||
<span>اشتراكي</span>
|
||
</button>
|
||
</div>
|
||
</nav>
|
||
|
||
<!-- User -->
|
||
<div class="sb-user-area">
|
||
<div class="sb-user-card">
|
||
<div class="user-avatar-sb" x-text="user?.name?.[0]"></div>
|
||
<div style="overflow:hidden; flex:1;">
|
||
<div style="font-size:14px; font-weight:800; color:white; white-space:nowrap; overflow:hidden; text-overflow:ellipsis;"
|
||
x-text="user?.name"></div>
|
||
<span class="role-pill" x-text="user?.role"></span>
|
||
</div>
|
||
</div>
|
||
<button @click="logout()" class="logout-btn">
|
||
<span>🚪</span>
|
||
<span>تسجيل الخروج</span>
|
||
</button>
|
||
</div>
|
||
</aside>
|
||
|
||
<!-- ── MAIN CONTENT ──────────────────────────── -->
|
||
<div class="main-area">
|
||
|
||
<!-- Page Header -->
|
||
<div class="page-header">
|
||
<div>
|
||
<h1 class="page-title" x-text="title()"></h1>
|
||
<p class="page-subtitle" x-text="subtitle()"></p>
|
||
</div>
|
||
<div style="display:flex; align-items:center; gap:10px; flex-wrap:wrap;">
|
||
<button x-show="page==='tenants' && user?.role === 'super_admin'" @click="showAddTenantModal = true"
|
||
class="btn btn-primary">
|
||
<span>➕</span> إضافة مكتب
|
||
</button>
|
||
<button x-show="page==='users'" @click="showAddUserModal = true" class="btn btn-primary">
|
||
<span>➕</span> إضافة مستخدم
|
||
</button>
|
||
<button x-show="page==='companies'" @click="showAddCompanyModal = true" class="btn btn-primary">
|
||
<span>➕</span> إضافة شركة
|
||
</button>
|
||
<button x-show="page==='invoices'" @click="showUploadModal = true" class="btn btn-success">
|
||
<span>📤</span> رفع فاتورة
|
||
</button>
|
||
<button x-show="page==='invoices'" @click="showBatchUploadModal = true" class="btn btn-primary">
|
||
<span>📁</span> استيراد مجمع (Batch)
|
||
</button>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Page Body -->
|
||
<div class="page-content" x-transition>
|
||
|
||
<!-- ════ DASHBOARD ═══════════════════════════ -->
|
||
<div x-show="page === 'dashboard'" style="display:flex; flex-direction:column; gap:32px;">
|
||
|
||
<!-- Welcome Banner -->
|
||
<div
|
||
style="background: linear-gradient(135deg, var(--primary-dark) 0%, #7c3aed 100%); border-radius: var(--radius-xl); padding: 28px 32px; color: white; position: relative; overflow: hidden;">
|
||
<div
|
||
style="position: absolute; top: -20px; left: -20px; width: 150px; height: 150px; background: rgba(255,255,255,0.08); border-radius: 50%;">
|
||
</div>
|
||
<div
|
||
style="position: absolute; bottom: -40px; right: 10%; width: 100px; height: 100px; background: rgba(255,255,255,0.05); border-radius: 50%;">
|
||
</div>
|
||
<div style="position: relative; z-index: 1;">
|
||
<div style="font-size: 14px; opacity: 0.8; margin-bottom: 6px; font-weight: 500;">أهلاً بك
|
||
مجدداً، <span x-text="user?.name"></span> 👋</div>
|
||
<h2 style="font-size: 22px; font-weight: 900; margin-bottom: 8px;">نظرة شاملة على نشاطك</h2>
|
||
<p style="opacity: 0.7; font-size: 14px;">إدارة فعّالة للفواتير والتقارير الضريبية في مكان
|
||
واحد</p>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Stat Cards -->
|
||
<div class="stats-grid">
|
||
<div class="stat-card stat-primary">
|
||
<div class="stat-header">
|
||
<div class="stat-icon-box">📄</div>
|
||
</div>
|
||
<div class="stat-label">إجمالي الفواتير</div>
|
||
<div class="stat-value" x-text="stats.total || 0"></div>
|
||
</div>
|
||
<div class="stat-card stat-accent">
|
||
<div class="stat-header">
|
||
<div class="stat-icon-box">⏳</div>
|
||
</div>
|
||
<div class="stat-label">بانتظار التدقيق</div>
|
||
<div class="stat-value" x-text="stats.pending || 0"></div>
|
||
</div>
|
||
<div class="stat-card stat-success">
|
||
<div class="stat-header">
|
||
<div class="stat-icon-box">✅</div>
|
||
</div>
|
||
<div class="stat-label">مدققة ومعتمدة</div>
|
||
<div class="stat-value" x-text="stats.approved || 0"></div>
|
||
</div>
|
||
<template x-if="user?.role === 'super_admin'">
|
||
<div class="stat-card stat-primary">
|
||
<div class="stat-header">
|
||
<div class="stat-icon-box">🏢</div>
|
||
</div>
|
||
<div class="stat-label">المكاتب والمستخدمين</div>
|
||
<div class="stat-value" x-text="(stats.tenants || 0) + ' / ' + (stats.users || 0)">
|
||
</div>
|
||
</div>
|
||
</template>
|
||
<template x-if="user?.role !== 'super_admin'">
|
||
<div class="stat-card"
|
||
:class="subscription?.invoices?.warning ? 'stat-danger' : 'stat-primary'">
|
||
<div class="stat-header">
|
||
<div class="stat-icon-box">📊</div>
|
||
</div>
|
||
<div class="stat-label">استهلاك الحصة</div>
|
||
<div class="stat-value" x-text="(subscription?.invoices?.percent || 0) + '%'"></div>
|
||
</div>
|
||
</template>
|
||
</div>
|
||
|
||
<!-- Quick Actions -->
|
||
<div>
|
||
<h3 style="font-size:16px; font-weight:800; color:var(--text-2); margin-bottom:16px;">إجراءات
|
||
سريعة</h3>
|
||
<div class="quick-actions-grid">
|
||
<div x-show="user?.role !== 'viewer'" class="quick-action-card"
|
||
@click="setPage('invoices'); showUploadModal = true">
|
||
<div class="qa-icon" style="background:var(--success-light);">📤</div>
|
||
<div>
|
||
<div style="font-weight:800; font-size:15px; color:var(--text-1);">رفع فاتورة</div>
|
||
<div style="font-size:13px; color:var(--text-3); margin-top:2px;">رفع وتحليل بالذكاء
|
||
الاصطناعي</div>
|
||
</div>
|
||
</div>
|
||
<div x-show="user?.role !== 'viewer'" class="quick-action-card"
|
||
@click="setPage('companies'); showAddCompanyModal = true">
|
||
<div class="qa-icon" style="background:var(--primary-ghost);">🏭</div>
|
||
<div>
|
||
<div style="font-weight:800; font-size:15px; color:var(--text-1);">إضافة شركة</div>
|
||
<div style="font-size:13px; color:var(--text-3); margin-top:2px;">تسجيل شركة جديدة
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div x-show="user?.role !== 'viewer'" class="quick-action-card"
|
||
@click="setPage('invoices')">
|
||
<div class="qa-icon" style="background:var(--accent-light);">📋</div>
|
||
<div>
|
||
<div style="font-weight:800; font-size:15px; color:var(--text-1);">الفواتير المعلقة
|
||
</div>
|
||
<div style="font-size:13px; color:var(--text-3); margin-top:2px;">عرض ومراجعة
|
||
الفواتير</div>
|
||
</div>
|
||
</div>
|
||
<div x-show="user?.role === 'super_admin' || user?.role === 'admin'"
|
||
class="quick-action-card" @click="setPage('users'); showAddUserModal = true">
|
||
<div class="qa-icon" style="background:#f3e8ff;">👤</div>
|
||
<div>
|
||
<div style="font-weight:800; font-size:15px; color:var(--text-1);">إضافة مستخدم
|
||
</div>
|
||
<div style="font-size:13px; color:var(--text-3); margin-top:2px;">إنشاء حساب جديد
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- ════ COMPANIES ═══════════════════════════ -->
|
||
<div x-show="page === 'companies'">
|
||
<div class="table-container">
|
||
<div class="table-top-bar">
|
||
<span style="font-size:20px;">🏭</span>
|
||
<h3>الشركات المسجلة</h3>
|
||
<span class="table-count" x-text="companies.length + ' شركة'"></span>
|
||
</div>
|
||
<table class="data-table">
|
||
<thead>
|
||
<tr>
|
||
<th>الشركة</th>
|
||
<th>الأرقام الرسمية</th>
|
||
<th style="text-align:center;">الفواتير</th>
|
||
<th style="text-align:center;">الإجمالي</th>
|
||
<th>الفوترة الحكومية</th>
|
||
<th>التقارير</th>
|
||
<th style="text-align:center;">إجراءات</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr x-show="companies.length === 0">
|
||
<td colspan="7">
|
||
<div class="empty-row">
|
||
<span class="empty-icon">🏭</span>
|
||
<p class="empty-msg">لا توجد شركات مسجلة بعد</p>
|
||
</div>
|
||
</td>
|
||
</tr>
|
||
<template x-for="c in companies" :key="c.id">
|
||
<tr>
|
||
<td>
|
||
<div style="font-weight:800; color:var(--primary-dark); font-size:15px;"
|
||
x-text="c.name"></div>
|
||
<div style="font-size:13px; color:var(--text-3); margin-top:3px;"
|
||
x-text="c.address"></div>
|
||
</td>
|
||
<td style="font-family:'Inter',sans-serif; font-size:13px;">
|
||
<div style="color:var(--text-2); font-weight:600;">TIN: <span
|
||
x-text="c.tax_identification_number"></span></div>
|
||
<div style="color:var(--text-3); margin-top:3px;">CRN: <span
|
||
x-text="c.commercial_registration_number || '—'"></span></div>
|
||
</td>
|
||
<td style="text-align:center; font-weight:800; color:var(--text-2); font-size:15px;"
|
||
x-text="c.invoices_count || 0"></td>
|
||
<td style="text-align:center; font-weight:800; color:var(--success); font-family:'Inter',sans-serif; font-size:14px;"
|
||
x-text="parseFloat(c.total_amount || 0).toLocaleString() + ' JOD'"></td>
|
||
<td>
|
||
<template x-if="c.jofotara_client_id_encrypted">
|
||
<button @click="openConnectModal(c)" class="jofotara-connected">
|
||
<span class="conn-dot on"></span>
|
||
متصل بالفوترة
|
||
</button>
|
||
</template>
|
||
<template x-if="!c.jofotara_client_id_encrypted">
|
||
<button @click="openConnectModal(c)" class="jofotara-connect">
|
||
<span class="conn-dot off"></span>
|
||
ربط الآن
|
||
</button>
|
||
</template>
|
||
</td>
|
||
<td>
|
||
<button @click="showCompanyStats(c.id)"
|
||
class="btn-table-action btn-ta-primary">
|
||
📊 تقرير
|
||
</button>
|
||
</td>
|
||
<td style="text-align:center;">
|
||
<button @click="confirmDeleteCompany(c)"
|
||
class="btn-table-action btn-ta-danger">🗑️</button>
|
||
</td>
|
||
</tr>
|
||
</template>
|
||
</tbody>
|
||
</table>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- ════ USERS ════════════════════════════════ -->
|
||
<div x-show="page === 'users'">
|
||
<div class="table-container">
|
||
<div class="table-top-bar">
|
||
<span style="font-size:20px;">👥</span>
|
||
<h3>فريق العمل</h3>
|
||
<span class="table-count" x-text="users.length + ' مستخدم'"></span>
|
||
</div>
|
||
<table class="data-table">
|
||
<thead>
|
||
<tr>
|
||
<th>المستخدم</th>
|
||
<th>المكتب</th>
|
||
<th>الصلاحية</th>
|
||
<th style="text-align:center;">الحالة</th>
|
||
<th style="text-align:center;">إجراءات</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr x-show="users.length === 0">
|
||
<td colspan="5">
|
||
<div class="empty-row">
|
||
<span class="empty-icon">👥</span>
|
||
<p class="empty-msg">لا يوجد مستخدمون مسجلون بعد</p>
|
||
</div>
|
||
</td>
|
||
</tr>
|
||
<template x-for="u in users" :key="u.id">
|
||
<tr>
|
||
<td>
|
||
<div style="display:flex; align-items:center; gap:12px;">
|
||
<div style="width:40px;height:40px;border-radius:12px;background:var(--primary-ghost);color:var(--primary-dark);display:flex;align-items:center;justify-content:center;font-weight:800;font-size:15px;flex-shrink:0;"
|
||
x-text="u.name?.[0]"></div>
|
||
<div>
|
||
<div style="font-weight:800; color:var(--text-1); font-size:15px;"
|
||
x-text="u.name">
|
||
</div>
|
||
<div style="font-size:13px; color:var(--text-3); font-family:'Inter',sans-serif;"
|
||
x-text="u.email"></div>
|
||
</div>
|
||
</div>
|
||
</td>
|
||
<td style="color:var(--text-2); font-size:14px; font-weight:500;"
|
||
x-text="u.tenant_name || '—'">
|
||
</td>
|
||
<td>
|
||
<span class="badge"
|
||
:class="u.role==='super_admin'?'badge-primary':(u.role==='admin'?'badge-info':(u.role==='accountant'?'badge-success':'badge-gray'))"
|
||
x-text="u.role==='super_admin'?'مدير عام':(u.role==='admin'?'مدير مكتب':(u.role==='accountant'?'محاسب':'مشاهد'))">
|
||
</span>
|
||
</td>
|
||
<td style="text-align:center;">
|
||
<span class="badge" :class="u.is_active ? 'badge-success' : 'badge-danger'">
|
||
<span class="badge-dot"
|
||
:class="u.is_active ? 'bg-success' : 'bg-danger'"
|
||
style="background:currentColor;"></span>
|
||
<span x-text="u.is_active ? 'نشط' : 'موقف'"></span>
|
||
</span>
|
||
</td>
|
||
<td style="text-align:center;">
|
||
<button x-show="u.id !== user.id" @click="confirmDeleteUser(u)"
|
||
class="btn-table-action btn-ta-danger">🗑️ حذف</button>
|
||
</td>
|
||
</tr>
|
||
</template>
|
||
</tbody>
|
||
</table>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- ════ INVOICES ═════════════════════════════ -->
|
||
<div x-show="page === 'invoices'">
|
||
<div class="table-container">
|
||
<div class="table-top-bar">
|
||
<span style="font-size:20px;">📄</span>
|
||
<h3>إدارة الفواتير</h3>
|
||
<span class="table-count" x-text="invoices.length + ' فاتورة'"></span>
|
||
<div class="flex-1"></div>
|
||
<button @click="showExcelModal = true" class="btn btn-success btn-sm">
|
||
<span>📊 استيراد اكسل (Bulk)</span>
|
||
</button>
|
||
</div>
|
||
<table class="data-table">
|
||
<thead>
|
||
<tr>
|
||
<th>الشركة / المورد</th>
|
||
<th>التاريخ</th>
|
||
<th>المجموع الكلي</th>
|
||
<th style="text-align:center;">الحالة</th>
|
||
<th style="text-align:center;">إجراءات</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr x-show="invoices.length === 0">
|
||
<td colspan="5">
|
||
<div class="empty-row">
|
||
<span class="empty-icon">📄</span>
|
||
<p class="empty-msg">لا توجد فواتير مرفوعة بعد</p>
|
||
</div>
|
||
</td>
|
||
</tr>
|
||
<template x-for="inv in invoices" :key="inv.id">
|
||
<tr>
|
||
<td>
|
||
<div style="font-weight:800; color:var(--primary-dark); font-size:15px;"
|
||
x-text="inv.company_name"></div>
|
||
<div style="font-size:14px; color:var(--text-2); margin-top:3px;"
|
||
x-text="inv.supplier_name"></div>
|
||
<div style="font-size:12px; color:var(--text-3); font-family:'Inter',sans-serif;"
|
||
x-text="inv.supplier_tin"></div>
|
||
</td>
|
||
<td style="color:var(--text-2); font-size:14px; font-family:'Inter',sans-serif; font-weight:500;"
|
||
x-text="inv.invoice_date || '—'"></td>
|
||
<td style="font-weight:800; color:var(--success); font-family:'Inter',sans-serif; font-size:15px;"
|
||
x-text="parseFloat(inv.grand_total).toLocaleString() + ' JOD'"></td>
|
||
<td style="text-align:center;">
|
||
<span class="badge"
|
||
:class="inv.status==='extracted' ? 'badge-info' : (inv.status==='approved' ? 'badge-success' : 'badge-gray')"
|
||
x-text="inv.status === 'approved' ? '✓ مدققة' : (inv.status === 'extracted' ? 'جاهزة للتدقيق' : inv.status)">
|
||
</span>
|
||
</td>
|
||
<td style="text-align:center;">
|
||
<button @click="viewInvoice(inv.id)"
|
||
class="btn-table-action btn-ta-primary">
|
||
👁️ عرض
|
||
</button>
|
||
</td>
|
||
</tr>
|
||
</template>
|
||
</tbody>
|
||
</table>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- ════ TENANTS (SUPER ADMIN) ════════════════ -->
|
||
<div x-show="page === 'tenants' && user?.role === 'super_admin'">
|
||
<div class="table-container">
|
||
<div class="table-top-bar">
|
||
<span style="font-size:20px;">🏢</span>
|
||
<h3>المكاتب المحاسبية</h3>
|
||
<span class="table-count" x-text="tenants.length + ' مكتب'"></span>
|
||
</div>
|
||
<table class="data-table">
|
||
<thead>
|
||
<tr>
|
||
<th>المكتب المحاسبي</th>
|
||
<th style="text-align:center;">الشركات</th>
|
||
<th style="text-align:center;">الفواتير</th>
|
||
<th>التواصل</th>
|
||
<th style="text-align:center;">الحالة</th>
|
||
<th style="text-align:center;">إجراءات</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr x-show="tenants.length === 0">
|
||
<td colspan="6">
|
||
<div class="empty-row">
|
||
<span class="empty-icon">🏢</span>
|
||
<p class="empty-msg">لا توجد مكاتب مسجلة بعد</p>
|
||
</div>
|
||
</td>
|
||
</tr>
|
||
<template x-for="t in tenants" :key="t.id">
|
||
<tr>
|
||
<td>
|
||
<div style="font-weight:800; color:var(--primary-dark); font-size:15px;"
|
||
x-text="t.name"></div>
|
||
<div style="font-size:12px; color:var(--text-3); margin-top:4px; font-family:'Inter',sans-serif;"
|
||
x-text="'انضم: ' + t.created_at.split(' ')[0]"></div>
|
||
</td>
|
||
<td style="text-align:center; font-weight:800; color:var(--text-2); font-size:15px;"
|
||
x-text="t.companies_count || 0"></td>
|
||
<td style="text-align:center; font-weight:800; color:var(--text-2); font-size:15px;"
|
||
x-text="t.invoices_count || 0"></td>
|
||
<td>
|
||
<div style="font-size:14px; color:var(--text-2); font-weight:600;"
|
||
x-text="t.email"></div>
|
||
<div style="font-size:13px; color:var(--text-3); font-family:'Inter',sans-serif;"
|
||
x-text="t.phone || '—'"></div>
|
||
</td>
|
||
<td style="text-align:center;">
|
||
<span class="badge"
|
||
:class="t.status==='active'?'badge-success':(t.status==='trial'?'badge-warning':'badge-danger')"
|
||
x-text="t.status==='active'?'نشط':(t.status==='trial'?'تجريبي':'معلق')">
|
||
</span>
|
||
</td>
|
||
<td style="text-align:center;">
|
||
<div style="display:inline-flex; gap:6px; align-items:center;">
|
||
<button @click="openEditTenantModal(t)"
|
||
class="btn-table-action btn-ta-primary" title="تعديل">⚙️
|
||
تعديل</button>
|
||
<button class="btn-table-action btn-ta-success" title="تقرير"
|
||
@click="showTenantStats(t)">📊</button>
|
||
</div>
|
||
</td>
|
||
</tr>
|
||
</template>
|
||
</tbody>
|
||
</table>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- ── SUBSCRIPTION PAGE ────────────────────────── -->
|
||
<div x-show="page === 'subscription'">
|
||
<div
|
||
style="display:flex; justify-content:space-between; align-items:end; margin-bottom:28px; flex-wrap:wrap; gap:16px;">
|
||
<div>
|
||
<h2 style="font-size:32px; font-weight:900; color:var(--text-1); letter-spacing:-0.02em;">
|
||
إدارة الاشتراك</h2>
|
||
<p style="color:var(--text-3); font-size:15px; margin-top:4px;">تفاصيل باقتك الحالية
|
||
واستهلاك الموارد</p>
|
||
</div>
|
||
<div
|
||
style="background: linear-gradient(135deg, var(--primary-dark), #7c3aed); padding:10px 24px; border-radius: 16px; color:white; display:flex; align-items:center; gap:12px; box-shadow: var(--shadow-md);">
|
||
<span style="font-size:14px; opacity:0.8;">الباقة الحالية:</span>
|
||
<span style="font-weight:900; font-size:20px;" x-text="subscription?.plan_name"></span>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Usage Stats Grid -->
|
||
<div
|
||
style="display:grid; grid-template-columns:repeat(auto-fit, minmax(280px, 1fr)); gap:20px; margin-bottom:40px;">
|
||
<!-- Invoices Card -->
|
||
<div class="table-container" style="padding:24px; position:relative; overflow:hidden;">
|
||
<div
|
||
style="display:flex; justify-content:space-between; margin-bottom:16px; align-items:center;">
|
||
<span style="font-weight:800; color:var(--primary-dark); font-size:15px;">الفواتير
|
||
الشهرية</span>
|
||
<span
|
||
style="font-weight:900; color:var(--success); font-family:'Inter',sans-serif; font-size:18px;"
|
||
x-text="subscription?.invoices?.used + ' / ' + subscription?.invoices?.limit"></span>
|
||
</div>
|
||
<div class="progress-bar-bg" style="margin-bottom:12px;">
|
||
<div class="progress-bar-fill"
|
||
:style="'width:' + (subscription?.invoices?.percent || 0) + '%;'"></div>
|
||
</div>
|
||
<div
|
||
style="display:flex; justify-content:space-between; font-size:13px; color:var(--text-3); font-weight:500;">
|
||
<span
|
||
x-text="'يتم التصفير في: ' + (subscription?.period_end?.split(' ')[0] || '—')"></span>
|
||
<span style="font-weight:800;"
|
||
x-text="(subscription?.invoices?.percent || 0) + '%'"></span>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Companies Card -->
|
||
<div class="table-container" style="padding:24px;">
|
||
<div
|
||
style="display:flex; justify-content:space-between; margin-bottom:16px; align-items:center;">
|
||
<span style="font-weight:800; color:var(--primary-dark); font-size:15px;">الشركات
|
||
المدارة</span>
|
||
<span
|
||
style="font-weight:900; color:var(--success); font-family:'Inter',sans-serif; font-size:18px;"
|
||
x-text="subscription?.companies?.used + ' / ' + subscription?.companies?.limit"></span>
|
||
</div>
|
||
<div class="progress-bar-bg" style="margin-bottom:12px;">
|
||
<div class="progress-bar-fill"
|
||
:style="'width:' + (subscription?.companies?.percent || 0) + '%;'"></div>
|
||
</div>
|
||
<div style="font-size:13px; color:var(--text-3); font-weight:500;"
|
||
x-text="'إجمالي الشركات المسموح بها'"></div>
|
||
</div>
|
||
|
||
<!-- Team Card -->
|
||
<div class="table-container" style="padding:24px;">
|
||
<div
|
||
style="display:flex; justify-content:space-between; margin-bottom:16px; align-items:center;">
|
||
<span style="font-weight:800; color:var(--primary-dark); font-size:15px;">فريق
|
||
العمل</span>
|
||
<span
|
||
style="font-weight:900; color:var(--success); font-family:'Inter',sans-serif; font-size:18px;"
|
||
x-text="subscription?.users?.used + ' / ' + subscription?.users?.limit"></span>
|
||
</div>
|
||
<div class="progress-bar-bg" style="margin-bottom:12px;">
|
||
<div class="progress-bar-fill"
|
||
:style="'width:' + (subscription?.users?.percent || 0) + '%;'"></div>
|
||
</div>
|
||
<div style="font-size:13px; color:var(--text-3); font-weight:500;"
|
||
x-text="'مستخدمين نشطين في النظام'"></div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Pending Payment Warning -->
|
||
<div x-show="subscription?.pending_payment"
|
||
style="background:var(--accent-light); border:2px dashed var(--accent); border-radius:20px; padding:28px; margin-bottom:40px; display:flex; justify-content:space-between; align-items:center; flex-wrap:wrap; gap:20px;">
|
||
<div style="display:flex; gap:16px; align-items:center;">
|
||
<div
|
||
style="background:var(--accent); color:white; width:52px; height:52px; border-radius:50%; display:flex; align-items:center; justify-content:center; font-size:24px; box-shadow:0 4px 12px rgba(245,158,11,0.3);">
|
||
⌛</div>
|
||
<div>
|
||
<h4 style="font-weight:900; color:var(--text-1); font-size:18px;">لديك طلب ترقية قيد
|
||
الانتظار</h4>
|
||
<p style="color:var(--text-2); font-size:14px; margin-top:4px;">بانتظار تأكيد عملية
|
||
التحويل برقم المرجع: <strong style="font-family:'Inter',sans-serif;"
|
||
x-text="subscription?.pending_payment?.reference"></strong></p>
|
||
</div>
|
||
</div>
|
||
<div style="display:flex; gap:10px;">
|
||
<button @click="upgradePlan({id: subscription.pending_payment.plan_id})"
|
||
class="btn btn-primary">متابعة الدفع</button>
|
||
<button @click="cancelPayment(subscription.pending_payment.id)" class="btn btn-ghost"
|
||
style="color:var(--danger);">إلغاء الطلب</button>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Pricing Section -->
|
||
<div style="text-align:center; margin-bottom:32px;">
|
||
<h3 style="font-size:28px; font-weight:900; color:var(--text-1); margin-bottom:8px;">تغيير أو
|
||
ترقية الباقة</h3>
|
||
<p style="color:var(--text-3); font-size:15px;">اختر الباقة التي تناسب احتياجات مكتبك المحاسبي
|
||
</p>
|
||
</div>
|
||
|
||
<div style="display:grid; grid-template-columns:repeat(auto-fit, minmax(300px, 1fr)); gap:24px;">
|
||
<template x-for="p in plans" :key="p.id">
|
||
<div class="plan-card" :class="subscription?.plan_id === p.id ? 'active' : ''">
|
||
|
||
<div x-show="subscription?.plan_id === p.id" class="plan-badge">
|
||
باقتك الحالية
|
||
</div>
|
||
|
||
<div style="text-align:center;">
|
||
<h4 style="font-size:24px; font-weight:900; color:var(--text-1); margin-bottom:8px;"
|
||
x-text="p.name_ar"></h4>
|
||
<p style="font-size:14px; color:var(--text-3); min-height:40px; font-weight:500;"
|
||
x-text="p.description_ar"></p>
|
||
</div>
|
||
|
||
<div
|
||
style="text-align:center; padding:24px 0; border-top:1px solid var(--border-light); border-bottom:1px solid var(--border-light);">
|
||
<span
|
||
style="font-size:56px; font-weight:900; color:var(--primary-dark); font-family:'Tajawal',sans-serif; line-height:1;"
|
||
x-text="p.price_jod"></span>
|
||
<span style="font-size:16px; color:var(--text-3); font-weight:600;"> دينار /
|
||
شهر</span>
|
||
</div>
|
||
|
||
<ul class="plan-features"
|
||
style="list-style:none; padding:0; margin:0; display:flex; flex-direction:column; gap:16px; flex:1;">
|
||
<template x-for="f in p.features">
|
||
<li>
|
||
<div class="feature-check">✔</div>
|
||
<span x-text="f"></span>
|
||
</li>
|
||
</template>
|
||
</ul>
|
||
|
||
<button x-show="subscription?.plan_id !== p.id" @click="upgradePlan(p)"
|
||
class="btn btn-success"
|
||
style="width:100%; height:56px; font-size:17px; font-weight:900; border-radius:16px; margin-top:8px;">
|
||
ترقية الباقة الآن
|
||
</button>
|
||
|
||
<div x-show="subscription?.plan_id === p.id"
|
||
style="text-align:center; padding:16px; border-radius:14px; background:var(--accent-light); color:#92400e; font-weight:800; font-size:15px; border:1px solid rgba(245,158,11,0.2);">
|
||
✨ أنت مشترك في هذه الباقة
|
||
</div>
|
||
</div>
|
||
</template>
|
||
</div>
|
||
</div>
|
||
|
||
</div><!-- /page-content -->
|
||
</div><!-- /main-area -->
|
||
</div><!-- /flex layout -->
|
||
|
||
|
||
<!-- ════════════════════════════════════════════════════════
|
||
MODALS
|
||
════════════════════════════════════════════════════════ -->
|
||
|
||
<!-- ── COMPANY STATS MODAL ──────────────────────────── -->
|
||
<div x-show="showCompanyStatsModal" x-cloak class="modal-backdrop" @click.self="showCompanyStatsModal = false">
|
||
<div class="modal-box" style="max-width:680px;">
|
||
<div class="modal-head" style="padding-bottom:0;">
|
||
<div class="modal-head-icon success">📊</div>
|
||
<div style="flex:1;">
|
||
<div class="modal-title" x-text="companyStats?.company?.name"></div>
|
||
<div class="modal-subtitle" style="font-family:'Inter',sans-serif;"
|
||
x-text="'TIN: ' + companyStats?.company?.tax_identification_number"></div>
|
||
</div>
|
||
<button @click="showCompanyStatsModal = false" class="modal-close-btn">✕</button>
|
||
</div>
|
||
<div class="modal-body">
|
||
<div style="display:grid; grid-template-columns:repeat(2,1fr); gap:12px; margin-bottom:24px;">
|
||
<div class="mini-stat">
|
||
<div class="mini-stat-label">إجمالي الفواتير</div>
|
||
<div class="mini-stat-val" x-text="companyStats?.totals?.total_invoices || 0"></div>
|
||
</div>
|
||
<div class="mini-stat">
|
||
<div class="mini-stat-label">المجموع (JOD)</div>
|
||
<div class="mini-stat-val" style="color:var(--success);"
|
||
x-text="parseFloat(companyStats?.totals?.total_amount || 0).toLocaleString()"></div>
|
||
</div>
|
||
<div class="mini-stat">
|
||
<div class="mini-stat-label">إجمالي الضريبة</div>
|
||
<div class="mini-stat-val" style="color:var(--accent);"
|
||
x-text="parseFloat(companyStats?.totals?.total_tax || 0).toLocaleString()"></div>
|
||
</div>
|
||
<div class="mini-stat">
|
||
<div class="mini-stat-label">المعتمدة</div>
|
||
<div class="mini-stat-val" style="color:var(--info);"
|
||
x-text="companyStats?.totals?.approved_count || 0"></div>
|
||
</div>
|
||
</div>
|
||
<div
|
||
style="font-size:12px; font-weight:800; color:var(--text-3); text-transform:uppercase; letter-spacing:0.07em; margin-bottom:12px;">
|
||
التحليل الشهري للفواتير والضرائب
|
||
</div>
|
||
<div
|
||
style="border:1px solid var(--border-light); border-radius:14px; overflow:hidden; max-height:260px; overflow-y:auto;">
|
||
<table class="stats-table">
|
||
<thead>
|
||
<tr>
|
||
<th>الشهر</th>
|
||
<th>عدد الفواتير</th>
|
||
<th>الضريبة المستحقة</th>
|
||
<th style="color:var(--success);">الإجمالي النهائي</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<template x-for="m in companyStats?.monthly" :key="m.month">
|
||
<tr>
|
||
<td style="font-family:'Inter',sans-serif; color:var(--text-2); font-weight:600;"
|
||
x-text="m.month"></td>
|
||
<td style="font-weight:700;" x-text="m.total_invoices"></td>
|
||
<td style="color:var(--accent); font-family:'Inter',sans-serif; font-weight:600;"
|
||
x-text="parseFloat(m.total_tax || 0).toLocaleString() + ' JOD'"></td>
|
||
<td style="font-weight:800; color:var(--success); font-family:'Inter',sans-serif;"
|
||
x-text="parseFloat(m.total_amount || 0).toLocaleString() + ' JOD'"></td>
|
||
</tr>
|
||
</template>
|
||
</tbody>
|
||
</table>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- ── TENANT STATS MODAL ───────────────────────────── -->
|
||
<div x-show="showTenantStatsModal" x-cloak class="modal-backdrop" @click.self="showTenantStatsModal = false">
|
||
<div class="modal-box" style="max-width:680px;">
|
||
<div class="modal-head">
|
||
<div class="modal-head-icon primary">🏢</div>
|
||
<div style="flex:1;">
|
||
<div class="modal-title" x-text="tenantStats?.tenant?.name"></div>
|
||
<div class="modal-subtitle">تقرير أداء المكتب المحاسبي</div>
|
||
</div>
|
||
<button @click="showTenantStatsModal = false" class="modal-close-btn">✕</button>
|
||
</div>
|
||
<div class="modal-body">
|
||
<div style="display:grid; grid-template-columns:repeat(2,1fr); gap:12px; margin-bottom:24px;">
|
||
<div class="mini-stat">
|
||
<div class="mini-stat-label">الشركات المدارة</div>
|
||
<div class="mini-stat-val" x-text="tenantStats?.summary?.total_companies || 0"></div>
|
||
</div>
|
||
<div class="mini-stat">
|
||
<div class="mini-stat-label">إجمالي الفواتير</div>
|
||
<div class="mini-stat-val" x-text="tenantStats?.summary?.total_invoices || 0"></div>
|
||
</div>
|
||
<div class="mini-stat">
|
||
<div class="mini-stat-label">المجموع (JOD)</div>
|
||
<div class="mini-stat-val" style="color:var(--success);"
|
||
x-text="parseFloat(tenantStats?.summary?.total_amount || 0).toLocaleString()"></div>
|
||
</div>
|
||
<div class="mini-stat">
|
||
<div class="mini-stat-label">إجمالي الضرائب</div>
|
||
<div class="mini-stat-val" style="color:var(--accent);"
|
||
x-text="parseFloat(tenantStats?.summary?.total_tax || 0).toLocaleString()"></div>
|
||
</div>
|
||
</div>
|
||
<div
|
||
style="font-size:12px; font-weight:800; color:var(--text-3); text-transform:uppercase; letter-spacing:0.07em; margin-bottom:12px;">
|
||
تحليل النشاط الشهري للمكتب
|
||
</div>
|
||
<div
|
||
style="border:1px solid var(--border-light); border-radius:14px; overflow:hidden; max-height:260px; overflow-y:auto;">
|
||
<table class="stats-table">
|
||
<thead>
|
||
<tr>
|
||
<th>الشهر</th>
|
||
<th>عدد الفواتير</th>
|
||
<th>الضريبة المستحقة</th>
|
||
<th style="color:var(--success);">الإجمالي النهائي</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<template x-for="m in tenantStats?.monthly" :key="m.month">
|
||
<tr>
|
||
<td style="font-family:'Inter',sans-serif; color:var(--text-2); font-weight:600;"
|
||
x-text="m.month"></td>
|
||
<td style="font-weight:700;" x-text="m.total_invoices"></td>
|
||
<td style="color:var(--accent); font-family:'Inter',sans-serif; font-weight:600;"
|
||
x-text="parseFloat(m.total_tax || 0).toLocaleString() + ' JOD'"></td>
|
||
<td style="font-weight:800; color:var(--success); font-family:'Inter',sans-serif;"
|
||
x-text="parseFloat(m.total_amount || 0).toLocaleString() + ' JOD'"></td>
|
||
</tr>
|
||
</template>
|
||
</tbody>
|
||
</table>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- ── ADD USER MODAL ───────────────────────────────── -->
|
||
<div x-show="showAddUserModal" x-cloak class="modal-backdrop">
|
||
<div class="modal-box">
|
||
<div class="modal-head">
|
||
<div class="modal-head-icon accent">👤</div>
|
||
<div style="flex:1;">
|
||
<div class="modal-title">إضافة مستخدم جديد</div>
|
||
<div class="modal-subtitle">أدخل بيانات الحساب الجديد</div>
|
||
</div>
|
||
<button @click="showAddUserModal = false" class="modal-close-btn">✕</button>
|
||
</div>
|
||
<div class="modal-divider"></div>
|
||
<form @submit.prevent="createUser">
|
||
<div class="modal-body" style="display:flex; flex-direction:column; gap:16px;">
|
||
<div class="form-group">
|
||
<label class="form-label">الاسم الكامل</label>
|
||
<input type="text" x-model="newUser.name" placeholder="مثال: أحمد محمد" class="form-input"
|
||
required>
|
||
</div>
|
||
<div class="form-group">
|
||
<label class="form-label">البريد الإلكتروني</label>
|
||
<input type="email" x-model="newUser.email" placeholder="example@office.com" class="form-input"
|
||
required>
|
||
</div>
|
||
<div class="form-group">
|
||
<label class="form-label">كلمة المرور</label>
|
||
<input type="password" x-model="newUser.password" placeholder="••••••••" class="form-input"
|
||
required>
|
||
</div>
|
||
<div class="form-group">
|
||
<label class="form-label">الصلاحية</label>
|
||
<select x-model="newUser.role" class="form-input" required>
|
||
<option value="accountant">محاسب</option>
|
||
<option value="viewer">مشاهد فقط</option>
|
||
<option x-show="user?.role === 'super_admin'" value="admin">مدير مكتب</option>
|
||
</select>
|
||
</div>
|
||
<template x-if="user?.role === 'super_admin'">
|
||
<div class="form-group">
|
||
<label class="form-label">المكتب المحاسبي</label>
|
||
<select x-model="newUser.tenant_id" class="form-input" required>
|
||
<option value="">— اختر المكتب —</option>
|
||
<template x-for="t in tenants" :key="t.id">
|
||
<option :value="t.id" x-text="t.name"></option>
|
||
</template>
|
||
</select>
|
||
</div>
|
||
</template>
|
||
</div>
|
||
<div class="modal-divider"></div>
|
||
<div class="modal-footer">
|
||
<button type="submit" class="btn btn-primary" :disabled="isBusy" style="flex:1;">
|
||
<span x-show="!isBusy">💾 حفظ المستخدم</span>
|
||
<span x-show="isBusy">⏳ جاري الحفظ...</span>
|
||
</button>
|
||
<button type="button" @click="showAddUserModal = false" class="btn btn-ghost">إلغاء</button>
|
||
</div>
|
||
</form>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- ── ADD COMPANY MODAL ────────────────────────────── -->
|
||
<div x-show="showAddCompanyModal" x-cloak class="modal-backdrop">
|
||
<div class="modal-box">
|
||
<div class="modal-head">
|
||
<div class="modal-head-icon primary">🏭</div>
|
||
<div style="flex:1;">
|
||
<div class="modal-title">إضافة شركة جديدة</div>
|
||
<div class="modal-subtitle">أدخل بيانات الشركة الرسمية</div>
|
||
</div>
|
||
<button @click="showAddCompanyModal = false" class="modal-close-btn">✕</button>
|
||
</div>
|
||
<div class="modal-divider"></div>
|
||
<form @submit.prevent="createCompany">
|
||
<div class="modal-body" style="display:flex; flex-direction:column; gap:16px;">
|
||
<div class="form-group">
|
||
<label class="form-label">اسم الشركة</label>
|
||
<input type="text" x-model="newCompany.name" placeholder="الاسم الكامل بالعربي"
|
||
class="form-input" required>
|
||
</div>
|
||
<div class="form-group">
|
||
<label class="form-label">الرقم الضريبي (TIN)</label>
|
||
<input type="text" x-model="newCompany.tax_identification_number"
|
||
placeholder="أدخل رقم التسجيل الضريبي" class="form-input mono" required>
|
||
</div>
|
||
<div class="form-group">
|
||
<label class="form-label">رقم السجل التجاري <span
|
||
class="form-label-sub">(اختياري)</span></label>
|
||
<input type="text" x-model="newCompany.commercial_registration_number"
|
||
placeholder="رقم السجل التجاري" class="form-input mono">
|
||
</div>
|
||
<div class="form-group">
|
||
<label class="form-label">العنوان <span class="form-label-sub">(اختياري)</span></label>
|
||
<input type="text" x-model="newCompany.address" placeholder="عنوان الشركة" class="form-input">
|
||
</div>
|
||
<template x-if="user?.role === 'super_admin'">
|
||
<div class="form-group">
|
||
<label class="form-label">المكتب المحاسبي المسؤول</label>
|
||
<select x-model="newCompany.tenant_id" class="form-input" required>
|
||
<option value="">— اختر المكتب —</option>
|
||
<template x-for="t in tenants" :key="t.id">
|
||
<option :value="t.id" x-text="t.name"></option>
|
||
</template>
|
||
</select>
|
||
</div>
|
||
</template>
|
||
</div>
|
||
<div class="modal-divider"></div>
|
||
<div class="modal-footer">
|
||
<button type="submit" class="btn btn-primary" :disabled="isBusy" style="flex:1;">
|
||
<span x-show="!isBusy">🏭 إضافة الشركة</span>
|
||
<span x-show="isBusy">⏳ جاري الإضافة...</span>
|
||
</button>
|
||
<button type="button" @click="showAddCompanyModal = false" class="btn btn-ghost">إلغاء</button>
|
||
</div>
|
||
</form>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- ── JOFOTARA CONNECT MODAL ───────────────────────── -->
|
||
<div x-show="showConnectModal" x-cloak class="modal-backdrop">
|
||
<div class="modal-box">
|
||
<div class="modal-head">
|
||
<div class="modal-head-icon success">🔗</div>
|
||
<div style="flex:1;">
|
||
<div class="modal-title">ربط نظام الفوترة الحكومي</div>
|
||
<div class="modal-subtitle" x-text="'الشركة: ' + currentCompany?.name"></div>
|
||
</div>
|
||
<button @click="showConnectModal = false" class="modal-close-btn">✕</button>
|
||
</div>
|
||
<div class="modal-divider"></div>
|
||
<form @submit.prevent="connectJoFotara">
|
||
<div class="modal-body" style="display:flex; flex-direction:column; gap:16px;">
|
||
<div
|
||
style="background:var(--success-light); border:1px solid rgba(16,185,129,0.2); border-radius:12px; padding:14px 16px; font-size:13px; color:#047857; font-weight:600;">
|
||
ℹ️ هذه البيانات تُستخرج من بوابة JoFotara الحكومية لربط الفوترة الإلكترونية
|
||
</div>
|
||
<div class="form-group">
|
||
<label class="form-label">Client ID</label>
|
||
<input type="text" x-model="connectData.client_id" placeholder="أدخل Client ID"
|
||
class="form-input mono" required>
|
||
</div>
|
||
<div class="form-group">
|
||
<label class="form-label">Secret Key</label>
|
||
<input type="password" x-model="connectData.secret_key" placeholder="أدخل Secret Key"
|
||
class="form-input mono" required>
|
||
</div>
|
||
|
||
</div>
|
||
<div class="modal-divider"></div>
|
||
<div class="modal-footer">
|
||
<button type="submit" class="btn btn-success" :disabled="isBusy" style="flex:1;">
|
||
<span x-show="!isBusy">🔗 حفظ وتفعيل الربط</span>
|
||
<span x-show="isBusy">⏳ جاري التفعيل...</span>
|
||
</button>
|
||
<button type="button" @click="showConnectModal = false" class="btn btn-ghost">إلغاء</button>
|
||
</div>
|
||
</form>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- ── UPLOAD INVOICE MODAL ─────────────────────────── -->
|
||
<div x-show="showUploadModal" x-cloak class="modal-backdrop">
|
||
<div class="modal-box">
|
||
<div class="modal-head">
|
||
<div class="modal-head-icon success">📤</div>
|
||
<div style="flex:1;">
|
||
<div class="modal-title">رفع فاتورة جديدة</div>
|
||
<div class="modal-subtitle">سيتم تحليلها آلياً بالذكاء الاصطناعي</div>
|
||
</div>
|
||
<button @click="showUploadModal = false" class="modal-close-btn">✕</button>
|
||
</div>
|
||
<div class="modal-divider"></div>
|
||
<form @submit.prevent="uploadInvoice">
|
||
<div class="modal-body" style="display:flex; flex-direction:column; gap:16px;">
|
||
<div class="form-group">
|
||
<label class="form-label">اختر الشركة</label>
|
||
<select x-model="uploadData.company_id" class="form-input" 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="form-group">
|
||
<label class="form-label">ملف الفاتورة</label>
|
||
<div style="font-size:13px; color:var(--text-3); margin-bottom:6px; font-weight:500;">مدعوم: صور
|
||
(JPG, PNG) أو ملفات PDF</div>
|
||
<input type="file" @change="selectedFile = $event.target.files[0]" class="form-input"
|
||
style="padding:10px;" required>
|
||
</div>
|
||
</div>
|
||
<div class="modal-divider"></div>
|
||
<div class="modal-footer">
|
||
<button type="submit" class="btn btn-success" :disabled="isUploading" style="flex:1;">
|
||
<span x-show="!isUploading">📤 رفع وتحليل</span>
|
||
<span x-show="isUploading">⏳ جاري التحليل والاستخراج...</span>
|
||
</button>
|
||
<button type="button" @click="showUploadModal = false" class="btn btn-ghost">إلغاء</button>
|
||
</div>
|
||
</form>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- ── BATCH UPLOAD MODAL ───────────────────────────── -->
|
||
<div x-show="showBatchUploadModal" x-cloak class="modal-backdrop">
|
||
<div class="modal-box">
|
||
<div class="modal-head">
|
||
<div class="modal-head-icon primary">📁</div>
|
||
<div style="flex:1;">
|
||
<div class="modal-title">رفع مجمع للفواتير (Batch Processing)</div>
|
||
<div class="modal-subtitle">اختر عدة فواتير لمعالجتها في الخلفية</div>
|
||
</div>
|
||
<button @click="showBatchUploadModal = false" class="modal-close-btn">✕</button>
|
||
</div>
|
||
<div class="modal-divider"></div>
|
||
<form @submit.prevent="uploadBatchInvoices">
|
||
<div class="modal-body" style="display:flex; flex-direction:column; gap:16px;">
|
||
<div class="form-group">
|
||
<label class="form-label">اختر الشركة</label>
|
||
<select x-model="uploadData.company_id" class="form-input" required
|
||
:disabled="isUploadingBatch">
|
||
<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="form-group">
|
||
<label class="form-label">ملفات الفواتير (متعدد)</label>
|
||
<div style="font-size:13px; color:var(--text-3); margin-bottom:6px; font-weight:500;">مدعوم: صور
|
||
أو PDF (يمكنك تحديد أكثر من ملف)</div>
|
||
<input type="file" id="batchFileInput" multiple accept="image/*,application/pdf"
|
||
class="form-input" style="padding:10px;" required :disabled="isUploadingBatch">
|
||
</div>
|
||
<div x-show="isUploadingBatch" style="margin-top:10px;">
|
||
<div
|
||
style="font-size:13px; font-weight:800; color:var(--primary-dark); margin-bottom:6px; display:flex; justify-content:space-between;">
|
||
<span>جاري رفع الدفعة...</span>
|
||
<span x-text="batchProgress.current + ' / ' + batchProgress.total"></span>
|
||
</div>
|
||
<div class="progress-bar-bg">
|
||
<div class="progress-bar-fill" style="background:var(--primary);"
|
||
:style="'width:' + (batchProgress.total ? (batchProgress.current/batchProgress.total*100) : 0) + '%'">
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div class="modal-divider"></div>
|
||
<div class="modal-footer">
|
||
<button type="submit" class="btn btn-primary" :disabled="isUploadingBatch" style="flex:1;">
|
||
<span x-show="!isUploadingBatch">📁 بدء الرفع المجمع</span>
|
||
<span x-show="isUploadingBatch">⏳ يرجى الانتظار...</span>
|
||
</button>
|
||
<button type="button" @click="showBatchUploadModal = false" class="btn btn-ghost"
|
||
:disabled="isUploadingBatch">إلغاء</button>
|
||
</div>
|
||
</form>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- ── VIEW INVOICE MODAL ───────────────────────────── -->
|
||
<div x-show="showViewModal" x-cloak class="modal-backdrop" @click.self="showViewModal = false">
|
||
<div
|
||
style="background:white; border-radius:24px; box-shadow:var(--shadow-lg); width:100%; max-width:900px; height:88vh; display:flex; overflow:hidden; border:1px solid var(--border-light);">
|
||
|
||
<!-- Document Preview -->
|
||
<div
|
||
style="flex:1; background:#f1f5f9; border-left:1px solid var(--border-light); position:relative; overflow:hidden;">
|
||
<div style="position:absolute; top:16px; right:16px; z-index:10;">
|
||
<span class="badge badge-primary">معاينة الملف</span>
|
||
</div>
|
||
<template x-if="currentInvoice?.file_url">
|
||
<iframe :src="currentInvoice.file_url + '&token=' + token()"
|
||
style="width:100%; height:100%; border:0;"></iframe>
|
||
</template>
|
||
<div x-show="!currentInvoice?.file_url"
|
||
style="position:absolute; inset:0; display:flex; flex-direction:column; align-items:center; justify-content:center; gap:12px; color:var(--text-3);">
|
||
<span style="font-size:56px; opacity:0.2;">📄</span>
|
||
<p style="font-size:15px; font-weight:600;">لا يوجد ملف مرفق</p>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Invoice Data Panel -->
|
||
<div
|
||
style="width:360px; flex-shrink:0; display:flex; flex-direction:column; overflow:hidden; background:var(--surface);">
|
||
<!-- Header -->
|
||
<div
|
||
style="padding:24px 24px 20px; border-bottom:1px solid var(--border-light); display:flex; align-items:center; justify-content:space-between;">
|
||
<div>
|
||
<div style="font-size:18px; font-weight:900; color:var(--text-1);">تفاصيل الفاتورة</div>
|
||
<span class="badge" style="margin-top:6px;"
|
||
:class="currentInvoice?.status==='extracted' ? 'badge-info' : (currentInvoice?.status==='approved' ? 'badge-success' : 'badge-gray')"
|
||
x-text="currentInvoice?.status === 'approved' ? '✓ مدققة' : (currentInvoice?.status === 'extracted' ? 'جاهزة للتدقيق' : currentInvoice?.status)">
|
||
</span>
|
||
</div>
|
||
<button @click="showViewModal = false" class="modal-close-btn">✕</button>
|
||
</div>
|
||
|
||
<!-- Validation Warnings Banner -->
|
||
<div x-show="currentInvoice?.validation_warnings?.length > 0"
|
||
style="background:#fffbeb; border-bottom:1px solid #fde68a; padding:14px 18px;">
|
||
<div style="display:flex; gap:12px; align-items:flex-start;">
|
||
<span style="font-size:20px;">⚠️</span>
|
||
<div>
|
||
<div style="font-weight:800; color:#92400e; font-size:13px; margin-bottom:6px;">تنبيهات
|
||
المدقق الذكي:</div>
|
||
<ul
|
||
style="margin:0; padding:0; list-style:none; display:flex; flex-direction:column; gap:5px;">
|
||
<template x-for="w in currentInvoice.validation_warnings">
|
||
<li style="font-size:12px; color:#b45309; display:flex; gap:6px; font-weight:500;">
|
||
<span>•</span>
|
||
<span x-text="w"></span>
|
||
</li>
|
||
</template>
|
||
</ul>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Fields -->
|
||
<div style="flex:1; overflow-y:auto; padding:20px; display:flex; flex-direction:column; gap:12px;">
|
||
|
||
<div class="invoice-field-card">
|
||
<div class="invoice-field-label">المورد (البائع)</div>
|
||
<div class="invoice-field-value" x-text="currentInvoice?.supplier_name || 'غير متوفر'"></div>
|
||
<div style="font-size:13px; color:var(--text-3); font-family:'Inter',sans-serif; margin-top:4px;"
|
||
x-text="'TIN: ' + (currentInvoice?.supplier_tin || '—')"></div>
|
||
</div>
|
||
|
||
<div class="invoice-field-card">
|
||
<div class="invoice-field-label">رقم الفاتورة والتاريخ</div>
|
||
<div class="invoice-field-value" x-text="currentInvoice?.invoice_number || '—'"></div>
|
||
<div style="font-size:14px; color:var(--text-2); font-family:'Inter',sans-serif; margin-top:4px; font-weight:600;"
|
||
x-text="currentInvoice?.invoice_date || '—'"></div>
|
||
</div>
|
||
|
||
<div class="invoice-field-card"
|
||
style="background:var(--success-light); border-color:rgba(16,185,129,0.2);">
|
||
<div class="invoice-field-label" style="color:#047857;">المجموع الكلي</div>
|
||
<div style="font-size:28px; font-weight:900; color:#047857; font-family:'Tajawal',sans-serif;"
|
||
x-text="parseFloat(currentInvoice?.grand_total || 0).toLocaleString() + ' JOD'"></div>
|
||
<div style="font-size:13px; color:var(--accent); margin-top:6px; font-family:'Inter',sans-serif; font-weight:700;"
|
||
x-text="'الضريبة: ' + parseFloat(currentInvoice?.tax_amount || 0).toLocaleString() + ' JOD'">
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Items Table -->
|
||
<div x-show="currentInvoice?.items?.length > 0"
|
||
style="border:1px solid var(--border-light); border-radius:14px; overflow-x:auto;">
|
||
<div
|
||
style="padding:10px 14px; background:#f8fafc; font-size:11px; font-weight:800; color:var(--text-3); text-transform:uppercase; letter-spacing:0.06em; position:sticky; top:0; z-index:2; border-bottom:1px solid var(--border-light);">
|
||
بنود الفاتورة
|
||
</div>
|
||
<table style="width:100%; border-collapse:collapse; font-size:12px; min-width:320px;">
|
||
<thead style="background:white;">
|
||
<tr>
|
||
<th
|
||
style="padding:10px 12px; text-align:right; color:var(--text-3); font-weight:700; border-bottom:1px solid var(--border-light);">
|
||
البند</th>
|
||
<th
|
||
style="padding:10px 12px; text-align:center; color:var(--text-3); font-weight:700; border-bottom:1px solid var(--border-light);">
|
||
الكمية</th>
|
||
<th
|
||
style="padding:10px 12px; text-align:left; color:var(--text-3); font-weight:700; border-bottom:1px solid var(--border-light);">
|
||
السعر</th>
|
||
<th
|
||
style="padding:10px 12px; text-align:left; color:var(--text-3); font-weight:700; border-bottom:1px solid var(--border-light);">
|
||
الضريبة</th>
|
||
<th
|
||
style="padding:10px 12px; text-align:left; color:var(--text-3); font-weight:700; border-bottom:1px solid var(--border-light);">
|
||
الإجمالي</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<template x-for="item in currentInvoice?.items" :key="item.id">
|
||
<tr style="border-bottom:1px solid var(--border-light);">
|
||
<td style="padding:10px 12px; color:var(--text-2); font-weight:600;"
|
||
x-text="item.description"></td>
|
||
<td style="padding:10px 12px; text-align:center; color:var(--text-3); font-family:'Inter',sans-serif; font-weight:600;"
|
||
x-text="item.quantity"></td>
|
||
<td style="padding:10px 12px; text-align:left; color:var(--success); font-family:'Inter',sans-serif; font-weight:700;"
|
||
x-text="item.unit_price"></td>
|
||
<td style="padding:10px 12px; text-align:left; color:var(--accent); font-family:'Inter',sans-serif; font-weight:600;"
|
||
x-text="(item.tax_rate || 0) + '%'"></td>
|
||
<td style="padding:10px 12px; text-align:left; font-weight:800; color:var(--primary-dark); font-family:'Inter',sans-serif;"
|
||
x-text="item.line_total"></td>
|
||
</tr>
|
||
</template>
|
||
</tbody>
|
||
</table>
|
||
</div>
|
||
|
||
<!-- QR Code -->
|
||
<div x-show="currentInvoice?.status === 'approved' || currentInvoice?.qr_code || currentInvoice?.jofotara?.qr_image_uri"
|
||
style="background:var(--bg); border:1px solid var(--border-light); border-radius:16px; padding:20px; display:flex; flex-direction:column; align-items:center; gap:10px;">
|
||
<div
|
||
style="font-size:11px; font-weight:800; color:var(--text-3); text-transform:uppercase; letter-spacing:0.07em;">
|
||
رمز QR الضريبي
|
||
</div>
|
||
<template x-if="getQrSrc(currentInvoice)">
|
||
<img :src="getQrSrc(currentInvoice)"
|
||
style="width:150px; height:150px; object-fit:contain; border-radius:12px;"
|
||
alt="QR Code">
|
||
</template>
|
||
<div x-show="!getQrSrc(currentInvoice)"
|
||
style="font-size:13px; color:var(--text-3); text-align:center; font-weight:500;">
|
||
جاري توليد الرمز...
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Actions -->
|
||
<div
|
||
style="padding:16px 20px 20px; border-top:1px solid var(--border-light); display:flex; flex-direction:column; gap:10px; background:var(--surface);">
|
||
<!-- Warnings Acknowledgement -->
|
||
<div x-show="currentInvoice?.status === 'extracted' && currentInvoice?.validation_warnings?.length > 0"
|
||
style="background:var(--bg); border:1px solid var(--border-light); border-radius:12px; padding:12px; margin-bottom:4px;">
|
||
<label style="display:flex; gap:10px; align-items:center; cursor:pointer; user-select:none;">
|
||
<input type="checkbox" x-model="acknowledgedWarnings"
|
||
style="width:18px; height:18px; accent-color:var(--success);">
|
||
<span style="font-size:13px; font-weight:700; color:var(--text-2);">لقد راجعت التنبيهات
|
||
وأتحمل مسؤولية الاعتماد</span>
|
||
</label>
|
||
</div>
|
||
|
||
<button x-show="currentInvoice?.status === 'extracted'" @click="approveInvoice"
|
||
class="btn btn-success"
|
||
:disabled="isBusy || (currentInvoice?.validation_warnings?.length > 0 && !acknowledgedWarnings)"
|
||
style="width:100%; justify-content:center;">
|
||
<span x-show="!isBusy">✔️ تدقيق واعتماد</span>
|
||
<span x-show="isBusy">⏳ جاري التدقيق...</span>
|
||
</button>
|
||
<button x-show="currentInvoice?.status === 'extracted'"
|
||
style="width:100%; background:var(--danger-light); color:var(--danger); border:none; padding:12px; border-radius:12px; font-family:inherit; font-size:14px; font-weight:800; cursor:pointer; transition:all 0.2s;"
|
||
onmouseover="this.style.background='#fecaca'"
|
||
onmouseout="this.style.background='var(--danger-light)'">❌ رفض الفاتورة</button>
|
||
<div x-show="currentInvoice?.status === 'approved'"
|
||
style="width:100%; background:var(--success-light); color:#047857; border:1px solid rgba(16,185,129,0.2); padding:12px; border-radius:12px; font-size:15px; font-weight:900; text-align:center;">
|
||
✅ مدققة ومعتمدة
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- ── EDIT TENANT MODAL ────────────────────────────── -->
|
||
<div x-show="showEditTenantModal" x-cloak class="modal-backdrop">
|
||
<div class="modal-box" style="max-width:540px;">
|
||
<div class="modal-head">
|
||
<div class="modal-head-icon primary">⚙️</div>
|
||
<div style="flex:1;">
|
||
<div class="modal-title">تعديل بيانات المكتب</div>
|
||
<div class="modal-subtitle" x-text="currentTenant.name"></div>
|
||
</div>
|
||
<button @click="showEditTenantModal = false" class="modal-close-btn">✕</button>
|
||
</div>
|
||
<div class="modal-divider"></div>
|
||
<form @submit.prevent="updateTenant">
|
||
<div class="modal-body" style="display:flex; flex-direction:column; gap:16px;">
|
||
<div class="form-group">
|
||
<label class="form-label">اسم المكتب</label>
|
||
<input type="text" x-model="currentTenant.name" class="form-input" required>
|
||
</div>
|
||
<div style="display:grid; grid-template-columns:1fr 1fr; gap:12px;">
|
||
<div class="form-group">
|
||
<label class="form-label">البريد الرسمي</label>
|
||
<input type="email" x-model="currentTenant.email" class="form-input" required>
|
||
</div>
|
||
<div class="form-group">
|
||
<label class="form-label">رقم الهاتف</label>
|
||
<input type="text" x-model="currentTenant.phone" class="form-input">
|
||
</div>
|
||
</div>
|
||
<div class="form-group">
|
||
<label class="form-label">حالة المكتب</label>
|
||
<select x-model="currentTenant.status" class="form-input">
|
||
<option value="active">✅ نشط (Active)</option>
|
||
<option value="suspended">🔴 معلق (Suspended)</option>
|
||
<option value="trial">🟡 تجريبي (Trial)</option>
|
||
</select>
|
||
</div>
|
||
</div>
|
||
<div class="modal-divider"></div>
|
||
<div class="modal-footer">
|
||
<button type="submit" class="btn btn-primary" :disabled="isBusy" style="flex:1;">
|
||
<span x-show="!isBusy">💾 حفظ التعديلات</span>
|
||
<span x-show="isBusy">⏳ جاري الحفظ...</span>
|
||
</button>
|
||
<button type="button" @click="showEditTenantModal = false" class="btn btn-ghost">إلغاء</button>
|
||
</div>
|
||
</form>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- ── ADD TENANT MODAL ─────────────────────────────── -->
|
||
<div x-show="showAddTenantModal" x-cloak class="modal-backdrop">
|
||
<div class="modal-box modal-box-lg">
|
||
<div class="modal-head">
|
||
<div class="modal-head-icon accent">🏗️</div>
|
||
<div style="flex:1;">
|
||
<div class="modal-title">إضافة مكتب محاسبي جديد</div>
|
||
<div class="modal-subtitle">سيتم إنشاء حساب المدير المسؤول تلقائياً</div>
|
||
</div>
|
||
<button @click="showAddTenantModal = false" class="modal-close-btn">✕</button>
|
||
</div>
|
||
<div class="modal-divider"></div>
|
||
<form @submit.prevent="createTenant">
|
||
<div class="modal-body" style="display:flex; flex-direction:column; gap:20px;">
|
||
|
||
<!-- Office Info -->
|
||
<div>
|
||
<div class="form-section-title">
|
||
<span>🏢</span> بيانات المكتب المحاسبي
|
||
</div>
|
||
<div style="display:grid; grid-template-columns:1fr 1fr; gap:12px; margin-top:14px;">
|
||
<div class="form-group" style="grid-column:1/-1;">
|
||
<label class="form-label">اسم المكتب</label>
|
||
<input type="text" x-model="newTenant.name" placeholder="اسم المكتب المحاسبي"
|
||
class="form-input" required>
|
||
</div>
|
||
<div class="form-group">
|
||
<label class="form-label">البريد الرسمي للمكتب</label>
|
||
<input type="email" x-model="newTenant.email" placeholder="info@office.com"
|
||
class="form-input" required>
|
||
</div>
|
||
<div class="form-group">
|
||
<label class="form-label">رقم الهاتف <span
|
||
class="form-label-sub">(اختياري)</span></label>
|
||
<input type="text" x-model="newTenant.phone" placeholder="+962 7x xxx xxxx"
|
||
class="form-input">
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Manager Info -->
|
||
<div>
|
||
<div class="form-section-title">
|
||
<span>👤</span> بيانات المدير المسؤول
|
||
</div>
|
||
<div style="display:grid; grid-template-columns:1fr 1fr; gap:12px; margin-top:14px;">
|
||
<div class="form-group" style="grid-column:1/-1;">
|
||
<label class="form-label">الاسم الكامل للمدير</label>
|
||
<input type="text" x-model="newTenant.manager_name" placeholder="الاسم الكامل"
|
||
class="form-input" required>
|
||
</div>
|
||
<div class="form-group">
|
||
<label class="form-label">بريد المدير الإلكتروني</label>
|
||
<input type="email" x-model="newTenant.manager_email" placeholder="manager@office.com"
|
||
class="form-input" required>
|
||
</div>
|
||
<div class="form-group">
|
||
<label class="form-label">كلمة مرور الدخول</label>
|
||
<input type="password" x-model="newTenant.manager_password" placeholder="••••••••"
|
||
class="form-input" required>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div class="modal-divider"></div>
|
||
<div class="modal-footer">
|
||
<button type="submit" class="btn btn-primary" :disabled="isBusy" style="flex:1;">
|
||
<span x-show="!isBusy">🏗️ إنشاء المكتب والمدير</span>
|
||
<span x-show="isBusy">⏳ جاري الإنشاء...</span>
|
||
</button>
|
||
<button type="button" @click="showAddTenantModal = false" class="btn btn-ghost">إلغاء</button>
|
||
</div>
|
||
</form>
|
||
</div>
|
||
</div>
|
||
|
||
|
||
<!-- ════ EXCEL IMPORT MODAL ════════════════════════════ -->
|
||
<div x-show="showExcelModal" class="modal-backdrop" x-cloak @click.self="showExcelModal = false">
|
||
<div class="modal-box" style="max-width: 500px;">
|
||
<div class="modal-head">
|
||
<div class="modal-head-icon success">📊</div>
|
||
<div style="flex:1;">
|
||
<div class="modal-title">استيراد فواتير (Excel/Bulk)</div>
|
||
<div class="modal-subtitle">استخدم هذه الميزة لاستيراد كميات كبيرة من الفواتير</div>
|
||
</div>
|
||
<button @click="showExcelModal = false" class="modal-close-btn">✕</button>
|
||
</div>
|
||
<div class="modal-divider"></div>
|
||
<div class="modal-body" style="display:flex; flex-direction:column; gap:16px;">
|
||
<p style="font-size:14px; color:var(--text-3); font-weight:500;">سيقوم النظام بمحاولة مطابقة الأعمدة
|
||
تلقائياً.</p>
|
||
|
||
<div class="form-group">
|
||
<label class="form-label">الشركة المستهدفة</label>
|
||
<select x-model="uploadData.company_id" class="form-input">
|
||
<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="form-group">
|
||
<label class="form-label">ملف الـ Excel (.xlsx, .csv)</label>
|
||
<input type="file" id="excelFileInput" class="form-input" accept=".xlsx, .xls, .csv"
|
||
style="padding:10px;">
|
||
</div>
|
||
</div>
|
||
<div class="modal-divider"></div>
|
||
<div class="modal-footer">
|
||
<button @click="uploadExcel()" class="btn btn-success w-full justify-center" :disabled="isBusy">
|
||
<span x-show="!isBusy">🚀 بدء الاستيراد الآن</span>
|
||
<span x-show="isBusy">⏳ جاري المعالجة...</span>
|
||
</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- ── UPLOAD PAYMENT RECEIPT MODAL ─────────────────────────── -->
|
||
<div x-show="showPaymentModal" x-cloak class="modal-backdrop">
|
||
<div class="modal-box" style="max-width:440px;">
|
||
<div class="modal-head" style="margin-bottom:8px;">
|
||
<div class="modal-head-icon accent" style="width:40px; height:40px; font-size:20px;">💳</div>
|
||
<div style="flex:1;">
|
||
<div class="modal-title" style="font-size:18px;">تأكيد دفع الاشتراك</div>
|
||
<div class="modal-subtitle" style="font-size:12px;">يرجى تحويل المبلغ ثم رفع وصل الدفع</div>
|
||
</div>
|
||
<button @click="showPaymentModal = false" class="modal-close-btn">✕</button>
|
||
</div>
|
||
|
||
<div class="modal-body" style="padding-top:12px;">
|
||
<div
|
||
style="background:var(--success-light); border:1px solid rgba(16,185,129,0.15); border-radius:12px; padding:14px; margin-bottom:14px;">
|
||
<div style="display:flex; justify-content:space-between; font-size:13px; margin-bottom:6px;">
|
||
<span style="color:var(--text-3); font-weight:600;">الباقة:</span>
|
||
<span style="font-weight:800; color:var(--text-1);" x-text="paymentData.plan_name"></span>
|
||
</div>
|
||
<div style="display:flex; justify-content:space-between; font-size:13px; margin-bottom:6px;">
|
||
<span style="color:var(--text-3); font-weight:600;">المبلغ:</span>
|
||
<span style="font-weight:800; color:var(--success);"
|
||
x-text="paymentData.amount + ' JOD'"></span>
|
||
</div>
|
||
<div
|
||
style="display:flex; justify-content:space-between; font-size:13px; margin-bottom:12px; border-bottom:1px dashed rgba(0,0,0,0.1); padding-bottom:8px;">
|
||
<span style="color:var(--text-3); font-weight:600;">Reference:</span>
|
||
<span style="font-weight:800; color:var(--accent); font-family:'Inter',sans-serif;"
|
||
x-text="paymentData.reference"></span>
|
||
</div>
|
||
|
||
<div style="text-align:center;">
|
||
<p style="margin-bottom:6px; font-size:12px; color:var(--text-2); font-weight:600;">التحويل عبر
|
||
CliQ للحساب:</p>
|
||
<div style="background:white; padding:8px 16px; border:2px solid var(--success); border-radius:10px; font-family:'Inter',sans-serif; font-size:18px; font-weight:900; color:var(--primary-dark); display:inline-block;"
|
||
x-text="paymentData.cliq_alias"></div>
|
||
</div>
|
||
|
||
<div
|
||
style="margin-top:14px; background:white; padding:12px; border-radius:10px; border:1px solid var(--success-light);">
|
||
<div
|
||
style="display:flex; justify-content:space-between; align-items:center; margin-bottom:6px;">
|
||
<label style="color:var(--primary-dark); font-weight:800; font-size:12px;">رقم المرجع (من
|
||
البنك)</label>
|
||
<span
|
||
style="background:var(--success); color:white; font-size:9px; padding:2px 8px; border-radius:6px; font-weight:800;">تفعيل
|
||
فوري ⚡</span>
|
||
</div>
|
||
<input type="text" x-model="paymentData.bank_reference" class="form-input"
|
||
placeholder="ألصق رقم المرجع هنا..."
|
||
style="font-family:'Inter',sans-serif; font-size:14px; padding:8px; border:2px solid var(--success); text-align:center; font-weight:700;">
|
||
</div>
|
||
</div>
|
||
|
||
<div style="margin-bottom:14px;">
|
||
<label
|
||
style="font-size:13px; font-weight:700; color:var(--text-2); display:block; margin-bottom:6px;">صورة
|
||
وصل التحويل (اختياري)</label>
|
||
<input type="file" @change="selectedFile = $event.target.files[0]" class="form-input"
|
||
style="padding:8px; font-size:13px;" accept="image/*,application/pdf">
|
||
</div>
|
||
|
||
<div style="display:flex; gap:8px;">
|
||
<button @click="showPaymentModal = false" class="btn btn-ghost"
|
||
style="flex:1; height:44px; font-size:14px;">إلغاء</button>
|
||
<button @click="uploadReceipt()" class="btn btn-primary" :disabled="isBusy"
|
||
style="flex:2; height:44px; font-size:14px;">
|
||
<span x-show="!isBusy">📤 إرسال الوصل للتفعيل</span>
|
||
<span x-show="isBusy">⏳ جاري الإرسال...</span>
|
||
</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- ════════════════════════════════════════════════════════
|
||
ALPINE.JS — LOGIC UNCHANGED
|
||
════════════════════════════════════════════════════════ -->
|
||
<script>
|
||
document.addEventListener('alpine:init', () => {
|
||
Alpine.data('app', () => ({
|
||
user: JSON.parse(localStorage.getItem('user')),
|
||
page: 'dashboard',
|
||
users: [], companies: [], invoices: [], tenants: [], subscription: null, plans: [],
|
||
showPaymentModal: false,
|
||
paymentData: { cliq_alias: '', amount: 0, plan_name: '', reference: '', payment_id: '' },
|
||
stats: { total: 0, pending: 0, approved: 0 },
|
||
|
||
showAddUserModal: false, showAddCompanyModal: false, showConnectModal: false,
|
||
showUploadModal: false, showViewModal: false, showCompanyStatsModal: false,
|
||
showExcelModal: false, showBatchUploadModal: false,
|
||
isUploadingBatch: false, batchProgress: { total: 0, current: 0 },
|
||
showAddTenantModal: false, showEditTenantModal: false, showTenantStatsModal: false,
|
||
acknowledgedWarnings: false,
|
||
isBusy: false, globalError: '',
|
||
|
||
newUser: { name: '', email: '', password: '', role: 'accountant', tenant_id: '' },
|
||
newCompany: { name: '', tax_identification_number: '', commercial_registration_number: '', address: '', tenant_id: '' },
|
||
newTenant: { name: '', email: '', phone: '', manager_name: '', manager_email: '', manager_password: '' },
|
||
connectData: { client_id: '', secret_key: '', income_source_sequence: '1' },
|
||
uploadData: { company_id: '' },
|
||
currentCompany: null, currentInvoice: null, companyStats: null,
|
||
currentTenant: { name: '', email: '', phone: '', status: '' }, tenantStats: null,
|
||
|
||
init() {
|
||
if (!this.user) { window.location.href = '/login.php'; return; }
|
||
this.loadAll();
|
||
},
|
||
setPage(p) { this.page = p; this.loadAll(); },
|
||
title() { return { dashboard: 'الرئيسية', users: 'فريق العمل', companies: 'الشركات', invoices: 'إدارة الفواتير', tenants: 'المكاتب المحاسبية', subscription: 'إدارة الاشتراك' }[this.page] || ''; },
|
||
subtitle() { return { dashboard: 'نظرة شاملة على نشاط النظام', users: 'إدارة المستخدمين والصلاحيات', companies: 'إدارة الشركات والربط بالفوترة الحكومية', invoices: 'رفع ومعالجة الفواتير الضريبية', tenants: 'إدارة المكاتب المحاسبية المشتركة', subscription: 'تفاصيل باقتك الحالية واستهلاك الموارد' }[this.page] || ''; },
|
||
token() { return localStorage.getItem('access_token'); },
|
||
showError(msg) { this.globalError = msg; setTimeout(() => this.globalError = '', 8000); },
|
||
|
||
async apiRequest(route, method = 'GET', body = null) {
|
||
try {
|
||
const options = {
|
||
method,
|
||
headers: { 'Authorization': 'Bearer ' + this.token(), 'Content-Type': 'application/json' }
|
||
};
|
||
if (body) options.body = JSON.stringify(body);
|
||
const res = await fetch('/index.php?route=' + route, options);
|
||
const json = await res.json();
|
||
if (res.status === 401) { localStorage.clear(); window.location.href = '/login.php'; return null; }
|
||
return json.success ? json.data : (this.showError(json.message), null);
|
||
} catch (e) { this.showError('فشل الاتصال بالخادم'); return null; }
|
||
},
|
||
|
||
async loadAll() {
|
||
const statsData = await this.apiRequest('v1/dashboard/stats');
|
||
this.stats = statsData ? {
|
||
total: statsData.invoices?.total || 0,
|
||
pending: statsData.invoices?.pending || 0,
|
||
approved: statsData.invoices?.approved || 0,
|
||
users: statsData.users || statsData.total_users || 0,
|
||
companies: statsData.companies || 0,
|
||
tenants: statsData.tenants || 0
|
||
} : { total: 0, pending: 0, approved: 0 };
|
||
this.companies = await this.apiRequest('v1/companies') || [];
|
||
this.subscription = await this.apiRequest('v1/subscriptions/current');
|
||
if (this.page === 'users') {
|
||
const usersRes = await this.apiRequest('v1/users');
|
||
this.users = usersRes?.items || usersRes || [];
|
||
}
|
||
if (this.page === 'invoices') {
|
||
const invRes = await this.apiRequest('v1/invoices');
|
||
this.invoices = invRes?.items || invRes || [];
|
||
}
|
||
if (this.page === 'subscription') this.plans = await this.apiRequest('v1/subscriptions/plans') || [];
|
||
if (this.user.role === 'super_admin') this.tenants = await this.apiRequest('v1/tenants') || [];
|
||
},
|
||
//
|
||
getQrSrc(inv) {
|
||
if (!inv) return '';
|
||
if (inv.jofotara?.qr_image_uri) return inv.jofotara.qr_image_uri;
|
||
|
||
let qrData = inv.qr_code;
|
||
|
||
// If no QR data in DB but approved, generate a fallback data string
|
||
if (!qrData && inv.status === 'approved') {
|
||
qrData = `Invoice: ${inv.invoice_number || 'N/A'}\nSupplier: ${inv.supplier_name || 'N/A'}\nTotal: ${inv.grand_total || '0'} JOD\nDate: ${inv.invoice_date || ''}`;
|
||
}
|
||
|
||
if (qrData) {
|
||
if (qrData.startsWith('data:')) return qrData;
|
||
try {
|
||
const qr = new QRious({
|
||
value: qrData,
|
||
size: 300,
|
||
level: 'M'
|
||
});
|
||
return qr.toDataURL();
|
||
} catch (e) {
|
||
console.error('QR Gen Error:', e);
|
||
return '';
|
||
}
|
||
}
|
||
return '';
|
||
},
|
||
|
||
async showCompanyStats(companyId) {
|
||
this.isBusy = true;
|
||
const res = await this.apiRequest('v1/companies/stats&company_id=' + companyId);
|
||
if (res) {
|
||
this.companyStats = res;
|
||
this.showCompanyStatsModal = true;
|
||
}
|
||
this.isBusy = false;
|
||
},
|
||
|
||
async showTenantStats(tenant) {
|
||
this.isBusy = true;
|
||
const res = await this.apiRequest('v1/tenants/stats&tenant_id=' + tenant.id);
|
||
if (res) {
|
||
this.tenantStats = res;
|
||
this.tenantStats.tenant = tenant;
|
||
this.showTenantStatsModal = true;
|
||
}
|
||
this.isBusy = false;
|
||
},
|
||
|
||
async viewInvoice(id) {
|
||
this.isBusy = true;
|
||
const res = await this.apiRequest('v1/invoices/view&id=' + id);
|
||
if (res) {
|
||
this.currentInvoice = res;
|
||
this.showViewModal = true;
|
||
if (!this.currentInvoice.jofotara?.qr_image_uri && !this.currentInvoice.qr_code && this.currentInvoice.status === 'approved') {
|
||
try {
|
||
const qr = new QRious({
|
||
value: 'Invoice: ' + this.currentInvoice.invoice_number + '\nSeller: ' + this.currentInvoice.supplier_name + '\nTotal: ' + this.currentInvoice.grand_total,
|
||
size: 250
|
||
});
|
||
this.currentInvoice.qr_code = qr.toDataURL();
|
||
} catch (e) { }
|
||
}
|
||
}
|
||
this.isBusy = false;
|
||
},
|
||
|
||
async createUser() {
|
||
this.isBusy = true;
|
||
const res = await this.apiRequest('v1/users/create', 'POST', this.newUser);
|
||
if (res) { this.showAddUserModal = false; this.loadAll(); alert('تم إضافة المستخدم بنجاح'); }
|
||
this.isBusy = false;
|
||
},
|
||
|
||
async createCompany() {
|
||
this.isBusy = true;
|
||
const res = await this.apiRequest('v1/companies/create', 'POST', this.newCompany);
|
||
if (res) { this.showAddCompanyModal = false; this.loadAll(); alert('تم إضافة الشركة بنجاح'); }
|
||
this.isBusy = false;
|
||
},
|
||
|
||
async createTenant() {
|
||
this.isBusy = true;
|
||
const res = await this.apiRequest('v1/tenants/create', 'POST', this.newTenant);
|
||
if (res) {
|
||
this.showAddTenantModal = false;
|
||
this.newTenant = { name: '', email: '', phone: '', manager_name: '', manager_email: '', manager_password: '' };
|
||
this.loadAll();
|
||
alert('تم إضافة المكتب المحاسبي والمدير المسؤول بنجاح');
|
||
}
|
||
this.isBusy = false;
|
||
},
|
||
|
||
openEditTenantModal(t) {
|
||
this.currentTenant = JSON.parse(JSON.stringify(t));
|
||
this.showEditTenantModal = true;
|
||
},
|
||
|
||
async updateTenant() {
|
||
this.isBusy = true;
|
||
const res = await this.apiRequest('v1/tenants/update', 'POST', this.currentTenant);
|
||
if (res) {
|
||
this.showEditTenantModal = false;
|
||
this.loadAll();
|
||
alert('تم تحديث بيانات المكتب بنجاح');
|
||
}
|
||
this.isBusy = false;
|
||
},
|
||
|
||
openConnectModal(company) {
|
||
this.currentCompany = company;
|
||
this.connectData.client_id = '';
|
||
this.connectData.secret_key = '';
|
||
this.showConnectModal = true;
|
||
},
|
||
|
||
async connectJoFotara() {
|
||
this.isBusy = true;
|
||
const res = await this.apiRequest('v1/companies/connect', 'POST', {
|
||
id: this.currentCompany.id,
|
||
...this.connectData
|
||
});
|
||
if (res) { this.showConnectModal = false; this.loadAll(); alert('تم تفعيل الربط الضريبي بنجاح'); }
|
||
this.isBusy = false;
|
||
},
|
||
|
||
selectedFile: null,
|
||
isUploading: false,
|
||
|
||
async uploadInvoice() {
|
||
if (!this.selectedFile) return alert('الرجاء اختيار ملف');
|
||
if (!this.uploadData.company_id) return alert('الرجاء اختيار الشركة');
|
||
|
||
this.isUploading = true;
|
||
const formData = new FormData();
|
||
formData.append('company_id', this.uploadData.company_id);
|
||
formData.append('invoice', this.selectedFile);
|
||
|
||
try {
|
||
const res = await fetch('/index.php?route=v1/invoices/upload', {
|
||
method: 'POST',
|
||
headers: { 'Authorization': 'Bearer ' + this.token() },
|
||
body: formData
|
||
});
|
||
const json = await res.json();
|
||
this.isUploading = false;
|
||
|
||
if (json.success) {
|
||
this.showUploadModal = false;
|
||
this.selectedFile = null;
|
||
await this.loadAll();
|
||
this.viewInvoice(json.data.id);
|
||
} else {
|
||
this.showError(json.message);
|
||
}
|
||
} catch (e) {
|
||
this.isUploading = false;
|
||
this.showError('فشل الاتصال بالخادم أثناء الرفع');
|
||
}
|
||
},
|
||
|
||
async uploadBatchInvoices() {
|
||
const fileInput = document.getElementById('batchFileInput');
|
||
if (!fileInput.files.length) return alert('الرجاء اختيار ملفات');
|
||
if (!this.uploadData.company_id) return alert('الرجاء اختيار الشركة');
|
||
|
||
this.isUploadingBatch = true;
|
||
this.batchProgress = { total: fileInput.files.length, current: 0 };
|
||
|
||
try {
|
||
// 1. Create batch
|
||
const batchRes = await fetch('/index.php?route=v1/batches/create', {
|
||
method: 'POST',
|
||
headers: { 'Authorization': 'Bearer ' + this.token(), 'Content-Type': 'application/json' },
|
||
body: JSON.stringify({ company_id: this.uploadData.company_id, expected_images: fileInput.files.length, source: 'web_batch' })
|
||
}).then(r => r.json());
|
||
|
||
if (!batchRes.success) {
|
||
this.isUploadingBatch = false;
|
||
return this.showError(batchRes.message);
|
||
}
|
||
|
||
const batchId = batchRes.data.batch_id;
|
||
|
||
// 2. Upload files sequentially
|
||
for (let i = 0; i < fileInput.files.length; i++) {
|
||
const file = fileInput.files[i];
|
||
const formData = new FormData();
|
||
formData.append('batch_id', batchId);
|
||
formData.append('image_order', i + 1);
|
||
formData.append('image', file);
|
||
|
||
await fetch('/index.php?route=v1/batches/upload-image', {
|
||
method: 'POST',
|
||
headers: { 'Authorization': 'Bearer ' + this.token() },
|
||
body: formData
|
||
});
|
||
this.batchProgress.current = i + 1;
|
||
}
|
||
|
||
// 3. Finalize
|
||
await fetch('/index.php?route=v1/batches/finalize', {
|
||
method: 'POST',
|
||
headers: { 'Authorization': 'Bearer ' + this.token(), 'Content-Type': 'application/json' },
|
||
body: JSON.stringify({ batch_id: batchId })
|
||
});
|
||
|
||
this.isUploadingBatch = false;
|
||
this.showBatchUploadModal = false;
|
||
alert('تم رفع الدفعة بنجاح! جاري معالجتها في الخلفية.');
|
||
this.loadAll();
|
||
fileInput.value = '';
|
||
} catch (e) {
|
||
this.isUploadingBatch = false;
|
||
this.showError('فشل الاتصال بالخادم أثناء الرفع المجمع');
|
||
}
|
||
},
|
||
|
||
async approveInvoice() {
|
||
if (!this.currentInvoice || this.isBusy) return;
|
||
this.isBusy = true;
|
||
try {
|
||
const res = await fetch('/index.php?route=v1/invoices/approve', {
|
||
method: 'POST',
|
||
headers: { 'Authorization': 'Bearer ' + this.token(), 'Content-Type': 'application/json' },
|
||
body: JSON.stringify({ id: this.currentInvoice.id })
|
||
});
|
||
const json = await res.json();
|
||
this.isBusy = false;
|
||
|
||
if (json.success) {
|
||
alert('تم اعتماد الفاتورة بنجاح!');
|
||
this.showViewModal = false;
|
||
this.loadAll();
|
||
} else {
|
||
this.showError(json.message);
|
||
}
|
||
} catch (e) {
|
||
this.isBusy = false;
|
||
this.showError('حدث خطأ أثناء الاعتماد');
|
||
}
|
||
},
|
||
|
||
async uploadExcel() {
|
||
const fileInput = document.getElementById('excelFileInput');
|
||
if (!fileInput.files[0]) return alert('الرجاء اختيار ملف اكسل');
|
||
if (!this.uploadData.company_id) return alert('الرجاء اختيار الشركة');
|
||
|
||
this.isBusy = true;
|
||
const formData = new FormData();
|
||
formData.append('company_id', this.uploadData.company_id);
|
||
formData.append('file', fileInput.files[0]);
|
||
|
||
try {
|
||
const res = await fetch('/index.php?route=v1/excel/import', {
|
||
method: 'POST',
|
||
headers: { 'Authorization': 'Bearer ' + this.token() },
|
||
body: formData
|
||
});
|
||
const json = await res.json();
|
||
this.isBusy = false;
|
||
|
||
if (json.success) {
|
||
alert(json.message);
|
||
this.showExcelModal = false;
|
||
fileInput.value = '';
|
||
this.loadAll();
|
||
} else {
|
||
this.showError(json.message);
|
||
}
|
||
} catch (e) {
|
||
this.isBusy = false;
|
||
this.showError('فشل الاتصال بالخادم أثناء استيراد الاكسل');
|
||
}
|
||
},
|
||
|
||
async upgradePlan(plan) {
|
||
if (this.isBusy) return;
|
||
this.isBusy = true;
|
||
try {
|
||
const res = await fetch('/index.php?route=v1/payments/create', {
|
||
method: 'POST',
|
||
headers: { 'Authorization': 'Bearer ' + this.token(), 'Content-Type': 'application/json' },
|
||
body: JSON.stringify({ plan_id: plan.id })
|
||
});
|
||
const json = await res.json();
|
||
this.isBusy = false;
|
||
|
||
if (json.success) {
|
||
this.paymentData = {
|
||
cliq_alias: json.data.cliq_alias,
|
||
amount: json.data.amount_jod,
|
||
plan_name: json.data.plan_name,
|
||
reference: json.data.reference_number,
|
||
payment_id: json.data.payment_id
|
||
};
|
||
this.showPaymentModal = true;
|
||
} else {
|
||
this.showError(json.message);
|
||
}
|
||
} catch (e) {
|
||
this.isBusy = false;
|
||
this.showError('فشل في إنشاء طلب الدفع. يرجى المحاولة لاحقاً.');
|
||
}
|
||
},
|
||
|
||
async uploadReceipt() {
|
||
if (!this.selectedFile || this.isBusy) return alert('الرجاء اختيار صورة الوصل');
|
||
if (!this.paymentData.bank_reference) return alert('الرجاء إدخال رقم مرجع الحوالة للتفعيل الآلي');
|
||
|
||
this.isBusy = true;
|
||
const formData = new FormData();
|
||
formData.append('payment_id', this.paymentData.payment_id);
|
||
formData.append('bank_reference', this.paymentData.bank_reference);
|
||
formData.append('receipt', this.selectedFile);
|
||
|
||
try {
|
||
const res = await fetch('/index.php?route=v1/payments/upload-receipt', {
|
||
method: 'POST',
|
||
headers: { 'Authorization': 'Bearer ' + this.token() },
|
||
body: formData
|
||
});
|
||
const json = await res.json();
|
||
this.isBusy = false;
|
||
|
||
if (json.success) {
|
||
alert(json.message);
|
||
this.showPaymentModal = false;
|
||
this.selectedFile = null;
|
||
await this.loadAll();
|
||
} else {
|
||
this.showError(json.message);
|
||
}
|
||
} catch (e) {
|
||
this.isBusy = false;
|
||
this.showError('حدث خطأ أثناء رفع الوصل.');
|
||
}
|
||
},
|
||
|
||
async cancelPayment(paymentId) {
|
||
if (!confirm('هل أنت متأكد من إلغاء طلب الترقية؟')) return;
|
||
this.isBusy = true;
|
||
try {
|
||
const res = await fetch('/index.php?route=v1/payments/delete', {
|
||
method: 'POST',
|
||
headers: { 'Authorization': 'Bearer ' + this.token(), 'Content-Type': 'application/json' },
|
||
body: JSON.stringify({ payment_id: paymentId })
|
||
});
|
||
const json = await res.json();
|
||
this.isBusy = false;
|
||
if (json.success) {
|
||
await this.loadAll();
|
||
} else {
|
||
this.showError(json.message);
|
||
}
|
||
} catch (e) {
|
||
this.isBusy = false;
|
||
this.showError('فشل إلغاء الطلب.');
|
||
}
|
||
},
|
||
|
||
logout() { localStorage.clear(); window.location.href = '/login.php'; }
|
||
}));
|
||
});
|
||
</script>
|
||
</body>
|
||
|
||
</html> |