307 lines
16 KiB
HTML
Executable File
307 lines
16 KiB
HTML
Executable File
<!DOCTYPE html>
|
|
<html lang="ar" dir="rtl">
|
|
<head>
|
|
<meta charset="UTF-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
<title>نظام إدارة المركبات</title>
|
|
<script src="https://cdn.tailwindcss.com"></script>
|
|
<link href="https://fonts.googleapis.com/css2?family=Cairo:wght@300;400;600;700&display=swap" rel="stylesheet">
|
|
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
|
|
<style>
|
|
body { font-family: 'Cairo', sans-serif; background-color: #f8fafc; }
|
|
.skeleton {
|
|
background: linear-gradient(90deg, #f0f0f0 25%, #e0e0e0 50%, #f0f0f0 75%);
|
|
background-size: 200% 100%;
|
|
animation: loading 1.5s infinite;
|
|
}
|
|
@keyframes loading { 0% { background-position: 200% 0; } 100% { background-position: -200% 0; } }
|
|
</style>
|
|
</head>
|
|
<body class="bg-slate-50 text-slate-800">
|
|
|
|
<nav class="bg-white shadow-sm border-b border-slate-200 sticky top-0 z-50">
|
|
<div class="max-w-7xl mx-auto px-4">
|
|
<div class="flex justify-between h-16 items-center">
|
|
<div class="flex items-center gap-2">
|
|
<i class="fas fa-car-side text-blue-600 text-2xl"></i>
|
|
<h1 class="text-xl font-bold text-slate-800">لوحة المركبات</h1>
|
|
</div>
|
|
<div class="flex items-center gap-3">
|
|
<span class="text-xs bg-green-50 text-green-600 px-2 py-1 rounded-full border border-green-200">
|
|
<i class="fas fa-circle text-[8px] ml-1"></i> متصل
|
|
</span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</nav>
|
|
|
|
<main class="max-w-7xl mx-auto px-4 py-8">
|
|
|
|
<!-- Search Section -->
|
|
<div class="max-w-2xl mx-auto text-center mb-10">
|
|
<h2 class="text-2xl font-bold mb-6 text-slate-700">الاستعلام عن بيانات السائق والمركبة</h2>
|
|
<div class="relative group">
|
|
<input type="text" id="searchInput" placeholder="أدخل رقم الهاتف (مثال: 963...)"
|
|
onkeypress="handleEnter(event)"
|
|
class="w-full pl-4 pr-14 py-4 border-2 border-slate-200 rounded-2xl focus:border-blue-500 focus:ring-0 outline-none transition text-lg shadow-sm group-hover:border-slate-300">
|
|
<button onclick="fetchData()" class="absolute inset-y-2 left-2 bg-blue-600 hover:bg-blue-700 text-white px-6 rounded-xl transition font-medium">
|
|
بحث
|
|
</button>
|
|
<div class="absolute inset-y-0 right-0 flex items-center pr-5 pointer-events-none text-slate-400">
|
|
<i class="fas fa-search text-xl"></i>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div id="statusMessage" class="hidden max-w-2xl mx-auto mb-6"></div>
|
|
|
|
<!-- Results Grid -->
|
|
<div id="resultsGrid" class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6"></div>
|
|
|
|
<!-- Empty State -->
|
|
<div id="emptyState" class="hidden text-center py-20">
|
|
<div class="inline-flex bg-slate-100 p-6 rounded-full mb-4">
|
|
<i class="fas fa-fingerprint text-4xl text-slate-300"></i>
|
|
</div>
|
|
<p class="text-slate-500">أدخل رقم الهاتف للبدء</p>
|
|
</div>
|
|
</main>
|
|
|
|
<!-- Modal -->
|
|
<div id="detailsModal" class="fixed inset-0 bg-black/60 z-50 hidden flex items-center justify-center p-4 backdrop-blur-sm transition-opacity duration-300 opacity-0">
|
|
<div class="bg-white rounded-3xl shadow-2xl w-full max-w-md overflow-hidden transform scale-95 transition-all duration-300" id="modalContent">
|
|
<div class="relative bg-slate-800 p-6 text-center text-white overflow-hidden">
|
|
<div class="absolute top-0 left-0 w-full h-1 bg-gradient-to-r from-blue-500 via-purple-500 to-pink-500"></div>
|
|
<h3 id="modalTitle" class="text-2xl font-bold mb-1"></h3>
|
|
<p id="modalYear" class="text-slate-400 text-sm"></p>
|
|
<button onclick="closeModal()" class="absolute top-4 right-4 text-white/50 hover:text-white transition">
|
|
<i class="fas fa-times text-xl"></i>
|
|
</button>
|
|
</div>
|
|
|
|
<div class="p-6 space-y-6">
|
|
<!-- Owner Info -->
|
|
<div class="flex items-center gap-4">
|
|
<div class="w-12 h-12 rounded-full bg-blue-50 text-blue-600 flex items-center justify-center text-xl">
|
|
<i class="fas fa-user"></i>
|
|
</div>
|
|
<div>
|
|
<p class="text-xs text-slate-400 uppercase tracking-wider">المالك / السائق</p>
|
|
<p id="modalOwner" class="font-bold text-slate-800 text-lg"></p>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Car Plate & Color -->
|
|
<div class="grid grid-cols-2 gap-4">
|
|
<div class="bg-slate-50 p-3 rounded-xl border border-slate-100 text-center">
|
|
<p class="text-xs text-slate-400 mb-1">رقم اللوحة</p>
|
|
<p id="modalPlate" class="font-mono font-bold text-slate-800 text-lg dir-ltr"></p>
|
|
</div>
|
|
<div class="bg-slate-50 p-3 rounded-xl border border-slate-100 text-center">
|
|
<p class="text-xs text-slate-400 mb-1">اللون</p>
|
|
<div class="flex items-center justify-center gap-2">
|
|
<span id="modalColorDot" class="w-4 h-4 rounded-full shadow-sm border border-slate-200"></span>
|
|
<span id="modalColor" class="font-bold text-slate-800"></span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Details List -->
|
|
<div class="space-y-3 pt-2">
|
|
<div class="flex justify-between items-center text-sm border-b border-slate-100 pb-2">
|
|
<span class="text-slate-500">الشركة المصنعة</span>
|
|
<span id="modalMake" class="font-medium text-slate-800"></span>
|
|
</div>
|
|
<div class="flex justify-between items-center text-sm border-b border-slate-100 pb-2">
|
|
<span class="text-slate-500">رقم الهاتف</span>
|
|
<span id="modalPhone" class="font-medium text-slate-800 dir-ltr"></span>
|
|
</div>
|
|
<div class="flex justify-between items-center text-sm border-b border-slate-100 pb-2">
|
|
<span class="text-slate-500">رقم الهيكل (VIN)</span>
|
|
<span id="modalVin" class="font-mono text-xs text-slate-600 bg-slate-100 px-2 py-1 rounded"></span>
|
|
</div>
|
|
<div class="flex justify-between items-center text-sm pt-1">
|
|
<span class="text-slate-500">حالة الصلاحية</span>
|
|
<span id="modalExpiry"></span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="p-4 bg-slate-50 border-t border-slate-100">
|
|
<button onclick="closeModal()" class="w-full py-3 bg-white border border-slate-200 text-slate-600 rounded-xl hover:bg-slate-50 transition font-medium">
|
|
إغلاق
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<script>
|
|
const API_URL = "https://api.intaleq.xyz/intaleq/serviceapp/web/getDrivers.php";
|
|
|
|
document.addEventListener('DOMContentLoaded', () => {
|
|
document.getElementById('emptyState').classList.remove('hidden');
|
|
});
|
|
|
|
function handleEnter(e) { if(e.key === 'Enter') fetchData(); }
|
|
|
|
async function fetchData() {
|
|
const grid = document.getElementById('resultsGrid');
|
|
const emptyState = document.getElementById('emptyState');
|
|
const statusMsg = document.getElementById('statusMessage');
|
|
const searchInput = document.getElementById('searchInput').value.trim();
|
|
|
|
statusMsg.classList.add('hidden');
|
|
if (!searchInput) {
|
|
showError("الرجاء إدخال رقم الهاتف");
|
|
return;
|
|
}
|
|
|
|
grid.innerHTML = generateSkeletons(1);
|
|
emptyState.classList.add('hidden');
|
|
|
|
try {
|
|
const url = `${API_URL}?phone_number=${encodeURIComponent(searchInput)}`;
|
|
const response = await fetch(url);
|
|
const textResponse = await response.text();
|
|
|
|
let rows = [];
|
|
try {
|
|
const json = JSON.parse(textResponse);
|
|
if (Array.isArray(json)) rows = json;
|
|
else if (json.status === "success" && json.data) rows = Array.isArray(json.data) ? json.data : [json.data];
|
|
else if (json.data) rows = [json.data]; // fallback
|
|
} catch (e) {
|
|
throw new Error("بيانات غير صالحة من السيرفر");
|
|
}
|
|
|
|
if (!rows || rows.length === 0) {
|
|
throw new Error("لم يتم العثور على بيانات لهذا الرقم");
|
|
}
|
|
|
|
renderCards(rows);
|
|
|
|
} catch (error) {
|
|
console.error(error);
|
|
grid.innerHTML = '';
|
|
showError(error.message);
|
|
}
|
|
}
|
|
|
|
function renderCards(data) {
|
|
const grid = document.getElementById('resultsGrid');
|
|
grid.innerHTML = '';
|
|
|
|
data.forEach(item => {
|
|
// منطق العرض: إذا كانت الماركة موجودة نعرضها، وإلا نعرض "لا توجد سيارة"
|
|
const hasCar = item.make && item.model;
|
|
|
|
const displayTitle = hasCar ? `${item.make} ${item.model}` : 'لا توجد مركبة مسجلة';
|
|
const displayOwner = item.owner || item.driverName || 'مجهول';
|
|
const displayPlate = item.car_plate || '---';
|
|
const displayYear = item.year || '';
|
|
|
|
// Expiry Logic
|
|
let expiryBadge = '<span class="text-slate-400 text-xs">غير محدد</span>';
|
|
if (item.expiration_date) {
|
|
const isExpired = new Date(item.expiration_date) < new Date();
|
|
expiryBadge = isExpired
|
|
? `<span class="bg-red-100 text-red-600 text-xs px-2 py-1 rounded font-bold">منتهي</span>`
|
|
: `<span class="bg-green-100 text-green-600 text-xs px-2 py-1 rounded font-bold">ساري</span>`;
|
|
}
|
|
|
|
const cardHTML = `
|
|
<div class="bg-white rounded-2xl shadow-sm border border-slate-100 hover:shadow-md transition duration-300 overflow-hidden group">
|
|
<div class="h-2 w-full" style="background-color: ${item.color_hex || '#cbd5e1'}"></div>
|
|
<div class="p-5">
|
|
<div class="flex justify-between items-start mb-4">
|
|
<div>
|
|
<h3 class="font-bold text-lg text-slate-800">${displayTitle}</h3>
|
|
${displayYear ? `<span class="text-xs bg-slate-100 text-slate-500 px-2 py-0.5 rounded mt-1 inline-block">${displayYear}</span>` : ''}
|
|
</div>
|
|
${expiryBadge}
|
|
</div>
|
|
|
|
<div class="flex items-center gap-3 mb-4 p-3 bg-slate-50 rounded-xl border border-slate-100">
|
|
<div class="w-10 h-10 rounded-full bg-white text-blue-500 flex items-center justify-center shadow-sm">
|
|
<i class="fas fa-user"></i>
|
|
</div>
|
|
<div class="overflow-hidden">
|
|
<p class="text-xs text-slate-400">الاسم</p>
|
|
<p class="font-bold text-slate-700 truncate">${displayOwner}</p>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="flex justify-between items-center">
|
|
<div class="text-sm text-slate-500">
|
|
<i class="fas fa-barcode ml-1 opacity-50"></i>
|
|
<span class="font-mono dir-ltr">${displayPlate}</span>
|
|
</div>
|
|
<button onclick='openModal(${JSON.stringify(item)})' class="text-blue-600 hover:text-blue-700 font-bold text-sm">
|
|
التفاصيل <i class="fas fa-arrow-left mr-1"></i>
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</div>`;
|
|
|
|
grid.innerHTML += cardHTML;
|
|
});
|
|
}
|
|
|
|
function openModal(item) {
|
|
const modal = document.getElementById('detailsModal');
|
|
const content = document.getElementById('modalContent');
|
|
|
|
// Populate Data
|
|
const hasCar = item.make && item.model;
|
|
document.getElementById('modalTitle').innerText = hasCar ? `${item.make} ${item.model}` : 'لا توجد مركبة';
|
|
document.getElementById('modalYear').innerText = item.year || '';
|
|
document.getElementById('modalOwner').innerText = item.owner || item.driverName || '---';
|
|
document.getElementById('modalPlate').innerText = item.car_plate || '---';
|
|
document.getElementById('modalColor').innerText = item.color || 'غير محدد';
|
|
document.getElementById('modalColorDot').style.backgroundColor = item.color_hex || '#e2e8f0';
|
|
|
|
document.getElementById('modalMake').innerText = item.make || '-';
|
|
document.getElementById('modalPhone').innerText = item.phone || '-';
|
|
document.getElementById('modalVin').innerText = item.vin || '---';
|
|
|
|
const expiryEl = document.getElementById('modalExpiry');
|
|
if (item.expiration_date) {
|
|
const isExpired = new Date(item.expiration_date) < new Date();
|
|
expiryEl.innerHTML = isExpired
|
|
? `<span class="text-red-600 font-bold">${item.expiration_date} (منتهي)</span>`
|
|
: `<span class="text-green-600 font-bold">${item.expiration_date} (ساري)</span>`;
|
|
} else {
|
|
expiryEl.innerText = 'غير محدد';
|
|
}
|
|
|
|
// Animation
|
|
modal.classList.remove('hidden');
|
|
setTimeout(() => {
|
|
modal.classList.remove('opacity-0');
|
|
content.classList.remove('scale-95');
|
|
content.classList.add('scale-100');
|
|
}, 10);
|
|
}
|
|
|
|
function closeModal() {
|
|
const modal = document.getElementById('detailsModal');
|
|
const content = document.getElementById('modalContent');
|
|
|
|
modal.classList.add('opacity-0');
|
|
content.classList.remove('scale-100');
|
|
content.classList.add('scale-95');
|
|
setTimeout(() => modal.classList.add('hidden'), 300);
|
|
}
|
|
|
|
function showError(msg) {
|
|
const el = document.getElementById('statusMessage');
|
|
el.innerHTML = `<div class="bg-red-50 text-red-600 p-4 rounded-xl border border-red-100 flex items-center gap-3"><i class="fas fa-exclamation-circle text-xl"></i><span>${msg}</span></div>`;
|
|
el.classList.remove('hidden');
|
|
}
|
|
|
|
function generateSkeletons(count) {
|
|
return `<div class="bg-white p-5 rounded-2xl border border-slate-100"><div class="h-4 bg-slate-100 w-1/3 mb-4 rounded skeleton"></div><div class="h-20 bg-slate-100 rounded-xl skeleton"></div></div>`;
|
|
}
|
|
</script>
|
|
</body>
|
|
</html> |