38808-vm/events.php
2026-04-13 15:20:18 +00:00

345 lines
14 KiB
PHP

<?php
require_once 'includes/header.php';
if (!canView('events')) {
echo "<div class='alert alert-danger m-4'>ليس لديك صلاحية لعرض هذه الصفحة.</div>";
require_once 'includes/footer.php';
exit;
}
$can_add = canAdd('events');
$can_edit = canEdit('events');
$can_delete = canDelete('events');
// Handle AJAX requests
if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_GET['ajax'])) {
ob_clean(); header('Content-Type: application/json');
$action = $_POST['action'] ?? '';
if ($action === 'fetch') {
$stmt = db()->query("SELECT id, title, description, event_date, start_time, end_time, location FROM events");
$events = $stmt->fetchAll();
$fc_events = [];
foreach($events as $e) {
$start = $e['event_date'];
if ($e['start_time']) $start .= 'T' . $e['start_time'];
$end = $e['event_date'];
if ($e['end_time']) $end .= 'T' . $e['end_time'];
$fc_events[] = [
'id' => $e['id'],
'title' => $e['title'],
'start' => $start,
'end' => $end,
'extendedProps' => [
'description' => $e['description'] ?? '',
'location' => $e['location'] ?? '',
'start_time' => $e['start_time'] ? substr($e['start_time'], 0, 5) : '',
'end_time' => $e['end_time'] ? substr($e['end_time'], 0, 5) : ''
]
];
}
echo json_encode($fc_events);
exit;
}
if ($action === 'save') {
$id = $_POST['id'] ?? 0;
$title = $_POST['title'] ?? '';
$date = $_POST['event_date'] ?? '';
$start_time = !empty($_POST['start_time']) ? $_POST['start_time'] : null;
$end_time = !empty($_POST['end_time']) ? $_POST['end_time'] : null;
$location = $_POST['location'] ?? '';
$description = $_POST['description'] ?? '';
if (!$title || !$date) {
echo json_encode(['success' => false, 'error' => 'البيانات الأساسية مطلوبة']);
exit;
}
try {
if ($id && $can_edit) {
$stmt = db()->prepare("UPDATE events SET title=?, description=?, event_date=?, start_time=?, end_time=?, location=? WHERE id=?");
$stmt->execute([$title, $description, $date, $start_time, $end_time, $location, $id]);
} elseif (!$id && $can_add) {
$stmt = db()->prepare("INSERT INTO events (title, description, event_date, start_time, end_time, location, created_by) VALUES (?, ?, ?, ?, ?, ?, ?)");
$stmt->execute([$title, $description, $date, $start_time, $end_time, $location, $_SESSION['user_id']]);
} else {
echo json_encode(['success' => false, 'error' => 'عفواً، لا تملك الصلاحيات الكافية للتقويم (إضافة/تعديل) في هذا الخادم. يرجى تفعيل الصلاحيات من صفحة إدارة المستخدمين.']);
exit;
}
echo json_encode(['success' => true]);
} catch (Exception $e) {
echo json_encode(['success' => false, 'error' => 'خطأ قاعدة البيانات: ' . $e->getMessage()]);
}
exit;
}
if ($action === 'delete') {
if (!$can_delete) {
echo json_encode(['success' => false, 'error' => 'لا تملك صلاحية الحذف.']);
exit;
}
try {
$id = $_POST['id'] ?? 0;
db()->prepare("DELETE FROM events WHERE id=?")->execute([$id]);
echo json_encode(['success' => true]);
} catch (Exception $e) {
echo json_encode(['success' => false, 'error' => 'خطأ قاعدة البيانات: ' . $e->getMessage()]);
}
exit;
}
}
?>
<link href='https://cdn.jsdelivr.net/npm/fullcalendar@5.11.3/main.min.css' rel='stylesheet' />
<script src='https://cdn.jsdelivr.net/npm/fullcalendar@5.11.3/main.min.js'></script>
<script src='https://cdn.jsdelivr.net/npm/fullcalendar@5.11.3/locales/ar.js'></script>
<div class="container-fluid py-4">
<div class="row mb-4 align-items-center">
<div class="col">
<h2 class="h4 mb-0"><i class="fas fa-calendar-alt text-primary me-2"></i> التقويم والأحداث</h2>
</div>
<div class="col-auto">
<a href="print_events.php" target="_blank" class="btn btn-secondary me-2">
<i class="fas fa-print me-1"></i> طباعة الأحداث
</a>
<?php if ($can_add): ?>
<button class="btn btn-primary" onclick="openEventModal()">
<i class="fas fa-plus me-1"></i> إضافة حدث جديد
</button>
<?php endif; ?>
</div>
</div>
<div class="card shadow-sm border-0">
<div class="card-body p-4">
<div id='calendar'></div>
</div>
</div>
</div>
<!-- Event Modal -->
<div class="modal fade" id="eventModal" tabindex="-1" aria-hidden="true">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="eventModalTitle">إضافة حدث</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body">
<form id="eventForm">
<input type="hidden" id="event_id" name="id">
<input type="hidden" name="action" value="save">
<div class="mb-3">
<label class="form-label">عنوان الحدث <span class="text-danger">*</span></label>
<input type="text" class="form-control" id="event_title" name="title" required>
</div>
<div class="mb-3">
<label class="form-label">التاريخ <span class="text-danger">*</span></label>
<input type="date" class="form-control" id="event_date" name="event_date" required>
</div>
<div class="row mb-3">
<div class="col-6">
<label class="form-label">وقت البدء</label>
<input type="time" class="form-control" id="event_start_time" name="start_time">
</div>
<div class="col-6">
<label class="form-label">وقت الانتهاء</label>
<input type="time" class="form-control" id="event_end_time" name="end_time">
</div>
</div>
<div class="mb-3">
<label class="form-label">المكان</label>
<input type="text" class="form-control" id="event_location" name="location">
</div>
<div class="mb-3">
<label class="form-label">التفاصيل</label>
<textarea class="form-control" id="event_description" name="description" rows="3"></textarea>
</div>
</form>
</div>
<div class="modal-footer justify-content-between">
<div>
<?php if ($can_delete): ?>
<button type="button" class="btn btn-danger d-none" id="btnDeleteEvent" onclick="deleteEvent()">حذف</button>
<?php endif; ?>
</div>
<div>
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">إلغاء</button>
<button type="button" class="btn btn-primary" onclick="saveEvent()">حفظ</button>
</div>
</div>
</div>
</div>
</div>
<script>
let calendar;
let eventModal;
document.addEventListener('DOMContentLoaded', function() {
var calendarEl = document.getElementById('calendar');
calendar = new FullCalendar.Calendar(calendarEl, {
locale: 'ar',
direction: 'rtl',
initialView: 'dayGridMonth',
headerToolbar: {
left: 'prev,next today',
center: 'title',
right: 'dayGridMonth,timeGridWeek,timeGridDay'
},
events: function(info, successCallback, failureCallback) {
let fd = new FormData();
fd.append('action', 'fetch');
fetch('events.php?ajax=1', {
method: 'POST',
body: fd
})
.then(r => r.json())
.then(data => successCallback(data))
.catch(err => failureCallback(err));
},
dateClick: function(info) {
openEventModal({ date: info.dateStr });
},
eventClick: function(info) {
let ev = info.event;
let props = ev.extendedProps;
// Format date for <input type="date"> which expects YYYY-MM-DD
let dateStr = ev.start.getFullYear() + '-' +
String(ev.start.getMonth() + 1).padStart(2, '0') + '-' +
String(ev.start.getDate()).padStart(2, '0');
openEventModal({
id: ev.id,
title: ev.title,
date: dateStr,
start_time: props.start_time,
end_time: props.end_time,
location: props.location,
description: props.description
});
}
});
calendar.render();
eventModal = new bootstrap.Modal(document.getElementById('eventModal'));
});
function openEventModal(data = null) {
document.getElementById('eventForm').reset();
document.getElementById('event_id').value = '';
document.getElementById('eventModalTitle').innerText = 'إضافة حدث جديد';
let btnDel = document.getElementById('btnDeleteEvent');
if (btnDel) btnDel.classList.add('d-none');
if (data) {
document.getElementById('event_id').value = data.id || '';
document.getElementById('event_title').value = data.title || '';
document.getElementById('event_date').value = data.date || '';
document.getElementById('event_start_time').value = data.start_time || '';
document.getElementById('event_end_time').value = data.end_time || '';
document.getElementById('event_location').value = data.location || '';
document.getElementById('event_description').value = data.description || '';
if (data.id) {
document.getElementById('eventModalTitle').innerText = 'تعديل حدث';
if (btnDel) btnDel.classList.remove('d-none');
}
}
eventModal.show();
}
function saveEvent() {
const form = document.getElementById('eventForm');
if (!form.reportValidity()) return;
fetch('events.php?ajax=1', {
method: 'POST',
body: new FormData(form)
})
.then(r => {
if (!r.ok) throw new Error("Network Error");
return r.json();
})
.then(res => {
if (res.success) {
eventModal.hide();
calendar.refetchEvents();
Swal.fire({icon: 'success', title: 'تم الحفظ', showConfirmButton: false, timer: 1500});
} else {
Swal.fire({icon: 'error', title: 'خطأ', text: res.error || 'حدث خطأ أثناء الحفظ'});
}
})
.catch(err => {
console.error(err);
Swal.fire({icon: 'error', title: 'حدث خطأ غير متوقع', text: 'إما أن جلسة تسجيل الدخول انتهت، أو لا توجد صلاحيات (راجع سجل وحدة التحكم). يرجى تحديث الصفحة والمحاولة مجدداً.'});
});
}
function deleteEvent() {
const id = document.getElementById('event_id').value;
if (!id) return;
Swal.fire({
title: 'هل أنت متأكد؟',
text: "لن تتمكن من التراجع عن هذا!",
icon: 'warning',
showCancelButton: true,
confirmButtonColor: '#d33',
cancelButtonColor: '#3085d6',
confirmButtonText: 'نعم، احذف!',
cancelButtonText: 'إلغاء'
}).then((result) => {
if (result.isConfirmed) {
let fd = new FormData();
fd.append('action', 'delete');
fd.append('id', id);
fetch('events.php?ajax=1', {
method: 'POST',
body: fd
})
.then(r => {
if (!r.ok) throw new Error("Network Error");
return r.json();
})
.then(res => {
if (res.success) {
eventModal.hide();
calendar.refetchEvents();
Swal.fire({icon: 'success', title: 'تم الحذف', showConfirmButton: false, timer: 1500});
} else {
Swal.fire({icon: 'error', title: 'خطأ', text: res.error || 'حدث خطأ أثناء الحذف'});
}
})
.catch(err => {
console.error(err);
Swal.fire({icon: 'error', title: 'حدث خطأ غير متوقع', text: 'إما أن جلسة تسجيل الدخول انتهت، أو لا توجد صلاحيات. يرجى تحديث الصفحة والمحاولة مجدداً.'});
});
}
});
}
</script>
<style>
/* FullCalendar customization */
.fc-theme-standard .fc-scrollgrid { border-color: var(--bs-border-color); }
.fc-theme-standard td, .fc-theme-standard th { border-color: var(--bs-border-color); }
.fc .fc-button-primary { background-color: #0d6efd; border-color: #0d6efd; }
.fc .fc-button-primary:not(:disabled):active, .fc .fc-button-primary:not(:disabled).fc-button-active {
background-color: #0a58ca; border-color: #0a53be;
}
.fc-event { cursor: pointer; }
.fc .fc-toolbar-title { font-size: 1.25em; font-weight: bold; }
[data-bs-theme="dark"] .fc-theme-standard .fc-scrollgrid { border-color: rgba(255,255,255,0.1); }
[data-bs-theme="dark"] .fc-theme-standard td, [data-bs-theme="dark"] .fc-theme-standard th { border-color: rgba(255,255,255,0.1); }
[data-bs-theme="dark"] .fc-day-today { background-color: rgba(255,255,255,0.05) !important; }
</style>
<?php require_once 'includes/footer.php'; ?>