Files
musadaq-saas/public/shell.php
2026-05-09 21:35:36 +03:00

3298 lines
161 KiB
PHP
Raw Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<!DOCTYPE html>
<html lang="ar" dir="rtl" data-theme="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=IBM+Plex+Sans+Arabic:wght@300;400;500;600;700&family=Outfit:wght@400;500;600;700;800&family=El+Messiri:wght@500;600;700&family=DM+Sans:wght@400;500;600;700;800&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>
/* ═══════════════════════════════════════════════
مُصادَق — INDIGO DUSK DESIGN SYSTEM
═══════════════════════════════════════════════ */
:root {
/* ── Core Background ── */
--bg: #EFEFF8;
--bg-card: #FFFFFF;
--border: #E2E1F0;
--border-mid: #CCCBE0;
/* ── Sidebar ── */
--sb-bg1: #0E0929;
--sb-bg2: #1C1550;
--sb-text: rgba(200, 195, 240, 0.65);
--sb-text-a: #FFFFFF;
--sb-hover: rgba(139, 92, 246, 0.10);
--sb-active-bg: rgba(139, 92, 246, 0.20);
--sb-accent: #A78BFA;
/* ── Violet (primary) ── */
--violet: #5B21B6;
--violet-mid: #6D28D9;
--violet-bright: #7C3AED;
--violet-subtle: #EDE9FE;
--violet-glow: rgba(109, 40, 217, 0.14);
/* ── Green ── */
--green: #065F46;
--green-mid: #059669;
--green-bright: #10B981;
--green-subtle: #D1FAE5;
/* ── Amber ── */
--amber: #92400E;
--amber-mid: #D97706;
--amber-bright: #F59E0B;
--amber-subtle: #FEF3C7;
/* ── Red ── */
--red: #991B1B;
--red-mid: #DC2626;
--red-subtle: #FEE2E2;
/* ── Blue ── */
--blue: #1D4ED8;
--blue-subtle: #DBEAFE;
/* ── Text ── */
--text-1: #1A1040;
--text-2: #3D3460;
--text-3: #8B82B0;
/* ── Shadows ── */
--shadow-xs: 0 1px 3px rgba(60, 30, 140, 0.06);
--shadow-sm: 0 2px 8px rgba(60, 30, 140, 0.08);
--shadow: 0 4px 18px rgba(60, 30, 140, 0.11);
--shadow-md: 0 8px 32px rgba(60, 30, 140, 0.16);
--shadow-lg: 0 20px 60px rgba(60, 30, 140, 0.22);
}
* {
box-sizing: border-box;
margin: 0;
padding: 0;
}
body {
font-family: 'IBM Plex Sans Arabic', -apple-system, BlinkMacSystemFont, sans-serif;
background: var(--bg);
color: var(--text-1);
-webkit-font-smoothing: antialiased;
font-size: 14px;
line-height: 1.6;
}
h1,
h2,
h3,
h4,
h5,
h6 {
font-family: 'El Messiri', 'IBM Plex Sans Arabic', sans-serif;
font-weight: 700;
}
.num-font {
font-family: 'Outfit', 'DM Sans', sans-serif;
}
[x-cloak] {
display: none !important;
}
/* ── SCROLLBAR ─────────────────────────── */
::-webkit-scrollbar {
width: 5px;
height: 5px;
}
::-webkit-scrollbar-track {
background: transparent;
}
::-webkit-scrollbar-thumb {
background: #C8C2E0;
border-radius: 10px;
}
/* ══════════════════════════════════════════
SIDEBAR
══════════════════════════════════════════ */
.sidebar {
width: 258px;
min-width: 258px;
background: linear-gradient(170deg, var(--sb-bg1) 0%, var(--sb-bg2) 100%);
display: flex;
flex-direction: column;
height: 100vh;
position: sticky;
top: 0;
z-index: 50;
box-shadow: 4px 0 30px rgba(10, 5, 50, 0.35);
}
/* Subtle grid overlay on sidebar */
.sidebar::before {
content: '';
position: absolute;
inset: 0;
background-image: radial-gradient(circle at 1px 1px, rgba(255, 255, 255, 0.03) 1px, transparent 0);
background-size: 24px 24px;
pointer-events: none;
}
.sb-logo-area {
padding: 26px 20px 18px;
border-bottom: 1px solid rgba(255, 255, 255, 0.06);
position: relative;
}
.sb-logo-wrap {
display: flex;
align-items: center;
gap: 12px;
}
.sb-logo-mark {
width: 42px;
height: 42px;
border-radius: 14px;
overflow: hidden;
background: rgba(167, 139, 250, 0.20);
display: flex;
align-items: center;
justify-content: center;
flex-shrink: 0;
box-shadow: 0 4px 14px rgba(139, 92, 246, 0.35);
border: 1px solid rgba(167, 139, 250, 0.25);
}
.sb-logo-mark img {
width: 100%;
height: 100%;
object-fit: cover;
}
.sb-brand-name {
font-family: 'El Messiri', sans-serif;
font-size: 22px;
font-weight: 700;
color: white;
line-height: 1;
}
.sb-brand-sub {
font-size: 10px;
color: rgba(167, 139, 250, 0.55);
letter-spacing: 0.05em;
margin-top: 4px;
}
/* ── NAV ── */
.sb-nav {
flex: 1;
padding: 14px 12px;
display: flex;
flex-direction: column;
gap: 2px;
overflow-y: auto;
}
.sb-section-label {
font-size: 9.5px;
font-weight: 700;
color: rgba(167, 139, 250, 0.35);
letter-spacing: 0.14em;
text-transform: uppercase;
padding: 14px 10px 6px;
}
.nav-btn {
display: flex;
align-items: center;
gap: 11px;
padding: 9px 12px;
border-radius: 12px;
cursor: pointer;
border: none;
background: transparent;
color: var(--sb-text);
font-family: 'IBM Plex Sans Arabic', sans-serif;
font-size: 13.5px;
font-weight: 500;
width: 100%;
text-align: right;
transition: all 0.16s ease;
position: relative;
}
.nav-btn:hover {
background: var(--sb-hover);
color: rgba(220, 215, 255, 0.9);
}
.nav-btn.active {
background: var(--sb-active-bg);
color: #FFFFFF;
font-weight: 600;
box-shadow: 0 2px 12px rgba(139, 92, 246, 0.22);
}
.nav-icon {
width: 32px;
height: 32px;
border-radius: 9px;
background: rgba(255, 255, 255, 0.06);
display: flex;
align-items: center;
justify-content: center;
font-size: 15px;
flex-shrink: 0;
transition: all 0.16s;
}
.nav-btn.active .nav-icon {
background: var(--sb-accent);
box-shadow: 0 3px 10px rgba(167, 139, 250, 0.40);
color: var(--sb-bg1);
}
.nav-btn:hover .nav-icon {
background: rgba(255, 255, 255, 0.10);
}
/* ── SIDEBAR USER AREA ── */
.sb-user-area {
padding: 14px;
border-top: 1px solid rgba(255, 255, 255, 0.06);
}
.sb-user-card {
display: flex;
align-items: center;
gap: 10px;
padding: 10px 12px;
border-radius: 12px;
background: rgba(255, 255, 255, 0.05);
border: 1px solid rgba(255, 255, 255, 0.07);
margin-bottom: 8px;
}
.user-avatar-sb {
width: 34px;
height: 34px;
border-radius: 9px;
background: linear-gradient(135deg, #7C3AED, #A78BFA);
color: white;
display: flex;
align-items: center;
justify-content: center;
font-weight: 700;
font-size: 14px;
flex-shrink: 0;
}
.role-pill {
display: inline-block;
padding: 2px 8px;
border-radius: 20px;
font-size: 10px;
font-weight: 600;
background: rgba(167, 139, 250, 0.15);
color: rgba(167, 139, 250, 0.80);
letter-spacing: 0.04em;
}
.logout-btn {
width: 100%;
display: flex;
align-items: center;
gap: 8px;
padding: 8px 12px;
border-radius: 10px;
background: transparent;
border: none;
color: rgba(167, 139, 250, 0.45);
cursor: pointer;
font-family: 'IBM Plex Sans Arabic', sans-serif;
font-size: 13px;
font-weight: 500;
transition: all 0.16s;
text-align: right;
}
.logout-btn:hover {
background: rgba(220, 38, 38, 0.12);
color: #FCA5A5;
}
/* ══════════════════════════════════════════
MAIN LAYOUT
══════════════════════════════════════════ */
.main-area {
flex: 1;
overflow-y: auto;
display: flex;
flex-direction: column;
min-width: 0;
}
/* ── PAGE HEADER ── */
.page-header {
padding: 22px 36px 20px;
background: var(--bg-card);
border-bottom: 1px solid var(--border);
display: flex;
align-items: center;
justify-content: space-between;
gap: 16px;
position: sticky;
top: 0;
z-index: 40;
box-shadow: var(--shadow-xs);
}
.page-title {
font-size: 20px;
font-weight: 700;
color: var(--text-1);
}
.page-subtitle {
font-size: 12px;
color: var(--text-3);
margin-top: 2px;
}
.page-content {
padding: 28px 36px;
flex: 1;
}
/* ══════════════════════════════════════════
STAT CARDS
══════════════════════════════════════════ */
.stat-card {
background: var(--bg-card);
border-radius: 20px;
padding: 24px;
border: 1px solid var(--border);
box-shadow: var(--shadow-sm);
position: relative;
overflow: hidden;
transition: transform 0.2s, box-shadow 0.2s;
}
.stat-card:hover {
transform: translateY(-3px);
box-shadow: var(--shadow);
}
.stat-card-bg {
position: absolute;
left: -10px;
bottom: -10px;
font-size: 90px;
opacity: 0.045;
line-height: 1;
pointer-events: none;
}
.stat-icon-box {
width: 48px;
height: 48px;
border-radius: 14px;
display: flex;
align-items: center;
justify-content: center;
font-size: 21px;
margin-bottom: 18px;
}
.stat-label {
font-size: 11.5px;
font-weight: 700;
color: var(--text-3);
text-transform: uppercase;
letter-spacing: 0.07em;
}
.stat-value {
font-family: 'El Messiri', sans-serif;
font-size: 42px;
font-weight: 700;
margin-top: 4px;
line-height: 1;
}
/* Violet variant */
.stat-violet .stat-icon-box {
background: var(--violet-subtle);
}
.stat-violet .stat-value {
color: var(--violet-mid);
}
.stat-violet {
border-top: 3px solid var(--violet-bright);
}
/* Amber variant */
.stat-amber .stat-icon-box {
background: var(--amber-subtle);
}
.stat-amber .stat-value {
color: var(--amber-mid);
}
.stat-amber {
border-top: 3px solid var(--amber-bright);
}
/* Green variant */
.stat-green .stat-icon-box {
background: var(--green-subtle);
}
.stat-green .stat-value {
color: var(--green-mid);
}
.stat-green {
border-top: 3px solid var(--green-bright);
}
/* Red variant */
.stat-red .stat-icon-box {
background: var(--red-subtle);
}
.stat-red .stat-value {
color: var(--red-mid);
}
.stat-red {
border-top: 3px solid var(--red-mid);
}
/* Blue variant */
.stat-blue .stat-icon-box {
background: var(--blue-subtle);
}
.stat-blue .stat-value {
color: var(--blue);
}
.stat-blue {
border-top: 3px solid var(--blue);
}
/* ══════════════════════════════════════════
QUICK ACTIONS
══════════════════════════════════════════ */
.quick-action-card {
background: var(--bg-card);
border: 1.5px solid var(--border);
border-radius: 16px;
padding: 18px 20px;
display: flex;
align-items: center;
gap: 14px;
cursor: pointer;
transition: all 0.2s;
text-align: right;
box-shadow: var(--shadow-xs);
}
.quick-action-card:hover {
border-color: var(--violet-bright);
box-shadow: 0 6px 20px rgba(109, 40, 217, 0.14);
transform: translateY(-2px);
}
.qa-icon {
width: 44px;
height: 44px;
border-radius: 12px;
display: flex;
align-items: center;
justify-content: center;
font-size: 20px;
flex-shrink: 0;
}
/* ══════════════════════════════════════════
DATA TABLE
══════════════════════════════════════════ */
.table-container {
background: var(--bg-card);
border-radius: 20px;
border: 1px solid var(--border);
box-shadow: var(--shadow-sm);
overflow: hidden;
}
.table-top-bar {
padding: 18px 24px;
border-bottom: 1px solid var(--border);
display: flex;
align-items: center;
gap: 10px;
background: linear-gradient(to left, rgba(237, 233, 254, 0.3), transparent);
}
.table-top-bar h3 {
font-size: 15.5px;
font-weight: 700;
color: var(--text-1);
}
.table-count {
padding: 2px 10px;
background: var(--violet-subtle);
border: 1px solid rgba(109, 40, 217, 0.15);
border-radius: 20px;
font-size: 12px;
font-weight: 600;
color: var(--violet-mid);
}
.data-table {
width: 100%;
border-collapse: collapse;
}
.data-table th {
padding: 12px 20px;
text-align: right;
font-size: 11px;
font-weight: 700;
color: var(--text-3);
text-transform: uppercase;
letter-spacing: 0.07em;
border-bottom: 1px solid var(--border);
background: #F8F7FD;
white-space: nowrap;
}
.data-table td {
padding: 14px 20px;
border-bottom: 1px solid #F0EFF8;
vertical-align: middle;
}
.data-table tbody tr:last-child td {
border-bottom: none;
}
.data-table tbody tr {
transition: background 0.12s;
}
.data-table tbody tr:hover {
background: #FAF9FE;
}
.empty-row {
text-align: center;
padding: 56px 24px;
}
.empty-icon {
font-size: 44px;
opacity: 0.20;
display: block;
margin-bottom: 14px;
}
.empty-msg {
font-size: 14px;
color: var(--text-3);
}
/* ══════════════════════════════════════════
BADGES
══════════════════════════════════════════ */
.badge {
display: inline-flex;
align-items: center;
gap: 5px;
padding: 4px 11px;
border-radius: 20px;
font-size: 12px;
font-weight: 600;
white-space: nowrap;
}
.badge-teal {
background: var(--green-subtle);
color: var(--green-mid);
}
.badge-amber {
background: var(--amber-subtle);
color: var(--amber-mid);
}
.badge-blue {
background: var(--blue-subtle);
color: var(--blue);
}
.badge-gray {
background: #F1F0FA;
color: #7B6FA0;
}
.badge-navy {
background: var(--violet-subtle);
color: var(--violet-mid);
}
.badge-red {
background: var(--red-subtle);
color: var(--red-mid);
}
.badge-purple {
background: var(--violet-subtle);
color: var(--violet-bright);
}
.badge-dot {
width: 7px;
height: 7px;
border-radius: 50%;
display: inline-block;
flex-shrink: 0;
}
/* ══════════════════════════════════════════
BUTTONS
══════════════════════════════════════════ */
.btn {
display: inline-flex;
align-items: center;
gap: 7px;
padding: 10px 20px;
border-radius: 12px;
font-family: 'IBM Plex Sans Arabic', sans-serif;
font-size: 14px;
font-weight: 600;
cursor: pointer;
border: none;
transition: all 0.18s;
white-space: nowrap;
}
.btn:disabled {
opacity: 0.55;
cursor: not-allowed;
}
.btn-navy {
background: linear-gradient(135deg, var(--violet-mid), var(--violet-bright));
color: white;
box-shadow: 0 3px 12px rgba(109, 40, 217, 0.28);
}
.btn-navy:hover:not(:disabled) {
transform: translateY(-1px);
box-shadow: 0 6px 18px rgba(109, 40, 217, 0.36);
}
.btn-teal {
background: linear-gradient(135deg, var(--green), var(--green-mid));
color: white;
box-shadow: 0 3px 12px rgba(5, 150, 105, 0.25);
}
.btn-teal:hover:not(:disabled) {
transform: translateY(-1px);
box-shadow: 0 6px 18px rgba(5, 150, 105, 0.32);
}
.btn-ghost {
background: var(--bg-card);
color: var(--text-2);
border: 1.5px solid var(--border);
}
.btn-ghost:hover {
background: var(--bg);
border-color: var(--border-mid);
}
.btn-sm {
padding: 7px 14px;
font-size: 13px;
}
.btn-icon {
padding: 8px 12px;
}
/* Table action buttons */
.btn-table-action {
display: inline-flex;
align-items: center;
gap: 5px;
padding: 6px 13px;
border-radius: 9px;
font-family: inherit;
font-size: 12px;
font-weight: 600;
cursor: pointer;
border: none;
transition: all 0.15s;
}
.btn-ta-navy {
background: var(--violet-subtle);
color: var(--violet-mid);
}
.btn-ta-navy:hover {
background: var(--violet-mid);
color: white;
}
.btn-ta-teal {
background: var(--green-subtle);
color: var(--green-mid);
}
.btn-ta-teal:hover {
background: var(--green-mid);
color: white;
}
.btn-ta-red {
background: transparent;
color: #B0A8CC;
border-radius: 8px;
padding: 6px 10px;
}
.btn-ta-red:hover {
background: var(--red-subtle);
color: var(--red-mid);
}
/* ══════════════════════════════════════════
FORM ELEMENTS
══════════════════════════════════════════ */
.form-group {
display: flex;
flex-direction: column;
gap: 6px;
}
.form-label {
font-size: 13px;
font-weight: 600;
color: var(--text-2);
display: flex;
align-items: center;
gap: 5px;
}
.form-label-sub {
font-size: 11px;
color: var(--text-3);
font-weight: 400;
}
.form-input {
width: 100%;
padding: 11px 14px;
border: 1.5px solid var(--border);
border-radius: 11px;
font-family: 'IBM Plex Sans Arabic', sans-serif;
font-size: 14px;
color: var(--text-1);
background: #FAFAFA;
outline: none;
transition: border-color 0.18s, box-shadow 0.18s;
direction: rtl;
}
.form-input:focus {
border-color: var(--violet-mid);
background: white;
box-shadow: 0 0 0 3px rgba(109, 40, 217, 0.08);
}
.form-input::placeholder {
color: var(--text-3);
}
.form-input.mono {
font-family: 'Outfit', sans-serif;
}
.form-section-title {
font-size: 11px;
font-weight: 700;
color: var(--text-3);
text-transform: uppercase;
letter-spacing: 0.08em;
padding-bottom: 12px;
border-bottom: 1px solid var(--border);
margin-bottom: 4px;
display: flex;
align-items: center;
gap: 8px;
}
/* ══════════════════════════════════════════
MODALS
══════════════════════════════════════════ */
.modal-backdrop {
position: fixed;
inset: 0;
background: rgba(14, 9, 41, 0.60);
backdrop-filter: blur(6px);
display: flex;
align-items: center;
justify-content: center;
z-index: 100;
padding: 20px;
}
.modal-box {
background: var(--bg-card);
border-radius: 22px;
box-shadow: var(--shadow-lg);
width: 100%;
max-width: 480px;
max-height: 92vh;
overflow-y: auto;
position: relative;
}
.modal-box-lg {
max-width: 620px;
}
.modal-head {
display: flex;
align-items: center;
gap: 14px;
padding: 22px 24px 18px;
}
.modal-head-icon {
width: 44px;
height: 44px;
border-radius: 13px;
display: flex;
align-items: center;
justify-content: center;
font-size: 20px;
flex-shrink: 0;
}
.modal-head-icon.navy {
background: var(--violet-subtle);
}
.modal-head-icon.gold {
background: var(--amber-subtle);
}
.modal-head-icon.teal {
background: var(--green-subtle);
}
.modal-head-icon.red {
background: var(--red-subtle);
}
.modal-title {
font-size: 17px;
font-weight: 700;
color: var(--text-1);
}
.modal-subtitle {
font-size: 12px;
color: var(--text-3);
margin-top: 2px;
}
.modal-close-btn {
width: 32px;
height: 32px;
border-radius: 9px;
background: var(--bg);
border: 1px solid var(--border);
cursor: pointer;
color: var(--text-3);
display: flex;
align-items: center;
justify-content: center;
font-size: 15px;
transition: all 0.15s;
flex-shrink: 0;
}
.modal-close-btn:hover {
background: var(--red-subtle);
color: var(--red-mid);
border-color: rgba(220, 38, 38, 0.2);
}
.modal-divider {
height: 1px;
background: var(--border);
margin: 0;
}
.modal-body {
padding: 20px 24px;
}
.modal-footer {
display: flex;
align-items: center;
gap: 10px;
padding: 16px 24px 20px;
}
/* ══════════════════════════════════════════
TOAST / ERROR
══════════════════════════════════════════ */
.toast-error {
position: fixed;
top: 20px;
left: 50%;
transform: translateX(-50%);
z-index: 300;
background: white;
border-right: 4px solid var(--red-mid);
border-radius: 14px;
padding: 14px 22px;
box-shadow: var(--shadow-md);
color: var(--red-mid);
font-weight: 600;
font-size: 14px;
max-width: 480px;
width: 90%;
text-align: center;
cursor: pointer;
}
/* ══════════════════════════════════════════
JOFOTARA STATUS
══════════════════════════════════════════ */
.jofotara-connected {
display: inline-flex;
align-items: center;
gap: 6px;
cursor: pointer;
font-size: 13px;
font-weight: 600;
color: var(--green-mid);
}
.jofotara-connect {
display: inline-flex;
align-items: center;
gap: 6px;
cursor: pointer;
font-size: 13px;
font-weight: 600;
color: var(--text-3);
text-decoration: underline;
}
.jofotara-connect:hover {
color: var(--violet-mid);
}
.conn-dot {
width: 8px;
height: 8px;
border-radius: 50%;
flex-shrink: 0;
}
.conn-dot.on {
background: var(--green-bright);
box-shadow: 0 0 0 3px rgba(16, 185, 129, 0.18);
}
.conn-dot.off {
background: #C8C2E0;
}
/* ══════════════════════════════════════════
INVOICE VIEW
══════════════════════════════════════════ */
.invoice-field-card {
background: #F8F7FD;
border: 1px solid var(--border);
border-radius: 12px;
padding: 13px 15px;
}
.invoice-field-label {
font-size: 10.5px;
font-weight: 700;
color: var(--text-3);
text-transform: uppercase;
letter-spacing: 0.07em;
margin-bottom: 5px;
}
.invoice-field-value {
font-size: 15px;
font-weight: 600;
color: var(--text-1);
}
/* ══════════════════════════════════════════
STATS MODAL TABLE
══════════════════════════════════════════ */
.stats-table {
width: 100%;
border-collapse: collapse;
font-size: 13px;
}
.stats-table th {
padding: 10px 16px;
text-align: right;
font-weight: 700;
color: var(--text-3);
font-size: 11px;
text-transform: uppercase;
background: #F8F7FD;
border-bottom: 1px solid var(--border);
}
.stats-table td {
padding: 11px 16px;
border-bottom: 1px solid #F0EFF8;
}
.stats-table tbody tr:last-child td {
border-bottom: none;
}
.stats-table tbody tr:hover {
background: #FAF9FE;
}
.mini-stat {
background: var(--bg-card);
border: 1px solid var(--border);
border-radius: 15px;
padding: 18px 20px;
}
.mini-stat-label {
font-size: 11px;
font-weight: 700;
color: var(--text-3);
text-transform: uppercase;
letter-spacing: 0.06em;
margin-bottom: 8px;
}
.mini-stat-val {
font-family: 'El Messiri', sans-serif;
font-size: 28px;
font-weight: 700;
color: var(--text-1);
}
/* ══════════════════════════════════════════
SUBSCRIPTION PAGE
══════════════════════════════════════════ */
.plan-card {
background: var(--bg-card);
border-radius: 22px;
padding: 36px;
border: 2px solid var(--border);
display: flex;
flex-direction: column;
gap: 18px;
transition: all 0.25s ease;
position: relative;
}
.plan-card:hover {
border-color: var(--violet-bright);
box-shadow: var(--shadow-md);
}
.plan-card.active-plan {
border-color: var(--violet-mid);
box-shadow: 0 8px 32px rgba(109, 40, 217, 0.18);
}
.plan-current-badge {
position: absolute;
top: -13px;
left: 50%;
transform: translateX(-50%);
background: linear-gradient(135deg, var(--violet-mid), var(--violet-bright));
color: white;
padding: 4px 18px;
border-radius: 20px;
font-size: 11.5px;
font-weight: 700;
box-shadow: 0 4px 14px rgba(109, 40, 217, 0.32);
white-space: nowrap;
}
.usage-card {
background: var(--bg-card);
border-radius: 18px;
padding: 22px 24px;
border: 1px solid var(--border);
box-shadow: var(--shadow-xs);
}
.usage-bar-bg {
height: 8px;
background: var(--bg);
border-radius: 8px;
overflow: hidden;
margin: 10px 0;
}
.usage-bar-fill {
height: 100%;
border-radius: 8px;
transition: width 1s ease;
}
/* ══════════════════════════════════════════
EXCEL MODAL (legacy tailwind classes)
══════════════════════════════════════════ */
.modal-card {
background: var(--bg-card);
border-radius: 22px;
box-shadow: var(--shadow-lg);
width: 100%;
max-width: 480px;
}
.modal-header {
display: flex;
align-items: center;
justify-content: space-between;
padding: 20px 24px;
border-bottom: 1px solid var(--border);
font-family: 'El Messiri', sans-serif;
font-size: 17px;
font-weight: 700;
color: var(--text-1);
}
.p-6 {
padding: 24px;
}
.mb-4 {
margin-bottom: 16px;
}
.mb-6 {
margin-bottom: 24px;
}
.text-sm {
font-size: 13px;
}
.text-gray-500 {
color: var(--text-3);
}
.text-2xl {
font-size: 22px;
}
.w-full {
width: 100%;
}
.justify-center {
justify-content: center;
}
.px-0 {
padding-right: 0;
padding-left: 0;
}
.pb-0 {
padding-bottom: 0;
}
.flex-1 {
flex: 1;
}
/* ══════════════════════════════════════════
ANIMATIONS
══════════════════════════════════════════ */
@keyframes pulse {
0%,
100% {
opacity: 1;
}
50% {
opacity: 0.75;
}
}
@keyframes fadeIn {
from {
opacity: 0;
transform: translateY(10px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
.fade-in {
animation: fadeIn 0.3s ease;
}
</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 class="sb-logo-wrap">
<div class="sb-logo-mark">
<img src="assets/img/logo.jpg" alt="Musadaq Logo">
</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 Area -->
<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:13px; font-weight:700; 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;">
<button x-show="page==='tenants' && user?.role === 'super_admin'" @click="showAddTenantModal = true"
class="btn btn-navy">
<span></span> إضافة مكتب
</button>
<button x-show="page==='users'" @click="showAddUserModal = true" class="btn btn-navy">
<span></span> إضافة مستخدم
</button>
<button x-show="page==='companies'" @click="showAddCompanyModal = true" class="btn btn-navy">
<span></span> إضافة شركة
</button>
<button x-show="page==='invoices'" @click="showUploadModal = true" class="btn btn-teal">
<span>📤</span> رفع فاتورة
</button>
<button x-show="page==='invoices'" @click="showBatchUploadModal = true" class="btn btn-navy">
<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:26px;">
<!-- Stat Cards -->
<div style="display:grid; grid-template-columns:repeat(4,1fr); gap:18px;">
<div class="stat-card stat-violet fade-in">
<span class="stat-card-bg">📄</span>
<div class="stat-icon-box">📄</div>
<div class="stat-label">إجمالي الفواتير</div>
<div class="stat-value" x-text="stats.total || 0"></div>
</div>
<div class="stat-card stat-amber fade-in">
<span class="stat-card-bg"></span>
<div class="stat-icon-box"></div>
<div class="stat-label">بانتظار التدقيق</div>
<div class="stat-value" x-text="stats.pending || 0"></div>
</div>
<div class="stat-card stat-green fade-in">
<span class="stat-card-bg"></span>
<div class="stat-icon-box"></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-blue fade-in">
<span class="stat-card-bg">🏢</span>
<div class="stat-icon-box">🏢</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 fade-in"
:class="subscription?.invoices?.warning ? 'stat-red' : 'stat-violet'">
<span class="stat-card-bg">📊</span>
<div class="stat-icon-box">📊</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:14px; font-weight:700; color:var(--text-2); margin-bottom:14px; display:flex; align-items:center; gap:8px;">
<span></span> إجراءات سريعة
</h3>
<div
style="display:grid; grid-template-columns:repeat(auto-fill, minmax(210px,1fr)); gap:14px;">
<div x-show="user?.role !== 'viewer'" class="quick-action-card"
@click="setPage('invoices'); showUploadModal = true">
<div class="qa-icon" style="background:var(--green-subtle);">📤</div>
<div>
<div style="font-weight:700; font-size:14px; color:var(--text-1);">رفع فاتورة</div>
<div style="font-size:12px; 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(--violet-subtle);">🏭</div>
<div>
<div style="font-weight:700; font-size:14px; color:var(--text-1);">إضافة شركة</div>
<div style="font-size:12px; 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(--amber-subtle);">📋</div>
<div>
<div style="font-weight:700; font-size:14px; color:var(--text-1);">الفواتير المعلقة
</div>
<div style="font-size:12px; 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:#F0FDF4;">👤</div>
<div>
<div style="font-weight:700; font-size:14px; color:var(--text-1);">إضافة مستخدم
</div>
<div style="font-size:12px; 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:18px;">🏭</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:700; color:var(--violet-mid); font-size:15px;"
x-text="c.name"></div>
<div style="font-size:12px; color:var(--text-3); margin-top:2px;"
x-text="c.address"></div>
</td>
<td class="num-font" style="font-size:12px;">
<div style="color:var(--text-2);">TIN: <span
x-text="c.tax_identification_number"></span></div>
<div style="color:var(--text-3); margin-top:2px;">CRN: <span
x-text="c.commercial_registration_number || '—'"></span></div>
</td>
<td style="text-align:center; font-weight:700; color:var(--text-2);"
x-text="c.invoices_count || 0"></td>
<td style="text-align:center; font-weight:700; color:var(--green-mid); font-family:'Outfit',sans-serif; font-size:13px;"
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-navy">
📊 تقرير
</button>
</td>
<td style="text-align:center;">
<button @click="confirmDeleteCompany(c)"
class="btn-table-action btn-ta-red">🗑️</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:18px;">👥</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:10px;">
<div style="width:34px;height:34px;border-radius:10px;background:var(--violet-subtle);color:var(--violet-mid);display:flex;align-items:center;justify-content:center;font-weight:700;font-size:14px;flex-shrink:0;"
x-text="u.name?.[0]"></div>
<div>
<div style="font-weight:700; color:var(--text-1);" x-text="u.name">
</div>
<div style="font-size:12px; color:var(--text-3); font-family:'Outfit',sans-serif;"
x-text="u.email"></div>
</div>
</div>
</td>
<td style="color:var(--text-2); font-size:13px;" x-text="u.tenant_name || '—'">
</td>
<td>
<span class="badge"
:class="u.role==='super_admin'?'badge-navy':(u.role==='admin'?'badge-blue':(u.role==='accountant'?'badge-teal':'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-teal' : 'badge-red'">
<span class="badge-dot" :class="u.is_active ? 'bg-teal' : 'bg-red'"
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-red">🗑️ حذف</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:18px;">📄</span>
<h3>إدارة الفواتير</h3>
<span class="table-count" x-text="invoices.length + ' فاتورة'"></span>
<div class="flex-1"></div>
<button @click="showExcelModal = true" class="btn btn-teal btn-sm">
<span>📊</span> استيراد Excel (Bulk)
</button>
</div>
<!-- Invoice Filters & Tabs -->
<div class="px-6 py-4 border-b border-gray-100 bg-gray-50/50">
<div class="flex flex-wrap items-center justify-between gap-4">
<!-- Tabs -->
<div class="flex flex-wrap bg-white p-1 rounded-xl border border-gray-200 shadow-sm gap-1">
<button @click="activeInvoiceTab = 'all'"
:class="activeInvoiceTab === 'all' ? 'bg-indigo-600 text-white shadow-md' : 'text-gray-600 hover:bg-gray-100'"
class="px-4 py-2 rounded-lg text-xs font-bold transition-all duration-200">
الكل
</button>
<button @click="activeInvoiceTab = 'companies'"
:class="activeInvoiceTab === 'companies' ? 'bg-indigo-600 text-white shadow-md' : 'text-gray-600 hover:bg-gray-100'"
class="px-4 py-2 rounded-lg text-xs font-bold transition-all duration-200">
حسب الشركات
</button>
<button @click="activeInvoiceTab = 'approved'"
:class="activeInvoiceTab === 'approved' ? 'bg-indigo-600 text-white shadow-md' : 'text-gray-600 hover:bg-gray-100'"
class="px-4 py-2 rounded-lg text-xs font-bold transition-all duration-200">
المعتمدة
</button>
<button @click="activeInvoiceTab = 'pending'"
:class="activeInvoiceTab === 'pending' ? 'bg-indigo-600 text-white shadow-md' : 'text-gray-600 hover:bg-gray-100'"
class="px-4 py-2 rounded-lg text-xs font-bold transition-all duration-200">
المعلقة
</button>
<button @click="activeInvoiceTab = 'rejected'"
:class="activeInvoiceTab === 'rejected' ? 'bg-indigo-600 text-white shadow-md' : 'text-gray-600 hover:bg-gray-100'"
class="px-4 py-2 rounded-lg text-xs font-bold transition-all duration-200">
المرفوضة
</button>
<button @click="activeInvoiceTab = 'jofotara'"
:class="activeInvoiceTab === 'jofotara' ? 'bg-indigo-600 text-white shadow-md' : 'text-gray-600 hover:bg-gray-100'"
class="px-4 py-2 rounded-lg text-xs font-bold transition-all duration-200">
جوفوترا
</button>
</div>
<!-- Company Filter -->
<div class="flex items-center gap-2">
<span class="text-xs font-bold text-gray-500">تصفية حسب الشركة:</span>
<select x-model="invoiceCompanyFilter" class="text-xs font-bold border border-gray-200 rounded-lg px-3 py-2 bg-white outline-none focus:border-indigo-500 transition-colors">
<option value="">جميع الشركات</option>
<template x-for="c in companies" :key="c.id">
<option :value="c.id" x-text="c.name"></option>
</template>
</select>
</div>
</div>
</div>
<!-- Companies Selection View -->
<div x-show="activeInvoiceTab === 'companies'" class="p-6 bg-gray-50 border-b border-gray-100">
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
<template x-for="c in companies" :key="c.id">
<div @click="invoiceCompanyFilter = c.id; activeInvoiceTab = 'all'"
class="bg-white p-4 rounded-xl border border-gray-200 shadow-sm cursor-pointer hover:border-indigo-500 hover:shadow-md transition-all group">
<div class="flex items-center gap-3">
<div class="w-10 h-10 rounded-lg bg-indigo-50 text-indigo-600 flex items-center justify-center font-bold group-hover:bg-indigo-600 group-hover:text-white transition-colors" x-text="c.name[0]"></div>
<div>
<div class="font-bold text-gray-800" x-text="c.name"></div>
<div class="text-xs text-gray-500 num-font" x-text="c.tax_identification_number"></div>
</div>
</div>
</div>
</template>
</div>
</div>
<table class="data-table" x-show="activeInvoiceTab !== 'companies'">
<thead>
<tr>
<th>الشركة / المورد</th>
<th>التاريخ</th>
<th>المجموع الكلي</th>
<th style="text-align:center;">الحالة</th>
<th style="text-align:center;">إجراءات</th>
</tr>
</thead>
<tbody>
<tr x-show="filteredInvoices.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 filteredInvoices" :key="inv.id">
<tr>
<td>
<div style="font-weight:700; color:var(--violet-mid); font-size:14px;"
x-text="inv.company_name"></div>
<div style="font-size:13px; color:var(--text-2); margin-top:2px;"
x-text="inv.supplier_name"></div>
<div style="font-size:11px; color:var(--text-3); font-family:'Outfit',sans-serif;"
x-text="inv.supplier_tin"></div>
</td>
<td style="color:var(--text-2); font-size:13px; font-family:'Outfit',sans-serif;"
x-text="inv.invoice_date || '—'"></td>
<td style="font-weight:700; color:var(--green-mid); font-family:'Outfit',sans-serif;"
x-text="parseFloat(inv.grand_total).toLocaleString() + ' JOD'"></td>
<td style="text-align:center;">
<span class="badge"
:class="inv.status==='extracted' ? 'badge-blue' : (inv.status==='approved' ? 'badge-teal' : (inv.status==='rejected'||inv.status==='failed'?'badge-red':'badge-gray'))"
x-text="inv.status === 'approved' ? '✓ مدققة' : (inv.status === 'extracted' ? 'جاهزة للتدقيق' : (inv.status === 'rejected' ? 'مرفوضة' : inv.status))">
</span>
</td>
<td style="text-align:center;">
<button @click="viewInvoice(inv.id)" class="btn-table-action btn-ta-navy">
👁️ عرض
</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:18px;">🏢</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:700; color:var(--violet-mid); font-size:15px;"
x-text="t.name"></div>
<div style="font-size:11px; color:var(--text-3); margin-top:3px; font-family:'Outfit',sans-serif;"
x-text="'انضم: ' + t.created_at.split(' ')[0]"></div>
</td>
<td style="text-align:center; font-weight:700; color:var(--text-2);"
x-text="t.companies_count || 0"></td>
<td style="text-align:center; font-weight:700; color:var(--text-2);"
x-text="t.invoices_count || 0"></td>
<td>
<div style="font-size:13px; color:var(--text-2);" x-text="t.email"></div>
<div style="font-size:12px; color:var(--text-3); font-family:'Outfit',sans-serif;"
x-text="t.phone || '—'"></div>
</td>
<td style="text-align:center;">
<span class="badge"
:class="t.status==='active'?'badge-teal':(t.status==='trial'?'badge-amber':'badge-red')"
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-navy" title="تعديل">⚙️ تعديل</button>
<button class="btn-table-action btn-ta-teal" title="تقرير"
@click="showTenantStats(t)">📊</button>
</div>
</td>
</tr>
</template>
</tbody>
</table>
</div>
</div>
<!-- ════ SUBSCRIPTION ════════════════════════ -->
<div x-show="page === 'subscription'">
<!-- Header -->
<div
style="display:flex; justify-content:space-between; align-items:center; margin-bottom:28px; flex-wrap:wrap; gap:14px;">
<div>
<h2 style="font-size:26px; font-weight:800; color:var(--text-1);">إدارة الاشتراك</h2>
<p style="color:var(--text-3); font-size:14px; margin-top:3px;">تفاصيل باقتك الحالية
واستهلاك الموارد</p>
</div>
<div
style="background:linear-gradient(135deg,var(--violet-mid),var(--violet-bright)); padding:10px 22px; border-radius:14px; color:white; display:flex; align-items:center; gap:10px; box-shadow:0 4px 14px rgba(109,40,217,0.28);">
<span style="font-size:13px; opacity:0.8;">الباقة الحالية:</span>
<span style="font-weight:700; font-size:17px;" x-text="subscription?.plan_name"></span>
</div>
</div>
<!-- Usage Stats -->
<div
style="display:grid; grid-template-columns:repeat(auto-fit, minmax(260px, 1fr)); gap:20px; margin-bottom:36px;">
<!-- Invoices -->
<div class="usage-card">
<div style="display:flex; justify-content:space-between; margin-bottom:6px;">
<span style="font-weight:700; color:var(--text-1); font-size:14px;">📄 الفواتير
الشهرية</span>
<span class="num-font" style="font-weight:700; color:var(--green-mid);"
x-text="(subscription?.invoices?.used || 0) + ' من ' + (subscription?.invoices?.limit || 0)"></span>
</div>
<div class="usage-bar-bg">
<div class="usage-bar-fill"
:style="'background:linear-gradient(to left, var(--green-bright), var(--green-mid)); min-width: 2px; width:' + Math.min(100, (subscription?.invoices?.percent || 0)) + '%'"></div>
</div>
<div
style="display:flex; justify-content:space-between; font-size:12px; color:var(--text-3);">
<span
x-text="'يتم التصفير في: ' + (subscription?.period_end?.split(' ')[0] || '—')"></span>
<span class="num-font font-bold" x-text="(subscription?.invoices?.percent || 0) + '%'"></span>
</div>
</div>
<!-- Companies -->
<div class="usage-card">
<div style="display:flex; justify-content:space-between; margin-bottom:6px;">
<span style="font-weight:700; color:var(--text-1); font-size:14px;">🏭 الشركات
المدارة</span>
<span class="num-font" style="font-weight:700; color:var(--violet-mid);"
x-text="(subscription?.companies?.used || 0) + ' من ' + (subscription?.companies?.limit || 0)"></span>
</div>
<div class="usage-bar-bg">
<div class="usage-bar-fill"
:style="'background:linear-gradient(to left, var(--sb-accent), var(--violet-mid)); min-width: 2px; width:' + Math.min(100, (subscription?.companies?.percent || 0)) + '%'"></div>
</div>
<div style="display:flex; justify-content:space-between; font-size:12px; color:var(--text-3);">
<span>إجمالي الشركات المسموح بها</span>
<span class="num-font font-bold" x-text="(subscription?.companies?.percent || 0) + '%'"></span>
</div>
</div>
<!-- Team -->
<div class="usage-card">
<div style="display:flex; justify-content:space-between; margin-bottom:6px;">
<span style="font-weight:700; color:var(--text-1); font-size:14px;">👥 فريق العمل</span>
<span class="num-font" style="font-weight:700; color:var(--amber-mid);"
x-text="(subscription?.users?.used || 0) + ' من ' + (subscription?.users?.limit || 0)"></span>
</div>
<div class="usage-bar-bg">
<div class="usage-bar-fill"
:style="'background:linear-gradient(to left, var(--amber-bright), var(--amber-mid)); min-width: 2px; width:' + Math.min(100, (subscription?.users?.percent || 0)) + '%'"></div>
</div>
<div style="display:flex; justify-content:space-between; font-size:12px; color:var(--text-3);">
<span>مستخدمين نشطين في النظام</span>
<span class="num-font font-bold" x-text="(subscription?.users?.percent || 0) + '%'"></span>
</div>
</div>
</div>
<!-- Pending Payment Warning -->
<div x-show="subscription?.pending_payment"
style="background:var(--amber-subtle); border:2px dashed var(--amber-bright); border-radius:18px; padding:24px 28px; margin-bottom:36px; display:flex; justify-content:space-between; align-items:center; animation:pulse 2s infinite; gap:20px; flex-wrap:wrap;">
<div style="display:flex; gap:16px; align-items:center;">
<div
style="background:var(--amber-mid); color:white; width:46px; height:46px; border-radius:50%; display:flex; align-items:center; justify-content:center; font-size:22px; flex-shrink:0;">
</div>
<div>
<h4 style="font-weight:800; color:var(--text-1); font-size:16px;">لديك طلب ترقية قيد
الانتظار</h4>
<p style="color:var(--text-2); font-size:13px; margin-top:3px;">بانتظار تأكيد عملية
التحويل برقم المرجع: <strong class="num-font"
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-navy" style="font-size:13px;">متابعة الدفع</button>
<button @click="cancelPayment(subscription.pending_payment.id)" class="btn btn-ghost"
style="color:var(--red-mid); font-size:13px;">إلغاء الطلب</button>
</div>
</div>
<!-- Pricing Section -->
<div style="text-align:center; margin-bottom:28px;">
<h3 style="font-size:22px; font-weight:800; color:var(--text-1); margin-bottom:6px;">تغيير أو
ترقية الباقة</h3>
<p style="color:var(--text-3); font-size:13px;">اختر الباقة التي تناسب احتياجات مكتبك المحاسبي
</p>
</div>
<div style="display:grid; grid-template-columns:repeat(auto-fit, minmax(290px, 1fr)); gap:24px;">
<template x-for="p in plans" :key="p.id">
<div class="plan-card" :class="subscription?.plan_id === p.id ? 'active-plan' : ''">
<div x-show="subscription?.plan_id === p.id" class="plan-current-badge">
باقتك الحالية
</div>
<div style="text-align:center;">
<h4 style="font-size:20px; font-weight:800; color:var(--text-1);"
x-text="p.name_ar"></h4>
<p style="font-size:13px; color:var(--text-3); min-height:38px; margin-top:6px;"
x-text="p.description_ar"></p>
</div>
<div
style="text-align:center; padding:18px 0; border-top:1px solid var(--border); border-bottom:1px solid var(--border);">
<span class="num-font"
style="font-size:46px; font-weight:800; color:var(--green-mid);"
x-text="p.price_jod"></span>
<span style="font-size:15px; color:var(--text-3); font-weight:500;"> دينار /
شهر</span>
</div>
<ul
style="list-style:none; padding:0; margin:0; display:flex; flex-direction:column; gap:12px; flex:1;">
<template x-for="f in p.features">
<li
style="font-size:13.5px; color:var(--text-2); display:flex; gap:11px; align-items:flex-start;">
<div
style="width:19px; height:19px; background:var(--green-subtle); color:var(--green-mid); border-radius:50%; display:flex; align-items:center; justify-content:center; font-size:9px; flex-shrink:0; margin-top:2px;">
</div>
<span x-text="f"></span>
</li>
</template>
</ul>
<button x-show="subscription?.plan_id !== p.id" @click="upgradePlan(p)"
:disabled="isBusy"
style="width:100%; height:50px; font-size:15px; font-weight:700; border-radius:14px; background:linear-gradient(135deg,var(--green),var(--green-mid)); color:white; box-shadow:0 4px 15px rgba(5,150,105,0.28); margin-top:14px; border:none; cursor:pointer; font-family:'IBM Plex Sans Arabic',sans-serif; transition:all 0.18s;">
ترقية الباقة الآن
</button>
<div x-show="subscription?.plan_id === p.id"
style="text-align:center; padding:14px; border-radius:12px; background:var(--violet-subtle); color:var(--violet-mid); font-weight:700; font-size:13px; margin-top:14px;">
أنت مشترك في هذه الباقة
</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">
<div class="modal-head-icon teal">📊</div>
<div style="flex:1;">
<div class="modal-title" x-text="companyStats?.company?.name"></div>
<div class="modal-subtitle num-font"
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:22px;">
<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(--green-mid);"
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(--amber-mid);"
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(--blue);"
x-text="companyStats?.totals?.approved_count || 0"></div>
</div>
</div>
<div
style="font-size:11px; font-weight:700; color:var(--text-3); text-transform:uppercase; letter-spacing:0.08em; margin-bottom:10px;">
التحليل الشهري للفواتير والضرائب
</div>
<div
style="border:1px solid var(--border); border-radius:14px; overflow:hidden; max-height:240px; overflow-y:auto;">
<table class="stats-table">
<thead>
<tr>
<th>الشهر</th>
<th>عدد الفواتير</th>
<th>الضريبة المستحقة</th>
<th style="color:var(--green-mid);">الإجمالي النهائي</th>
</tr>
</thead>
<tbody>
<template x-for="m in companyStats?.monthly" :key="m.month">
<tr>
<td class="num-font" style="color:var(--text-2);" x-text="m.month"></td>
<td x-text="m.total_invoices"></td>
<td style="color:var(--amber-mid); font-family:'Outfit',sans-serif;"
x-text="parseFloat(m.total_tax || 0).toLocaleString() + ' JOD'"></td>
<td style="font-weight:700; color:var(--green-mid); font-family:'Outfit',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 navy">🏢</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:22px;">
<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(--green-mid);"
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(--amber-mid);"
x-text="parseFloat(tenantStats?.summary?.total_tax || 0).toLocaleString()"></div>
</div>
</div>
<div
style="font-size:11px; font-weight:700; color:var(--text-3); text-transform:uppercase; letter-spacing:0.08em; margin-bottom:10px;">
تحليل النشاط الشهري للمكتب
</div>
<div
style="border:1px solid var(--border); border-radius:14px; overflow:hidden; max-height:240px; overflow-y:auto;">
<table class="stats-table">
<thead>
<tr>
<th>الشهر</th>
<th>عدد الفواتير</th>
<th>الضريبة المستحقة</th>
<th style="color:var(--green-mid);">الإجمالي النهائي</th>
</tr>
</thead>
<tbody>
<template x-for="m in tenantStats?.monthly" :key="m.month">
<tr>
<td class="num-font" style="color:var(--text-2);" x-text="m.month"></td>
<td x-text="m.total_invoices"></td>
<td style="color:var(--amber-mid); font-family:'Outfit',sans-serif;"
x-text="parseFloat(m.total_tax || 0).toLocaleString() + ' JOD'"></td>
<td style="font-weight:700; color:var(--green-mid); font-family:'Outfit',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 gold">👤</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:14px;">
<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-navy" :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 navy">🏭</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:14px;">
<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-navy" :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 teal">🔗</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:14px;">
<div
style="background:var(--green-subtle); border:1px solid rgba(5,150,105,0.2); border-radius:11px; padding:12px 14px; font-size:13px; color:var(--green-mid);">
هذه البيانات تُستخرج من بوابة 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-teal" :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 teal">📤</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:14px;">
<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:12px; color:var(--text-3); margin-bottom:6px;">مدعوم: صور (JPG, PNG) أو
ملفات PDF</div>
<input type="file" @change="selectedFile = $event.target.files[0]" class="form-input"
style="padding:8px;" required>
</div>
</div>
<div class="modal-divider"></div>
<div class="modal-footer">
<button type="submit" class="btn btn-teal" :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 navy">📁</div>
<div style="flex:1;">
<div class="modal-title">رفع مجمع للفواتير (Batch)</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:14px;">
<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:12px; color:var(--text-3); margin-bottom:6px;">مدعوم: صور أو PDF (يمكنك
تحديد أكثر من ملف)</div>
<input type="file" id="batchFileInput" multiple accept="image/*,application/pdf"
class="form-input" style="padding:8px;" required :disabled="isUploadingBatch">
</div>
<div x-show="isUploadingBatch" style="margin-top:6px;">
<div
style="font-size:12px; font-weight:700; color:var(--violet-mid); margin-bottom:5px; display:flex; justify-content:space-between;">
<span>جاري رفع الدفعة...</span>
<span x-text="batchProgress.current + ' / ' + batchProgress.total"></span>
</div>
<div style="height:7px; background:var(--bg); border-radius:7px; overflow:hidden;">
<div style="height:100%; background:linear-gradient(to left,var(--violet-bright),var(--violet-mid)); transition:width 0.3s; border-radius:7px;"
: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-navy" :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:var(--bg-card); border-radius:22px; box-shadow:var(--shadow-lg); width:100%; max-width:900px; height:88vh; display:flex; overflow:hidden;">
<!-- Document Preview -->
<div
style="flex:1; background:#F2F1FA; border-left:1px solid var(--border); position:relative; overflow:hidden;">
<div style="position:absolute; top:12px; right:12px; z-index:10;">
<span class="badge badge-navy">معاينة الملف</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:48px; opacity:0.3;">📄</span>
<p style="font-size:14px;">لا يوجد ملف مرفق</p>
</div>
</div>
<!-- Invoice Data Panel -->
<div style="width:345px; flex-shrink:0; display:flex; flex-direction:column; overflow:hidden;">
<!-- Header -->
<div
style="padding:18px 20px 14px; border-bottom:1px solid var(--border); display:flex; align-items:center; justify-content:space-between;">
<div>
<div style="font-size:16px; font-weight:700; color:var(--text-1);">تفاصيل الفاتورة</div>
<span class="badge" style="margin-top:4px;"
:class="currentInvoice?.status==='extracted' ? 'badge-blue' : (currentInvoice?.status==='approved' ? 'badge-teal' : '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 -->
<div x-show="currentInvoice?.validation_warnings?.length > 0"
style="background:#FFFBEB; border-bottom:1px solid #FDE68A; padding:12px 16px;">
<div style="display:flex; gap:10px; align-items:flex-start;">
<span style="font-size:18px;">⚠️</span>
<div>
<div style="font-weight:700; color:#92400E; font-size:13px; margin-bottom:4px;">تنبيهات
المدقق الذكي:</div>
<ul
style="margin:0; padding:0; list-style:none; display:flex; flex-direction:column; gap:4px;">
<template x-for="w in currentInvoice.validation_warnings">
<li style="font-size:12px; color:#B45309; display:flex; gap:6px;">
<span></span>
<span x-text="w"></span>
</li>
</template>
</ul>
</div>
</div>
</div>
<!-- Fields -->
<div style="flex:1; overflow-y:auto; padding:16px; display:flex; flex-direction:column; gap:10px;">
<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:12px; color:var(--text-3); font-family:'Outfit',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:13px; color:var(--text-2); font-family:'Outfit',sans-serif; margin-top:3px;"
x-text="currentInvoice?.invoice_date || '—'"></div>
</div>
<div class="invoice-field-card"
style="background:var(--green-subtle); border-color:rgba(5,150,105,0.2);">
<div class="invoice-field-label" style="color:var(--green-mid);">المجموع الكلي</div>
<div style="font-size:26px; font-weight:700; color:var(--green-mid); font-family:'El Messiri',sans-serif;"
x-text="parseFloat(currentInvoice?.grand_total || 0).toLocaleString() + ' JOD'"></div>
<div style="font-size:12px; color:var(--amber-mid); margin-top:4px; font-family:'Outfit',sans-serif;"
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); border-radius:11px; overflow-x:auto;">
<div
style="padding:8px 12px; background:#F8F7FD; font-size:11px; font-weight:700; color:var(--text-3); text-transform:uppercase; letter-spacing:0.06em; position:sticky; top:0; z-index:2; border-bottom:1px solid var(--border);">
بنود الفاتورة
</div>
<table style="width:100%; border-collapse:collapse; font-size:11px; min-width:320px;">
<thead style="background:white;">
<tr>
<th
style="padding:8px 10px; text-align:right; color:var(--text-3); font-weight:600; border-bottom:1px solid var(--border);">
البند</th>
<th
style="padding:8px 10px; text-align:center; color:var(--text-3); font-weight:600; border-bottom:1px solid var(--border);">
الكمية</th>
<th
style="padding:8px 10px; text-align:left; color:var(--text-3); font-weight:600; border-bottom:1px solid var(--border);">
السعر</th>
<th
style="padding:8px 10px; text-align:left; color:var(--text-3); font-weight:600; border-bottom:1px solid var(--border);">
الضريبة</th>
<th
style="padding:8px 10px; text-align:left; color:var(--text-3); font-weight:600; border-bottom:1px solid var(--border);">
الإجمالي</th>
</tr>
</thead>
<tbody>
<template x-for="item in currentInvoice?.items" :key="item.id">
<tr style="border-bottom:1px solid #F0EFF8;">
<td style="padding:8px 10px; color:var(--text-2);" x-text="item.description">
</td>
<td style="padding:8px 10px; text-align:center; color:var(--text-3); font-family:'Outfit',sans-serif;"
x-text="item.quantity"></td>
<td style="padding:8px 10px; text-align:left; color:var(--green-mid); font-family:'Outfit',sans-serif;"
x-text="item.unit_price"></td>
<td style="padding:8px 10px; text-align:left; color:var(--amber-mid); font-family:'Outfit',sans-serif;"
x-text="(item.tax_rate || 0) + '%'"></td>
<td style="padding:8px 10px; text-align:left; font-weight:bold; color:var(--violet-mid); font-family:'Outfit',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:white; border:1px solid var(--border); border-radius:13px; padding:16px; display:flex; flex-direction:column; align-items:center; gap:8px;">
<div
style="font-size:11px; font-weight:700; color:var(--text-3); text-transform:uppercase; letter-spacing:0.07em;">
رمز QR الضريبي
</div>
<template x-if="getQrSrc(currentInvoice)">
<img :src="getQrSrc(currentInvoice)" style="width:140px; height:140px; object-fit:contain;"
alt="QR Code">
</template>
<div x-show="!getQrSrc(currentInvoice)"
style="font-size:12px; color:var(--text-3); text-align:center;">
جاري توليد الرمز...
</div>
</div>
</div>
<!-- Actions -->
<div
style="padding:14px 16px; border-top:1px solid var(--border); display:flex; flex-direction:column; gap:8px;">
<!-- Warnings Acknowledgement -->
<div x-show="currentInvoice?.status === 'extracted' && currentInvoice?.validation_warnings?.length > 0"
style="background:#F8F7FD; border:1px solid var(--border); border-radius:10px; padding:10px; 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:16px; height:16px; accent-color:var(--violet-mid);">
<span style="font-size:12px; font-weight:600; color:var(--text-2);">لقد راجعت التنبيهات
وأتحمل مسؤولية الاعتماد</span>
</label>
</div>
<button x-show="currentInvoice?.status === 'extracted'" @click="approveInvoice" class="btn btn-teal"
: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(--red-subtle); color:var(--red-mid); border:none; padding:10px; border-radius:11px; font-family:inherit; font-size:14px; font-weight:600; cursor:pointer; transition:all 0.18s;"
onmouseover="this.style.background='#FECACA'"
onmouseout="this.style.background='var(--red-subtle)'"> رفض الفاتورة</button>
<div x-show="currentInvoice?.status === 'approved'"
style="width:100%; background:var(--green-subtle); color:var(--green-mid); border:1px solid rgba(5,150,105,0.2); padding:10px; border-radius:11px; font-size:14px; font-weight:700; 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 navy">⚙️</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:14px;">
<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-navy" :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 gold">🏗️</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:18px;">
<!-- 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-navy" :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-card" style="max-width:500px;">
<div class="modal-header">
<span>📊 استيراد فواتير (Excel/Bulk)</span>
<button @click="showExcelModal = false" class="modal-close-btn" style="font-size:18px;">&times;</button>
</div>
<div class="p-6">
<p class="text-sm text-gray-500 mb-6">استخدم هذه الميزة لاستيراد كميات كبيرة من الفواتير من ملف Excel.
سيقوم النظام بمحاولة مطابقة الأعمدة تلقائياً.</p>
<div class="form-group mb-4">
<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 mb-6">
<label class="form-label">ملف الـ Excel (.xlsx, .csv)</label>
<input type="file" id="excelFileInput" class="form-input" accept=".xlsx, .xls, .csv">
</div>
<div class="modal-footer px-0 pb-0">
<button @click="uploadExcel()" class="btn btn-teal w-full justify-center" :disabled="isBusy">
<span x-show="!isBusy">🚀 بدء الاستيراد الآن</span>
<span x-show="isBusy"> جاري المعالجة...</span>
</button>
</div>
</div>
</div>
</div>
<!-- ── 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="padding-bottom:14px;">
<div class="modal-head-icon gold" style="width:38px; height:38px; font-size:18px;">💳</div>
<div style="flex:1;">
<div class="modal-title" style="font-size:16px;">تأكيد دفع الاشتراك</div>
<div class="modal-subtitle">يرجى تحويل المبلغ ثم رفع وصل الدفع</div>
</div>
<button @click="showPaymentModal = false" class="modal-close-btn"></button>
</div>
<div class="modal-divider"></div>
<div class="modal-body">
<div
style="background:var(--green-subtle); border:1px solid rgba(5,150,105,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);">الباقة:</span>
<span style="font-weight:700; 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);">المبلغ:</span>
<span style="font-weight:700; color:var(--green-mid);"
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.08); padding-bottom:8px;">
<span style="color:var(--text-3);">Reference:</span>
<span style="font-weight:700; color:var(--amber-mid);" x-text="paymentData.reference"></span>
</div>
<div style="text-align:center;">
<p style="margin-bottom:6px; font-size:11px; color:var(--text-2);">التحويل عبر CliQ للحساب:</p>
<div style="background:white; padding:7px 14px; border:2px solid var(--green-mid); border-radius:9px; font-family:'Outfit',sans-serif; font-size:17px; font-weight:bold; color:var(--text-1); display:inline-block;"
x-text="paymentData.cliq_alias"></div>
</div>
<div
style="margin-top:12px; background:white; padding:10px 12px; border-radius:10px; border:1px solid rgba(5,150,105,0.15);">
<div
style="display:flex; justify-content:space-between; align-items:center; margin-bottom:5px;">
<label style="color:var(--text-1); font-weight:700; font-size:11px;">رقم المرجع (من
البنك)</label>
<span
style="background:var(--green-mid); color:white; font-size:8px; padding:2px 6px; border-radius:5px; font-weight:700;">تفعيل
فوري </span>
</div>
<input type="text" x-model="paymentData.bank_reference" class="form-input"
placeholder="ألصق رقم المرجع هنا..."
style="font-family:'Outfit',sans-serif; font-size:14px; padding:7px; border:1.5px solid var(--green-mid); text-align:center;">
</div>
</div>
<div style="margin-bottom:14px;">
<label
style="font-size:12px; font-weight:600; color:var(--text-2); display:block; margin-bottom:5px;">صورة
وصل التحويل (اختياري)</label>
<input type="file" @change="selectedFile = $event.target.files[0]" class="form-input"
style="padding:6px; font-size:12px;" accept="image/*,application/pdf">
</div>
<div style="display:flex; gap:8px;">
<button @click="showPaymentModal = false" class="btn btn-ghost"
style="flex:1; font-size:13px;">إلغاء</button>
<button @click="uploadReceipt()" class="btn btn-navy" :disabled="isBusy"
style="flex:2; font-size:13px;">
<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: [],
activeInvoiceTab: 'all', // all, approved, pending, rejected, jofotara
invoiceCompanyFilter: '',
showPaymentModal: false,
paymentData: { cliq_alias: '', amount: 0, plan_name: '', reference: '', payment_id: '' },
stats: { total: 0, pending: 0, approved: 0 },
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] || ''; },
get filteredInvoices() {
const now = new Date();
const startOfMonth = new Date(now.getFullYear(), now.getMonth(), 1);
return this.invoices.filter(inv => {
// 1. Time Filter (Current Month)
const invDate = new Date(inv.invoice_date || inv.created_at);
if (invDate < startOfMonth) return false;
// 2. Company Filter
if (this.invoiceCompanyFilter && inv.company_id != this.invoiceCompanyFilter) return false;
// 3. Tab Filter
if (this.activeInvoiceTab === 'approved' && inv.status !== 'approved') return false;
if (this.activeInvoiceTab === 'pending' && inv.status !== 'extracted' && inv.status !== 'pending') return false;
if (this.activeInvoiceTab === 'rejected' && inv.status !== 'rejected' && inv.status !== 'failed') return false;
if (this.activeInvoiceTab === 'jofotara' && !inv.jofotara_uuid && !inv.jofotara) return false;
return true;
});
},
token() { return localStorage.getItem('access_token'); },
showError(msg) { this.globalError = msg; setTimeout(() => this.globalError = '', 8000); },
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('فشل إلغاء الطلب.');
}
},
async confirmDeleteCompany(c) {
if (!confirm('هل أنت متأكد من حذف الشركة "' + c.name + '"؟ سيتم حذف جميع الفواتير المرتبطة بها.')) return;
this.isBusy = true;
const res = await this.apiRequest('v1/companies/delete', 'POST', { id: c.id });
if (res !== null) { this.loadAll(); }
this.isBusy = false;
},
async confirmDeleteUser(u) {
if (!confirm('هل أنت متأكد من حذف المستخدم "' + u.name + '"؟')) return;
this.isBusy = true;
const res = await this.apiRequest('v1/users/delete', 'POST', { id: u.id });
if (res !== null) { this.loadAll(); }
this.isBusy = false;
},
logout() { localStorage.clear(); window.location.href = '/login.php'; }
}));
});
</script>
</body>
</html>