اليكم الان أجواء لبنان .. خريطة البلاغات والمتابعة المباشرة عاجل والان إلى التفاصيل من المصدر بتوقيت بيروت_ beiruttime
أجواء
رقم واتساب غير موثّقيمكنك الدخول، لكن التبليغ يتطلب توثيق الرقم.
${waiting>0?’يمكنك طلب رابط جديد بعد’:’يمكنك طلب الرابط الآن’}${waiting>0?formatPhoneCountdown(waiting):’متاح’}
${adminEscape(reviewLabel)}`;
}
const shell=document.querySelector(‘.report-shell’);if(shell)shell.scrollTo({top:0,behavior:force?’auto’:’smooth’});
if(stage===1&&state.maps.mini)setTimeout(()=>state.maps.mini.invalidateSize(),80);
return true;
}
async function handleReportSubmit(e){
e.preventDefault();
showConfirmModal(“هل أنت متأكد من إرسال هذا التقرير؟”, async () => {
const data=Object.fromEntries(new FormData(e.target).entries());
data.lat=document.getElementById(‘lat’).value;data.lon=document.getElementById(‘lon’).value;
if (data.type === ‘assault_mode’) {
if (!data.assault_subtype) { showAppModal(‘خطأ’, “يرجى تحديد نوع الاعتداء”, ‘error’); return; }
data.type = data.assault_subtype;
delete data.assault_subtype;
}
const circ=document.querySelector(‘input[value=”circular”]’).checked; data.is_circular_flight=circ?1:0;
if(document.querySelector(‘input[value=”direction”]’).checked){const dir=document.querySelector(‘input[name=”aircraft_direction”]:checked’);if(dir)data.aircraft_direction=dir.value;}
if(!data.lat){showAppModal(‘خطأ’, “حدد الموقع”, ‘error’);return;}
if(!data.city || data.city.trim() === “”) { showAppModal(‘خطأ’, “يرجى تحديد المنطقة (اسم القرية أو المدينة)!”, ‘error’); return; }
try{const res=await fetch(`${API_URL}?action=add_report`,{method:’POST’,body:JSON.stringify(data)});const result=await res.json();if(res.ok){showAppModal(‘وصل بلاغك’, “ظهر البلاغ كبلاغ منفرد، وسيتحوّل إلى متقاطع جماعياً عند ورود بلاغ مستقل قريب.”, ‘success’);e.target.reset();document.getElementById(‘lat’).value=””;document.getElementById(‘lon’).value=””;document.getElementById(‘selected-location-name’).textContent=”لم تحدد موقعاً بعد”;delete document.getElementById(‘selected-location-name’).dataset.state;toggleReportStage(1,true);router.navigate(‘home’);}else showAppModal(‘تعذر الإرسال’, result.error||”فشل الإرسال”, ‘error’);}catch(x){showAppModal(‘خطأ’, “خطأ في الاتصال”, ‘error’);}
});
}
// — STATS OVERHAUL LOGIC —
state.statsData = null;
state.statsPeriod = ‘daily’;
state.statsMapMode=”markers”; // ‘markers’ | ‘heat’
state.statsHeatPoints = [];
state.statsCurrentFilter=”all”;
function stepStatsDate(days) {
const input = document.getElementById(‘stats-date’);
const curVal = input.value || getBeirutDateString();
const d = new Date(curVal);
d.setDate(d.getDate() + days);
input.value = d.toISOString().split(‘T’)[0];
loadStats();
}
function setStatsToday() {
const input = document.getElementById(‘stats-date’);
input.value = getBeirutDateString();
loadStats();
}
function switchStatsPeriod(period) {
state.statsPeriod = period;
document.querySelectorAll(‘.period-btn’).forEach(b => {
b.classList.toggle(‘active’, b.dataset.period === period);
});
updateStatsKPIs();
}
function updateStatsKPIs() {
if(!state.statsData) return;
const p = state.statsPeriod;
let tot = state.statsData.totals;
if(p === ‘weekly’) tot = state.statsData.weekly_totals;
if(p === ‘monthly’) tot = state.statsData.monthly_totals;
if(!tot) tot = {};
const totalCount = parseInt(tot.total || 0, 10);
const uav = parseInt(tot.uav || 0, 10);
const fighter = parseInt(tot.fighter || 0, 10);
const shelling = parseInt(tot.shelling || 0, 10);
const drone = parseInt(tot.drone || 0, 10);
const heli = parseInt(tot.helicopter || 0, 10);
const calcPct = (v) => totalCount > 0 ? `${Math.round((v / totalCount) * 100)}%` : ‘0%’;
document.getElementById(‘kpi-uav’).innerText = uav;
document.getElementById(‘kpi-uav-pct’).innerText = calcPct(uav);
document.getElementById(‘kpi-uav-bar’).style.width = calcPct(uav);
document.getElementById(‘kpi-fighter’).innerText = fighter;
document.getElementById(‘kpi-fighter-pct’).innerText = calcPct(fighter);
document.getElementById(‘kpi-fighter-bar’).style.width = calcPct(fighter);
document.getElementById(‘kpi-shelling’).innerText = shelling;
document.getElementById(‘kpi-shelling-pct’).innerText = calcPct(shelling);
document.getElementById(‘kpi-shelling-bar’).style.width = calcPct(shelling);
document.getElementById(‘kpi-drone’).innerText = drone;
document.getElementById(‘kpi-drone-pct’).innerText = calcPct(drone);
document.getElementById(‘kpi-drone-bar’).style.width = calcPct(drone);
document.getElementById(‘kpi-helicopter’).innerText = heli;
document.getElementById(‘kpi-helicopter-pct’).innerText = calcPct(heli);
document.getElementById(‘kpi-helicopter-bar’).style.width = calcPct(heli);
const periodLabel = p === ‘daily’ ? ‘اليوم’ : (p === ‘weekly’ ? ‘آخر 7 أيام’ : ‘هذا الشهر’);
document.getElementById(‘stats-total-badge’).innerText = `${totalCount} بلاغ (${periodLabel})`;
}
function setStatsMapMode(mode) {
state.statsMapMode = mode;
const btnMarkers = document.getElementById(‘btn-mode-markers’);
const btnHeat = document.getElementById(‘btn-mode-heat’);
if(btnMarkers) btnMarkers.classList.toggle(‘active’, mode === ‘markers’);
if(btnHeat) btnHeat.classList.toggle(‘active’, mode === ‘heat’);
const labelEl = document.getElementById(‘stats-map-mode-label’);
if(!state.maps.stats) return;
if(mode === ‘heat’) {
// Remove icon markers
state.statsMarkers.forEach(m => state.maps.stats.removeLayer(m));
// Add heat layer
if(!state.heatLayer && state.statsHeatPoints.length > 0) {
state.heatLayer = L.heatLayer(state.statsHeatPoints, {
radius: 32, blur: 24, maxZoom: 16, minOpacity: 0.45,
gradient: { 0.2: ‘#38bdf8’, 0.4: ‘#10b981’, 0.6: ‘#eab308’, 0.8: ‘#f97316’, 1.0: ‘#ef4444’ }
});
}
if(state.heatLayer) state.heatLayer.addTo(state.maps.stats);
if(labelEl) labelEl.innerHTML = ‘ خريطة الكثافة الحرارية (Heatmap)’;
} else {
// Remove heat layer
if(state.heatLayer) state.maps.stats.removeLayer(state.heatLayer);
// Re-add icon markers
state.statsMarkers.forEach(m => m.addTo(state.maps.stats));
if(labelEl) labelEl.innerHTML = ‘ عرض الرموز المباشرة’;
}
}
function recenterStatsMap() {
if(state.maps.stats) {
state.maps.stats.setView([33.8547, 35.8623], 9);
}
}
function filterStatsList(type) {
state.statsCurrentFilter = type;
document.querySelectorAll(‘#stats-type-filter .filter-chip’).forEach(c => {
c.classList.toggle(‘active’, c.dataset.type === type);
});
renderStatsList();
}
function renderStatsList() {
if(!state.statsData || !state.statsData.details) return;
const filter = state.statsCurrentFilter;
const rows = state.statsData.details.filter(r => {
if(filter === ‘all’) return true;
if(filter === ‘shelling’) return assaultTypes.includes(r.type);
if(filter === ‘uav’) return [‘uav’, ‘raid_drone’, ‘bomb_drone’].includes(r.type);
if(filter === ‘fighter’) return [‘fighter’, ‘raid_fighter’].includes(r.type);
return r.type === filter;
});
const tb = document.getElementById(‘stats-table-body’);
const mobList = document.getElementById(‘stats-mobile-list’);
if(tb) tb.innerHTML = ”;
if(mobList) mobList.innerHTML = ”;
if(rows.length === 0) {
if(tb) tb.innerHTML = ‘
لا توجد بلاغات مسجلة لهذا النوع في هذا اليوم
‘;
if(mobList) mobList.innerHTML = ‘لا توجد بلاغات مسجلة لهذا النوع في هذا اليوم
‘;
return;
}
rows.forEach(r => {
const c = assaultTypes.includes(r.type) ? ‘text-shelling font-bold’ : (r.type===’uav’?’text-danger font-bold’:(r.type===’fighter’?’text-accent font-bold’:’text-warning font-bold’));
const s = translate.source[r.report_source] || ‘سماع’;
const sIcon = r.report_source === ‘visual’ ? ‘‘ : ‘‘;
const timeStr = formatBeirutTime(r.created_at);
const cityName = adminEscape(r.city_name || ‘منطقة غير محددة’);
const typeLabel = translate.type[r.type] || r.type;
// Desktop Table Row
if(tb) {
tb.innerHTML += `
${timeStr}
${cityName}
${eventIconMarkup(r.type,’table-event-icon’)}${typeLabel}
${sIcon} ${s}
${!isNaN(parseFloat(r.lat)) ? ` عرض` : ‘-‘}
`;
}
// Mobile Card Item
if(mobList) {
mobList.innerHTML += `
${eventIconMarkup(r.type, ‘w-7 h-7 object-contain drop-shadow-sm’)}
${typeLabel}
• ${sIcon} ${s}
${cityName}
${timeStr}
${!isNaN(parseFloat(r.lat)) ? ` الخريطة` : ”}
`;
}
});
}
function focusStatsReport(lat, lon) {
if(state.maps.stats) {
state.maps.stats.setView([lat, lon], 12, { animate: true });
const card = document.querySelector(‘.stats-map-card’);
if(card) window.scrollTo({ top: card.offsetTop – 60, behavior: ‘smooth’ });
}
}
const arabicDays = [‘الأحد’, ‘الاثنين’, ‘الثلاثاء’, ‘الأربعاء’, ‘الخميس’, ‘الجمعة’, ‘السبت’];
async function loadStats(){
const d = document.getElementById(‘stats-date’).value || getBeirutDateString();
const dateBadge = document.getElementById(‘stats-map-date’);
if(dateBadge) dateBadge.innerText = d;
// Single Unified Map
if(!state.maps.stats) {
state.maps.stats = L.map(‘stats-map’, {
zoomControl: false,
zoomSnap: 0.5,
scrollWheelZoom: false // Prevent touch/wheel trapping
}).setView([33.8547, 35.8623], 9);
L.tileLayer(‘ {s}.tile.openstreetmap.org/{z}/{x}/{y}.png’, {
className: ‘ajwa-soft-tiles’,
maxZoom: 19,
attribution: ‘© OpenStreetMap’
}).addTo(state.maps.stats);
L.control.zoom({ position: ‘topright’ }).addTo(state.maps.stats);
}
try {
const r = await fetch(`${API_URL}?action=get_stats&date=${d}&t=${Date.now()}`);
const j = await r.json();
state.statsData = j;
// Update date subtitle
const dateObj = new Date(d);
const dayName = !isNaN(dateObj.getTime()) ? arabicDays[dateObj.getDay()] : ”;
const subEl = document.getElementById(‘period-sub-daily’);
if(subEl) subEl.innerText = dayName;
// Update KPIs
updateStatsKPIs();
// Clear markers
state.statsMarkers.forEach(mk => state.maps.stats.removeLayer(mk));
state.statsMarkers = [];
state.statsHeatPoints = [];
let geoReportsCount = 0;
j.details.forEach(r => {
const timeStr = formatBeirutTime(r.created_at);
if(!isNaN(parseFloat(r.lat))) {
geoReportsCount++;
const ic = L.divIcon({
html: `${eventIconMarkup(r.type,’map-event-icon’)}
`,
className: ‘custom-div-icon’,
iconSize: [32,32],
iconAnchor: [16,16]
});
const marker = L.marker([r.lat, r.lon], {icon: ic}).bindPopup(`${translate.type[r.type] || r.type}${r.city_name || ”}${timeStr}`);
state.statsMarkers.push(marker);
let intensity = 0.55;
if(assaultTypes.includes(r.type)) intensity = 1.0;
state.statsHeatPoints.push([r.lat, r.lon, intensity]);
}
});
const countEl = document.getElementById(‘stats-map-count-badge’);
if(countEl) countEl.innerText = `${geoReportsCount} حدث محدد جغرافياً`;
// Set map layer based on current mode (default: markers, heatmap only on-demand)
setStatsMapMode(state.statsMapMode);
if(state.statsMarkers && state.statsMarkers.length > 0) {
try {
const group = L.featureGroup(state.statsMarkers);
state.maps.stats.fitBounds(group.getBounds().pad(0.25));
} catch(e) {}
} else if(state.maps.stats) {
state.maps.stats.setView([33.8547, 35.8623], 9);
}
// Render activity list
renderStatsList();
// Format Chart.js Activity curve
if(j.chart && j.chart.length > 0) {
const chartLabels = j.chart.map(i => {
try {
const dt = new Date(i.date);
return `${arabicDays[dt.getDay()]} ${dt.getDate()}`;
} catch(e) { return i.date; }
});
const chartValues = j.chart.map(i => i.count);
const peak = Math.max(…chartValues, 0);
const peakBadge = document.getElementById(‘chart-peak-badge’);
if(peakBadge) peakBadge.innerText = `أعلى نشاط: ${peak} بلاغ`;
if(state.chart) state.chart.destroy();
const canvas = document.getElementById(‘statsChart’);
if(canvas) {
const ctx = canvas.getContext(‘2d’);
const gradient = ctx.createLinearGradient(0, 0, 0, 240);
gradient.addColorStop(0, ‘rgba(56, 189, 248, 0.45)’);
gradient.addColorStop(1, ‘rgba(56, 189, 248, 0.0)’);
state.chart = new Chart(ctx, {
type: ‘line’,
data: {
labels: chartLabels,
datasets: [{
label: ‘عدد البلاغات’,
data: chartValues,
borderColor: ‘#38bdf8’,
borderWidth: 3,
backgroundColor: gradient,
fill: true,
tension: 0.35,
pointBackgroundColor: ‘#38bdf8’,
pointBorderColor: ‘#0f172a’,
pointBorderWidth: 2,
pointRadius: 4,
pointHoverRadius: 6
}]
},
options: {
responsive: true,
maintainAspectRatio: false,
plugins: {
legend: { display: false },
tooltip: {
backgroundColor: ‘#0f172a’,
titleColor: ‘#38bdf8’,
bodyColor: ‘#ffffff’,
borderColor: ‘#334155’,
borderWidth: 1,
padding: 10,
displayColors: false,
callbacks: {
label: (context) => ` ${context.parsed.y} بلاغ مسجل`
}
}
},
scales: {
y: {
beginAtZero: true,
grid: { color: ‘rgba(255, 255, 255, 0.06)’ },
ticks: { color: ‘#94a3b8’, font: { size: 10 } }
},
x: {
grid: { display: false },
ticks: { color: ‘#94a3b8’, font: { size: 11, weight: ‘bold’ } }
}
}
}
});
}
}
} catch(x){
console.error(“loadStats error:”, x);
}
}
async function generateAISummary(){
if (!state.user || state.user.is_active != 1) return;
document.getElementById(‘modal-ai-analysis’).classList.remove(‘hidden’);
document.getElementById(‘ai-analysis-content’).innerHTML=’جاري التحليل…
‘;
try {
const d = document.getElementById(‘stats-date’).value || getBeirutDateString();
const r = await fetch(`${API_URL}?action=get_stats&date=${d}&t=${Date.now()}`);
const j = await r.json();
const c = `التاريخ: ${d}nإجمالي: ${j.totals.total}nالمناطق: ${j.details.slice(0,10).map(x=>x.city_name).join(‘, ‘)}`;
const s = await callGemini(“لخص الوضع”, c, d);
document.getElementById(‘ai-analysis-content’).innerHTML = marked.parse(s);
} catch(x) {
document.getElementById(‘ai-analysis-content’).innerHTML = ‘فشل في التحليل’;
}
}
async function callGemini(p, c, d = null) {
if(!aiEnabled) return “غير مفعل”;
const payload = { prompt: p, context: c }; if(d) payload.date = d;
const r = await fetch(`${API_URL}?action=ask_ai`, { method: ‘POST’, body: JSON.stringify(payload) });
const j = await r.json(); return j.response || “خطأ”;
}
const adminEscape=value=>String(value??”).replace(/[&”‘]/g,c=>({‘&’:’&’,'’,'”‘:'”‘,”‘”:”’}[c]));
const adminSearchText=value=>String(value??”).toLowerCase().trim();
async function refreshAdmin(){
const refreshButtons=document.querySelectorAll(‘#view-admin button[onclick=”refreshAdmin()”]’);refreshButtons.forEach(button=>button.disabled=true);
try{
const r=await fetch(`${API_URL}?action=admin_dashboard&admin_id=${state.user.id}&t=${Date.now()}`,{cache:’no-store’});const j=await r.json();
if(!r.ok||!Array.isArray(j.users)||!Array.isArray(j.reports))throw new Error(j.error||’تعذر تحميل بيانات الإدارة’);
state.adminData=j;
const userIds=new Set(j.users.map(item=>item.id)),reportIds=new Set(j.reports.map(item=>item.id));
state.adminSelection.users.forEach(id=>{if(!userIds.has(id))state.adminSelection.users.delete(id);});state.adminSelection.reports.forEach(id=>{if(!reportIds.has(id))state.adminSelection.reports.delete(id);});
document.getElementById(‘admin-total-users’).textContent=j.users.length;document.getElementById(‘admin-total-reports’).textContent=j.reports.length;document.getElementById(‘admin-total-reports-detail’).textContent=`${j.reports.filter(report=>Number(report.is_active)!==0).length} ظاهر · ${j.reports.filter(report=>Number(report.is_active)===0).length} مخفي`;document.getElementById(‘admin-banned-users’).textContent=j.users.filter(u=>Number(u.is_banned)===1).length;document.getElementById(‘admin-verified-users’).textContent=j.users.filter(u=>u.phone_verified_at).length;
document.getElementById(‘admin-users-tab-count’).textContent=j.users.length;document.getElementById(‘admin-reports-tab-count’).textContent=j.reports.length;document.getElementById(‘admin-push-count’).textContent=j.push_count||0;
renderAdminTables();
}catch(error){showAppModal(‘تعذر التحديث’,error.message||’فشل تحميل لوحة الإدارة’,’error’);}
finally{refreshButtons.forEach(button=>button.disabled=false);}
}
function switchAdminTab(tab){
state.adminTab=tab;
document.getElementById(‘admin-users’).classList.toggle(‘hidden’,tab!==’users’);document.getElementById(‘admin-reports’).classList.toggle(‘hidden’,tab!==’reports’);document.getElementById(‘admin-settings’).classList.toggle(‘hidden’,tab!==’settings’);
document.getElementById(‘admin-toolbar’).classList.toggle(‘hidden’,tab===’settings’);document.getElementById(‘admin-user-filters’).classList.toggle(‘hidden’,tab!==’users’);document.getElementById(‘admin-report-filters’).classList.toggle(‘hidden’,tab!==’reports’);
[‘users’,’reports’,’settings’].forEach(name=>{const button=document.getElementById(`btn-admin-${name}`);button.classList.toggle(‘is-active’,name===tab);button.setAttribute(‘aria-selected’,String(name===tab));});
const search=document.getElementById(‘admin-search’);if(search)search.placeholder=tab===’reports’?’ابحث بالمنطقة أو المبلّغ’:’ابحث بالاسم أو البريد أو الهاتف’;
if(tab===’settings’){document.getElementById(‘admin-bulk-bar’).classList.add(‘hidden’);loadAdminSettings();}else renderAdminTables();
}
function adminFiltersChanged(){state.pagination.adminUsers=1;state.pagination.adminReports=1;renderAdminTables();}
function clearAdminSearch(){const input=document.getElementById(‘admin-search’);input.value=””;input.focus();adminFiltersChanged();}
function resetAdminFilters(){
document.getElementById(‘admin-search’).value=””;[‘admin-filter-users’,’admin-filter-verification’,’admin-filter-reports’,’admin-filter-source’,’admin-filter-report-status’,’admin-filter-date’].forEach(id=>{const input=document.getElementById(id);if(input)input.value=”all”;});adminFiltersChanged();
}
async function loadAdminSettings(){try{const r=await fetch(`${API_URL}?action=get_admin_settings`);const j=await r.json();const input=document.getElementById(‘admin-gemini-key’);input.value=””;input.placeholder=j.gemini_configured?’المفتاح محفوظ — أدخل مفتاحاً جديداً لاستبداله’:’أدخل مفتاح Gemini’;}catch(x){}}
async function saveAdminSettings(){const k=document.getElementById(‘admin-gemini-key’).value;try{const r=await fetch(`${API_URL}?action=update_settings`,{method:’POST’,body:JSON.stringify({admin_id:state.user.id,gemini_key:k})});if(r.ok){showAppModal(‘تم’, “تم حفظ الإعدادات”, ‘success’);fetchConfig();}else showAppModal(‘خطأ’, “فشل الحفظ”, ‘error’);}catch(x){}}
function toggleKeyVisibility(){const i=document.getElementById(‘admin-gemini-key’);i.type=i.type===’password’?’text’:’password’;}
function renderPaginationControls(totalItems, itemsPerPage, currentPage, context) {
const totalPages = Math.ceil(totalItems / itemsPerPage); if(totalPages {
let va = a[sortConfig.col], vb = b[sortConfig.col];
if (typeof va === ‘string’) va = va.toLowerCase();
if (typeof vb === ‘string’) vb = vb.toLowerCase();
if (va vb) return sortConfig.dir === ‘asc’ ? 1 : -1;
return 0;
});
}
function adminSort(context, col) {
const current = state.adminSort[context];
if(current.col === col) current.dir = current.dir === ‘asc’ ? ‘desc’ : ‘asc’;
else { current.col = col; current.dir=”asc”; }
renderAdminTables();
}
function filteredAdminUsers(){
const query=adminSearchText(document.getElementById(‘admin-search’).value),status=document.getElementById(‘admin-filter-users’).value,verification=document.getElementById(‘admin-filter-verification’).value;
let users=state.adminData.users.filter(user=>[user.username,user.email,user.phone_number,user.registration_ip].some(value=>adminSearchText(value).includes(query)));
if(status===’active’)users=users.filter(user=>Number(user.is_active)===1&&Number(user.is_banned)===0);if(status===’banned’)users=users.filter(user=>Number(user.is_banned)===1);if(status===’inactive’)users=users.filter(user=>Number(user.is_active)===0);
if(verification===’phone’)users=users.filter(user=>user.phone_verified_at);if(verification===’email’)users=users.filter(user=>user.email_verified_at);if(verification===’unverified’)users=users.filter(user=>!user.phone_verified_at&&!user.email_verified_at);
return sortData(users,state.adminSort.users);
}
function filteredAdminReports(){
const query=adminSearchText(document.getElementById(‘admin-search’).value),type=document.getElementById(‘admin-filter-reports’).value,source=document.getElementById(‘admin-filter-source’).value,status=document.getElementById(‘admin-filter-report-status’).value,period=document.getElementById(‘admin-filter-date’).value;
let reports=state.adminData.reports.filter(report=>[report.city_name,report.username,translate.type[report.type],report.type].some(value=>adminSearchText(value).includes(query)));
if(type!==’all’)reports=reports.filter(report=>type===’assault’?assaultTypes.includes(report.type):report.type===type);if(source!==’all’)reports=reports.filter(report=>(report.report_source===’audio’?’auditory’:report.report_source)===source);
if(status===’active’)reports=reports.filter(report=>Number(report.is_active)!==0);if(status===’inactive’)reports=reports.filter(report=>Number(report.is_active)===0);
if(period!==’all’){const days=period===’today’?0:period===’7d’?6:29;const cutoff=new Date();cutoff.setHours(0,0,0,0);cutoff.setDate(cutoff.getDate()-days);reports=reports.filter(report=>{const parsed=new Date(String(report.created_at||”).replace(‘ ‘,’T’));return !Number.isNaN(parsed.getTime())&&parsed>=cutoff;});}
return sortData(reports,state.adminSort.reports);
}
function renderAdminTables(){
if(!state.adminData||!Array.isArray(state.adminData.users)||!Array.isArray(state.adminData.reports))return;
const itemsPerPage = parseInt(document.getElementById(‘admin-rows’).value);
const users=filteredAdminUsers();
const userPages=Math.max(1,Math.ceil(users.length/itemsPerPage));state.pagination.adminUsers=Math.min(state.pagination.adminUsers,userPages);const usersPage=state.pagination.adminUsers;
const usersSlice = users.slice((usersPage – 1) * itemsPerPage, usersPage * itemsPerPage);
state.adminVisibleIds.users=usersSlice.filter(user=>user.role!==’admin’).map(user=>user.id);
const ub = document.getElementById(‘admin-users-body’); ub.innerHTML=”;
if(!usersSlice.length)ub.innerHTML=’
لا توجد حسابات مطابقةغيّر الفلاتر أو امسح عبارة البحث.
‘;
usersSlice.forEach(u=>{
const isAdmin=u.role===’admin’,checked=state.adminSelection.users.has(u.id);let status=””;if(Number(u.is_banned)===1)status=”محظور”;else if(Number(u.is_active)===1)status=”نشط”;else status=”غير مفعل”;
const phoneVerification=`${u.phone_verified_at?’واتساب موثق’:’واتساب غير موثق’}${(!u.phone_verified_at&&u.phone_number&&!isAdmin)?`إرسال الرابط`:”}`;
const verification=`${phoneVerification}${u.email_verified_at?’بريد موثق’:’بريد غير موثق’}`;
const actions=isAdmin?’مدير النظام’:`${Number(u.is_active)===1?’تعطيل’:’تفعيل’}${Number(u.is_banned)===1?’فك الحظر’:’حظر’}كلمة المرورحذف`;
ub.innerHTML+=`
${isAdmin?’‘:“}
${adminEscape(String(u.username||’?’).trim().charAt(0)||’?’)}${adminEscape(u.username)}${adminEscape(u.registration_ip||’IP غير متاح’)} · ${adminEscape(String(u.created_at||”).split(‘ ‘)[0])}
${adminEscape(u.email)}${adminEscape(u.phone_number||’لا يوجد رقم’)}${verification}
${status}
${Number(u.report_count)||0}
${actions}
`;
});
document.getElementById(‘admin-users-pagination’).innerHTML = renderPaginationControls(users.length, itemsPerPage, usersPage, ‘users’);
document.getElementById(‘admin-users-result-count’).textContent=`عرض ${usersSlice.length} من ${users.length} عضواً`;
const reports=filteredAdminReports();const reportPages=Math.max(1,Math.ceil(reports.length/itemsPerPage));state.pagination.adminReports=Math.min(state.pagination.adminReports,reportPages);const reportsPage=state.pagination.adminReports;
const reportsSlice = reports.slice((reportsPage – 1) * itemsPerPage, reportsPage * itemsPerPage);
state.adminVisibleIds.reports=reportsSlice.map(report=>report.id);
const rb = document.getElementById(‘admin-reports-body’); rb.innerHTML=”;
if(!reportsSlice.length)rb.innerHTML=’
لا توجد بلاغات مطابقةغيّر النوع أو المصدر أو الفترة.
‘;
reportsSlice.forEach(r=>{
const c = assaultTypes.includes(r.type) ? ‘text-shelling’ : (r.type===’uav’?’text-danger’:(r.type===’fighter’?’text-accent’:’text-warning’));
const time = formatBeirutTime(r.created_at);
const date = formatBeirutDateTime(r.created_at).split(‘,’)[0];
const checked=state.adminSelection.reports.has(r.id),sourceLabel=(r.report_source===’visual’?’مشاهدة’:’سماع’),reportActive=Number(r.is_active)!==0;
rb.innerHTML+=`
${eventIconMarkup(r.type,’table-event-icon’)}${adminEscape(translate.type[r.type]||r.type)}${reportActive?’ظاهر للعامة’:’مخفي’}${adminEscape(String(r.id).slice(0,8))}
${adminEscape(r.city_name||’منطقة غير محددة’)}${sourceLabel}
${adminEscape(r.username||’مجهول’)}
${adminEscape(time)}${adminEscape(date)}
${reportActive?’إخفاء’:’إظهار’}تعديلحذف
`;
});
document.getElementById(‘admin-reports-pagination’).innerHTML = renderPaginationControls(reports.length, itemsPerPage, reportsPage, ‘reports’);
document.getElementById(‘admin-reports-result-count’).textContent=`عرض ${reportsSlice.length} من ${reports.length} بلاغاً`;
document.querySelectorAll(‘[data-sort-context]’).forEach(icon=>{const config=state.adminSort[icon.dataset.sortContext],active=config.col===icon.dataset.sortCol;icon.className=`sort-icon fa-solid ${active?(config.dir===’asc’?’fa-sort-up’:’fa-sort-down’):’fa-sort’}${active?’ sort-active’:”}`;});
[‘users’,’reports’].forEach(context=>{const all=document.getElementById(`admin-select-all-${context}`),ids=state.adminVisibleIds[context];if(all){all.checked=ids.length>0&&ids.every(id=>state.adminSelection[context].has(id));all.indeterminate=ids.some(id=>state.adminSelection[context].has(id))&&!all.checked;}});
updateAdminBulkBar();
}
function toggleAdminSelection(context,id,checked){if(checked)state.adminSelection[context].add(id);else state.adminSelection[context].delete(id);const all=document.getElementById(`admin-select-all-${context}`),ids=state.adminVisibleIds[context];if(all){all.checked=ids.length>0&&ids.every(item=>state.adminSelection[context].has(item));all.indeterminate=ids.some(item=>state.adminSelection[context].has(item))&&!all.checked;}updateAdminBulkBar();}
function toggleAdminPageSelection(context,checked){state.adminVisibleIds[context].forEach(id=>checked?state.adminSelection[context].add(id):state.adminSelection[context].delete(id));renderAdminTables();}
function clearAdminSelection(){if(state.adminTab===’users’||state.adminTab===’reports’)state.adminSelection[state.adminTab].clear();renderAdminTables();}
function updateAdminBulkBar(){const context=state.adminTab,count=(context===’users’||context===’reports’)?state.adminSelection[context].size:0,bar=document.getElementById(‘admin-bulk-bar’);bar.classList.toggle(‘hidden’,count===0||context===’settings’);document.getElementById(‘admin-selection-count’).textContent=count;document.getElementById(‘admin-user-bulk-actions’).classList.toggle(‘hidden’,context!==’users’);document.getElementById(‘admin-report-bulk-actions’).classList.toggle(‘hidden’,context!==’reports’);}
function bulkAdminAction(operation){const context=operation.endsWith(‘_reports’)?’reports’:’users’,ids=[…state.adminSelection[context]];if(!ids.length)return;const labels={activate_users:’تفعيل الحسابات المحددة’,deactivate_users:’تعطيل الحسابات المحددة’,ban_users:’حظر الحسابات المحددة’,unban_users:’فك حظر الحسابات المحددة’,delete_users:’حذف الحسابات المحددة نهائياً’,activate_reports:’إظهار البلاغات المحددة للعامة’,deactivate_reports:’إخفاء البلاغات المحددة عن العامة’,delete_reports:’حذف البلاغات المحددة نهائياً’};showConfirmModal(`هل تريد ${labels[operation]}؟`,async()=>{try{const response=await fetch(`${API_URL}?action=admin_bulk_action`,{method:’POST’,body:JSON.stringify({type:operation,target_ids:ids})});const result=await response.json();if(!response.ok)throw new Error(result.error||’فشل تنفيذ الإجراء’);state.adminSelection[context].clear();await refreshAdmin();showAppModal(‘تم التنفيذ’,`تم تحديث ${result.affected||ids.length} عنصراً`,’success’);}catch(error){showAppModal(‘تعذر التنفيذ’,error.message,’error’);}});}
async function openUserModal(uid){
const u=state.adminData.users.find(x=>x.id===uid);if(!u)return;
toggleModal(‘modal-user’, true);
document.getElementById(‘modal-user-content’).innerHTML = ‘
جاري التحميل…
‘;
try {
const r=await fetch(`${API_URL}?action=get_user_reports&target_id=${uid}&t=${Date.now()}`);
const j=await r.json();
const reps=j.data||[];
document.getElementById(‘modal-user-content’).innerHTML=`
IP
${u.registration_ip||’-‘}
تاريخ الانضمام
${u.created_at.split(‘ ‘)[0]}
آخر التقارير
النوع
المنطقة
الوقت
${reps.map(x=>`
${translate.type[x.type]}
${x.city_name}
${formatBeirutDateTime(x.created_at)}
`).join(”)}
`;
} catch(e) {
document.getElementById(‘modal-user-content’).innerHTML = ‘
فشل تحميل البيانات
‘;
}
}
async function adminAction(t,tid){
showConfirmModal(“هل أنت متأكد من هذا الإجراء؟”, async () => {
try{const response=await fetch(`${API_URL}?action=admin_action`,{method:’POST’,body:JSON.stringify({admin_id:state.user.id,target_id:tid,type:t})});const result=await response.json();if(!response.ok)throw new Error(result.error||’فشل تنفيذ الإجراء’);await refreshAdmin();showAppModal(‘تم’,’تم تنفيذ الإجراء بنجاح’,’success’);}catch(error){showAppModal(‘تعذر التنفيذ’,error.message,’error’);}
});
}
async function adminResendPhoneActivation(userId,button){
const original=button.innerHTML;
button.disabled=true;
button.innerHTML=’جارٍ الإرسال’;
try{
const response=await fetch(`${API_URL}?action=admin_action`,{method:’POST’,body:JSON.stringify({admin_id:state.user.id,target_id:userId,type:’resend_phone_activation’})});
const result=await response.json();
if(!response.ok)throw new Error(result.error||’تعذر إرسال رابط التوثيق’);
showAppModal(‘تم الإرسال’,result.message||’تم إرسال رابط التوثيق عبر واتساب’,’success’);
}catch(error){showAppModal(‘تعذر الإرسال’,error.message,’error’);}
finally{button.disabled=false;button.innerHTML=original;}
}
function openEditReportModal(reportId) {
const report = state.adminData.reports.find(r => r.id === reportId); if(!report) return;
document.getElementById(‘edit-report-id’).value = report.id; document.getElementById(‘edit-report-type’).value = report.type; document.getElementById(‘edit-report-city’).value = report.city_name; document.getElementById(‘edit-report-source’).value = report.report_source || ‘visual’;
toggleModal(‘modal-edit-report’, true);
}
async function submitEditReport(e) {
e.preventDefault(); const formData = new FormData(e.target); const data = Object.fromEntries(formData.entries()); data.admin_id = state.user.id;
try { const res = await fetch(`${API_URL}?action=edit_report`, { method: ‘POST’, body: JSON.stringify(data) }); const j = await res.json(); if (res.ok) { showAppModal(‘نجاح’, “تم التعديل”, ‘success’); toggleModal(‘modal-edit-report’, false); refreshAdmin(); } else { showAppModal(‘خطأ’, j.error, ‘error’); } } catch (err) { showAppModal(‘خطأ’, “فشل الاتصال”, ‘error’); }
}
window.addEventListener(‘load’, async () => {
// 1. تهيئة الخريطة إذا كانت موجودة
try {
if(!state.maps.main && document.getElementById(‘map’)) {
state.maps.main = L.map(‘map’, {zoomControl:false,zoomSnap:.25}).setView([33.8547,35.8623], 9);
L.tileLayer(‘ {s}.tile.openstreetmap.org/{z}/{x}/{y}.png’, { className: ‘ajwa-soft-tiles’, maxZoom:19, attribution:’© OpenStreetMap’ }).addTo(state.maps.main);
L.control.zoom({position:’bottomleft’}).addTo(state.maps.main);
}
} catch(e) { console.error(“Map Init Error”, e); }
insertFooters();
updateAuthUI();
const u = new URLSearchParams(window.location.search);
const action = u.get(‘action’);
// 2. معالجة تفعيل البريد الإلكتروني
if(action === ‘activate_verify’){
try {
const r = await fetch(`${API_URL}?action=activate_account`, {
method:’POST’,
body:JSON.stringify({token:u.get(‘token’)})
});
const j = await r.json();
showAppModal(‘تنبيه’, j.message || j.error, r.ok?’success’:’error’);
if(r.ok) router.navigate(‘auth’);
} catch(x) {
showAppModal(‘خطأ’, “فشل الاتصال”, ‘error’);
}
}
// 3. معالجة تفعيل الهاتف (الواتساب) – الكود الجديد
else if(action === ‘activate_phone’){
try {
const r = await fetch(`${API_URL}?action=activate_phone`, {
method:’POST’,
body:JSON.stringify({token:u.get(‘token’)})
});
const j = await r.json();
// عرض رسالة مناسبة حسب النتيجة
if(r.ok) {
showAppModal(‘تفعيل الهاتف’, j.message, ‘success’);
router.navigate(‘auth’); // الذهاب لصفحة تسجيل الدخول
} else {
showAppModal(‘تنبيه’, j.error || “رابط التفعيل غير صالح أو منتهي الصلاحية”, ‘error’);
router.navigate(‘home’);
}
} catch(x) {
showAppModal(‘خطأ’, “فشل الاتصال بالخادم”, ‘error’);
router.navigate(‘home’);
}
}
// 4. الوضع الطبيعي (الذهاب للرئيسية)
else {
const initialView=window.location.hash.slice(1);
router.navigate(document.getElementById(`view-${initialView}`)?initialView:’home’);
}
window.addEventListener(‘hashchange’, () => {
const hv = window.location.hash.slice(1);
if(hv && document.getElementById(`view-${hv}`)) {
router.navigate(hv);
}
});
// 5. التحقق من وجود إحداثيات في الرابط (للمشاركة)
const latParam = u.get(‘lat’);
const lonParam = u.get(‘lon’);
if (latParam && lonParam && Number.isFinite(Number(latParam)) && Number.isFinite(Number(lonParam)) && Math.abs(Number(latParam)) prompt.classList.add(‘show’), 5000); } }
function closePushPrompt() { const p = document.getElementById(‘push-prompt’); if(p) p.classList.remove(‘show’); }
async function subscribeToPush() {
if (!vapidPublicKey) return showAppModal(‘خطأ’, “خطأ في إعدادات الخادم”, ‘error’);
if (‘serviceWorker’ in navigator) {
try {
const registration = await navigator.serviceWorker.ready;
const subscription = await registration.pushManager.subscribe({ userVisibleOnly: true, applicationServerKey: urlBase64ToUint8Array(vapidPublicKey) });
const region=document.getElementById(‘push-region’)?.value||localStorage.getItem(‘ajwa_push_region’)||’all’;
const payload=subscription.toJSON();payload.region=region;localStorage.setItem(‘ajwa_push_region’,region);
await fetch(`${API_URL}?action=subscribe_push`, { method: ‘POST’, body: JSON.stringify(payload) });
showAppModal(‘تم’, region===’all’?”تم تفعيل تنبيهات كل لبنان “:`تم تفعيل تنبيهات ${region} `, ‘success’);
const icon = document.getElementById(‘notif-btn-icon’); if(icon) icon.className = “fa-solid fa-bell text-yellow-400 text-lg”;
closePushPrompt();
} catch (err) { console.error(err); showAppModal(‘خطأ’, “فشل التفعيل. تأكد من إعطاء الصلاحية.”, ‘error’); }
}
}
async function togglePushSubscription() {
if (‘serviceWorker’ in navigator) {
const reg = await navigator.serviceWorker.ready;
const sub = await reg.pushManager.getSubscription();
if (sub) {
showConfirmModal(“هل تريد إيقاف التنبيهات؟”, async () => {
await sub.unsubscribe();
await fetch(`${API_URL}?action=unsubscribe_push`, {method:’POST’, body:JSON.stringify({endpoint: sub.endpoint})});
document.getElementById(‘notif-btn-icon’).className = “fa-regular fa-bell text-gray-400 text-lg”;
showAppModal(‘تم’, “تم إيقاف التنبيهات.”, ‘success’);
});
} else { subscribeToPush(); }
}
}
المصدر: ajwa2.com
أجواء لبنان خريطة البلاغات والمتابعة المباشرة عاجل
المزيد من التفاصيل - اضغط هنا
بتوقيت بيروت_ beiruttime أجواء لبنان خريطة البلاغات والمتابعة المباشرة عاجل
كانت هذه تفاصيل أجواء لبنان .. خريطة البلاغات والمتابعة المباشرة عاجل نرجوا بأن نكون قد وفقنا بإعطائك التفاصيل والمعلومات الكامله .
وننوه بأن الموضوع الأصلي قد تم نشرة ومتواجد على بتوقيت بيروت_ beiruttime و قام فريق العمل في برسبي نيوز بالتاكد منه او ربما تم التعديل علية او قد يكون تم نقله بالكامل اوالاقتباس ويمكنك قراءة الموضوع من مصدره الاساسي.

