diff --git a/v2.html b/v2.html
index d851147..0c8f808 100644
--- a/v2.html
+++ b/v2.html
@@ -1217,26 +1217,55 @@
نموذج جديد
// ═══════════════════════════════════════════════════════════
document.addEventListener('DOMContentLoaded', () => {
- renderParkingSections();
- initializeSampleBuses(); // 30 حافلة تجريبية
- updateStatistics();
- updateTimeAndShift();
- updateBusRecordsList();
-
- setInterval(updateTimeAndShift, 1000);
- setInterval(updateCountdowns, 1000);
- setInterval(() => {
+ try {
renderParkingSections();
+ initializeSampleBuses(); // 30 حافلة تجريبية
updateStatistics();
- }, 30000);
-
- // Close form menu on outside click
- document.addEventListener('click', (e) => {
- const menu = document.getElementById('formMenu');
- if (!menu.contains(e.target) && !e.target.closest('.parking-spot')) {
- menu.classList.remove('show');
- }
- });
+ updateTimeAndShift();
+ updateBusRecordsList();
+
+ setInterval(() => {
+ try {
+ updateTimeAndShift();
+ } catch (e) {
+ console.warn('خطأ في تحديث الوقت:', e);
+ }
+ }, 1000);
+
+ setInterval(() => {
+ try {
+ updateCountdowns();
+ } catch (e) {
+ console.warn('خطأ في تحديث العد التنازلي:', e);
+ }
+ }, 1000);
+
+ setInterval(() => {
+ try {
+ renderParkingSections();
+ updateStatistics();
+ } catch (e) {
+ console.warn('خطأ في التحديث الدوري:', e);
+ }
+ }, 30000);
+
+ // Close form menu on outside click
+ document.addEventListener('click', (e) => {
+ try {
+ const menu = document.getElementById('formMenu');
+ if (menu && !menu.contains(e.target) && !e.target.closest('.parking-spot')) {
+ menu.classList.remove('show');
+ }
+ } catch (clickError) {
+ console.warn('خطأ في معالجة النقر:', clickError);
+ }
+ });
+
+ console.log('✅ تم تحميل التطبيق بنجاح');
+ } catch (error) {
+ console.error('❌ خطأ في تهيئة التطبيق:', error);
+ alert('حدث خطأ أثناء تحميل التطبيق. يرجى تحديث الصفحة.');
+ }
});
// ═══════════════════════════════════════════════════════════
@@ -1244,85 +1273,106 @@ نموذج جديد
// ═══════════════════════════════════════════════════════════
function initializeSampleBuses() {
- const now = new Date();
- const samples = [
- // مبكر (10 حافلات)
- { plate: 'ABC 1001', spot: 1, flight: 'SV1001', pax: 45, gate: 'G10', hoursOffset: 10 },
- { plate: 'ABC 1002', spot: 3, flight: 'SV1002', pax: 52, gate: 'G12', hoursOffset: 12 },
- { plate: 'ABC 1003', spot: 5, flight: 'SV1003', pax: 48, gate: 'G15', hoursOffset: 9 },
- { plate: 'ABC 1004', spot: 7, flight: 'SV1004', pax: 50, gate: 'G18', hoursOffset: 11 },
- { plate: 'ABC 1005', spot: 9, flight: 'SV1005', pax: 47, gate: 'G20', hoursOffset: 8 },
- { plate: 'ABC 1006', spot: 11, flight: 'SV1006', pax: 55, gate: 'G22', hoursOffset: 14 },
- { plate: 'ABC 1007', spot: 13, flight: 'SV1007', pax: 43, gate: 'G25', hoursOffset: 10 },
- { plate: 'ABC 1008', spot: 15, flight: 'SV1008', pax: 51, gate: 'G28', hoursOffset: 9 },
- { plate: 'ABC 1009', spot: 43, flight: 'SV1009', pax: 49, gate: 'G30', hoursOffset: 12 },
- { plate: 'ABC 1010', spot: 45, flight: 'SV1010', pax: 46, gate: 'G32', hoursOffset: 8 },
-
- // في الموعد (10 حافلات)
- { plate: 'DEF 2001', spot: 17, flight: 'SV2001', pax: 44, gate: 'G11', hoursOffset: 5 },
- { plate: 'DEF 2002', spot: 19, flight: 'SV2002', pax: 53, gate: 'G13', hoursOffset: 6 },
- { plate: 'DEF 2003', spot: 21, flight: 'SV2003', pax: 47, gate: 'G16', hoursOffset: 4 },
- { plate: 'DEF 2004', spot: 23, flight: 'SV2004', pax: 50, gate: 'G19', hoursOffset: 5.5 },
- { plate: 'DEF 2005', spot: 25, flight: 'SV2005', pax: 48, gate: 'G21', hoursOffset: 6.5 },
- { plate: 'DEF 2006', spot: 27, flight: 'SV2006', pax: 52, gate: 'G23', hoursOffset: 4.5 },
- { plate: 'DEF 2007', spot: 47, flight: 'SV2007', pax: 45, gate: 'G26', hoursOffset: 5 },
- { plate: 'DEF 2008', spot: 49, flight: 'SV2008', pax: 54, gate: 'G29', hoursOffset: 6 },
- { plate: 'DEF 2009', spot: 51, flight: 'SV2009', pax: 46, gate: 'G31', hoursOffset: 4 },
- { plate: 'DEF 2010', spot: 53, flight: 'SV2010', pax: 49, gate: 'G33', hoursOffset: 5.5 },
-
- // متأخر (10 حافلات)
- { plate: 'GHI 3001', spot: 29, flight: 'SV3001', pax: 51, gate: 'G14', hoursOffset: 2 },
- { plate: 'GHI 3002', spot: 31, flight: 'SV3002', pax: 47, gate: 'G17', hoursOffset: 1.5 },
- { plate: 'GHI 3003', spot: 33, flight: 'SV3003', pax: 53, gate: 'G24', hoursOffset: 0.5 },
- { plate: 'GHI 3004', spot: 35, flight: 'SV3004', pax: 48, gate: 'G27', hoursOffset: 2.5 },
- { plate: 'GHI 3005', spot: 37, flight: 'SV3005', pax: 50, gate: 'G34', hoursOffset: 1 },
- { plate: 'GHI 3006', spot: 39, flight: 'SV3006', pax: 44, gate: 'G35', hoursOffset: 0.25 },
- { plate: 'GHI 3007', spot: 55, flight: 'SV3007', pax: 52, gate: 'G36', hoursOffset: 2 },
- { plate: 'GHI 3008', spot: 57, flight: 'SV3008', pax: 46, gate: 'G37', hoursOffset: 1.5 },
- { plate: 'GHI 3009', spot: 59, flight: 'SV3009', pax: 55, gate: 'G38', hoursOffset: 0.5 },
- { plate: 'GHI 3010', spot: 61, flight: 'SV3010', pax: 49, gate: 'G39', hoursOffset: -0.25 }
- ];
-
- samples.forEach(s => {
- const arrTime = new Date(now.getTime() - 2*60*60*1000);
- const depTime = new Date(now.getTime() + s.hoursOffset*60*60*1000);
-
- const bus = {
- BusPlate: s.plate,
- spotNumber: s.spot,
- forms: {
- ScrLogIn: { timestamp: arrTime.toISOString(), FlightNO: s.flight, PaxAdlt: s.pax },
- ScrWelcomeLounge: {
- timestamp: arrTime.toISOString(),
- ParkNO: s.spot,
- ArrTime: arrTime.toTimeString().slice(0,5),
- DepTime: depTime.toTimeString().slice(0,5),
- FlightNO: s.flight,
- PaxAdlt: s.pax,
- GateNO: s.gate
+ try {
+ const now = new Date();
+ const samples = [
+ // مبكر (10 حافلات)
+ { plate: 'ABC 1001', spot: 1, flight: 'SV1001', pax: 45, gate: 'G10', hoursOffset: 10 },
+ { plate: 'ABC 1002', spot: 3, flight: 'SV1002', pax: 52, gate: 'G12', hoursOffset: 12 },
+ { plate: 'ABC 1003', spot: 5, flight: 'SV1003', pax: 48, gate: 'G15', hoursOffset: 9 },
+ { plate: 'ABC 1004', spot: 7, flight: 'SV1004', pax: 50, gate: 'G18', hoursOffset: 11 },
+ { plate: 'ABC 1005', spot: 9, flight: 'SV1005', pax: 47, gate: 'G20', hoursOffset: 8 },
+ { plate: 'ABC 1006', spot: 11, flight: 'SV1006', pax: 55, gate: 'G22', hoursOffset: 14 },
+ { plate: 'ABC 1007', spot: 13, flight: 'SV1007', pax: 43, gate: 'G25', hoursOffset: 10 },
+ { plate: 'ABC 1008', spot: 15, flight: 'SV1008', pax: 51, gate: 'G28', hoursOffset: 9 },
+ { plate: 'ABC 1009', spot: 43, flight: 'SV1009', pax: 49, gate: 'G30', hoursOffset: 12 },
+ { plate: 'ABC 1010', spot: 45, flight: 'SV1010', pax: 46, gate: 'G32', hoursOffset: 8 },
+
+ // في الموعد (10 حافلات)
+ { plate: 'DEF 2001', spot: 17, flight: 'SV2001', pax: 44, gate: 'G11', hoursOffset: 5 },
+ { plate: 'DEF 2002', spot: 19, flight: 'SV2002', pax: 53, gate: 'G13', hoursOffset: 6 },
+ { plate: 'DEF 2003', spot: 21, flight: 'SV2003', pax: 47, gate: 'G16', hoursOffset: 4 },
+ { plate: 'DEF 2004', spot: 23, flight: 'SV2004', pax: 50, gate: 'G19', hoursOffset: 5.5 },
+ { plate: 'DEF 2005', spot: 25, flight: 'SV2005', pax: 48, gate: 'G21', hoursOffset: 6.5 },
+ { plate: 'DEF 2006', spot: 27, flight: 'SV2006', pax: 52, gate: 'G23', hoursOffset: 4.5 },
+ { plate: 'DEF 2007', spot: 47, flight: 'SV2007', pax: 45, gate: 'G26', hoursOffset: 5 },
+ { plate: 'DEF 2008', spot: 49, flight: 'SV2008', pax: 54, gate: 'G29', hoursOffset: 6 },
+ { plate: 'DEF 2009', spot: 51, flight: 'SV2009', pax: 46, gate: 'G31', hoursOffset: 4 },
+ { plate: 'DEF 2010', spot: 53, flight: 'SV2010', pax: 49, gate: 'G33', hoursOffset: 5.5 },
+
+ // متأخر (10 حافلات)
+ { plate: 'GHI 3001', spot: 29, flight: 'SV3001', pax: 51, gate: 'G14', hoursOffset: 2 },
+ { plate: 'GHI 3002', spot: 31, flight: 'SV3002', pax: 47, gate: 'G17', hoursOffset: 1.5 },
+ { plate: 'GHI 3003', spot: 33, flight: 'SV3003', pax: 53, gate: 'G24', hoursOffset: 0.5 },
+ { plate: 'GHI 3004', spot: 35, flight: 'SV3004', pax: 48, gate: 'G27', hoursOffset: 2.5 },
+ { plate: 'GHI 3005', spot: 37, flight: 'SV3005', pax: 50, gate: 'G34', hoursOffset: 1 },
+ { plate: 'GHI 3006', spot: 39, flight: 'SV3006', pax: 44, gate: 'G35', hoursOffset: 0.25 },
+ { plate: 'GHI 3007', spot: 55, flight: 'SV3007', pax: 52, gate: 'G36', hoursOffset: 2 },
+ { plate: 'GHI 3008', spot: 57, flight: 'SV3008', pax: 46, gate: 'G37', hoursOffset: 1.5 },
+ { plate: 'GHI 3009', spot: 59, flight: 'SV3009', pax: 55, gate: 'G38', hoursOffset: 0.5 },
+ { plate: 'GHI 3010', spot: 61, flight: 'SV3010', pax: 49, gate: 'G39', hoursOffset: -0.25 }
+ ];
+
+ let successCount = 0;
+ samples.forEach(s => {
+ try {
+ if (!s.plate || !s.spot || s.spot < 1 || s.spot > 87) {
+ console.warn('بيانات حافلة تجريبية غير صالحة:', s);
+ return;
}
- },
- displayData: {
- plate: s.plate,
- flight: s.flight,
- pax: s.pax,
- gate: s.gate,
- arrival: arrTime.toTimeString().slice(0,5),
- departure: depTime.toTimeString().slice(0,5),
- depTime: depTime
- },
- createdAt: arrTime.toISOString(),
- lastUpdated: now.toISOString()
- };
-
- busRecords.push(bus);
- dailyStats.buses++;
- dailyStats.passengers += s.pax;
- dailyStats.flights.add(s.flight);
- });
-
- renderParkingSections();
- showNotification('info', 'بيانات تجريبية', 'تم تحميل 30 حافلة للمعاينة');
+
+ const arrTime = new Date(now.getTime() - 2*60*60*1000);
+ const depTime = new Date(now.getTime() + s.hoursOffset*60*60*1000);
+
+ if (isNaN(arrTime.getTime()) || isNaN(depTime.getTime())) {
+ console.warn('وقت غير صالح للحافلة:', s.plate);
+ return;
+ }
+
+ const bus = {
+ BusPlate: s.plate,
+ spotNumber: s.spot,
+ forms: {
+ ScrLogIn: { timestamp: arrTime.toISOString(), FlightNO: s.flight, PaxAdlt: s.pax },
+ ScrWelcomeLounge: {
+ timestamp: arrTime.toISOString(),
+ ParkNO: s.spot,
+ ArrTime: arrTime.toTimeString().slice(0,5),
+ DepTime: depTime.toTimeString().slice(0,5),
+ FlightNO: s.flight,
+ PaxAdlt: s.pax,
+ GateNO: s.gate
+ }
+ },
+ displayData: {
+ plate: s.plate,
+ flight: s.flight,
+ pax: s.pax,
+ gate: s.gate,
+ arrival: arrTime.toTimeString().slice(0,5),
+ departure: depTime.toTimeString().slice(0,5),
+ depTime: depTime
+ },
+ createdAt: arrTime.toISOString(),
+ lastUpdated: now.toISOString()
+ };
+
+ busRecords.push(bus);
+ dailyStats.buses++;
+ dailyStats.passengers += (parseInt(s.pax) || 0);
+ if (s.flight) dailyStats.flights.add(s.flight);
+ successCount++;
+ } catch (busError) {
+ console.warn('خطأ في إنشاء حافلة تجريبية:', busError, s);
+ }
+ });
+
+ renderParkingSections();
+ showNotification('info', 'بيانات تجريبية', `تم تحميل ${successCount} حافلة للمعاينة`);
+ } catch (error) {
+ console.error('خطأ في تهيئة البيانات التجريبية:', error);
+ showNotification('warning', 'تنبيه', 'فشل في تحميل بعض البيانات التجريبية');
+ }
}
// ═══════════════════════════════════════════════════════════
@@ -1330,35 +1380,61 @@ نموذج جديد
// ═══════════════════════════════════════════════════════════
function renderParkingSections() {
- const entranceSection = document.getElementById('entranceSection');
- const exitSection = document.getElementById('exitSection');
-
- entranceSection.innerHTML = '';
- exitSection.innerHTML = '';
-
- // جهة المدخل: 3 صفوف × 14 = 42
- let spotNumber = 1;
- for (let row = 0; row < 3; row++) {
- const rowDiv = document.createElement('div');
- rowDiv.className = 'parking-row';
- for (let i = 0; i < 14; i++) {
- const bus = busRecords.find(b => b.spotNumber === spotNumber && b.forms.ScrWelcomeLounge);
- rowDiv.appendChild(createParkingSpot(spotNumber, bus));
- spotNumber++;
+ try {
+ const entranceSection = document.getElementById('entranceSection');
+ const exitSection = document.getElementById('exitSection');
+
+ if (!entranceSection || !exitSection) {
+ console.error('لم يتم العثور على أقسام المواقف');
+ return;
}
- entranceSection.appendChild(rowDiv);
- }
-
- // جهة المخرج: 3 صفوف × 15 = 45
- for (let row = 0; row < 3; row++) {
- const rowDiv = document.createElement('div');
- rowDiv.className = 'parking-row';
- for (let i = 0; i < 15; i++) {
- const bus = busRecords.find(b => b.spotNumber === spotNumber && b.forms.ScrWelcomeLounge);
- rowDiv.appendChild(createParkingSpot(spotNumber, bus));
- spotNumber++;
+
+ entranceSection.innerHTML = '';
+ exitSection.innerHTML = '';
+
+ // جهة المدخل: 3 صفوف × 14 = 42
+ let spotNumber = 1;
+ for (let row = 0; row < 3; row++) {
+ const rowDiv = document.createElement('div');
+ rowDiv.className = 'parking-row';
+ for (let i = 0; i < 14; i++) {
+ try {
+ const bus = busRecords.find(b => b && b.spotNumber === spotNumber && b.forms && b.forms.ScrWelcomeLounge);
+ rowDiv.appendChild(createParkingSpot(spotNumber, bus));
+ } catch (spotError) {
+ console.warn(`خطأ في إنشاء الموقف ${spotNumber}:`, spotError);
+ const emptySpot = document.createElement('div');
+ emptySpot.className = 'parking-spot empty';
+ emptySpot.innerHTML = `#${spotNumber}
⚠️
`;
+ rowDiv.appendChild(emptySpot);
+ }
+ spotNumber++;
+ }
+ entranceSection.appendChild(rowDiv);
+ }
+
+ // جهة المخرج: 3 صفوف × 15 = 45
+ for (let row = 0; row < 3; row++) {
+ const rowDiv = document.createElement('div');
+ rowDiv.className = 'parking-row';
+ for (let i = 0; i < 15; i++) {
+ try {
+ const bus = busRecords.find(b => b && b.spotNumber === spotNumber && b.forms && b.forms.ScrWelcomeLounge);
+ rowDiv.appendChild(createParkingSpot(spotNumber, bus));
+ } catch (spotError) {
+ console.warn(`خطأ في إنشاء الموقف ${spotNumber}:`, spotError);
+ const emptySpot = document.createElement('div');
+ emptySpot.className = 'parking-spot empty';
+ emptySpot.innerHTML = `#${spotNumber}
⚠️
`;
+ rowDiv.appendChild(emptySpot);
+ }
+ spotNumber++;
+ }
+ exitSection.appendChild(rowDiv);
}
- exitSection.appendChild(rowDiv);
+ } catch (error) {
+ console.error('خطأ في عرض أقسام المواقف:', error);
+ showNotification('error', 'خطأ', 'فشل في تحميل المواقف');
}
}
@@ -1418,32 +1494,61 @@ نموذج جديد
}
function calculateStatus(arrival, departure) {
- const now = new Date();
- const today = now.toISOString().slice(0,10);
-
- let depTime = new Date(`${today}T${departure}`);
- if (depTime < now) depTime.setDate(depTime.getDate() + 1);
-
- const diffHours = (depTime - now) / (1000 * 60 * 60);
-
- if (diffHours > settings.earlyThreshold) return { status: 'early', label: 'مبكر', hours: diffHours };
- if (diffHours < settings.lateThreshold) return { status: 'late', label: 'متأخر', hours: diffHours };
- return { status: 'ontime', label: 'في الموعد', hours: diffHours };
+ try {
+ if (!departure || departure === '-') {
+ return { status: 'ontime', label: 'غير محدد', hours: 0 };
+ }
+
+ const now = new Date();
+ const today = now.toISOString().slice(0,10);
+
+ let depTime = new Date(`${today}T${departure}`);
+ if (isNaN(depTime.getTime())) {
+ console.warn('وقت الإقلاع غير صالح:', departure);
+ return { status: 'ontime', label: 'غير محدد', hours: 0 };
+ }
+
+ if (depTime < now) depTime.setDate(depTime.getDate() + 1);
+
+ const diffHours = (depTime - now) / (1000 * 60 * 60);
+
+ const earlyThreshold = settings?.earlyThreshold || 7;
+ const lateThreshold = settings?.lateThreshold || 3;
+
+ if (diffHours > earlyThreshold) return { status: 'early', label: 'مبكر', hours: diffHours };
+ if (diffHours < lateThreshold) return { status: 'late', label: 'متأخر', hours: diffHours };
+ return { status: 'ontime', label: 'في الموعد', hours: diffHours };
+ } catch (error) {
+ console.error('خطأ في حساب الحالة:', error);
+ return { status: 'ontime', label: 'خطأ', hours: 0 };
+ }
}
function getCountdownString(depTime) {
- if (!depTime) return '--:--';
- const now = new Date();
- const dep = new Date(depTime);
- const diff = dep - now;
-
- if (diff <= 0) return '⚠️ متأخر!';
-
- const hours = Math.floor(diff / (1000 * 60 * 60));
- const mins = Math.floor((diff % (1000 * 60 * 60)) / (1000 * 60));
- const secs = Math.floor((diff % (1000 * 60)) / 1000);
-
- return `${hours.toString().padStart(2,'0')}:${mins.toString().padStart(2,'0')}:${secs.toString().padStart(2,'0')}`;
+ try {
+ if (!depTime) return '--:--';
+
+ const now = new Date();
+ const dep = new Date(depTime);
+
+ if (isNaN(dep.getTime())) {
+ console.warn('وقت الإقلاع غير صالح:', depTime);
+ return '--:--';
+ }
+
+ const diff = dep - now;
+
+ if (diff <= 0) return '⚠️ متأخر!';
+
+ const hours = Math.floor(diff / (1000 * 60 * 60));
+ const mins = Math.floor((diff % (1000 * 60 * 60)) / (1000 * 60));
+ const secs = Math.floor((diff % (1000 * 60)) / 1000);
+
+ return `${hours.toString().padStart(2,'0')}:${mins.toString().padStart(2,'0')}:${secs.toString().padStart(2,'0')}`;
+ } catch (error) {
+ console.warn('خطأ في حساب العد التنازلي:', error);
+ return '--:--';
+ }
}
function updateCountdowns() {
@@ -1723,85 +1828,140 @@ نموذج جديد
}
function saveForm() {
- const plateInput = document.getElementById('BusPlate');
- const plate = plateInput ? plateInput.value.trim().toUpperCase() : '';
-
- if (!plate) {
- showNotification('error', 'خطأ', 'يرجى إدخال لوحة الحافلة');
- return;
- }
-
- let bus = busRecords.find(b => b.BusPlate === plate);
-
- if (!bus) {
- bus = {
- BusPlate: plate,
- spotNumber: null,
- forms: {},
- displayData: null,
- createdAt: new Date().toISOString(),
- lastUpdated: new Date().toISOString()
- };
- busRecords.push(bus);
- dailyStats.buses++;
- }
-
- // جمع بيانات النموذج
- const formData = { timestamp: new Date().toISOString() };
- document.querySelectorAll('#modalBody input, #modalBody select').forEach(el => {
- if (el.id && el.id !== 'BusPlate') {
- formData[el.id] = el.value;
+ try {
+ const plateInput = document.getElementById('BusPlate');
+ if (!plateInput) {
+ showNotification('error', 'خطأ', 'لم يتم العثور على حقل لوحة الحافلة');
+ return;
}
- });
-
- bus.forms[currentForm] = formData;
- bus.lastUpdated = new Date().toISOString();
-
- // معالجة ScrWelcomeLounge - تحديث بيانات العرض
- if (currentForm === 'ScrWelcomeLounge') {
- const parkNO = parseInt(formData.ParkNO);
- if (parkNO >= 1 && parkNO <= 87) {
+
+ const plate = plateInput.value.trim().toUpperCase();
+
+ if (!plate) {
+ showNotification('error', 'خطأ', 'يرجى إدخال لوحة الحافلة');
+ return;
+ }
+
+ if (plate.length < 3) {
+ showNotification('error', 'خطأ', 'لوحة الحافلة قصيرة جداً (يجب أن تكون 3 أحرف على الأقل)');
+ return;
+ }
+
+ if (!currentForm) {
+ showNotification('error', 'خطأ', 'لم يتم تحديد نوع النموذج');
+ return;
+ }
+
+ let bus = busRecords.find(b => b.BusPlate === plate);
+
+ if (!bus) {
+ bus = {
+ BusPlate: plate,
+ spotNumber: null,
+ forms: {},
+ displayData: null,
+ createdAt: new Date().toISOString(),
+ lastUpdated: new Date().toISOString()
+ };
+ busRecords.push(bus);
+ dailyStats.buses++;
+ }
+
+ // جمع بيانات النموذج
+ const formData = { timestamp: new Date().toISOString() };
+ const modalBody = document.getElementById('modalBody');
+ if (!modalBody) {
+ showNotification('error', 'خطأ', 'لم يتم العثور على محتوى النموذج');
+ return;
+ }
+
+ modalBody.querySelectorAll('input, select').forEach(el => {
+ if (el.id && el.id !== 'BusPlate') {
+ formData[el.id] = el.value;
+ }
+ });
+
+ bus.forms[currentForm] = formData;
+ bus.lastUpdated = new Date().toISOString();
+
+ // معالجة ScrWelcomeLounge - تحديث بيانات العرض
+ if (currentForm === 'ScrWelcomeLounge') {
+ const parkNO = parseInt(formData.ParkNO);
+ if (isNaN(parkNO) || parkNO < 1 || parkNO > 87) {
+ showNotification('error', 'خطأ', 'رقم الموقف يجب أن يكون بين 1 و 87');
+ return;
+ }
+
+ // التحقق من أن الموقف غير مشغول بحافلة أخرى
+ const existingBus = busRecords.find(b => b.spotNumber === parkNO && b.BusPlate !== plate && b.forms.ScrWelcomeLounge);
+ if (existingBus) {
+ showNotification('error', 'خطأ', `الموقف ${parkNO} مشغول بالفعل بالحافلة ${existingBus.BusPlate}`);
+ return;
+ }
+
bus.spotNumber = parkNO;
-
+
const now = new Date();
const today = now.toISOString().slice(0,10);
- let depTime = formData.DepTime ? new Date(`${today}T${formData.DepTime}`) : null;
- if (depTime && depTime < now) depTime.setDate(depTime.getDate() + 1);
-
+ let depTime = null;
+
+ if (formData.DepTime) {
+ try {
+ depTime = new Date(`${today}T${formData.DepTime}`);
+ if (isNaN(depTime.getTime())) {
+ throw new Error('وقت الإقلاع غير صالح');
+ }
+ if (depTime < now) depTime.setDate(depTime.getDate() + 1);
+ } catch (dateError) {
+ console.warn('خطأ في تحليل وقت الإقلاع:', dateError);
+ depTime = null;
+ }
+ }
+
+ const paxCount = parseInt(formData.PaxAdlt);
+ if (formData.PaxAdlt && (isNaN(paxCount) || paxCount < 0)) {
+ showNotification('warning', 'تنبيه', 'عدد الركاب غير صالح، تم تعيينه إلى 0');
+ }
+
bus.displayData = {
plate: plate,
flight: formData.FlightNO || '-',
- pax: parseInt(formData.PaxAdlt) || 0,
+ pax: isNaN(paxCount) ? 0 : Math.max(0, paxCount),
gate: formData.GateNO || '-',
arrival: formData.ArrTime || '-',
departure: formData.DepTime || '-',
depTime: depTime
};
-
- if (formData.PaxAdlt) dailyStats.passengers += parseInt(formData.PaxAdlt);
+
+ if (formData.PaxAdlt && !isNaN(paxCount) && paxCount > 0) {
+ dailyStats.passengers += paxCount;
+ }
if (formData.FlightNO) dailyStats.flights.add(formData.FlightNO);
}
- }
-
- // معالجة ScrSegregationExit - إخلاء الموقف
- if (currentForm === 'ScrSegregationExit' && formData.ExitStatus === 'completed') {
- const spotNum = bus.spotNumber;
- busRecords = busRecords.filter(b => b.BusPlate !== plate);
- showNotification('success', 'تم الإخلاء', `تم إخلاء الحافلة ${plate} من الموقف ${spotNum}`);
- addAlert('success', 'خروج حافلة', `الحافلة ${plate} غادرت الموقف ${spotNum}`);
- closeModal();
+
+ // معالجة ScrSegregationExit - إخلاء الموقف
+ if (currentForm === 'ScrSegregationExit' && formData.ExitStatus === 'completed') {
+ const spotNum = bus.spotNumber;
+ busRecords = busRecords.filter(b => b.BusPlate !== plate);
+ showNotification('success', 'تم الإخلاء', `تم إخلاء الحافلة ${plate} من الموقف ${spotNum || 'غير محدد'}`);
+ addAlert('success', 'خروج حافلة', `الحافلة ${plate} غادرت الموقف ${spotNum || 'غير محدد'}`);
+ closeModal();
+ renderParkingSections();
+ updateStatistics();
+ updateBusRecordsList();
+ return;
+ }
+
renderParkingSections();
updateStatistics();
updateBusRecordsList();
- return;
+ showNotification('success', 'تم الحفظ', `تم حفظ ${currentForm} للحافلة ${plate}`);
+ addAlert('info', 'تسجيل جديد', `${currentForm} - ${plate}`);
+ closeModal();
+ } catch (error) {
+ console.error('خطأ في حفظ النموذج:', error);
+ showNotification('error', 'خطأ', 'حدث خطأ أثناء حفظ النموذج: ' + error.message);
}
-
- renderParkingSections();
- updateStatistics();
- updateBusRecordsList();
- showNotification('success', 'تم الحفظ', `تم حفظ ${currentForm} للحافلة ${plate}`);
- addAlert('info', 'تسجيل جديد', `${currentForm} - ${plate}`);
- closeModal();
}
// ═══════════════════════════════════════════════════════════
@@ -1809,90 +1969,126 @@ نموذج جديد
// ═══════════════════════════════════════════════════════════
function updateStatistics() {
- let early = 0, ontime = 0, late = 0, totalPax = 0;
-
- busRecords.forEach(bus => {
- if (bus.displayData) {
- const status = calculateStatus(bus.displayData.arrival, bus.displayData.departure).status;
- if (status === 'early') early++;
- else if (status === 'ontime') ontime++;
- else late++;
- totalPax += bus.displayData.pax || 0;
+ try {
+ let early = 0, ontime = 0, late = 0, totalPax = 0;
+
+ if (busRecords && Array.isArray(busRecords)) {
+ busRecords.forEach(bus => {
+ try {
+ if (bus && bus.displayData) {
+ const status = calculateStatus(bus.displayData.arrival, bus.displayData.departure).status;
+ if (status === 'early') early++;
+ else if (status === 'ontime') ontime++;
+ else late++;
+ totalPax += parseInt(bus.displayData.pax) || 0;
+ }
+ } catch (busError) {
+ console.warn('خطأ في معالجة بيانات حافلة:', busError);
+ }
+ });
}
- });
-
- const total = busRecords.filter(b => b.displayData).length;
- const occupancy = Math.round((total / 87) * 100);
-
- // Stats Bar
- document.getElementById('totalBuses').textContent = total;
- document.getElementById('earlyCount').textContent = early;
- document.getElementById('ontimeCount').textContent = ontime;
- document.getElementById('lateCount').textContent = late;
- document.getElementById('totalPassengers').textContent = dailyStats.passengers;
- document.getElementById('totalFlights').textContent = dailyStats.flights.size;
- document.getElementById('occupancyRate').textContent = occupancy + '%';
-
- // Main Tab
- document.getElementById('mainTotalBuses').textContent = total;
- document.getElementById('mainEarlyBuses').textContent = early;
- document.getElementById('mainOnTimeBuses').textContent = ontime;
- document.getElementById('mainLateBuses').textContent = late;
- document.getElementById('mainTotalPax').textContent = dailyStats.passengers;
-
- // KPIs Tab
- document.getElementById('kpiOccupancy').textContent = occupancy + '%';
- document.getElementById('occupancyBar').style.width = occupancy + '%';
- document.getElementById('kpiDailyBuses').textContent = dailyStats.buses;
- document.getElementById('kpiDailyPax').textContent = dailyStats.passengers;
-
- if (total > 0) {
- const earlyPct = Math.round((early / total) * 100);
- const ontimePct = Math.round((ontime / total) * 100);
- const latePct = Math.round((late / total) * 100);
-
- document.getElementById('kpiEarlyPct').textContent = earlyPct + '%';
- document.getElementById('kpiOntimePct').textContent = ontimePct + '%';
- document.getElementById('kpiLatePct').textContent = latePct + '%';
-
- document.getElementById('statusChart').style.background =
- `linear-gradient(90deg, var(--green) ${earlyPct}%, var(--blue) ${earlyPct}% ${earlyPct+ontimePct}%, var(--red) ${earlyPct+ontimePct}%)`;
+
+ const total = busRecords ? busRecords.filter(b => b && b.displayData).length : 0;
+ const occupancy = Math.round((total / 87) * 100);
+
+ // Helper function for safe element update
+ const safeSetText = (id, value) => {
+ const el = document.getElementById(id);
+ if (el) el.textContent = value;
+ };
+
+ const safeSetStyle = (id, property, value) => {
+ const el = document.getElementById(id);
+ if (el) el.style[property] = value;
+ };
+
+ // Stats Bar
+ safeSetText('totalBuses', total);
+ safeSetText('earlyCount', early);
+ safeSetText('ontimeCount', ontime);
+ safeSetText('lateCount', late);
+ safeSetText('totalPassengers', dailyStats?.passengers || 0);
+ safeSetText('totalFlights', dailyStats?.flights?.size || 0);
+ safeSetText('occupancyRate', occupancy + '%');
+
+ // Main Tab
+ safeSetText('mainTotalBuses', total);
+ safeSetText('mainEarlyBuses', early);
+ safeSetText('mainOnTimeBuses', ontime);
+ safeSetText('mainLateBuses', late);
+ safeSetText('mainTotalPax', dailyStats?.passengers || 0);
+
+ // KPIs Tab
+ safeSetText('kpiOccupancy', occupancy + '%');
+ safeSetStyle('occupancyBar', 'width', occupancy + '%');
+ safeSetText('kpiDailyBuses', dailyStats?.buses || 0);
+ safeSetText('kpiDailyPax', dailyStats?.passengers || 0);
+
+ if (total > 0) {
+ const earlyPct = Math.round((early / total) * 100);
+ const ontimePct = Math.round((ontime / total) * 100);
+ const latePct = Math.round((late / total) * 100);
+
+ safeSetText('kpiEarlyPct', earlyPct + '%');
+ safeSetText('kpiOntimePct', ontimePct + '%');
+ safeSetText('kpiLatePct', latePct + '%');
+
+ safeSetStyle('statusChart', 'background',
+ `linear-gradient(90deg, var(--green) ${earlyPct}%, var(--blue) ${earlyPct}% ${earlyPct+ontimePct}%, var(--red) ${earlyPct+ontimePct}%)`);
+ }
+ } catch (error) {
+ console.error('خطأ في تحديث الإحصائيات:', error);
}
}
function updateBusRecordsList() {
- const container = document.getElementById('busRecordsList');
- const registeredBuses = busRecords.filter(b => Object.keys(b.forms).length > 0);
-
- if (registeredBuses.length === 0) {
- container.innerHTML = 'لا توجد حافلات مسجلة
';
- return;
+ try {
+ const container = document.getElementById('busRecordsList');
+ if (!container) {
+ console.error('لم يتم العثور على حاوية قائمة الحافلات');
+ return;
+ }
+
+ const registeredBuses = (busRecords || []).filter(b => b && b.forms && Object.keys(b.forms).length > 0);
+
+ if (registeredBuses.length === 0) {
+ container.innerHTML = 'لا توجد حافلات مسجلة
';
+ return;
+ }
+
+ container.innerHTML = registeredBuses.map(bus => {
+ try {
+ const completedForms = Object.keys(bus.forms || {}).length;
+ const progress = (completedForms / 5) * 100;
+ const formsList = ['ScrLogIn', 'ScrSegregationIn', 'ScrWelcomeLounge', 'ScrSegregationExit', 'ScrCurbside'];
+ const safePlate = (bus.BusPlate || '').replace(/'/g, "\\'");
+
+ return `
+
+
+
+ ✈️ ${bus.displayData?.flight || '-'}
+ 👥 ${bus.displayData?.pax || 0}
+
+
+
+ ${formsList.map(f => `${f.replace('Scr','')}`).join('')}
+
+
+ `;
+ } catch (busError) {
+ console.warn('خطأ في عرض سجل حافلة:', busError);
+ return '';
+ }
+ }).join('');
+ } catch (error) {
+ console.error('خطأ في تحديث قائمة الحافلات:', error);
}
-
- container.innerHTML = registeredBuses.map(bus => {
- const completedForms = Object.keys(bus.forms).length;
- const progress = (completedForms / 5) * 100;
- const formsList = ['ScrLogIn', 'ScrSegregationIn', 'ScrWelcomeLounge', 'ScrSegregationExit', 'ScrCurbside'];
-
- return `
-
-
-
- ✈️ ${bus.displayData?.flight || '-'}
- 👥 ${bus.displayData?.pax || 0}
-
-
-
- ${formsList.map(f => `${f.replace('Scr','')}`).join('')}
-
-
- `;
- }).join('');
}
function openBusRecord(plate) {
@@ -2070,39 +2266,100 @@ نموذج جديد
// ═══════════════════════════════════════════════════════════
function exportToJSON() {
- const data = {
- exportDate: new Date().toISOString(),
- busRecords: busRecords,
- dailyStats: {
- buses: dailyStats.buses,
- passengers: dailyStats.passengers,
- flights: Array.from(dailyStats.flights)
+ try {
+ if (!busRecords || busRecords.length === 0) {
+ showNotification('warning', 'تنبيه', 'لا توجد بيانات للتصدير');
+ return;
}
- };
- downloadFile(JSON.stringify(data, null, 2), 'locc_data.json', 'application/json');
- showNotification('success', 'تصدير', 'تم تصدير البيانات بصيغة JSON');
+
+ const data = {
+ exportDate: new Date().toISOString(),
+ busRecords: busRecords,
+ dailyStats: {
+ buses: dailyStats.buses,
+ passengers: dailyStats.passengers,
+ flights: Array.from(dailyStats.flights)
+ }
+ };
+
+ const jsonString = JSON.stringify(data, null, 2);
+ if (!jsonString) {
+ throw new Error('فشل في تحويل البيانات إلى JSON');
+ }
+
+ downloadFile(jsonString, 'locc_data.json', 'application/json');
+ showNotification('success', 'تصدير', 'تم تصدير البيانات بصيغة JSON');
+ } catch (error) {
+ console.error('خطأ في تصدير JSON:', error);
+ showNotification('error', 'خطأ في التصدير', 'فشل في تصدير البيانات: ' + error.message);
+ }
}
function exportToCSV() {
- let csv = 'BusPlate,SpotNumber,FlightNO,PaxAdlt,GateNO,ArrTime,DepTime,Status,FormsCompleted\n';
- busRecords.forEach(bus => {
- if (bus.displayData) {
- const status = calculateStatus(bus.displayData.arrival, bus.displayData.departure).status;
- csv += `${bus.BusPlate},${bus.spotNumber},${bus.displayData.flight},${bus.displayData.pax},${bus.displayData.gate},${bus.displayData.arrival},${bus.displayData.departure},${status},${Object.keys(bus.forms).length}\n`;
+ try {
+ if (!busRecords || busRecords.length === 0) {
+ showNotification('warning', 'تنبيه', 'لا توجد بيانات للتصدير');
+ return;
}
- });
- downloadFile(csv, 'locc_data.csv', 'text/csv');
- showNotification('success', 'تصدير', 'تم تصدير البيانات بصيغة CSV');
+
+ const busesWithData = busRecords.filter(bus => bus && bus.displayData);
+ if (busesWithData.length === 0) {
+ showNotification('warning', 'تنبيه', 'لا توجد حافلات مع بيانات عرض للتصدير');
+ return;
+ }
+
+ let csv = 'BusPlate,SpotNumber,FlightNO,PaxAdlt,GateNO,ArrTime,DepTime,Status,FormsCompleted\n';
+ busesWithData.forEach(bus => {
+ try {
+ const status = calculateStatus(bus.displayData.arrival, bus.displayData.departure).status;
+ const plate = (bus.BusPlate || '').replace(/,/g, ';');
+ const flight = (bus.displayData.flight || '').replace(/,/g, ';');
+ const gate = (bus.displayData.gate || '').replace(/,/g, ';');
+ csv += `${plate},${bus.spotNumber || ''},${flight},${bus.displayData.pax || 0},${gate},${bus.displayData.arrival || ''},${bus.displayData.departure || ''},${status},${Object.keys(bus.forms || {}).length}\n`;
+ } catch (rowError) {
+ console.warn('تخطي صف بسبب خطأ:', rowError, bus);
+ }
+ });
+
+ downloadFile(csv, 'locc_data.csv', 'text/csv');
+ showNotification('success', 'تصدير', 'تم تصدير البيانات بصيغة CSV');
+ } catch (error) {
+ console.error('خطأ في تصدير CSV:', error);
+ showNotification('error', 'خطأ في التصدير', 'فشل في تصدير البيانات: ' + error.message);
+ }
}
function downloadFile(content, filename, type) {
- const blob = new Blob([content], { type: type });
- const url = URL.createObjectURL(blob);
- const a = document.createElement('a');
- a.href = url;
- a.download = filename;
- a.click();
- URL.revokeObjectURL(url);
+ let url = null;
+ try {
+ if (!content) {
+ throw new Error('لا يوجد محتوى للتنزيل');
+ }
+ if (!filename) {
+ throw new Error('اسم الملف مطلوب');
+ }
+
+ const blob = new Blob([content], { type: type || 'text/plain' });
+ url = URL.createObjectURL(blob);
+
+ if (!url) {
+ throw new Error('فشل في إنشاء رابط التنزيل');
+ }
+
+ const a = document.createElement('a');
+ a.href = url;
+ a.download = filename;
+ document.body.appendChild(a);
+ a.click();
+ document.body.removeChild(a);
+ } catch (error) {
+ console.error('خطأ في تنزيل الملف:', error);
+ showNotification('error', 'خطأ في التنزيل', 'فشل في تنزيل الملف: ' + error.message);
+ } finally {
+ if (url) {
+ URL.revokeObjectURL(url);
+ }
+ }
}