3518 lines
175 KiB
PHP
3518 lines
175 KiB
PHP
<!DOCTYPE html>
|
||
<html lang="ar" dir="rtl" data-theme="light">
|
||
|
||
<head>
|
||
<meta charset="UTF-8">
|
||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||
<title>مُصادَق | لوحة التحكم الذكية</title>
|
||
<link rel="icon" type="image/jpeg" href="assets/img/logo.jpg">
|
||
<link
|
||
href="https://fonts.googleapis.com/css2?family=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>
|
||
<button x-show="page==='invoices'" @click="showExportExcelModal = true" class="btn btn-ghost btn-sm" style="border-color: var(--green-mid); color: var(--green);">
|
||
<span>📥</span> تصدير Excel
|
||
</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==='submitted' ? 'badge-purple' : (inv.status==='rejected'||inv.status==='failed'?'badge-red':'badge-gray')))"
|
||
x-text="inv.status === 'approved' ? '✓ مدققة' : (inv.status === 'submitted' ? '🚀 مُرسلة للفوترة' : (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>
|
||
<button @click="submitToJoFotara(inv.id)"
|
||
x-show="inv.status === 'approved'"
|
||
class="btn-table-action btn-ta-teal"
|
||
style="margin-right:4px;">
|
||
📤 جوفوترا
|
||
</button>
|
||
<button @click="deleteInvoice(inv.id)" class="btn-table-action btn-ta-red" style="margin-right:4px;">
|
||
🗑️ حذف
|
||
</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 x-text="subscription?.billing_cycle === 'monthly' ? '(شهري)' : '(سنوي)'"></span></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>
|
||
|
||
<!-- Cycle Toggle -->
|
||
<div style="display:flex; justify-content:center; margin-bottom:36px;">
|
||
<div style="background:var(--bg-secondary); padding:5px; border-radius:12px; display:flex; gap:5px; border:1px solid var(--border);">
|
||
<button @click="billingCycle = 'monthly'"
|
||
:class="billingCycle === 'monthly' ? 'btn-navy' : 'btn-ghost'"
|
||
style="font-size:13px; padding:8px 20px; border-radius:8px; transition:all 0.2s;">
|
||
دفع شهري
|
||
</button>
|
||
<button @click="billingCycle = 'annual'"
|
||
:class="billingCycle === 'annual' ? 'btn-navy' : 'btn-ghost'"
|
||
style="font-size:13px; padding:8px 20px; border-radius:8px; transition:all 0.2s;">
|
||
دفع سنوي (توفير ✨)
|
||
</button>
|
||
</div>
|
||
</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="billingCycle === 'monthly' ? (p.price_monthly_jod || p.price_jod) : (p.price_annual_jod || (p.price_jod * 10))"></span>
|
||
<span style="font-size:15px; color:var(--text-3); font-weight:500;"> دينار /
|
||
<span x-text="billingCycle === 'monthly' ? 'شهر' : 'سنة'"></span></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:95vw; height:95vh; display:flex; overflow:hidden;">
|
||
|
||
<!-- Document Preview -->
|
||
<div
|
||
style="flex:3; 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:25%; min-width:320px; max-width:450px; 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); display:flex; gap:10px; align-items:center;">
|
||
تفاصيل الفاتورة
|
||
<button x-show="currentInvoice?.status === 'extracted' && !isEditingInvoice" @click="isEditingInvoice = true" class="btn-sm btn-ghost" style="font-size:11px; padding:2px 8px;">تعديل ✏️</button>
|
||
<button x-show="isEditingInvoice" @click="updateInvoice" class="btn-sm btn-teal" style="font-size:11px; padding:2px 8px;">حفظ 💾</button>
|
||
<button x-show="isEditingInvoice" @click="isEditingInvoice = false" class="btn-sm btn-ghost" style="font-size:11px; padding:2px 8px;">إلغاء ✕</button>
|
||
</div>
|
||
<span class="badge" style="margin-top:4px;"
|
||
:class="currentInvoice?.status==='extracted' ? 'badge-blue' : (currentInvoice?.status==='approved' ? 'badge-teal' : (currentInvoice?.status==='rejected' ? 'badge-red' : 'badge-gray'))"
|
||
x-text="currentInvoice?.status === 'approved' ? '✓ مدققة' : (currentInvoice?.status === 'extracted' ? 'جاهزة للتدقيق' : (currentInvoice?.status === 'rejected' ? 'مرفوضة' : 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 x-show="!isEditingInvoice" class="invoice-field-value" x-text="currentInvoice?.supplier_name || 'غير متوفر'"></div>
|
||
<input x-show="isEditingInvoice" type="text" x-model="currentInvoice.supplier_name" class="form-input" style="padding:4px; font-size:13px; margin-bottom:4px;" placeholder="اسم المورد">
|
||
|
||
<div x-show="!isEditingInvoice" style="font-size:12px; color:var(--text-3); font-family:'Outfit',sans-serif; margin-top:4px;"
|
||
x-text="'TIN: ' + (currentInvoice?.supplier_tin || '—')"></div>
|
||
<input x-show="isEditingInvoice" type="text" x-model="currentInvoice.supplier_tin" class="form-input" placeholder="الرقم الضريبي" style="padding:4px; font-size:12px;">
|
||
</div>
|
||
|
||
<div class="invoice-field-card">
|
||
<div class="invoice-field-label">رقم الفاتورة والتاريخ</div>
|
||
<div x-show="!isEditingInvoice" class="invoice-field-value" x-text="currentInvoice?.invoice_number || '—'"></div>
|
||
<input x-show="isEditingInvoice" type="text" x-model="currentInvoice.invoice_number" class="form-input" style="padding:4px; font-size:13px; margin-bottom:4px;" placeholder="رقم الفاتورة">
|
||
|
||
<div x-show="!isEditingInvoice" style="font-size:13px; color:var(--text-2); font-family:'Outfit',sans-serif; margin-top:3px;"
|
||
x-text="currentInvoice?.invoice_date || '—'"></div>
|
||
<input x-show="isEditingInvoice" type="date" x-model="currentInvoice.invoice_date" class="form-input" style="padding:4px; font-size:13px;">
|
||
</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 x-show="!isEditingInvoice" 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>
|
||
<input x-show="isEditingInvoice" type="number" step="0.01" x-model="currentInvoice.grand_total" class="form-input" style="padding:4px; font-size:16px; font-weight:bold; color:var(--green-mid); margin-bottom:4px;" placeholder="المجموع">
|
||
|
||
<div x-show="!isEditingInvoice" 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>
|
||
<input x-show="isEditingInvoice" type="number" step="0.01" x-model="currentInvoice.tax_amount" class="form-input" style="padding:4px; font-size:13px; color:var(--amber-mid);" placeholder="الضريبة">
|
||
</div>
|
||
|
||
<!-- Items Button -->
|
||
<div x-show="currentInvoice?.items?.length > 0" style="margin-top:4px;">
|
||
<button @click="showItemsModal = true" class="btn btn-navy" style="width:100%; justify-content:center; border-radius:11px; padding:12px;">
|
||
📋 عرض بنود الفاتورة (<span x-text="currentInvoice?.items?.length"></span>)
|
||
</button>
|
||
</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>
|
||
|
||
<div style="display:flex; gap:8px;">
|
||
<button x-show="currentInvoice?.status === 'extracted' || currentInvoice?.status === 'rejected'"
|
||
@click="rejectInvoice(currentInvoice.id)"
|
||
style="flex:1; background:var(--amber-subtle); color:var(--amber-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='#FDE68A'"
|
||
onmouseout="this.style.background='var(--amber-subtle)'">❌ رفض</button>
|
||
|
||
<button @click="deleteInvoice(currentInvoice.id)"
|
||
style="flex:1; 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>
|
||
|
||
<div x-show="currentInvoice?.status === 'approved'"
|
||
style="width:100%; display:flex; flex-direction:column; gap:8px;">
|
||
<div 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>
|
||
<button @click="exportExcel(currentInvoice.id)" class="btn btn-ghost" style="width:100%; justify-content:center; border-color:var(--green-mid); color:var(--green-mid);">
|
||
<span>📥</span> تحميل كـ Excel
|
||
</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- ── VIEW ITEMS MODAL ────────────────────────────── -->
|
||
<div x-show="showItemsModal" x-cloak class="modal-backdrop" style="z-index:200;">
|
||
<div class="modal-box" style="max-width:800px; width:95vw; background:var(--bg-card);">
|
||
<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="'عدد البنود: ' + (currentInvoice?.items?.length || 0)"></div>
|
||
</div>
|
||
<button @click="showItemsModal = false" class="modal-close-btn">✕</button>
|
||
</div>
|
||
<div class="modal-divider"></div>
|
||
<div class="modal-body" style="max-height:70vh; overflow-y:auto; padding:0;">
|
||
<table class="data-table" style="width:100%; border-collapse:collapse; text-align:right;">
|
||
<thead style="position:sticky; top:0; background:var(--bg-body); z-index:10; border-bottom:1px solid var(--border);">
|
||
<tr>
|
||
<th style="padding:12px 16px;">البند</th>
|
||
<th style="padding:12px 16px; text-align:center;">الكمية</th>
|
||
<th style="padding:12px 16px;">السعر</th>
|
||
<th style="padding:12px 16px;">الضريبة</th>
|
||
<th style="padding:12px 16px;">التصنيف</th>
|
||
<th style="padding:12px 16px;">الإجمالي</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<template x-for="item in currentInvoice?.items" :key="item.id">
|
||
<tr style="border-bottom:1px solid var(--border);">
|
||
<td style="padding:12px 16px; font-weight:600; color:var(--text-1);" x-text="item.description"></td>
|
||
<td style="padding:12px 16px; text-align:center; font-family:'Outfit',sans-serif;" x-text="item.quantity"></td>
|
||
<td style="padding:12px 16px; color:var(--green-mid); font-family:'Outfit',sans-serif;" x-text="item.unit_price"></td>
|
||
<td style="padding:12px 16px; color:var(--amber-mid); font-family:'Outfit',sans-serif;" x-text="((parseFloat(item.tax_rate) || 0) * 100).toFixed(0) + '%'"></td>
|
||
<td style="padding:12px 16px;">
|
||
<span class="badge"
|
||
:class="{
|
||
'badge-teal': item.tax_category === 'standard',
|
||
'badge-blue': item.tax_category === 'zero_rated',
|
||
'badge-amber': item.tax_category === 'exempt',
|
||
'badge-red': item.tax_category === 'special'
|
||
}"
|
||
x-text="item.tax_category === 'standard' ? '16%' : (item.tax_category === 'zero_rated' ? '0%' : (item.tax_category === 'exempt' ? 'معفى' : 'خاص'))">
|
||
</span>
|
||
</td>
|
||
<td style="padding:12px 16px; font-weight:700; color:var(--violet-mid); font-family:'Outfit',sans-serif;" x-text="item.net_total || item.line_total"></td>
|
||
</tr>
|
||
</template>
|
||
</tbody>
|
||
</table>
|
||
</div>
|
||
<div class="modal-divider"></div>
|
||
<div class="modal-footer" style="padding:16px;">
|
||
<button @click="showItemsModal = false" class="btn btn-navy" style="width:100%; justify-content:center;">إغلاق البنود والعودة للفاتورة</button>
|
||
</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">رقم الهاتف</label>
|
||
<input type="text" x-model="newTenant.phone" placeholder="+962 7x xxx xxxx"
|
||
class="form-input" required>
|
||
</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" style="grid-column:1/-1;">
|
||
<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;">×</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>
|
||
|
||
<!-- ── EXCEL EXPORT MODAL ──────────────────────────── -->
|
||
<div x-show="showExportExcelModal" class="modal-backdrop" x-cloak @click.self="showExportExcelModal = false">
|
||
<div class="modal-card" style="max-width:420px;">
|
||
<div class="modal-header">
|
||
<span>📥 تصدير فواتير Excel</span>
|
||
<button @click="showExportExcelModal = false" class="modal-close-btn" style="font-size:18px;">×</button>
|
||
</div>
|
||
<div class="p-6" style="display:flex; flex-direction:column; gap:16px;">
|
||
<p class="text-sm text-gray-500">حدد المعايير المطلوبة لتصدير الفواتير إلى ملف Excel احترافي.</p>
|
||
|
||
<!-- Company Select -->
|
||
<div>
|
||
<label class="form-label">الشركة</label>
|
||
<select x-model="exportExcelForm.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>
|
||
|
||
<!-- Date Range -->
|
||
<div style="display:grid; grid-template-columns:1fr 1fr; gap:12px;">
|
||
<div>
|
||
<label class="form-label">من تاريخ</label>
|
||
<input type="date" x-model="exportExcelForm.date_from" class="form-input">
|
||
</div>
|
||
<div>
|
||
<label class="form-label">إلى تاريخ</label>
|
||
<input type="date" x-model="exportExcelForm.date_to" class="form-input">
|
||
</div>
|
||
</div>
|
||
|
||
<div style="margin-top:10px;">
|
||
<button @click="exportExcelWithFilters()" class="btn btn-teal w-full justify-center">
|
||
📥 تصدير ملف Excel
|
||
</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, showExportExcelModal: false, showBatchUploadModal: false, showItemsModal: false,
|
||
exportExcelForm: { company_id: '', date_from: '', date_to: '' },
|
||
isUploadingBatch: false, batchProgress: { total: 0, current: 0 },
|
||
showAddTenantModal: false, showEditTenantModal: false, showTenantStatsModal: false,
|
||
acknowledgedWarnings: false, isEditingInvoice: false,
|
||
isBusy: false,
|
||
billingCycle: 'annual', // 'monthly' or 'annual'
|
||
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_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() {
|
||
return this.invoices.filter(inv => {
|
||
// Removed time filter to prevent invoices from disappearing after a month
|
||
// 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') || [];
|
||
},
|
||
// ── Excel Export ──
|
||
exportExcel(invoiceId = null) {
|
||
let url = '/index.php?route=v1/invoices/export-excel';
|
||
if (invoiceId) {
|
||
url += '&invoice_id=' + encodeURIComponent(invoiceId);
|
||
} else {
|
||
if (this.invoiceCompanyFilter) url += '&company_id=' + encodeURIComponent(this.invoiceCompanyFilter);
|
||
if (this.activeInvoiceTab === 'approved') url += '&status=approved';
|
||
else if (this.activeInvoiceTab === 'pending') url += '&status=extracted';
|
||
else if (this.activeInvoiceTab === 'rejected') url += '&status=rejected';
|
||
}
|
||
// Download via hidden link with auth token
|
||
const link = document.createElement('a');
|
||
link.href = url + '&token=' + encodeURIComponent(this.token());
|
||
link.download = '';
|
||
document.body.appendChild(link);
|
||
link.click();
|
||
document.body.removeChild(link);
|
||
},
|
||
exportExcelWithFilters() {
|
||
let url = '/index.php?route=v1/invoices/export-excel';
|
||
if (this.exportExcelForm.company_id) url += '&company_id=' + encodeURIComponent(this.exportExcelForm.company_id);
|
||
if (this.exportExcelForm.date_from) url += '&date_from=' + encodeURIComponent(this.exportExcelForm.date_from);
|
||
if (this.exportExcelForm.date_to) url += '&date_to=' + encodeURIComponent(this.exportExcelForm.date_to);
|
||
|
||
const link = document.createElement('a');
|
||
link.href = url + '&token=' + encodeURIComponent(this.token());
|
||
link.download = '';
|
||
document.body.appendChild(link);
|
||
link.click();
|
||
document.body.removeChild(link);
|
||
this.showExportExcelModal = false;
|
||
},
|
||
//
|
||
getQrSrc(inv) {
|
||
if (!inv) return '';
|
||
if (inv.jofotara?.qr_image_uri) return inv.jofotara.qr_image_uri;
|
||
const verifyUrl = `https://musadaq.intaleqapp.com/index.php?route=verify_qr&id=${inv.id}`;
|
||
const qr = new QRious({ value: verifyUrl, size: 300, level: 'H' });
|
||
return qr.toDataURL();
|
||
},
|
||
|
||
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_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 rejectInvoice(id) {
|
||
if (this.isBusy) return;
|
||
if (!confirm('هل أنت متأكد من رفض هذه الفاتورة؟')) return;
|
||
this.isBusy = true;
|
||
try {
|
||
const res = await fetch('/index.php?route=v1/invoices/reject', {
|
||
method: 'POST',
|
||
headers: { 'Authorization': 'Bearer ' + this.token(), 'Content-Type': 'application/json' },
|
||
body: JSON.stringify({ id: 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 deleteInvoice(id) {
|
||
if (this.isBusy) return;
|
||
if (!confirm('هل أنت متأكد من حذف هذه الفاتورة بشكل نهائي؟ لا يمكن التراجع عن هذا الإجراء.')) return;
|
||
this.isBusy = true;
|
||
try {
|
||
const res = await fetch('/index.php?route=v1/invoices/delete', {
|
||
method: 'POST',
|
||
headers: { 'Authorization': 'Bearer ' + this.token(), 'Content-Type': 'application/json' },
|
||
body: JSON.stringify({ id: 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 submitToJoFotara(id) {
|
||
if (this.isBusy) return;
|
||
this.isBusy = true;
|
||
try {
|
||
const res = await fetch('/index.php?route=v1/invoices/submit-jofotara', {
|
||
method: 'POST',
|
||
headers: { 'Authorization': 'Bearer ' + this.token(), 'Content-Type': 'application/json' },
|
||
body: JSON.stringify({ invoice_id: id })
|
||
});
|
||
const json = await res.json();
|
||
this.isBusy = false;
|
||
if (json.success) {
|
||
alert('تم إرسال الفاتورة لجوفتورة بنجاح!');
|
||
this.loadAll();
|
||
} else {
|
||
this.showError(json.message);
|
||
}
|
||
} catch (e) {
|
||
this.isBusy = false;
|
||
this.showError('حدث خطأ أثناء الإرسال لجوفتورة');
|
||
}
|
||
},
|
||
|
||
async updateInvoice() {
|
||
if (!this.currentInvoice || this.isBusy) return;
|
||
this.isBusy = true;
|
||
try {
|
||
const res = await fetch('/index.php?route=v1/invoices/update', {
|
||
method: 'POST',
|
||
headers: { 'Authorization': 'Bearer ' + this.token(), 'Content-Type': 'application/json' },
|
||
body: JSON.stringify(this.currentInvoice)
|
||
});
|
||
const json = await res.json();
|
||
this.isBusy = false;
|
||
|
||
if (json.success) {
|
||
alert('تم تحديث بيانات الفاتورة بنجاح!');
|
||
this.isEditingInvoice = 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,
|
||
billing_cycle: this.billingCycle
|
||
})
|
||
});
|
||
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> |