490 lines
35 KiB
PHP
490 lines
35 KiB
PHP
<!DOCTYPE html>
|
||
<html lang="ar" dir="rtl" data-theme="dark">
|
||
<head>
|
||
<meta charset="UTF-8">
|
||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||
<title>مُصادَق | لوحة التحكم</title>
|
||
<link href="https://fonts.googleapis.com/css2?family=IBM+Plex+Sans+Arabic:wght@300;400;500;600;700&family=IBM+Plex+Mono:wght@400;500&display=swap" rel="stylesheet">
|
||
<script src="https://cdn.tailwindcss.com"></script>
|
||
<script defer src="https://unpkg.com/alpinejs@3.x.x/dist/cdn.min.js"></script>
|
||
<style>
|
||
:root {
|
||
--emerald: #10b981;
|
||
--bg-base: #080c14;
|
||
--bg-surface: #0d1424;
|
||
--border-default: rgba(255,255,255,0.1);
|
||
--text-primary: #f0f6fc;
|
||
}
|
||
body { font-family: 'IBM Plex Sans Arabic', sans-serif; background-color: var(--bg-base); color: var(--text-primary); }
|
||
[x-cloak] { display: none !important; }
|
||
.glass { background: rgba(13, 20, 36, 0.7); backdrop-filter: blur(12px); border: 1px solid rgba(255,255,255,0.05); }
|
||
.scrollbar-hide::-webkit-scrollbar { display: none; }
|
||
</style>
|
||
</head>
|
||
<body x-data="app" x-init="init()">
|
||
|
||
<!-- Global Error Toast -->
|
||
<div x-show="globalError" x-cloak
|
||
class="fixed top-4 left-1/2 -translate-x-1/2 z-[100] bg-red-900/90 border border-red-500 text-white px-6 py-3 rounded-lg shadow-2xl max-w-lg text-center glass"
|
||
x-text="globalError"
|
||
x-transition
|
||
@click="globalError = ''">
|
||
</div>
|
||
|
||
<div class="flex h-screen overflow-hidden">
|
||
<!-- Sidebar -->
|
||
<aside class="w-64 bg-surface border-l border-gray-800 flex flex-col">
|
||
<div class="p-6">
|
||
<h1 class="text-2xl font-bold text-emerald-500 tracking-tight">مُصادَق</h1>
|
||
<p class="text-[10px] text-gray-500 mt-1 uppercase tracking-widest">Enterprise SaaS</p>
|
||
</div>
|
||
<nav class="flex-1 px-4 space-y-2 mt-4">
|
||
<a href="#" @click="setPage('dashboard')" class="flex items-center gap-3 p-3 rounded-lg transition-all" :class="page==='dashboard'?'bg-emerald-500/10 text-emerald-500 shadow-[inset_0_0_20px_rgba(16,185,129,0.05)]':'text-gray-400 hover:bg-gray-800'">
|
||
<span class="text-xl">📊</span>
|
||
<span class="font-medium">لوحة التحكم</span>
|
||
</a>
|
||
<a x-show="user?.role === 'super_admin'" href="#" @click="setPage('tenants')" class="flex items-center gap-3 p-3 rounded-lg transition-all" :class="page==='tenants'?'bg-emerald-500/10 text-emerald-500':'text-gray-400 hover:bg-gray-800'">
|
||
<span class="text-xl">🏢</span>
|
||
<span class="font-medium">المكاتب المحاسبية</span>
|
||
</a>
|
||
<a x-show="user?.role !== 'viewer'" href="#" @click="setPage('companies')" class="flex items-center gap-3 p-3 rounded-lg transition-all" :class="page==='companies'?'bg-emerald-500/10 text-emerald-500':'text-gray-400 hover:bg-gray-800'">
|
||
<span class="text-xl">🏭</span>
|
||
<span class="font-medium">الشركات</span>
|
||
</a>
|
||
<a x-show="user?.role !== 'viewer'" href="#" @click="setPage('invoices')" class="flex items-center gap-3 p-3 rounded-lg transition-all" :class="page==='invoices'?'bg-emerald-500/10 text-emerald-500':'text-gray-400 hover:bg-gray-800'">
|
||
<span class="text-xl">📄</span>
|
||
<span class="font-medium">الفواتير</span>
|
||
</a>
|
||
<a x-show="user?.role === 'super_admin' || user?.role === 'admin'" href="#" @click="setPage('users')" class="flex items-center gap-3 p-3 rounded-lg transition-all" :class="page==='users'?'bg-emerald-500/10 text-emerald-500':'text-gray-400 hover:bg-gray-800'">
|
||
<span class="text-xl">👥</span>
|
||
<span class="font-medium">المستخدمون</span>
|
||
</a>
|
||
</nav>
|
||
<div class="p-6 border-t border-gray-800">
|
||
<div class="flex items-center gap-3 mb-4">
|
||
<div class="w-8 h-8 rounded-full bg-emerald-500/20 flex items-center justify-center text-emerald-500 font-bold text-xs" x-text="user?.name?.[0]"></div>
|
||
<div class="truncate">
|
||
<p class="text-xs font-bold truncate" x-text="user?.name"></p>
|
||
<p class="text-[10px] text-gray-500 uppercase" x-text="user?.role"></p>
|
||
</div>
|
||
</div>
|
||
<button @click="logout()" class="w-full text-right text-red-400 text-xs font-bold hover:text-red-300 transition">🚪 تسجيل الخروج</button>
|
||
</div>
|
||
</aside>
|
||
|
||
<!-- Main -->
|
||
<main class="flex-1 overflow-y-auto p-10 bg-[#060910]">
|
||
<header class="mb-10 flex justify-between items-end">
|
||
<div>
|
||
<h2 class="text-3xl font-bold tracking-tight" x-text="title()"></h2>
|
||
<p class="text-gray-500 mt-1 text-sm" x-text="subtitle()"></p>
|
||
</div>
|
||
<div class="flex items-center gap-3">
|
||
<button x-show="page==='tenants'" @click="showAddTenantModal = true" class="bg-emerald-600 hover:bg-emerald-500 px-5 py-2.5 rounded-lg text-sm font-bold shadow-lg shadow-emerald-900/20 transition-all active:scale-95">➕ إضافة مكتب</button>
|
||
<button x-show="page==='users'" @click="showAddModal = true" class="bg-emerald-600 hover:bg-emerald-500 px-5 py-2.5 rounded-lg text-sm font-bold shadow-lg shadow-emerald-900/20 transition-all active:scale-95">➕ إضافة مستخدم</button>
|
||
<button x-show="page==='companies'" @click="showAddCompanyModal = true" class="bg-emerald-600 hover:bg-emerald-500 px-5 py-2.5 rounded-lg text-sm font-bold shadow-lg shadow-emerald-900/20 transition-all active:scale-95">➕ إضافة شركة</button>
|
||
<button x-show="page==='invoices'" @click="showUploadModal = true" class="bg-emerald-600 hover:bg-emerald-500 px-5 py-2.5 rounded-lg text-sm font-bold shadow-lg shadow-emerald-900/20 transition-all active:scale-95">📤 رفع فواتير</button>
|
||
</div>
|
||
</header>
|
||
|
||
<div id="content" x-transition>
|
||
<!-- Invoices -->
|
||
<div x-show="page === 'invoices'">
|
||
<div class="bg-surface border border-gray-800 rounded-2xl overflow-hidden shadow-2xl">
|
||
<table class="w-full text-right border-collapse">
|
||
<thead class="bg-gray-900/50">
|
||
<tr>
|
||
<th class="p-5 text-xs font-bold text-gray-500 uppercase">الشركة</th>
|
||
<th class="p-5 text-xs font-bold text-gray-500 uppercase">المورد</th>
|
||
<th class="p-5 text-xs font-bold text-gray-500 uppercase">التاريخ</th>
|
||
<th class="p-5 text-xs font-bold text-gray-500 uppercase">المجموع</th>
|
||
<th class="p-5 text-xs font-bold text-gray-500 uppercase">الحالة</th>
|
||
<th class="p-5 text-xs font-bold text-gray-500 uppercase">إجراءات</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody class="divide-y divide-gray-800">
|
||
<tr x-show="invoices.length === 0"><td colspan="6" class="p-12 text-center text-gray-600">لا توجد فواتير بعد</td></tr>
|
||
<template x-for="inv in invoices" :key="inv.id">
|
||
<tr class="hover:bg-white/[0.02] transition-colors group">
|
||
<td class="p-5">
|
||
<p class="font-bold text-emerald-500" x-text="inv.company_name"></p>
|
||
</td>
|
||
<td class="p-5">
|
||
<p class="text-sm font-medium" x-text="inv.supplier_name"></p>
|
||
<p class="text-[10px] text-gray-500 mt-1" x-text="inv.supplier_tin"></p>
|
||
</td>
|
||
<td class="p-5 text-sm text-gray-400" x-text="inv.invoice_date || '-'"></td>
|
||
<td class="p-5">
|
||
<span class="font-mono text-sm" x-text="parseFloat(inv.grand_total).toLocaleString()"></span>
|
||
<span class="text-[10px] text-gray-600 mr-1">JOD</span>
|
||
</td>
|
||
<td class="p-5">
|
||
<span class="px-2 py-1 rounded-md text-[10px] font-bold uppercase"
|
||
:class="inv.status==='extracted'?'bg-blue-900/30 text-blue-400':(inv.status==='approved'?'bg-emerald-900/30 text-emerald-400':'bg-gray-800 text-gray-400')"
|
||
x-text="inv.status"></span>
|
||
</td>
|
||
<td class="p-5">
|
||
<button @click="viewInvoice(inv.id)" class="text-gray-500 hover:text-emerald-400 p-2 rounded-lg hover:bg-emerald-400/10 transition">👁️</button>
|
||
</td>
|
||
</tr>
|
||
</template>
|
||
</tbody>
|
||
</table>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Dashboard -->
|
||
<div x-show="page === 'dashboard'">
|
||
<div class="grid grid-cols-1 md:grid-cols-3 gap-6">
|
||
<div class="p-8 bg-surface border border-gray-800 rounded-2xl shadow-xl relative overflow-hidden group">
|
||
<div class="absolute top-0 right-0 p-4 opacity-10 group-hover:opacity-20 transition-opacity"><span class="text-4xl">📄</span></div>
|
||
<p class="text-gray-500 text-xs uppercase font-bold tracking-widest">إجمالي الفواتير</p>
|
||
<p class="text-4xl font-bold mt-3" x-text="stats.total || 0"></p>
|
||
</div>
|
||
<div class="p-8 bg-surface border border-gray-800 rounded-2xl shadow-xl relative overflow-hidden group">
|
||
<div class="absolute top-0 right-0 p-4 opacity-10 group-hover:opacity-20 transition-opacity"><span class="text-4xl text-yellow-500">⏳</span></div>
|
||
<p class="text-gray-500 text-xs uppercase font-bold tracking-widest">قيد المعالجة</p>
|
||
<p class="text-4xl font-bold mt-3 text-yellow-500" x-text="stats.pending || 0"></p>
|
||
</div>
|
||
<div class="p-8 bg-surface border border-gray-800 rounded-2xl shadow-xl relative overflow-hidden group">
|
||
<div class="absolute top-0 right-0 p-4 opacity-10 group-hover:opacity-20 transition-opacity"><span class="text-4xl text-emerald-500">✅</span></div>
|
||
<p class="text-gray-500 text-xs uppercase font-bold tracking-widest">تم الاعتماد</p>
|
||
<p class="text-4xl font-bold mt-3 text-emerald-500" x-text="stats.approved || 0"></p>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Companies -->
|
||
<div x-show="page === 'companies'">
|
||
<div class="bg-surface border border-gray-800 rounded-2xl overflow-hidden shadow-2xl">
|
||
<table class="w-full text-right divide-y divide-gray-800">
|
||
<thead class="bg-gray-900/50">
|
||
<tr>
|
||
<th class="p-5 text-xs font-bold text-gray-500 uppercase">الشركة</th>
|
||
<th class="p-5 text-xs font-bold text-gray-500 uppercase">الأرقام الرسمية</th>
|
||
<th class="p-5 text-xs font-bold text-gray-500 uppercase">العنوان</th>
|
||
<th class="p-5 text-xs font-bold text-gray-500 uppercase">المكتب</th>
|
||
<th class="p-5 text-xs font-bold text-gray-500 uppercase">إجراءات</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody class="divide-y divide-gray-800">
|
||
<tr x-show="companies.length === 0"><td colspan="5" class="p-12 text-center text-gray-600">لا توجد شركات بعد</td></tr>
|
||
<template x-for="c in companies" :key="c.id">
|
||
<tr class="hover:bg-white/[0.01] transition-colors group">
|
||
<td class="p-5"><p class="font-bold text-emerald-500" x-text="c.name"></p></td>
|
||
<td class="p-5">
|
||
<p class="text-xs text-gray-400">TIN: <span class="font-mono" x-text="c.tax_identification_number"></span></p>
|
||
<p class="text-xs text-gray-400">CRN: <span class="font-mono" x-text="c.commercial_registration_number"></span></p>
|
||
</td>
|
||
<td class="p-5 text-sm text-gray-500" x-text="c.address"></td>
|
||
<td class="p-5 text-xs text-gray-500" x-text="c.tenant_name || '-'"></td>
|
||
<td class="p-5">
|
||
<button x-show="user?.role === 'super_admin' || user?.role === 'admin'" @click="confirmDeleteCompany(c)" class="text-gray-500 hover:text-red-500 p-2 rounded-lg hover:bg-red-500/10 transition">🗑️</button>
|
||
</td>
|
||
</tr>
|
||
</template>
|
||
</tbody>
|
||
</table>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Users List -->
|
||
<div x-show="page === 'users'">
|
||
<div class="bg-surface border border-gray-800 rounded-2xl overflow-hidden shadow-2xl">
|
||
<table class="w-full text-right divide-y divide-gray-800">
|
||
<thead class="bg-gray-900/50">
|
||
<tr>
|
||
<th class="p-5 text-xs font-bold text-gray-500 uppercase">المستخدم</th>
|
||
<th class="p-5 text-xs font-bold text-gray-500 uppercase">المكتب</th>
|
||
<th class="p-5 text-xs font-bold text-gray-500 uppercase">الدور</th>
|
||
<th class="p-5 text-xs font-bold text-gray-500 uppercase">الحالة</th>
|
||
<th class="p-5 text-xs font-bold text-gray-500 uppercase">إجراءات</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody class="divide-y divide-gray-800">
|
||
<tr x-show="users.length === 0"><td colspan="5" class="p-12 text-center text-gray-600">لا يوجد مستخدمون بعد</td></tr>
|
||
<template x-for="u in users" :key="u.id">
|
||
<tr class="hover:bg-white/[0.01] transition-colors group">
|
||
<td class="p-5">
|
||
<p class="font-bold text-emerald-500" x-text="u.name"></p>
|
||
<p class="text-xs text-gray-500" x-text="u.email"></p>
|
||
</td>
|
||
<td class="p-5 text-sm text-gray-400" x-text="u.tenant_name || '-'"></td>
|
||
<td class="p-5"><span class="px-2 py-1 bg-gray-800 rounded text-[10px] font-bold uppercase" x-text="u.role"></span></td>
|
||
<td class="p-5"><span class="w-2 h-2 rounded-full inline-block" :class="u.is_active?'bg-emerald-500 shadow-[0_0_8px_rgba(16,185,129,0.5)]':'bg-red-500'"></span></td>
|
||
<td class="p-5 flex gap-2">
|
||
<button x-show="u.id !== user.id" @click="confirmDeleteUser(u)" class="text-gray-500 hover:text-red-500 p-2 rounded-lg hover:bg-red-500/10 transition">🗑️</button>
|
||
</td>
|
||
</tr>
|
||
</template>
|
||
</tbody>
|
||
</table>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</main>
|
||
|
||
<!-- Full Invoice View Modal -->
|
||
<div x-show="showViewModal" x-cloak class="fixed inset-0 bg-black/95 flex items-center justify-center p-6 z-[100]">
|
||
<div class="bg-surface border border-gray-800 w-full h-full max-w-7xl rounded-3xl shadow-2xl flex overflow-hidden glass">
|
||
|
||
<!-- Left: File Preview -->
|
||
<div class="w-1/2 bg-black/40 border-l border-gray-800 flex flex-col relative">
|
||
<div class="p-4 border-b border-gray-800 flex justify-between items-center bg-gray-950/50">
|
||
<span class="text-xs font-bold text-gray-500 uppercase tracking-widest">معاينة المستند الأصلي</span>
|
||
<a :href="currentInvoice?.file_url + '&token=' + token()" target="_blank" class="text-[10px] bg-gray-800 px-3 py-1 rounded hover:bg-gray-700 transition">تحميل الملف 📥</a>
|
||
</div>
|
||
<div class="flex-1 overflow-auto p-4 flex items-start justify-center scrollbar-hide">
|
||
<template x-if="currentInvoice?.original_file_path?.toLowerCase().endsWith('.pdf')">
|
||
<iframe :src="currentInvoice?.file_url + '&token=' + token()" class="w-full h-full rounded-lg" frameborder="0"></iframe>
|
||
</template>
|
||
<template x-if="!currentInvoice?.original_file_path?.toLowerCase().endsWith('.pdf')">
|
||
<img :src="currentInvoice?.file_url + '&token=' + token()" @error="$el.src='https://placehold.co/600x800?text=Error+Loading+Image'" class="max-w-full rounded-lg shadow-2xl border border-white/5">
|
||
</template>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Right: Extracted Data -->
|
||
<div class="w-1/2 flex flex-col">
|
||
<div class="p-6 border-b border-gray-800 flex justify-between items-center bg-emerald-900/10">
|
||
<div>
|
||
<h3 class="text-xl font-bold">تفاصيل الفاتورة المستخرجة</h3>
|
||
<p class="text-[10px] text-emerald-500/70 mt-1 uppercase tracking-tighter">AI-Powered Extraction (Gemini 1.5 Flash)</p>
|
||
</div>
|
||
<button @click="showViewModal = false" class="text-gray-500 hover:text-white text-2xl transition">✕</button>
|
||
</div>
|
||
|
||
<div class="flex-1 overflow-y-auto p-8 space-y-10 scrollbar-hide">
|
||
<!-- Supplier Info -->
|
||
<div class="grid grid-cols-2 gap-8">
|
||
<div class="p-4 bg-gray-950/50 border border-gray-800 rounded-2xl">
|
||
<label class="block text-[10px] text-gray-500 font-bold uppercase mb-2">المورد</label>
|
||
<p class="text-sm font-bold text-emerald-400" x-text="currentInvoice?.supplier_name"></p>
|
||
<p class="text-[10px] text-gray-500 mt-1">TIN: <span x-text="currentInvoice?.supplier_tin"></span></p>
|
||
<p class="text-[10px] text-gray-500 mt-1" x-text="currentInvoice?.supplier_address"></p>
|
||
</div>
|
||
<div class="p-4 bg-gray-950/50 border border-gray-800 rounded-2xl">
|
||
<label class="block text-[10px] text-gray-500 font-bold uppercase mb-2">رقم وتاريخ الفاتورة</label>
|
||
<p class="text-sm font-bold" x-text="currentInvoice?.invoice_number"></p>
|
||
<p class="text-[10px] text-gray-400 mt-1" x-text="currentInvoice?.invoice_date"></p>
|
||
<p class="text-[10px] text-gray-500 mt-1 uppercase" x-text="currentInvoice?.invoice_type + ' / ' + currentInvoice?.invoice_category"></p>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Line Items -->
|
||
<div>
|
||
<label class="block text-[10px] text-gray-500 font-bold uppercase mb-4 tracking-widest">بنود الفاتورة التفصيلية</label>
|
||
<div class="bg-gray-950/30 border border-gray-800 rounded-2xl overflow-hidden">
|
||
<table class="w-full text-right text-xs">
|
||
<thead class="bg-gray-900/50">
|
||
<tr>
|
||
<th class="p-3 text-gray-500 font-bold">#</th>
|
||
<th class="p-3 text-gray-500 font-bold">الوصف</th>
|
||
<th class="p-3 text-gray-500 font-bold">الكمية</th>
|
||
<th class="p-3 text-gray-500 font-bold">السعر</th>
|
||
<th class="p-3 text-gray-500 font-bold">الضريبة</th>
|
||
<th class="p-3 text-gray-500 font-bold text-emerald-500">الإجمالي</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody class="divide-y divide-gray-800">
|
||
<template x-for="item in currentInvoice?.items" :key="item.id">
|
||
<tr class="hover:bg-white/[0.02]">
|
||
<td class="p-3 text-gray-600" x-text="item.line_number"></td>
|
||
<td class="p-3 font-medium" x-text="item.description"></td>
|
||
<td class="p-3" x-text="parseFloat(item.quantity).toLocaleString()"></td>
|
||
<td class="p-3" x-text="parseFloat(item.unit_price).toLocaleString()"></td>
|
||
<td class="p-3 text-gray-500" x-text="(item.tax_rate * 100) + '%'"></td>
|
||
<td class="p-3 font-bold text-emerald-500" x-text="parseFloat(item.line_total).toLocaleString()"></td>
|
||
</tr>
|
||
</template>
|
||
</tbody>
|
||
</table>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Totals Section -->
|
||
<div class="flex justify-end">
|
||
<div class="w-64 space-y-3 p-6 bg-emerald-500/5 border border-emerald-500/20 rounded-2xl">
|
||
<div class="flex justify-between text-xs">
|
||
<span class="text-gray-500 font-bold">المجموع الفرعي</span>
|
||
<span class="font-mono" x-text="parseFloat(currentInvoice?.subtotal || 0).toLocaleString()"></span>
|
||
</div>
|
||
<div class="flex justify-between text-xs">
|
||
<span class="text-gray-500 font-bold">الخصم الإجمالي</span>
|
||
<span class="font-mono text-red-400" x-text="'-' + parseFloat(currentInvoice?.discount_total || 0).toLocaleString()"></span>
|
||
</div>
|
||
<div class="flex justify-between text-xs">
|
||
<span class="text-gray-500 font-bold">ضريبة المبيعات</span>
|
||
<span class="font-mono" x-text="parseFloat(currentInvoice?.tax_amount || 0).toLocaleString()"></span>
|
||
</div>
|
||
<div class="border-t border-emerald-500/20 pt-3 flex justify-between">
|
||
<span class="text-sm font-bold text-emerald-400">الإجمالي النهائي</span>
|
||
<span class="text-lg font-bold text-emerald-400 font-mono" x-text="parseFloat(currentInvoice?.grand_total || 0).toLocaleString() + ' JOD'"></span>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="p-6 bg-gray-950/50 border-t border-gray-800 flex gap-4">
|
||
<button class="flex-1 bg-emerald-600 hover:bg-emerald-500 py-3 rounded-xl font-bold transition">✅ اعتماد الفاتورة</button>
|
||
<button class="flex-1 border border-gray-800 hover:bg-gray-800 py-3 rounded-xl font-bold transition">📝 تعديل البيانات</button>
|
||
</div>
|
||
</div>
|
||
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Upload Invoice Modal -->
|
||
<div x-show="showUploadModal" x-cloak class="fixed inset-0 bg-black/90 backdrop-blur-md flex items-center justify-center p-4 z-50">
|
||
<div class="bg-surface border border-gray-800 w-full max-w-lg p-10 rounded-3xl shadow-2xl glass" @click.away="showUploadModal = false">
|
||
<h3 class="text-2xl font-bold mb-2">رفع فواتير جديدة 📤</h3>
|
||
<p class="text-gray-500 text-sm mb-8">سيقوم النظام باستخراج البيانات آلياً باستخدام الذكاء الاصطناعي</p>
|
||
<form @submit.prevent="uploadInvoice" class="space-y-6">
|
||
<div>
|
||
<label class="block text-xs font-bold text-gray-500 uppercase mb-2 tracking-widest">اختر الشركة</label>
|
||
<select x-model="uploadData.company_id" class="w-full bg-gray-950 border border-gray-800 p-4 rounded-xl outline-none focus:ring-2 focus:ring-emerald-500/20 transition-all" 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="border-2 border-dashed border-gray-800 rounded-2xl p-12 text-center hover:border-emerald-500/50 transition-colors relative cursor-pointer group">
|
||
<input type="file" @change="handleFile" class="absolute inset-0 opacity-0 cursor-pointer" required>
|
||
<div class="space-y-2">
|
||
<span class="text-4xl block group-hover:scale-110 transition-transform">📄</span>
|
||
<p class="text-sm font-bold text-gray-400" x-text="selectedFile ? selectedFile.name : 'اسحب الملف هنا أو اضغط للاختيار'"></p>
|
||
<p class="text-[10px] text-gray-600 uppercase">PDF, PNG, JPG (Max 5MB)</p>
|
||
</div>
|
||
</div>
|
||
<div class="pt-4 flex gap-4">
|
||
<button type="submit" class="flex-1 bg-emerald-600 hover:bg-emerald-500 py-4 rounded-xl font-bold shadow-lg shadow-emerald-900/20 transition-all active:scale-95 disabled:opacity-50" :disabled="isUploading">
|
||
<span x-show="!isUploading">بدء المعالجة الذكية</span>
|
||
<span x-show="isUploading" class="flex items-center justify-center gap-2">جارِ التحليل...</span>
|
||
</button>
|
||
<button type="button" @click="showUploadModal = false" class="px-8 py-4 border border-gray-800 rounded-xl hover:bg-gray-800 transition-all">إلغاء</button>
|
||
</div>
|
||
</form>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Add User Modal -->
|
||
<div x-show="showAddModal" x-cloak class="fixed inset-0 bg-black/80 backdrop-blur-sm flex items-center justify-center p-4 z-50">
|
||
<div class="bg-surface border border-gray-800 w-full max-w-md p-8 rounded-3xl shadow-2xl glass" @click.away="showAddModal = false">
|
||
<h3 class="text-xl font-bold mb-6">إضافة مستخدم جديد 👥</h3>
|
||
<form @submit.prevent="createUser" class="space-y-4">
|
||
<div><input type="text" x-model="newUser.name" placeholder="الاسم الكامل" class="w-full bg-gray-950 border border-gray-800 p-3 rounded-xl outline-none" required></div>
|
||
<div><input type="email" x-model="newUser.email" placeholder="البريد الإلكتروني" class="w-full bg-gray-950 border border-gray-800 p-3 rounded-xl outline-none" required></div>
|
||
<div><input type="password" x-model="newUser.password" placeholder="كلمة المرور" class="w-full bg-gray-950 border border-gray-800 p-3 rounded-xl outline-none" required></div>
|
||
<div>
|
||
<select x-model="newUser.role" class="w-full bg-gray-950 border border-gray-800 p-3 rounded-xl outline-none">
|
||
<option value="employee">موظف</option><option value="accountant">محاسب</option><option value="admin">مدير مكتب</option>
|
||
</select>
|
||
</div>
|
||
<div class="pt-4 flex gap-3">
|
||
<button type="submit" class="flex-1 bg-emerald-600 hover:bg-emerald-500 py-3.5 rounded-xl font-bold transition">حفظ</button>
|
||
<button type="button" @click="showAddModal = false" class="px-6 py-3.5 border border-gray-800 rounded-xl hover:bg-gray-800 transition">إلغاء</button>
|
||
</div>
|
||
</form>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<script>
|
||
document.addEventListener('alpine:init', () => {
|
||
Alpine.data('app', () => ({
|
||
user: JSON.parse(localStorage.getItem('user')),
|
||
page: 'dashboard',
|
||
users: [],
|
||
companies: [],
|
||
tenants: [],
|
||
invoices: [],
|
||
stats: { total: 0, pending: 0, approved: 0 },
|
||
|
||
showAddModal: false,
|
||
showAddCompanyModal: false,
|
||
showAddTenantModal: false,
|
||
showUploadModal: false,
|
||
showViewModal: false,
|
||
isUploading: false,
|
||
globalError: '',
|
||
|
||
newUser: { name: '', email: '', password: '', role: 'employee', tenant_id: '' },
|
||
uploadData: { company_id: '' },
|
||
selectedFile: null,
|
||
currentInvoice: null,
|
||
|
||
init() {
|
||
if (!this.user) { window.location.href = '/login.php'; return; }
|
||
this.loadAll();
|
||
},
|
||
|
||
setPage(p) {
|
||
this.page = p;
|
||
this.loadAll();
|
||
},
|
||
|
||
title() { return { dashboard: 'نظرة عامة', users: 'إدارة المستخدمين', companies: 'الشركات', invoices: 'الفواتير' }[this.page] || ''; },
|
||
subtitle() { return { dashboard: 'تحليلات المنصة', users: 'إدارة أدوار الوصول', invoices: 'إدارة الفواتير المرفوعة' }[this.page] || ''; },
|
||
|
||
showError(msg) { this.globalError = msg; setTimeout(() => this.globalError = '', 6000); },
|
||
token() { return localStorage.getItem('access_token'); },
|
||
|
||
async apiGet(route) {
|
||
const res = await fetch('/index.php?route=' + route, { headers: { 'Authorization': 'Bearer ' + this.token() } });
|
||
const json = await res.json();
|
||
return json.success ? json.data : (this.showError(json.message), null);
|
||
},
|
||
|
||
async loadAll() {
|
||
this.loadStats();
|
||
this.loadCompanies();
|
||
if (this.page === 'users') this.loadUsers();
|
||
if (this.page === 'invoices') this.loadInvoices();
|
||
if (this.page === 'tenants') this.loadTenants();
|
||
},
|
||
|
||
async loadUsers() { this.users = await this.apiGet('v1/users') || []; },
|
||
async loadCompanies() { this.companies = await this.apiGet('v1/companies') || []; },
|
||
async loadInvoices() { this.invoices = await this.apiGet('v1/invoices') || []; },
|
||
async loadStats() { this.stats = await this.apiGet('v1/dashboard/stats') || {}; },
|
||
async loadTenants() { this.tenants = await this.apiGet('v1/tenants') || []; },
|
||
|
||
async viewInvoice(id) {
|
||
const data = await this.apiGet('v1/invoices/view&id=' + id);
|
||
if (data) {
|
||
this.currentInvoice = data;
|
||
this.showViewModal = true;
|
||
}
|
||
},
|
||
|
||
handleFile(e) { this.selectedFile = e.target.files[0]; },
|
||
|
||
async uploadInvoice() {
|
||
if (!this.selectedFile) return alert('الرجاء اختيار ملف');
|
||
this.isUploading = true;
|
||
const formData = new FormData();
|
||
formData.append('company_id', this.uploadData.company_id);
|
||
formData.append('invoice', this.selectedFile);
|
||
|
||
const res = await fetch('/index.php?route=v1/invoices/upload', {
|
||
method: 'POST',
|
||
headers: { 'Authorization': 'Bearer ' + this.token() },
|
||
body: formData
|
||
});
|
||
const json = await res.json();
|
||
this.isUploading = false;
|
||
if (json.success) {
|
||
this.showUploadModal = false;
|
||
this.loadInvoices();
|
||
this.viewInvoice(json.data.id); // Open view modal immediately!
|
||
} else {
|
||
this.showError(json.message);
|
||
}
|
||
},
|
||
|
||
logout() { localStorage.clear(); window.location.href = '/login.php'; }
|
||
}));
|
||
});
|
||
</script>
|
||
</body>
|
||
</html>
|