Autosave: 20260416-131924

This commit is contained in:
Flatlogic Bot 2026-04-16 13:19:21 +00:00
parent d70f2ad2ca
commit e5366bdd58
26 changed files with 1214 additions and 162 deletions

15
add_logo_to_hero.py Normal file
View File

@ -0,0 +1,15 @@
with open('approved_school.php', 'r', encoding='utf-8') as f:
content = f.read()
replacement = """
<div class="d-flex align-items-center gap-3 mb-3">
<?php if ($application['logo']): ?>
<img src="<?= e((string)$application['logo']) ?>" alt="Logo" style="width: 64px; height: 64px; border-radius: 12px; object-fit: cover; box-shadow: 0 4px 12px rgba(0,0,0,0.1);">
<?php endif; ?>
<h1 class="page-title mb-0"><?= e((string) $application['center_name']) ?></h1>
</div>"""
content = content.replace('<h1 class="page-title mb-3"><?= e((string) $application["center_name"]) ?></h1>', replacement)
with open('approved_school.php', 'w', encoding='utf-8') as f:
f.write(content)

View File

@ -18,45 +18,7 @@ render_flash($flash);
<div class="row g-4 align-items-start">
<div class="col-lg-3">
<aside class="admin-sidebar sticky-top" style="top: 2rem; z-index: 10;">
<div class="sidebar-nav">
<div class="sidebar-label">الإدارة المركزية</div>
<a href="admin.php" class="sidebar-link active">
<svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" fill="currentColor" viewBox="0 0 16 16"><path d="M6 1H1v14h5V1zm9 0h-5v5h5V1zm0 9h-5v5h5v-5z"/><path d="M0 1a1 1 0 0 1 1-1h5a1 1 0 0 1 1 1v14a1 1 0 0 1-1 1H1a1 1 0 0 1-1-1V1zm9 0a1 1 0 0 1 1-1h5a1 1 0 0 1 1-1v5a1 1 0 0 1-1 1h-5a1 1 0 0 1-1-1V1zm0 9a1 1 0 0 1 1-1h5a1 1 0 0 1 1 1v5a1 1 0 0 1-1 1h-5a1 1 0 0 1-1-1v-5z"/></svg>
لوحة الإدارة
</a>
<a href="dashboard.php" class="sidebar-link">
<svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" fill="currentColor" viewBox="0 0 16 16"><path d="M8 4a.5.5 0 0 1 .5.5V6a.5.5 0 0 1-1 0V4.5A.5.5 0 0 1 8 4zM3.732 5.732a.5.5 0 0 1 .707 0l.915.914a.5.5 0 1 1-.708.708l-.914-.915a.5.5 0 0 1 0-.707zM2 10a.5.5 0 0 1 .5-.5h1.586a.5.5 0 0 1 0 1H2.5A.5.5 0 0 1 2 10zm9.5 0a.5.5 0 0 1 .5-.5h1.5a.5.5 0 0 1 0 1H12a.5.5 0 0 1-.5-.5zm.754-4.246a.389.389 0 0 0-.527-.02L7.547 9.31a.91.91 0 1 0 1.302 1.258l3.434-4.297a.389.389 0 0 0-.029-.518z"/><path fill-rule="evenodd" d="M0 10a8 8 0 1 1 15.547 2.661c-.442 1.253-1.845 1.602-2.932 1.25C11.309 13.488 9.475 13 8 13c-1.474 0-3.31.488-4.615.911-1.087.352-2.49.003-2.932-1.25A7.988 7.988 0 0 1 0 10zm8-7a7 7 0 0 0-6.603 9.329c.203.575.923.876 1.68.63C4.397 12.533 6.358 12 8 12s3.604.532 4.923.96c.757.245 1.477-.056 1.68-.631A7 7 0 0 0 8 3z"/></svg>
لوحة القيادة
</a>
<a href="applications.php" class="sidebar-link">
<svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" fill="currentColor" viewBox="0 0 16 16"><path d="M4.98 4a.5.5 0 0 0-.39.188L1.54 8H6a.5.5 0 0 1 .5.5 1.5 1.5 0 1 0 3 0A.5.5 0 0 1 10 8h4.46l-3.05-3.812A.5.5 0 0 0 11.02 4H4.98zm-1.17-.437A1.5 1.5 0 0 1 4.98 3h6.04a1.5 1.5 0 0 1 1.17.563l3.7 4.625a.5.5 0 0 1 .106.304l-.228 6.2a1.5 1.5 0 0 1-1.5 1.464H1.74a1.5 1.5 0 0 1-1.498-1.464l-.228-6.2a.5.5 0 0 1 .106-.304l3.7-4.625zM1 8.5v6a.5.5 0 0 0 .5.5h13a.5.5 0 0 0 .5-.5v-6h-3.415a2.5 2.5 0 0 1-4.17 0H1z"/></svg>
لوحة الطلبات
</a>
<a href="applications.php?status=approved" class="sidebar-link">
<svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" fill="currentColor" viewBox="0 0 16 16"><path fill-rule="evenodd" d="M14.763.075A.5.5 0 0 1 15 .5v15a.5.5 0 0 1-.5.5h-3a.5.5 0 0 1-.5-.5V14h-1v1.5a.5.5 0 0 1-.5.5h-9a.5.5 0 0 1-.5-.5V10a.5.5 0 0 1 .342-.474L6 7.64V4.5a.5.5 0 0 1 .276-.447l8-4a.5.5 0 0 1 .487.022zM6 8.694 1 10.36V15h5V8.694zM7 15h2v-1.5a.5.5 0 0 1 .5-.5h2a.5.5 0 0 1 .5.5V15h2V1.309l-7 3.5V15z"/><path d="M2 11h1v1H2v-1zm2 0h1v1H4v-1zm-2 2h1v1H2v-1zm2 0h1v1H4v-1zm4-4h1v1H8V9zm2 0h1v1h-1V9zm-2 2h1v1H8v-1zm2 0h1v1h-1v-1zm2-2h1v1h-1V9zm0 2h1v1h-1v-1zM8 7h1v1H8V7zm2 0h1v1h-1V7zm2 0h1v1h-1V7zM8 5h1v1H8V5zm2 0h1v1h-1V5zm2 0h1v1h-1V5zm0-2h1v1h-1V3z"/></svg>
المراكز المعتمدة
</a>
<a href="center_application.php" class="sidebar-link">
<svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" fill="currentColor" viewBox="0 0 16 16"><path d="M8 6.5a.5.5 0 0 1 .5.5v1.5H10a.5.5 0 0 1 0 1H8.5V11a.5.5 0 0 1-1 0V9.5H6a.5.5 0 0 1 0-1h1.5V7a.5.5 0 0 1 .5-.5z"/><path d="M14 4.5V14a2 2 0 0 1-2 2H4a2 2 0 0 1-2-2V2a2 2 0 0 1 2-2h5.5L14 4.5zm-3 0A1.5 1.5 0 0 1 9.5 3V1H4a1 1 0 0 0-1 1v12a1 1 0 0 0 1 1h8a1 1 0 0 0 1-1V4.5h-2z"/></svg>
طلب فتح مركز
</a>
<div class="sidebar-label mt-3">هيكلية النظام</div>
<a href="modules.php" class="sidebar-link">
<svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" fill="currentColor" viewBox="0 0 16 16"><path fill-rule="evenodd" d="M15.817.113A.5.5 0 0 1 16 .5v14a.5.5 0 0 1-.402.49l-5 1a.502.502 0 0 1-.196 0L5.5 15.01l-4.902.98A.5.5 0 0 1 0 15.5v-14a.5.5 0 0 1 .402-.49l5-1a.5.5 0 0 1 .196 0L10.5.99l4.902-.98a.5.5 0 0 1 .415.103zM10 1.91l-4-.8v12.98l4 .8V1.91zm1 12.98 4-.8V1.11l-4 .8v12.98zm-6-.8V1.11l-4 .8v12.98l4-.8z"/></svg>
خريطة الصفحات
</a>
<?php if (isset($recentApproved)): ?>
<div class="sidebar-label mt-3">أحدث مركز معتمد</div>
<a href="approved_school.php?id=<?= e((string) $recentApproved["id"]) ?>" class="sidebar-link">
<svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" fill="currentColor" viewBox="0 0 16 16"><path d="M4 16s-1 0-1-1 1-4 5-4 5 3 5 4-1 1-1 1H4Zm4-5.95a2.5 2.5 0 1 0 0-5 2.5 2.5 0 0 0 0 5Z"/><path d="M2 1a2 2 0 0 0-2 2v9.5A1.5 1.5 0 0 0 1.5 14h.653a5.373 5.373 0 0 1 1.066-2H1V3a1 1 0 0 1 1-1h12a1 1 0 0 1 1 1v9h-2.219c.554.654.89 1.373 1.066 2h.653a1.5 1.5 0 0 0 1.5-1.5V3a2 2 0 0 0-2-2H2Z"/></svg>
<?= e((string) $recentApproved["center_name"]) ?>
</a>
<?php endif; ?>
</div>
</aside>
<?php require __DIR__ . '/includes/sidebar.php'; ?>
</div>
<div class="col-lg-9">

139
app_settings.php Normal file
View File

@ -0,0 +1,139 @@
<?php
declare(strict_types=1);
require_once __DIR__ . '/includes/app.php';
$flash = consume_flash();
$settings = get_app_settings();
$errors = [];
$values = [
'app_name' => $settings['app_name'] ?? '',
'app_email' => $settings['app_email'] ?? '',
'app_telephone' => $settings['app_telephone'] ?? '',
];
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$values['app_name'] = clean_text($_POST['app_name'] ?? '', 190);
$values['app_email'] = clean_text($_POST['app_email'] ?? '', 190);
$values['app_telephone'] = clean_text($_POST['app_telephone'] ?? '', 60);
if ($values['app_name'] === '') $errors['app_name'] = 'مطلوب';
$logoPath = $settings['app_logo'] ?? '';
$faviconPath = $settings['app_favicon'] ?? '';
// Handle Uploads
$uploadDir = __DIR__ . '/assets/images/uploads/';
if (!is_dir($uploadDir)) {
mkdir($uploadDir, 0755, true);
}
if (isset($_FILES['logo']) && $_FILES['logo']['error'] === UPLOAD_ERR_OK) {
$logoExt = strtolower(pathinfo($_FILES['logo']['name'], PATHINFO_EXTENSION));
if (in_array($logoExt, ['png', 'jpg', 'jpeg', 'svg', 'gif'])) {
$logoName = 'app_logo_' . time() . '.' . $logoExt;
if (move_uploaded_file($_FILES['logo']['tmp_name'], $uploadDir . $logoName)) {
$logoPath = 'assets/images/uploads/' . $logoName;
}
} else {
$errors['logo'] = 'صيغة غير مدعومة';
}
}
if (isset($_FILES['favicon']) && $_FILES['favicon']['error'] === UPLOAD_ERR_OK) {
$faviconExt = strtolower(pathinfo($_FILES['favicon']['name'], PATHINFO_EXTENSION));
if (in_array($faviconExt, ['png', 'ico', 'svg'])) {
$faviconName = 'app_favicon_' . time() . '.' . $faviconExt;
if (move_uploaded_file($_FILES['favicon']['tmp_name'], $uploadDir . $faviconName)) {
$faviconPath = 'assets/images/uploads/' . $faviconName;
}
} else {
$errors['favicon'] = 'صيغة غير مدعومة';
}
}
if (empty($errors)) {
try {
$stmt = db()->prepare('UPDATE app_settings SET app_name = ?, app_email = ?, app_telephone = ?, app_logo = ?, app_favicon = ?, updated_at = NOW() WHERE id = 1');
$stmt->execute([
$values['app_name'],
$values['app_email'],
$values['app_telephone'],
$logoPath,
$faviconPath
]);
set_flash('success', 'تم تحديث الإعدادات العامة بنجاح.');
header('Location: app_settings.php');
exit;
} catch (Throwable $e) {
$errors['form'] = 'تعذر الحفظ.';
}
}
}
render_page_start('إعدادات النظام', 'app_settings', 'إعدادات النظام العامة');
render_flash($flash);
?>
<section class="py-4 py-lg-5">
<div class="container-xxl">
<div class="row g-4 align-items-start">
<div class="col-lg-3">
<?php require __DIR__ . '/includes/sidebar.php'; ?>
</div>
<div class="col-lg-9">
<div class="page-banner mb-4">
<h1 class="page-title mb-2">الإعدادات العامة للنظام</h1>
<p class="page-copy mb-0">تعديل اسم النظام، الشعار (Logo)، الأيقونة (Favicon)، وبيانات التواصل.</p>
</div>
<div class="app-card form-card">
<?php if (!empty($errors['form'])): ?>
<div class="alert alert-danger mb-4"><?= e($errors['form']) ?></div>
<?php endif; ?>
<form method="post" enctype="multipart/form-data" novalidate>
<div class="row g-4">
<div class="col-md-12">
<label class="form-label">اسم النظام (المنصة)</label>
<input class="form-control <?= isset($errors['app_name']) ? 'is-invalid' : '' ?>" name="app_name" value="<?= e($values['app_name']) ?>">
<?php if (isset($errors['app_name'])): ?><div class="invalid-feedback"><?= e($errors['app_name']) ?></div><?php endif; ?>
</div>
<div class="col-md-6">
<label class="form-label">البريد الإلكتروني العام</label>
<input type="email" class="form-control <?= isset($errors['app_email']) ? 'is-invalid' : '' ?>" name="app_email" value="<?= e($values['app_email']) ?>">
<?php if (isset($errors['app_email'])): ?><div class="invalid-feedback"><?= e($errors['app_email']) ?></div><?php endif; ?>
</div>
<div class="col-md-6">
<label class="form-label">رقم الهاتف العام</label>
<input class="form-control <?= isset($errors['app_telephone']) ? 'is-invalid' : '' ?>" name="app_telephone" value="<?= e($values['app_telephone']) ?>">
<?php if (isset($errors['app_telephone'])): ?><div class="invalid-feedback"><?= e($errors['app_telephone']) ?></div><?php endif; ?>
</div>
<div class="col-md-6">
<label class="form-label">الشعار (Logo)</label>
<?php if (!empty($settings['app_logo'])): ?>
<div class="mb-2"><img src="<?= e((string)$settings['app_logo']) ?>" alt="Logo" style="max-height: 80px; max-width: 100%; border-radius: 8px;"></div>
<?php endif; ?>
<input type="file" class="form-control <?= isset($errors['logo']) ? 'is-invalid' : '' ?>" name="logo" accept="image/*">
<?php if (isset($errors['logo'])): ?><div class="invalid-feedback"><?= e($errors['logo']) ?></div><?php endif; ?>
</div>
<div class="col-md-6">
<label class="form-label">الأيقونة (Favicon)</label>
<?php if (!empty($settings['app_favicon'])): ?>
<div class="mb-2"><img src="<?= e((string)$settings['app_favicon']) ?>" alt="Favicon" style="max-height: 40px; max-width: 100%; border-radius: 4px;"></div>
<?php endif; ?>
<input type="file" class="form-control <?= isset($errors['favicon']) ? 'is-invalid' : '' ?>" name="favicon" accept=".ico,.png,.svg">
<?php if (isset($errors['favicon'])): ?><div class="invalid-feedback"><?= e($errors['favicon']) ?></div><?php endif; ?>
</div>
</div>
<div class="form-actions mt-4">
<button class="btn btn-dark px-4" type="submit">حفظ التغييرات</button>
</div>
</form>
</div>
</div>
</div>
</div>
</section>
<?php render_page_end(); ?>

View File

@ -27,6 +27,19 @@ if (!$application) {
$reviewErrors = [];
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
if (isset($_POST['action']) && $_POST['action'] === 'update_subjects') {
$selected_subjects = isset($_POST['subjects']) && is_array($_POST['subjects']) ? $_POST['subjects'] : [];
$valid_subjects = [];
foreach ($selected_subjects as $sid) {
$sid = filter_var($sid, FILTER_VALIDATE_INT);
if ($sid) $valid_subjects[] = $sid;
}
update_application_subjects($applicationId, $valid_subjects);
set_flash('success', 'تم تحديث المواد الدراسية بنجاح.');
header('Location: application_detail.php?id=' . urlencode((string) $applicationId));
exit;
}
$status = clean_text((string) ($_POST['status'] ?? 'submitted'), 30);
$adminNotes = clean_text((string) ($_POST['admin_notes'] ?? ''), 1000);
$evaluationScore = filter_var($_POST['evaluation_score'] ?? null, FILTER_VALIDATE_INT, [
@ -63,6 +76,12 @@ render_flash($flash);
?>
<section class="py-4 py-lg-5">
<div class="container-xxl">
<div class="row g-4 align-items-start">
<div class="col-lg-3">
<?php require __DIR__ . '/includes/sidebar.php'; ?>
</div>
<div class="col-lg-9">
<div class="page-banner mb-4 mb-lg-5">
<div class="row g-4 align-items-start">
<div class="col-lg-8">
@ -111,6 +130,31 @@ render_flash($flash);
<div class="col-md-6"><div class="detail-item"><span>فترة البرنامج</span><strong><?= e((string) $application['start_date']) ?> — <?= e((string) $application['end_date']) ?></strong></div></div>
</div>
<div class="notes-block mt-4">
<div class="d-flex justify-content-between align-items-center mb-3">
<div class="section-title small-title mb-0">المواد الدراسية المطلوبة</div>
<button type="button" class="btn btn-outline-secondary btn-sm" data-bs-toggle="modal" data-bs-target="#editSubjectsModal">تعديل المواد</button>
</div>
<div class="d-flex flex-wrap gap-2">
<?php
$selected_subjects = json_decode((string) ($application['subjects'] ?? '[]'), true) ?: [];
$all_subjects = get_enabled_subjects();
$subject_map = [];
foreach ($all_subjects as $sub) {
$subject_map[$sub['id']] = $sub['name'];
}
if (empty($selected_subjects)):
?>
<span class="text-muted small">لم يتم اختيار أي مواد.</span>
<?php else: ?>
<?php foreach ($selected_subjects as $sub_id): ?>
<span class="badge bg-secondary"><?= e($subject_map[$sub_id] ?? 'مادة غير معروفة') ?></span>
<?php endforeach; ?>
<?php endif; ?>
</div>
</div>
<div class="notes-block mt-4">
<div class="section-title small-title mb-2">ملخص البرنامج والاحتياجات</div>
<p class="mb-0 text-muted"><?= nl2br(e((string) ($application['notes'] ?: 'لا توجد ملاحظات إضافية.'))) ?></p>
@ -187,6 +231,44 @@ render_flash($flash);
</div>
</div>
</div>
</div>
</div>
</div>
</section>
<!-- Modal for Editing Subjects -->
<div class="modal fade" id="editSubjectsModal" tabindex="-1" aria-labelledby="editSubjectsModalLabel" aria-hidden="true">
<div class="modal-dialog">
<div class="modal-content">
<form method="post">
<input type="hidden" name="action" value="update_subjects">
<div class="modal-header border-bottom-0">
<h5 class="modal-title" id="editSubjectsModalLabel">تعديل المواد الدراسية</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="إغلاق"></button>
</div>
<div class="modal-body">
<div class="row g-2">
<?php foreach ($all_subjects as $sub): ?>
<?php $isChecked = in_array($sub['id'], $selected_subjects); ?>
<div class="col-md-6">
<div class="form-check">
<input class="form-check-input" type="checkbox" name="subjects[]" value="<?= e((string)$sub['id']) ?>" id="subject_modal_<?= e((string)$sub['id']) ?>" <?= $isChecked ? 'checked' : '' ?>/>
<label class="form-check-label" for="subject_modal_<?= e((string)$sub['id']) ?>">
<?= e($sub['name']) ?>
</label>
</div>
</div>
<?php endforeach; ?>
</div>
</div>
<div class="modal-footer border-top-0">
<button type="button" class="btn btn-outline-secondary" data-bs-dismiss="modal">إلغاء</button>
<button type="submit" class="btn btn-dark">حفظ التغييرات</button>
</div>
</form>
</div>
</div>
</div>
<?php render_page_end(); ?>

View File

@ -2,131 +2,146 @@
declare(strict_types=1);
require_once __DIR__ . '/includes/app.php';
$flash = consume_flash();
$statusFilter = clean_text((string) ($_GET['status'] ?? 'all'), 50);
$applications = list_applications($statusFilter);
$stats = dashboard_metrics();
$statusTabs = ['all' => 'الكل'] + array_map(fn ($meta) => $meta['label'], status_map());
$currentCount = count($applications);
$reviewBacklog = $stats['submitted'] + $stats['under_review'];
$approvalRate = $stats['all'] > 0 ? (int) round(($stats['approved'] / $stats['all']) * 100) : 0;
// Handle POST actions (e.g. Delete)
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$action = $_POST['action'] ?? '';
if ($action === 'delete') {
$id = (int)($_POST['id'] ?? 0);
if ($id > 0) {
$stmt = db()->prepare('DELETE FROM center_applications WHERE id = ?');
$stmt->execute([$id]);
set_flash('success', 'تم حذف الطلب بنجاح.');
}
header('Location: applications.php');
exit;
}
}
render_page_start('لوحة الطلبات', 'applications', 'قائمة موحدة لطلبات فتح المراكز الصيفية مع فلترة حسب حالة المراجعة.');
// Read list
$search = clean_text($_GET['search'] ?? '', 255);
$query = 'SELECT * FROM center_applications';
$params = [];
if ($search !== '') {
$query .= ' WHERE center_name LIKE ? OR city LIKE ? OR director_name LIKE ?';
$params[] = "%$search%";
$params[] = "%$search%";
$params[] = "%$search%";
}
$query .= ' ORDER BY id DESC';
$stmt = db()->prepare($query);
$stmt->execute($params);
$applications = $stmt->fetchAll();
$flash = consume_flash();
render_page_start('إدارة الطلبات', 'applications', 'قائمة بطلبات فتح المراكز');
render_flash($flash);
?>
<section class="py-4 py-lg-5">
<div class="container-xxl">
<div class="page-banner mb-4 mb-lg-5">
<div class="row g-4 align-items-start">
<div class="col-lg-8">
<span class="eyebrow mb-3">صفحة مستقلة للمراجعة</span>
<h1 class="page-title mb-3">لوحة طلبات فتح المراكز</h1>
<p class="page-copy mb-0">تم تخصيص هذه الصفحة بالكامل لمتابعة الطلبات: مؤشرات سريعة، فلاتر واضحة، وجدول مراجعة عملي يقود مباشرة إلى صفحة التفاصيل واتخاذ القرار.</p>
</div>
<div class="col-lg-4">
<div class="page-banner-panel h-100">
<div class="mini-stat-label">الحمل التشغيلي الحالي</div>
<div class="mini-stat-value"><?= e((string) $reviewBacklog) ?></div>
<div class="mini-stat-copy">طلبات تحتاج عملاً من المشرف العام الآن، مع نسبة اعتماد إجمالية تبلغ <?= e((string) $approvalRate) ?>٪.</div>
<div class="row g-4 align-items-start">
<div class="col-lg-3">
<?php require __DIR__ . '/includes/sidebar.php'; ?>
</div>
<div class="col-lg-9">
<div class="app-card mb-4">
<div class="d-flex justify-content-between align-items-center mb-4 flex-wrap gap-3">
<div class="section-title mb-0">إدارة طلبات فتح المراكز</div>
<a class="btn btn-dark" href="center_application.php">
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="me-1" viewBox="0 0 16 16">
<path d="M8 4a.5.5 0 0 1 .5.5v3h3a.5.5 0 0 1 0 1h-3v3a.5.5 0 0 1-1 0v-3h-3a.5.5 0 0 1 0-1h3v-3A.5.5 0 0 1 8 4z"/>
</svg>
إضافة طلب
</a>
</div>
</div>
</div>
</div>
<div class="row g-3 mb-4">
<div class="col-md-6 col-xl-3"><div class="app-card stat-tile"><div class="mini-stat-label">إجمالي الطلبات</div><div class="mini-stat-value"><?= e((string) $stats['all']) ?></div><div class="mini-stat-copy">جميع الطلبات في قاعدة البيانات.</div></div></div>
<div class="col-md-6 col-xl-3"><div class="app-card stat-tile"><div class="mini-stat-label">بانتظار الاستلام</div><div class="mini-stat-value"><?= e((string) $stats['submitted']) ?></div><div class="mini-stat-copy">طلبات جديدة لم يبدأ تقييمها بعد.</div></div></div>
<div class="col-md-6 col-xl-3"><div class="app-card stat-tile"><div class="mini-stat-label">تحت المراجعة</div><div class="mini-stat-value"><?= e((string) $stats['under_review']) ?></div><div class="mini-stat-copy">ملفات قيد التحقق والملاحظات.</div></div></div>
<div class="col-md-6 col-xl-3"><div class="app-card stat-tile"><div class="mini-stat-label">معتمد</div><div class="mini-stat-value"><?= e((string) $stats['approved']) ?></div><div class="mini-stat-copy">مراكز جاهزة للانتقال لمرحلة التشغيل.</div></div></div>
</div>
<!-- Search Bar -->
<form method="GET" action="applications.php" class="mb-4">
<div class="input-group">
<input type="text" name="search" class="form-control" placeholder="ابحث باسم المركز، المدينة، أو المسؤول..." value="<?= e($search) ?>">
<button class="btn btn-outline-secondary" type="submit">بحث</button>
<?php if ($search !== ''): ?>
<a href="applications.php" class="btn btn-outline-danger">إلغاء</a>
<?php endif; ?>
</div>
</form>
<div class="app-card mb-4">
<div class="section-head flex-column flex-lg-row align-items-start align-items-lg-center gap-3 mb-3">
<div>
<div class="section-title">أدوات الفرز والمتابعة</div>
<div class="section-copy">اختر حالة محددة للتركيز على شريحة واحدة من الطلبات، أو ارجع إلى الكل لمراجعة الصورة العامة.</div>
</div>
<div class="d-flex flex-wrap gap-2">
<a class="btn btn-dark btn-sm px-3" href="admin.php">لوحة الإدارة</a>
<a class="btn btn-outline-secondary btn-sm px-3" href="center_application.php">طلب جديد</a>
<a class="btn btn-outline-secondary btn-sm px-3" href="applications.php">إعادة ضبط</a>
</div>
</div>
<div class="filter-pills mb-3">
<?php foreach ($statusTabs as $value => $label): ?>
<a class="filter-pill <?= $statusFilter === $value ? 'active' : '' ?>" href="applications.php?status=<?= e($value) ?>">
<?= e($label) ?>
<span class="pill-count">
<?= e((string) ($value === 'all' ? $stats['all'] : ($stats[$value] ?? 0))) ?>
</span>
</a>
<?php endforeach; ?>
</div>
<div class="table-toolbar">
<div class="table-toolbar-copy">الفلتر الحالي: <strong><?= e($statusTabs[$statusFilter] ?? 'الكل') ?></strong></div>
<div class="table-toolbar-copy">عدد السجلات المعروضة: <strong><?= e((string) $currentCount) ?></strong></div>
</div>
</div>
<div class="app-card">
<?php if ($applications === []): ?>
<div class="empty-state text-center py-5">
<div class="empty-title mb-2">لا توجد طلبات ضمن هذا التصنيف.</div>
<p class="text-muted mb-3">يمكنك إنشاء طلب جديد أو العودة إلى جميع الطلبات لاستكمال المراجعة.</p>
<div class="d-flex flex-wrap justify-content-center gap-2">
<a class="btn btn-dark" href="admin.php">لوحة الإدارة</a>
<a class="btn btn-outline-secondary" href="center_application.php">إنشاء طلب</a>
<a class="btn btn-outline-secondary" href="applications.php">عرض الكل</a>
</div>
</div>
<?php else: ?>
<div class="table-responsive">
<table class="table app-table align-middle mb-0">
<thead>
<tr>
<th>المرجع</th>
<th>المركز</th>
<th>المدينة</th>
<th>المسؤول</th>
<th>الفترة</th>
<th>الحالة</th>
<th>التقييم</th>
<th>إجراء</th>
</tr>
</thead>
<tbody>
<?php foreach ($applications as $application): ?>
<div class="table-responsive">
<table class="table app-table align-middle">
<thead>
<tr>
<td><a class="table-link" href="application_detail.php?id=<?= e((string) $application['id']) ?>">#<?= e((string) $application['id']) ?></a></td>
<td>
<div class="fw-semibold"><?= e((string) $application['center_name']) ?></div>
<div class="text-muted small">سعة متوقعة: <?= e((string) $application['expected_students']) ?> طالب</div>
</td>
<td><?= e((string) $application['city']) ?></td>
<td>
<div><?= e((string) $application['director_name']) ?></div>
<div class="text-muted small"><?= e((string) $application['phone']) ?></div>
</td>
<td>
<div><?= e((string) $application['start_date']) ?></div>
<div class="text-muted small">حتى <?= e((string) $application['end_date']) ?></div>
</td>
<td><?= status_badge((string) $application['status']) ?></td>
<td><?= $application['evaluation_score'] !== null ? e((string) $application['evaluation_score']) . '%' : '—' ?></td>
<td>
<?php if ((string) $application['status'] === 'approved'): ?>
<a class="btn btn-dark btn-sm px-3" href="approved_school.php?id=<?= e((string) $application['id']) ?>">صفحة المركز</a>
<?php else: ?>
<a class="btn btn-outline-secondary btn-sm px-3" href="application_detail.php?id=<?= e((string) $application['id']) ?>">فتح الملف</a>
<?php endif; ?>
</td>
<th>المرجع</th>
<th>المركز</th>
<th>المدينة</th>
<th>المسؤول</th>
<th>الفترة</th>
<th>الحالة</th>
<th>الإجراءات</th>
</tr>
<?php endforeach; ?>
</tbody>
</table>
</thead>
<tbody>
<?php if (count($applications) === 0): ?>
<tr>
<td colspan="7" class="text-center py-4 text-muted">لا توجد طلبات مسجلة أو لم يتم العثور على نتائج.</td>
</tr>
<?php else: ?>
<?php foreach ($applications as $application): ?>
<tr>
<td><a class="table-link" href="application_detail.php?id=<?= e((string) $application['id']) ?>">#<?= e((string) $application['id']) ?></a></td>
<td>
<div class="fw-semibold"><?= e((string) $application['center_name']) ?></div>
<div class="text-muted small">سعة متوقعة: <?= e((string) $application['expected_students']) ?> طالب</div>
</td>
<td><?= e((string) $application['city']) ?></td>
<td>
<div><?= e((string) $application['director_name']) ?></div>
<div class="text-muted small"><?= e((string) $application['phone']) ?></div>
</td>
<td>
<div><?= e((string) $application['start_date']) ?></div>
<div class="text-muted small">حتى <?= e((string) $application['end_date']) ?></div>
</td>
<td><?= status_badge((string) $application['status']) ?></td>
<td>
<div class="d-flex gap-2">
<!-- View/Edit btn -->
<?php if ((string) $application['status'] === 'approved'): ?>
<a href="approved_school.php?id=<?= e((string) $application['id']) ?>" class="btn btn-sm btn-outline-secondary" title="صفحة المركز">
<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" fill="currentColor" viewBox="0 0 16 16">
<path d="M10.5 8a2.5 2.5 0 1 1-5 0 2.5 2.5 0 0 1 5 0z"/>
<path d="M0 8s3-5.5 8-5.5S16 8 16 8s-3 5.5-8 5.5S0 8 0 8zm8 3.5a3.5 3.5 0 1 0 0-7 3.5 3.5 0 0 0 0 7z"/>
</svg>
</a>
<?php else: ?>
<a href="application_detail.php?id=<?= e((string) $application['id']) ?>" class="btn btn-sm btn-outline-secondary" title="تعديل">
<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" fill="currentColor" viewBox="0 0 16 16">
<path d="M12.146.146a.5.5 0 0 1 .708 0l3 3a.5.5 0 0 1 0 .708l-10 10a.5.5 0 0 1-.168.11l-5 2a.5.5 0 0 1-.65-.65l2-5a.5.5 0 0 1 .11-.168l10-10zM11.207 2.5 13.5 4.793 14.793 3.5 12.5 1.207 11.207 2.5zm1.586 3L10.5 3.207 4 9.707V10h.5a.5.5 0 0 1 .5.5v.5h.5a.5.5 0 0 1 .5.5v.5h.293l6.5-6.5zm-9.761 5.175-.106.106-1.528 3.821 3.821-1.528.106-.106A.5.5 0 0 1 5 12.5V12h-.5a.5.5 0 0 1-.5-.5V11h-.5a.5.5 0 0 1-.468-.325z"/>
</svg>
</a>
<?php endif; ?>
<!-- Delete btn -->
<form method="POST" action="applications.php" onsubmit="return confirm('هل أنت متأكد من حذف هذا الطلب؟');">
<input type="hidden" name="action" value="delete">
<input type="hidden" name="id" value="<?= e((string)$application['id']) ?>">
<button type="submit" class="btn btn-sm btn-outline-danger" title="حذف">
<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" fill="currentColor" viewBox="0 0 16 16">
<path d="M5.5 5.5A.5.5 0 0 1 6 6v6a.5.5 0 0 1-1 0V6a.5.5 0 0 1 .5-.5zm2.5 0a.5.5 0 0 1 .5.5v6a.5.5 0 0 1-1 0V6a.5.5 0 0 1 .5-.5zm3 .5a.5.5 0 0 0-1 0v6a.5.5 0 0 0 1 0V6z"/>
<path fill-rule="evenodd" d="M14.5 3a1 1 0 0 1-1 1H13v9a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V4h-.5a1 1 0 0 1-1-1V2a1 1 0 0 1 1-1H6a1 1 0 0 1 1-1h2a1 1 0 0 1 1 1h3.5a1 1 0 0 1 1 1v1zM4.118 4 4 4.059V13a1 1 0 0 0 1 1h6a1 1 0 0 0 1-1V4.059L11.882 4H4.118zM2.5 3V2h11v1h-11z"/>
</svg>
</button>
</form>
</div>
</td>
</tr>
<?php endforeach; ?>
<?php endif; ?>
</tbody>
</table>
</div>
</div>
<?php endif; ?>
</div>
</div>
</div>
</section>
<?php render_page_end(); ?>
<?php render_page_end(); ?>

View File

@ -17,6 +17,12 @@ if (!$application) {
?>
<section class="py-5">
<div class="container-xxl">
<div class="row g-4 align-items-start">
<div class="col-lg-3">
<?php require __DIR__ . '/includes/sidebar.php'; ?>
</div>
<div class="col-lg-9">
<div class="app-card text-center py-5">
<div class="empty-title mb-2">المركز غير موجود</div>
<p class="text-muted mb-3">تحقق من رقم الطلب أو ارجع إلى قائمة المراكز المعتمدة.</p>
@ -138,7 +144,7 @@ $pageDescription = $isApproved
? 'صفحة تشغيلية للمركز المعتمد تعرض الجاهزية، الدورات الموسمية، والخطوات التالية بعد الموافقة.'
: 'هذه الصفحة تصبح متاحة بعد اعتماد طلب المركز من المشرف العام.';
render_page_start($pageTitle, 'approved', $pageDescription);
render_page_start($pageTitle, 'approved', $pageDescription, (string) ($application['favicon'] ?? ''));
render_flash($flash);
?>
<section class="py-4 py-lg-5">
@ -177,6 +183,7 @@ render_flash($flash);
<div class="cta-stack mt-4">
<a class="btn btn-dark" href="admin.php">لوحة الإدارة</a>
<a class="btn btn-outline-secondary" href="application_detail.php?id=<?= e((string) $application['id']) ?>">فتح ملف الاعتماد</a>
<a class="btn btn-outline-secondary" href="center_profile.php?id=<?= e((string) $application['id']) ?>">إعدادات المركز</a>
<a class="btn btn-outline-secondary" href="<?= e($studentsUrl) ?>">تسجيل الطلاب</a>
<a class="btn btn-outline-secondary" href="<?= e($teachersUrl) ?>">فريق المعلمين</a>
<a class="btn btn-outline-secondary" href="<?= e($assessmentsUrl) ?>">التقييمات والأوزان</a>
@ -409,6 +416,7 @@ render_flash($flash);
<a class="quick-link-item" href="<?= e($assessmentsUrl) ?>"><strong>التقييمات والأوزان</strong><span>تعريف أنواع التقييم، المقاييس، والأوزان التشغيلية لهذا المركز.</span></a>
<a class="quick-link-item" href="<?= e($attendanceUrl) ?>"><strong>غياب الطلاب</strong><span>تسجيل الغياب اليومي، الأعذار، وحالات التأخر للطلاب المعتمدين.</span></a>
<a class="quick-link-item" href="application_detail.php?id=<?= e((string) $application['id']) ?>"><strong>ملف الاعتماد</strong><span>العودة إلى سجل القرار والتقييم.</span></a>
<a class="quick-link-item" href="center_profile.php?id=<?= e((string) $application['id']) ?>"><strong>إعدادات المركز</strong><span>تعديل الشعار والهوية وبيانات التواصل.</span></a>
<a class="quick-link-item" href="applications.php?status=approved"><strong>المراكز المعتمدة</strong><span>عرض بقية المراكز الجاهزة للتشغيل.</span></a>
<a class="quick-link-item" href="dashboard.php"><strong>لوحة القيادة</strong><span>الرجوع إلى المؤشرات العامة على مستوى الولاية.</span></a>
<a class="quick-link-item" href="admin.php"><strong>لوحة الإدارة</strong><span>الرجوع إلى البوابة المركزية لكل الشاشات الإدارية.</span></a>
@ -417,6 +425,10 @@ render_flash($flash);
</div>
</div>
<?php endif; ?>
</div>
</div>
</div>
</section>
<?php render_page_end();

View File

@ -89,11 +89,17 @@ if (!$application) {
http_response_code(404);
}
render_page_start($pageTitle, 'approved', $pageDescription);
render_page_start($pageTitle, 'approved', $pageDescription, (string) ($application['favicon'] ?? ''));
render_flash($flash);
?>
<section class="py-4 py-lg-5">
<div class="container-xxl">
<div class="row g-4 align-items-start">
<div class="col-lg-3">
<?php require __DIR__ . '/includes/sidebar.php'; ?>
</div>
<div class="col-lg-9">
<?php if (!$application): ?>
<div class="app-card text-center py-5">
<div class="empty-title mb-2">المدرسة غير موجودة</div>
@ -366,6 +372,10 @@ render_flash($flash);
</div>
</div>
<?php endif; ?>
</div>
</div>
</div>
</section>
<?php render_page_end();

View File

@ -348,8 +348,8 @@ h6,
}
.app-table tbody td {
padding-top: 1rem;
padding-bottom: 1rem;
padding-top: 0.5rem;
padding-bottom: 0.5rem;
vertical-align: middle;
}

View File

@ -107,11 +107,17 @@ if (!$application) {
http_response_code(404);
}
render_page_start($pageTitle, 'approved', $pageDescription);
render_page_start($pageTitle, 'approved', $pageDescription, (string) ($application['favicon'] ?? ''));
render_flash($flash);
?>
<section class="py-4 py-lg-5">
<div class="container-xxl">
<div class="row g-4 align-items-start">
<div class="col-lg-3">
<?php require __DIR__ . '/includes/sidebar.php'; ?>
</div>
<div class="col-lg-9">
<?php if (!$application): ?>
<div class="app-card text-center py-5">
<div class="empty-title mb-2">المدرسة غير موجودة</div>
@ -368,6 +374,10 @@ render_flash($flash);
</div>
</div>
<?php endif; ?>
</div>
</div>
</div>
</section>
<?php render_page_end();

View File

@ -159,6 +159,35 @@ render_flash($flash);
</div>
</div>
<div class="form-section-block mb-4">
<div class="form-section-heading">
<div>
<h2 class="form-section-title">المواد الدراسية</h2>
<p class="form-section-copy">اختر المواد الدراسية المطلوب تدريسها في هذا المركز.</p>
</div>
</div>
<div class="row g-3">
<div class="col-12">
<?php $available_subjects = get_enabled_subjects(); ?>
<?php if (empty($available_subjects)): ?>
<p class="text-muted small">لا توجد مواد دراسية مفعلة حالياً.</p>
<?php else: ?>
<div class="d-flex flex-wrap gap-3 <?= isset($errors['subjects']) ? 'is-invalid' : '' ?>">
<?php foreach ($available_subjects as $subject): ?>
<div class="form-check">
<input class="form-check-input" type="checkbox" name="subjects[]" value="<?= e((string) $subject['id']) ?>" id="subject_<?= e((string) $subject['id']) ?>" <?= in_array((string) $subject['id'], array_map('strval', $values['subjects'] ?? []), true) ? 'checked' : '' ?>>
<label class="form-check-label" for="subject_<?= e((string) $subject['id']) ?>">
<?= e((string) $subject['name']) ?>
</label>
</div>
<?php endforeach; ?>
</div>
<?php if (isset($errors['subjects'])): ?><div class="invalid-feedback d-block"><?= e($errors['subjects']) ?></div><?php endif; ?>
<?php endif; ?>
</div>
</div>
</div>
<div class="form-actions d-flex flex-wrap gap-2 pt-2">
<button class="btn btn-dark px-4" type="submit">إرسال الطلب</button>
<a class="btn btn-outline-secondary px-4" href="applications.php">العودة إلى لوحة الطلبات</a>

173
center_profile.php Normal file
View File

@ -0,0 +1,173 @@
<?php
declare(strict_types=1);
require_once __DIR__ . '/includes/app.php';
$flash = consume_flash();
$applicationId = filter_input(INPUT_GET, 'id', FILTER_VALIDATE_INT) ?: 0;
$application = $applicationId > 0 ? get_application($applicationId) : null;
if (!$application) {
http_response_code(404);
render_page_start('ملف المركز غير موجود', 'approved', 'تعذر العثور على المركز.');
render_flash($flash);
?>
<section class="py-5">
<div class="container-xxl">
<div class="row g-4 align-items-start">
<div class="col-lg-3">
<?php require __DIR__ . '/includes/sidebar.php'; ?>
</div>
<div class="col-lg-9">
<div class="app-card text-center py-5">
<div class="empty-title mb-2">المركز غير موجود</div>
<a class="btn btn-dark" href="applications.php?status=approved">العودة</a>
</div>
</div>
</div>
</div>
</section>
<?php
render_page_end();
exit;
}
$isApproved = (string) $application['status'] === 'approved';
if (!$isApproved) {
set_flash('error', 'هذه الصفحة متاحة فقط للمراكز المعتمدة.');
header('Location: application_detail.php?id=' . $applicationId);
exit;
}
$errors = [];
$values = [
'center_name' => $application['center_name'],
'email' => $application['email'],
'phone' => $application['phone'],
];
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$values['center_name'] = clean_text($_POST['center_name'] ?? '', 190);
$values['email'] = clean_text($_POST['email'] ?? '', 190);
$values['phone'] = clean_text($_POST['phone'] ?? '', 60);
if ($values['center_name'] === '') $errors['center_name'] = 'مطلوب';
if ($values['email'] === '') $errors['email'] = 'مطلوب';
if ($values['phone'] === '') $errors['phone'] = 'مطلوب';
$logoPath = $application['logo'];
$faviconPath = $application['favicon'];
// Handle Uploads
$uploadDir = __DIR__ . '/assets/images/uploads/';
if (isset($_FILES['logo']) && $_FILES['logo']['error'] === UPLOAD_ERR_OK) {
$logoExt = strtolower(pathinfo($_FILES['logo']['name'], PATHINFO_EXTENSION));
if (in_array($logoExt, ['png', 'jpg', 'jpeg', 'svg', 'gif'])) {
$logoName = 'logo_' . $applicationId . '_' . time() . '.' . $logoExt;
if (move_uploaded_file($_FILES['logo']['tmp_name'], $uploadDir . $logoName)) {
$logoPath = 'assets/images/uploads/' . $logoName;
}
} else {
$errors['logo'] = 'صيغة غير مدعومة';
}
}
if (isset($_FILES['favicon']) && $_FILES['favicon']['error'] === UPLOAD_ERR_OK) {
$faviconExt = strtolower(pathinfo($_FILES['favicon']['name'], PATHINFO_EXTENSION));
if (in_array($faviconExt, ['png', 'ico', 'svg'])) {
$faviconName = 'favicon_' . $applicationId . '_' . time() . '.' . $faviconExt;
if (move_uploaded_file($_FILES['favicon']['tmp_name'], $uploadDir . $faviconName)) {
$faviconPath = 'assets/images/uploads/' . $faviconName;
}
} else {
$errors['favicon'] = 'صيغة غير مدعومة';
}
}
if (empty($errors)) {
try {
$stmt = db()->prepare('UPDATE center_applications SET center_name = ?, email = ?, phone = ?, logo = ?, favicon = ?, updated_at = NOW() WHERE id = ?');
$stmt->execute([
$values['center_name'],
$values['email'],
$values['phone'],
$logoPath,
$faviconPath,
$applicationId
]);
set_flash('success', 'تم تحديث بيانات المركز بنجاح.');
header('Location: center_profile.php?id=' . $applicationId);
exit;
} catch (Throwable $e) {
$errors['form'] = 'تعذر الحفظ.';
}
}
}
render_page_start('إعدادات المركز', 'approved', 'تعديل بيانات وهوية المركز.', (string) ($application['favicon'] ?? ''));
render_flash($flash);
?>
<section class="py-4 py-lg-5">
<div class="container-xxl">
<div class="row g-4 align-items-start">
<div class="col-lg-3">
<?php require __DIR__ . '/includes/sidebar.php'; ?>
</div>
<div class="col-lg-9">
<div class="page-banner mb-4">
<h1 class="page-title mb-2">إعدادات وهوية المركز</h1>
<p class="page-copy mb-0">تعديل اسم المركز، الشعار (Logo)، الأيقونة (Favicon)، وبيانات التواصل.</p>
</div>
<div class="app-card form-card">
<?php if (!empty($errors['form'])): ?>
<div class="alert alert-danger mb-4"><?= e($errors['form']) ?></div>
<?php endif; ?>
<form method="post" enctype="multipart/form-data" novalidate>
<div class="row g-4">
<div class="col-md-12">
<label class="form-label">اسم المركز</label>
<input class="form-control <?= isset($errors['center_name']) ? 'is-invalid' : '' ?>" name="center_name" value="<?= e($values['center_name']) ?>">
<?php if (isset($errors['center_name'])): ?><div class="invalid-feedback"><?= e($errors['center_name']) ?></div><?php endif; ?>
</div>
<div class="col-md-6">
<label class="form-label">البريد الإلكتروني</label>
<input type="email" class="form-control <?= isset($errors['email']) ? 'is-invalid' : '' ?>" name="email" value="<?= e($values['email']) ?>">
<?php if (isset($errors['email'])): ?><div class="invalid-feedback"><?= e($errors['email']) ?></div><?php endif; ?>
</div>
<div class="col-md-6">
<label class="form-label">رقم الهاتف</label>
<input class="form-control <?= isset($errors['phone']) ? 'is-invalid' : '' ?>" name="phone" value="<?= e($values['phone']) ?>">
<?php if (isset($errors['phone'])): ?><div class="invalid-feedback"><?= e($errors['phone']) ?></div><?php endif; ?>
</div>
<div class="col-md-6">
<label class="form-label">الشعار (Logo)</label>
<?php if ($application['logo']): ?>
<div class="mb-2"><img src="<?= e((string)$application['logo']) ?>" alt="Logo" style="max-height: 80px; max-width: 100%; border-radius: 8px;"></div>
<?php endif; ?>
<input type="file" class="form-control <?= isset($errors['logo']) ? 'is-invalid' : '' ?>" name="logo" accept="image/*">
<?php if (isset($errors['logo'])): ?><div class="invalid-feedback"><?= e($errors['logo']) ?></div><?php endif; ?>
</div>
<div class="col-md-6">
<label class="form-label">الأيقونة (Favicon)</label>
<?php if ($application['favicon']): ?>
<div class="mb-2"><img src="<?= e((string)$application['favicon']) ?>" alt="Favicon" style="max-height: 40px; max-width: 100%; border-radius: 4px;"></div>
<?php endif; ?>
<input type="file" class="form-control <?= isset($errors['favicon']) ? 'is-invalid' : '' ?>" name="favicon" accept=".ico,.png,.svg">
<?php if (isset($errors['favicon'])): ?><div class="invalid-feedback"><?= e($errors['favicon']) ?></div><?php endif; ?>
</div>
</div>
<div class="form-actions mt-4">
<button class="btn btn-dark px-4" type="submit">حفظ التغييرات</button>
<a class="btn btn-outline-secondary px-4" href="approved_school.php?id=<?= e((string)$applicationId) ?>">إلغاء</a>
</div>
</form>
</div>
</div>
</div>
</div>
</section>
<?php render_page_end(); ?>

View File

@ -11,6 +11,12 @@ render_flash($flash);
?>
<section class="py-4 py-lg-5">
<div class="container-xxl">
<div class="row g-4 align-items-start">
<div class="col-lg-3">
<?php require __DIR__ . '/includes/sidebar.php'; ?>
</div>
<div class="col-lg-9">
<div class="app-card mb-4">
<div class="section-head flex-column flex-lg-row align-items-start align-items-lg-center gap-3">
<div>
@ -123,6 +129,9 @@ render_flash($flash);
</div>
</div>
</div>
</div>
</div>
</div>
</section>
<?php render_page_end(); ?>

View File

@ -0,0 +1 @@
ALTER TABLE center_applications ADD COLUMN subjects JSON NULL AFTER notes;

View File

@ -0,0 +1,10 @@
CREATE TABLE IF NOT EXISTS app_settings (
id INT PRIMARY KEY DEFAULT 1,
app_name VARCHAR(255),
app_email VARCHAR(255),
app_telephone VARCHAR(255),
app_logo VARCHAR(255),
app_favicon VARCHAR(255),
updated_at DATETIME
);
INSERT IGNORE INTO app_settings (id, app_name) VALUES (1, 'Central Admin');

View File

@ -0,0 +1,8 @@
CREATE TABLE IF NOT EXISTS subjects (
id INT AUTO_INCREMENT PRIMARY KEY,
name VARCHAR(255) NOT NULL,
description TEXT,
status ENUM('enabled', 'disabled') DEFAULT 'enabled',
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;

7
fix_sidebar.py Normal file
View File

@ -0,0 +1,7 @@
with open('includes/sidebar.php', 'r', encoding='utf-8') as f:
content = f.read()
content = content.replace("href=\"approved_school.php', 'center_profile.php?id=", "href=\"approved_school.php?id=")
with open('includes/sidebar.php', 'w', encoding='utf-8') as f:
f.write(content)

View File

@ -409,6 +409,7 @@ function application_defaults(): array
'start_date' => '',
'end_date' => '',
'notes' => '',
'subjects' => [],
];
}
@ -416,6 +417,10 @@ function validate_application_input(array $input): array
{
$data = application_defaults();
foreach ($data as $key => $_value) {
if ($key === 'subjects') {
$data[$key] = is_array($input[$key] ?? null) ? $input[$key] : [];
continue;
}
$data[$key] = clean_text((string) ($input[$key] ?? ''), $key === 'notes' ? 1000 : 190);
}
@ -467,6 +472,12 @@ function validate_application_input(array $input): array
$errors['end_date'] = 'يجب أن يكون تاريخ النهاية بعد البداية.';
}
if (empty($data['subjects'])) {
$errors['subjects'] = 'يرجى اختيار مادة واحدة على الأقل.';
} else {
$data['subjects'] = array_map('intval', $data['subjects']);
}
return [$data, $errors];
}
@ -476,10 +487,10 @@ function create_application(array $data): int
$stmt = $pdo->prepare(
'INSERT INTO center_applications (
center_name, city, center_type, gender_scope, director_name, phone, email,
expected_students, start_date, end_date, notes, status, submitted_at, updated_at
expected_students, start_date, end_date, notes, subjects, status, submitted_at, updated_at
) VALUES (
:center_name, :city, :center_type, :gender_scope, :director_name, :phone, :email,
:expected_students, :start_date, :end_date, :notes, :status, NOW(), NOW()
:expected_students, :start_date, :end_date, :notes, :subjects, :status, NOW(), NOW()
)'
);
@ -495,6 +506,7 @@ function create_application(array $data): int
':start_date' => $data['start_date'],
':end_date' => $data['end_date'],
':notes' => $data['notes'],
':subjects' => json_encode($data['subjects']),
':status' => 'submitted',
]);
@ -1334,3 +1346,25 @@ function render_page_end(): void
</html>
<?php
}
function update_application_subjects(int $id, array $subjects): void
{
$pdo = db_connection();
$stmt = $pdo->prepare('UPDATE center_applications SET subjects = :subjects, updated_at = NOW() WHERE id = :id');
$stmt->bindValue(':subjects', json_encode(array_values($subjects)), PDO::PARAM_STR);
$stmt->bindValue(':id', $id, PDO::PARAM_INT);
$stmt->execute();
}
function get_enabled_subjects(): array
{
try {
$pdo = db_connection();
$stmt = $pdo->query("SELECT id, name, description FROM subjects WHERE status = 'enabled' ORDER BY name ASC");
$result = $stmt->fetchAll(PDO::FETCH_ASSOC);
return $result !== false ? $result : [];
} catch (Throwable $e) {
return [];
}
}

67
includes/sidebar.php Normal file
View File

@ -0,0 +1,67 @@
<?php
$script = basename($_SERVER['SCRIPT_NAME'] ?? '');
$statusQuery = $_GET['status'] ?? '';
$activePage = 'admin';
if ($script === 'app_settings.php') $activePage = 'app_settings';
if ($script === 'subjects.php') $activePage = 'subjects';
if ($script === 'dashboard.php') $activePage = 'dashboard';
if ($script === 'applications.php') $activePage = ($statusQuery === 'approved') ? 'approved' : 'applications';
if (in_array($script, ['approved_school.php', 'center_profile.php', 'students.php', 'teachers.php', 'assessments.php', 'attendance.php'], true)) $activePage = 'approved';
if ($script === 'application_detail.php') $activePage = 'applications';
if ($script === 'modules.php') $activePage = 'modules';
if (!isset($recentApproved)) {
$app_list_for_sidebar = list_applications('approved');
$recentApproved = $app_list_for_sidebar[0] ?? null;
}
?>
<aside class="admin-sidebar sticky-top" style="top: 2rem; z-index: 10;">
<div class="sidebar-nav">
<div class="sidebar-label">الإدارة المركزية</div>
<a href="admin.php" class="sidebar-link <?= $activePage === 'admin' ? 'active' : '' ?>">
<svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" fill="currentColor" viewBox="0 0 16 16"><path d="M6 1H1v14h5V1zm9 0h-5v5h5V1zm0 9h-5v5h5v-5z"/><path d="M0 1a1 1 0 0 1 1-1h5a1 1 0 0 1 1 1v14a1 1 0 0 1-1 1H1a1 1 0 0 1-1-1V1zm9 0a1 1 0 0 1 1-1h5a1 1 0 0 1 1-1v5a1 1 0 0 1-1 1h-5a1 1 0 0 1-1-1V1zm0 9a1 1 0 0 1 1-1h5a1 1 0 0 1 1-1v5a1 1 0 0 1-1 1h-5a1 1 0 0 1-1-1v-5z"/></svg>
لوحة الإدارة
</a>
<a href="dashboard.php" class="sidebar-link <?= $activePage === 'dashboard' ? 'active' : '' ?>">
<svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" fill="currentColor" viewBox="0 0 16 16"><path d="M8 4a.5.5 0 0 1 .5.5V6a.5.5 0 0 1-1 0V4.5A.5.5 0 0 1 8 4zM3.732 5.732a.5.5 0 0 1 .707 0l.915.914a.5.5 0 1 1-.708.708l-.914-.915a.5.5 0 0 1 0-.707zM2 10a.5.5 0 0 1 .5-.5h1.586a.5.5 0 0 1 0 1H2.5A.5.5 0 0 1 2 10zm9.5 0a.5.5 0 0 1 .5-.5h1.5a.5.5 0 0 1 0 1H12a.5.5 0 0 1-.5-.5zm.754-4.246a.389.389 0 0 0-.527-.02L7.547 9.31a.91.91 0 1 0 1.302 1.258l3.434-4.297a.389.389 0 0 0-.029-.518z"/><path fill-rule="evenodd" d="M0 10a8 8 0 1 1 15.547 2.661c-.442 1.253-1.845 1.602-2.932 1.25C11.309 13.488 9.475 13 8 13c-1.474 0-3.31.488-4.615.911-1.087.352-2.49.003-2.932-1.25A7.988 7.988 0 0 1 0 10zm8-7a7 7 0 0 0-6.603 9.329c.203.575.923.876 1.68.63C4.397 12.533 6.358 12 8 12s3.604.532 4.923.96c.757.245 1.477-.056 1.68-.631A7 7 0 0 0 8 3z"/></svg>
لوحة القيادة
</a>
<a href="applications.php?status=approved" class="sidebar-link <?= $activePage === 'approved' ? 'active' : '' ?>">
<svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" fill="currentColor" viewBox="0 0 16 16"><path fill-rule="evenodd" d="M14.763.075A.5.5 0 0 1 15 .5v15a.5.5 0 0 1-.5.5h-3a.5.5 0 0 1-.5-.5V14h-1v1.5a.5.5 0 0 1-.5.5h-9a.5.5 0 0 1-.5-.5V10a.5.5 0 0 1 .342-.474L6 7.64V4.5a.5.5 0 0 1 .276-.447l8-4a.5.5 0 0 1 .487.022zM6 8.694 1 10.36V15h5V8.694zM7 15h2v-1.5a.5.5 0 0 1 .5-.5h2a.5.5 0 0 1 .5.5V15h2V1.309l-7 3.5V15z"/><path d="M2 11h1v1H2v-1zm2 0h1v1H4v-1zm-2 2h1v1H2v-1zm2 0h1v1H4v-1zm4-4h1v1H8V9zm2 0h1v1h-1V9zm-2 2h1v1H8v-1zm2 0h1v1h-1v-1zm2-2h1v1h-1V9zm0 2h1v1h-1v-1zM8 7h1v1H8V7zm2 0h1v1h-1V7zm2 0h1v1h-1V7zM8 5h1v1H8V5zm2 0h1v1h-1V5zm2 0h1v1h-1V5zm0-2h1v1h-1V3z"/></svg>
المراكز المعتمدة
</a>
<a href="center_application.php" class="sidebar-link">
<svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" fill="currentColor" viewBox="0 0 16 16"><path d="M8 6.5a.5.5 0 0 1 .5.5v1.5H10a.5.5 0 0 1 0 1H8.5V11a.5.5 0 0 1-1 0V9.5H6a.5.5 0 0 1 0-1h1.5V7a.5.5 0 0 1 .5-.5z"/><path d="M14 4.5V14a2 2 0 0 1-2 2H4a2 2 0 0 1-2-2V2a2 2 0 0 1 2-2h5.5L14 4.5zm-3 0A1.5 1.5 0 0 1 9.5 3V1H4a1 1 0 0 0-1 1v12a1 1 0 0 0 1 1h8a1 1 0 0 0 1-1V4.5h-2z"/></svg>
طلب فتح مركز
</a>
<div class="sidebar-label mt-3">الإعدادات</div>
<a href="app_settings.php" class="sidebar-link <?= $activePage === 'app_settings' ? 'active' : '' ?>">
<svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" fill="currentColor" viewBox="0 0 16 16"><path d="M8 4.754a3.246 3.246 0 1 0 0 6.492 3.246 3.246 0 0 0 0-6.492zM5.754 8a2.246 2.246 0 1 1 4.492 0 2.246 2.246 0 0 1-4.492 0z"/><path d="M9.796 1.343c-.527-1.79-3.065-1.79-3.592 0l-.094.319a.873.873 0 0 1-1.255.52l-.292-.16c-1.64-.892-3.433.902-2.54 2.541l.159.292a.873.873 0 0 1-.52 1.255l-.319.094c-1.79.527-1.79 3.065 0 3.592l.319.094a.873.873 0 0 1 .52 1.255l-.16.292c-.892 1.64.901 3.434 2.541 2.54l.292-.159a.873.873 0 0 1 1.255.52l.094.319c.527 1.79 3.065 1.79 3.592 0l.094-.319a.873.873 0 0 1 1.255-.52l.292.16c1.64.893 3.434-.902 2.54-2.541l-.159-.292a.873.873 0 0 1 .52-1.255l.319-.094c1.79-.527 1.79-3.065 0-3.592l-.319-.094a.873.873 0 0 1-.52-1.255l.16-.292c.893-1.64-.902-3.433-2.541-2.54l-.292.159a.873.873 0 0 1-1.255-.52l-.094-.319zm-2.633.283c.246-.835 1.428-.835 1.674 0l.094.319a1.873 1.873 0 0 0 2.693 1.115l.291-.16c.764-.415 1.6.42 1.184 1.185l-.159.292a1.873 1.873 0 0 0 1.116 2.692l.318.094c.835.246.835 1.428 0 1.674l-.319.094a1.873 1.873 0 0 0-1.115 2.693l.16.291c.415.764-.42 1.6-1.185 1.184l-.291-.159a1.873 1.873 0 0 0-2.693 1.116l-.094.318c-.246.835-1.428.835-1.674 0l-.094-.319a1.873 1.873 0 0 0-2.692-1.115l-.292.16c-.764.415-1.6-.42-1.184-1.185l.159-.291A1.873 1.873 0 0 0 1.945 8.93l-.319-.094c-.835-.246-.835-1.428 0-1.674l.319-.094A1.873 1.873 0 0 0 3.06 4.377l-.16-.292c-.415-.764.42-1.6 1.185-1.184l.292.159a1.873 1.873 0 0 0 2.692-1.115l.094-.319z"/></svg>
الإعدادات العامة
</a>
<a href="applications.php" class="sidebar-link <?= $activePage === 'applications' ? 'active' : '' ?>">
<svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" fill="currentColor" viewBox="0 0 16 16"><path d="M4.98 4a.5.5 0 0 0-.39.188L1.54 8H6a.5.5 0 0 1 .5.5 1.5 1.5 0 1 0 3 0A.5.5 0 0 1 10 8h4.46l-3.05-3.812A.5.5 0 0 0 11.02 4H4.98zm-1.17-.437A1.5 1.5 0 0 1 4.98 3h6.04a1.5 1.5 0 0 1 1.17.563l3.7 4.625a.5.5 0 0 1 .106.304l-.228 6.2a1.5 1.5 0 0 1-1.5 1.464H1.74a1.5 1.5 0 0 1-1.498-1.464l-.228-6.2a.5.5 0 0 1 .106-.304l3.7-4.625zM1 8.5v6a.5.5 0 0 0 .5.5h13a.5.5 0 0 0 .5-.5v-6h-3.415a2.5 2.5 0 0 1-4.17 0H1z"/></svg>
إدارة الطلبات
</a>
<a href="subjects.php" class="sidebar-link <?= $activePage === 'subjects' ? 'active' : '' ?>">
<svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" fill="currentColor" viewBox="0 0 16 16"><path d="M1 2.828c.885-.37 2.154-.769 3.388-.893 1.33-.134 2.458.156 3.112.752v9.746c-.935-.53-2.12-.603-3.213-.493-1.18.12-2.37.461-3.287.811V2.828zm7.5-.141c.654-.596 1.782-.886 3.112-.752 1.234.124 2.503.523 3.388.893v9.923c-.918-.35-2.107-.692-3.287-.81-1.094-.111-2.278-.039-3.213.492V2.687zM8 1.783C7.015.936 5.587.81 4.287.94c-1.514.153-3.042.672-3.994 1.105A.5.5 0 0 0 0 2.5v11a.5.5 0 0 0 .707.455c.882-.4 2.303-.881 3.68-1.02 1.409-.142 2.59.087 3.223.877a.5.5 0 0 0 .78 0c.633-.79 1.814-1.019 3.222-.877 1.378.139 2.8.62 3.681 1.02A.5.5 0 0 0 16 13.5v-11a.5.5 0 0 0-.293-.455c-.952-.433-2.48-.952-3.994-1.105C10.413.809 8.985.936 8 1.783z"/></svg>
المواد الدراسية
</a>
<div class="sidebar-label mt-3">هيكلية النظام</div>
<a href="modules.php" class="sidebar-link <?= $activePage === 'modules' ? 'active' : '' ?>">
<svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" fill="currentColor" viewBox="0 0 16 16"><path fill-rule="evenodd" d="M15.817.113A.5.5 0 0 1 16 .5v14a.5.5 0 0 1-.402.49l-5 1a.502.502 0 0 1-.196 0L5.5 15.01l-4.902.98A.5.5 0 0 1 0 15.5v-14a.5.5 0 0 1 .402-.49l5-1a.5.5 0 0 1 .196 0L10.5.99l4.902-.98a.5.5 0 0 1 .415.103zM10 1.91l-4-.8v12.98l4 .8V1.91zm1 12.98 4-.8V1.11l-4 .8v12.98zm-6-.8V1.11l-4 .8v12.98l4-.8z"/></svg>
خريطة الصفحات
</a>
<?php if (isset($recentApproved)): ?>
<div class="sidebar-label mt-3">أحدث مركز معتمد</div>
<a href="approved_school.php?id=<?= e((string) $recentApproved["id"]) ?>" class="sidebar-link">
<svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" fill="currentColor" viewBox="0 0 16 16"><path d="M4 16s-1 0-1-1 1-4 5-4 5 3 5 4-1 1-1 1H4Zm4-5.95a2.5 2.5 0 1 0 0-5 2.5 2.5 0 0 0 0 5Z"/><path d="M2 1a2 2 0 0 0-2 2v9.5A1.5 1.5 0 0 0 1.5 14h.653a5.373 5.373 0 0 1 1.066-2H1V3a1 1 0 0 1 1-1h12a1 1 0 0 1 1 1v9h-2.219c.554.654.89 1.373 1.066 2h.653a1.5 1.5 0 0 0 1.5-1.5V3a2 2 0 0 0-2-2H2Z"/></svg>
<?= e((string) $recentApproved["center_name"]) ?>
</a>
<?php endif; ?>
</div>
</aside>

View File

@ -9,6 +9,12 @@ render_flash($flash);
?>
<section class="py-4 py-lg-5">
<div class="container-xxl">
<div class="row g-4 align-items-start">
<div class="col-lg-3">
<?php require __DIR__ . '/includes/sidebar.php'; ?>
</div>
<div class="col-lg-9">
<div class="app-card mb-4">
<div class="section-title mb-2">هيكل الصفحات والوحدات</div>
<p class="text-muted mb-0">تم فصل المحتوى التعريفي والتنظيمي في هذه الصفحة حتى تبقى الصفحات التشغيلية مركزة: التقديم في شاشة، المتابعة في شاشة، والتفاصيل في شاشة مستقلة.</p>
@ -65,6 +71,9 @@ render_flash($flash);
</div>
</div>
</div>
</div>
</div>
</div>
</section>
<?php render_page_end(); ?>

93
patch_app.php Normal file
View File

@ -0,0 +1,93 @@
<?php
$content = file_get_contents('includes/app.php');
$content = str_replace(
'\'notes\' => \'',
'\'notes\' => \'',
'\'subjects\' => [],
$content
);
$old_loop = <<<'EOD'
foreach ($data as $key => $_value) {
$data[$key] = clean_text((string) ($input[$key] ?? ''), $key === 'notes' ? 1000 : 190);
}
EOD;
$new_loop = <<<'EOD'
foreach ($data as $key => $_value) {
if ($key === 'subjects') {
$data[$key] = is_array($input[$key] ?? []) ? $input[$key] : [];
continue;
}
$data[$key] = clean_text((string) ($input[$key] ?? ''), $key === 'notes' ? 1000 : 190);
}
EOD;
$content = str_replace($old_loop, $new_loop, $content);
$old_val = <<<'EOD'
if ($startDate !== '' && $endDate !== '' && strtotime($endDate) < strtotime($startDate)) {
$errors['end_date'] = 'يجب أن يكون تاريخ النهاية بعد البداية.';
}
EOD;
$new_val = $old_val . <<<'EOD'
if (empty($data['subjects'])) {
$errors['subjects'] = 'يرجى اختيار مادة واحدة على الأقل.';
} else {
$data['subjects'] = array_map('intval', $data['subjects']);
}
EOD;
$content = str_replace($old_val, $new_val, $content);
$old_insert_sql = <<<'EOD'
'INSERT INTO center_applications (
center_name, city, center_type, gender_scope, director_name, phone, email,
expected_students, start_date, end_date, notes, status, submitted_at, updated_at
) VALUES (
:center_name, :city, :center_type, :gender_scope, :director_name, :phone, :email,
:expected_students, :start_date, :end_date, :notes, :status, NOW(), NOW()
)'
EOD;
$new_insert_sql = <<<'EOD'
'INSERT INTO center_applications (
center_name, city, center_type, gender_scope, director_name, phone, email,
expected_students, start_date, end_date, notes, subjects, status, submitted_at, updated_at
) VALUES (
:center_name, :city, :center_type, :gender_scope, :director_name, :phone, :email,
:expected_students, :start_date, :end_date, :notes, :subjects, :status, NOW(), NOW()
)'
EOD;
$content = str_replace($old_insert_sql, $new_insert_sql, $content);
$old_execute = <<<'EOD'
':notes' => $data['notes'],
':status' => 'submitted',
]);
EOD;
$new_execute = <<<'EOD'
':notes' => $data['notes'],
':subjects' => json_encode($data['subjects']),
':status' => 'submitted',
]);
EOD;
$content = str_replace($old_execute, $new_execute, $content);
if (strpos($content, "function get_enabled_subjects") === false) {
$content .= <<<'EOD'
function get_enabled_subjects(): array
{
$pdo = db_connection();
$stmt = $pdo->query("SELECT id, name, description FROM subjects WHERE status = 'enabled' ORDER BY name ASC");
$result = $stmt->fetchAll(PDO::FETCH_ASSOC);
return $result !== false ? $result : [];
}
EOD;
}
file_put_contents('includes/app.php', $content);
echo "Patch applied.";

34
patch_app2.php Normal file
View File

@ -0,0 +1,34 @@
<?php
$content = file_get_contents('includes/app.php');
$content = str_replace(
'\'notes\' => \'',
'\'notes\' => \'',
'\'subjects\' => [],
$content
);
$old_loop = " foreach (\$data as \$key => \$_value) {\n \$data[\$key] = clean_text((string) (\$input[\$key] ?? \'\'), \$key === 'notes' ? 1000 : 190);\n }";
$new_loop = " foreach (\$data as \$key => \$_value) {\n if (\$key === 'subjects') {\n \$data[\$key] = is_array(\$input[\$key] ?? []) ? \$input[\$key] : [];\n continue;\n }\n \$data[\$key] = clean_text((string) (\$input[\$key] ?? \'\'), \$key === 'notes' ? 1000 : 190);\n }";
$content = str_replace($old_loop, $new_loop, $content);
$old_val = " if (\$startDate !== '' && \$endDate !== '' && strtotime(\$endDate) < strtotime(\$startDate)) {\n \$errors['end_date'] = 'يجب أن يكون تاريخ النهاية بعد البداية.';\n }";
$new_val = $old_val . "\n\n if (empty(\$data['subjects'])) {\n \$errors['subjects'] = 'يرجى اختيار مادة واحدة على الأقل.';\n } else {\n \$data['subjects'] = array_map('intval', \$data['subjects']);\n }";
$content = str_replace($old_val, $new_val, $content);
$old_insert_sql = " 'INSERT INTO center_applications (\n center_name, city, center_type, gender_scope, director_name, phone, email,\n expected_students, start_date, end_date, notes, status, submitted_at, updated_at\n ) VALUES (\n :center_name, :city, :center_type, :gender_scope, :director_name, :phone, :email,\n :expected_students, :start_date, :end_date, :notes, :status, NOW(), NOW()\n )'";
$new_insert_sql = " 'INSERT INTO center_applications (\n center_name, city, center_type, gender_scope, director_name, phone, email,\n expected_students, start_date, end_date, notes, subjects, status, submitted_at, updated_at\n ) VALUES (\n :center_name, :city, :center_type, :gender_scope, :director_name, :phone, :email,\n :expected_students, :start_date, :end_date, :notes, :subjects, :status, NOW(), NOW()\n )'";
$content = str_replace($old_insert_sql, $new_insert_sql, $content);
$old_execute = " ':notes' => \$data['notes'],\n ':status' => 'submitted',\n ]);";
$new_execute = " ':notes' => \$data['notes'],\n ':subjects' => json_encode(\$data['subjects']),
':status' => 'submitted',\n ]);";
$content = str_replace($old_execute, $new_execute, $content);
if (strpos($content, "function get_enabled_subjects") === false) {
$content .= "\nfunction get_enabled_subjects(): array\n{\n \$pdo = db_connection();\n \$stmt = \$pdo->query(\"SELECT id, name, description FROM subjects WHERE status = 'enabled' ORDER BY name ASC\");\n \$result = \$stmt->fetchAll(PDO::FETCH_ASSOC);\n return \$result !== false ? \$result : [];\n}\n";
}
file_put_contents('includes/app.php', $content);
echo "Patch applied.\n";

View File

@ -77,11 +77,17 @@ if (!$application) {
http_response_code(404);
}
render_page_start($pageTitle, 'approved', $pageDescription);
render_page_start($pageTitle, 'approved', $pageDescription, (string) ($application['favicon'] ?? ''));
render_flash($flash);
?>
<section class="py-4 py-lg-5">
<div class="container-xxl">
<div class="row g-4 align-items-start">
<div class="col-lg-3">
<?php require __DIR__ . '/includes/sidebar.php'; ?>
</div>
<div class="col-lg-9">
<?php if (!$application): ?>
<div class="app-card text-center py-5">
<div class="empty-title mb-2">المدرسة غير موجودة</div>
@ -378,6 +384,9 @@ render_flash($flash);
</div>
</div>
<?php endif; ?>
</div>
</div>
</div>
</section>
<?php render_page_end(); ?>

251
subjects.php Normal file
View File

@ -0,0 +1,251 @@
<?php
declare(strict_types=1);
require_once __DIR__ . '/includes/app.php';
// Handle POST actions
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$action = $_POST['action'] ?? '';
if ($action === 'create') {
$name = clean_text($_POST['name'] ?? '', 255);
$description = clean_text($_POST['description'] ?? '', 1000);
$status = in_array($_POST['status'] ?? '', ['enabled', 'disabled']) ? $_POST['status'] : 'enabled';
if ($name !== '') {
$stmt = db()->prepare('INSERT INTO subjects (name, description, status) VALUES (?, ?, ?)');
$stmt->execute([$name, $description, $status]);
set_flash('success', 'تمت إضافة المادة بنجاح.');
} else {
set_flash('error', 'اسم المادة مطلوب.');
}
header('Location: subjects.php');
exit;
}
if ($action === 'edit') {
$id = (int)($_POST['id'] ?? 0);
$name = clean_text($_POST['name'] ?? '', 255);
$description = clean_text($_POST['description'] ?? '', 1000);
$status = in_array($_POST['status'] ?? '', ['enabled', 'disabled']) ? $_POST['status'] : 'enabled';
if ($id > 0 && $name !== '') {
$stmt = db()->prepare('UPDATE subjects SET name = ?, description = ?, status = ? WHERE id = ?');
$stmt->execute([$name, $description, $status, $id]);
set_flash('success', 'تم تحديث المادة بنجاح.');
} else {
set_flash('error', 'تأكد من إدخال اسم المادة.');
}
header('Location: subjects.php');
exit;
}
if ($action === 'delete') {
$id = (int)($_POST['id'] ?? 0);
if ($id > 0) {
$stmt = db()->prepare('DELETE FROM subjects WHERE id = ?');
$stmt->execute([$id]);
set_flash('success', 'تم حذف المادة بنجاح.');
}
header('Location: subjects.php');
exit;
}
}
// Read list
$search = clean_text($_GET['search'] ?? '', 255);
$query = 'SELECT * FROM subjects';
$params = [];
if ($search !== '') {
$query .= ' WHERE name LIKE ? OR description LIKE ?';
$params[] = "%$search%";
$params[] = "%$search%";
}
$query .= ' ORDER BY id DESC';
$stmt = db()->prepare($query);
$stmt->execute($params);
$subjects = $stmt->fetchAll();
$flash = consume_flash();
render_page_start('المواد الدراسية', 'subjects', 'إدارة المواد الدراسية الخاصة بالمراكز');
render_flash($flash);
?>
<section class="py-4 py-lg-5">
<div class="container-xxl">
<div class="row g-4 align-items-start">
<div class="col-lg-3">
<?php require __DIR__ . '/includes/sidebar.php'; ?>
</div>
<div class="col-lg-9">
<div class="app-card mb-4">
<div class="d-flex justify-content-between align-items-center mb-4 flex-wrap gap-3">
<div class="section-title mb-0">إدارة المواد الدراسية</div>
<button class="btn btn-dark" data-bs-toggle="modal" data-bs-target="#createModal">
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="me-1" viewBox="0 0 16 16">
<path d="M8 4a.5.5 0 0 1 .5.5v3h3a.5.5 0 0 1 0 1h-3v3a.5.5 0 0 1-1 0v-3h-3a.5.5 0 0 1 0-1h3v-3A.5.5 0 0 1 8 4z"/>
</svg>
إضافة مادة
</button>
</div>
<!-- Search Bar -->
<form method="GET" action="subjects.php" class="mb-4">
<div class="input-group">
<input type="text" name="search" class="form-control" placeholder="ابحث باسم المادة أو الوصف..." value="<?= e($search) ?>">
<button class="btn btn-outline-secondary" type="submit">بحث</button>
<?php if ($search !== ''): ?>
<a href="subjects.php" class="btn btn-outline-danger">إلغاء</a>
<?php endif; ?>
</div>
</form>
<div class="table-responsive">
<table class="table app-table align-middle">
<thead>
<tr>
<th>#</th>
<th>الاسم</th>
<th>الوصف</th>
<th>الحالة</th>
<th>الإجراءات</th>
</tr>
</thead>
<tbody>
<?php if (count($subjects) === 0): ?>
<tr>
<td colspan="5" class="text-center py-4 text-muted">لا توجد مواد مسجلة أو لم يتم العثور على نتائج.</td>
</tr>
<?php else: ?>
<?php foreach ($subjects as $subject): ?>
<tr>
<td><?= e((string)$subject['id']) ?></td>
<td class="fw-semibold"><?= e($subject['name']) ?></td>
<td class="text-muted" style="max-width: 250px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis;" title="<?= e($subject['description']) ?>"><?= e($subject['description'] ?: '—') ?></td>
<td>
<?php if ($subject['status'] === 'enabled'): ?>
<span class="badge bg-success-subtle text-success">مفعل</span>
<?php else: ?>
<span class="badge bg-secondary-subtle text-secondary">معطل</span>
<?php endif; ?>
</td>
<td>
<div class="d-flex gap-2">
<!-- Edit btn -->
<button class="btn btn-sm btn-outline-secondary" title="تعديل"
data-bs-toggle="modal" data-bs-target="#editModal"
data-id="<?= e((string)$subject['id']) ?>"
data-name="<?= e($subject['name']) ?>"
data-description="<?= e($subject['description']) ?>"
data-status="<?= e($subject['status']) ?>"
onclick="fillEditModal(this)">
<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" fill="currentColor" viewBox="0 0 16 16">
<path d="M12.146.146a.5.5 0 0 1 .708 0l3 3a.5.5 0 0 1 0 .708l-10 10a.5.5 0 0 1-.168.11l-5 2a.5.5 0 0 1-.65-.65l2-5a.5.5 0 0 1 .11-.168l10-10zM11.207 2.5 13.5 4.793 14.793 3.5 12.5 1.207 11.207 2.5zm1.586 3L10.5 3.207 4 9.707V10h.5a.5.5 0 0 1 .5.5v.5h.5a.5.5 0 0 1 .5.5v.5h.293l6.5-6.5zm-9.761 5.175-.106.106-1.528 3.821 3.821-1.528.106-.106A.5.5 0 0 1 5 12.5V12h-.5a.5.5 0 0 1-.5-.5V11h-.5a.5.5 0 0 1-.468-.325z"/>
</svg>
</button>
<!-- Delete btn -->
<form method="POST" action="subjects.php" onsubmit="return confirm('هل أنت متأكد من حذف هذه المادة؟');">
<input type="hidden" name="action" value="delete">
<input type="hidden" name="id" value="<?= e((string)$subject['id']) ?>">
<button type="submit" class="btn btn-sm btn-outline-danger" title="حذف">
<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" fill="currentColor" viewBox="0 0 16 16">
<path d="M5.5 5.5A.5.5 0 0 1 6 6v6a.5.5 0 0 1-1 0V6a.5.5 0 0 1 .5-.5zm2.5 0a.5.5 0 0 1 .5.5v6a.5.5 0 0 1-1 0V6a.5.5 0 0 1 .5-.5zm3 .5a.5.5 0 0 0-1 0v6a.5.5 0 0 0 1 0V6z"/>
<path fill-rule="evenodd" d="M14.5 3a1 1 0 0 1-1 1H13v9a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V4h-.5a1 1 0 0 1-1-1V2a1 1 0 0 1 1-1H6a1 1 0 0 1 1-1h2a1 1 0 0 1 1 1h3.5a1 1 0 0 1 1 1v1zM4.118 4 4 4.059V13a1 1 0 0 0 1 1h6a1 1 0 0 0 1-1V4.059L11.882 4H4.118zM2.5 3V2h11v1h-11z"/>
</svg>
</button>
</form>
</div>
</td>
</tr>
<?php endforeach; ?>
<?php endif; ?>
</tbody>
</table>
</div>
</div>
</div>
</div>
</div>
</section>
<!-- Create Modal -->
<div class="modal fade" id="createModal" tabindex="-1" aria-labelledby="createModalLabel" aria-hidden="true">
<div class="modal-dialog">
<div class="modal-content">
<form method="POST" action="subjects.php">
<input type="hidden" name="action" value="create">
<div class="modal-header">
<h5 class="modal-title" id="createModalLabel">إضافة مادة دراسية</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body">
<div class="mb-3">
<label class="form-label">اسم المادة</label>
<input type="text" name="name" class="form-control" required>
</div>
<div class="mb-3">
<label class="form-label">الوصف</label>
<textarea name="description" class="form-control" rows="3"></textarea>
</div>
<div class="mb-3">
<label class="form-label">الحالة</label>
<select name="status" class="form-select">
<option value="enabled">مفعل</option>
<option value="disabled">معطل</option>
</select>
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">إلغاء</button>
<button type="submit" class="btn btn-dark">إضافة</button>
</div>
</form>
</div>
</div>
</div>
<!-- Edit Modal -->
<div class="modal fade" id="editModal" tabindex="-1" aria-labelledby="editModalLabel" aria-hidden="true">
<div class="modal-dialog">
<div class="modal-content">
<form method="POST" action="subjects.php">
<input type="hidden" name="action" value="edit">
<input type="hidden" name="id" id="edit_id">
<div class="modal-header">
<h5 class="modal-title" id="editModalLabel">تعديل مادة دراسية</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body">
<div class="mb-3">
<label class="form-label">اسم المادة</label>
<input type="text" name="name" id="edit_name" class="form-control" required>
</div>
<div class="mb-3">
<label class="form-label">الوصف</label>
<textarea name="description" id="edit_description" class="form-control" rows="3"></textarea>
</div>
<div class="mb-3">
<label class="form-label">الحالة</label>
<select name="status" id="edit_status" class="form-select">
<option value="enabled">مفعل</option>
<option value="disabled">معطل</option>
</select>
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">إلغاء</button>
<button type="submit" class="btn btn-dark">حفظ التعديلات</button>
</div>
</form>
</div>
</div>
</div>
<script>
function fillEditModal(btn) {
document.getElementById('edit_id').value = btn.getAttribute('data-id');
document.getElementById('edit_name').value = btn.getAttribute('data-name');
document.getElementById('edit_description').value = btn.getAttribute('data-description');
document.getElementById('edit_status').value = btn.getAttribute('data-status');
}
</script>
<?php render_page_end(); ?>

View File

@ -76,11 +76,17 @@ if (!$application) {
http_response_code(404);
}
render_page_start($pageTitle, 'approved', $pageDescription);
render_page_start($pageTitle, 'approved', $pageDescription, (string) ($application['favicon'] ?? ''));
render_flash($flash);
?>
<section class="py-4 py-lg-5">
<div class="container-xxl">
<div class="row g-4 align-items-start">
<div class="col-lg-3">
<?php require __DIR__ . '/includes/sidebar.php'; ?>
</div>
<div class="col-lg-9">
<?php if (!$application): ?>
<div class="app-card text-center py-5">
<div class="empty-title mb-2">المدرسة غير موجودة</div>
@ -356,6 +362,9 @@ render_flash($flash);
</div>
</div>
<?php endif; ?>
</div>
</div>
</div>
</section>
<?php render_page_end(); ?>

64
update.py Normal file
View File

@ -0,0 +1,64 @@
import json
with open("includes/app.php", "r") as f:
c = f.read()
c = c.replace(
"'notes' => '',",
"'notes' => '',\n 'subjects' => [],"
)
old_loop = """ foreach ($data as $key => $_value) {
$data[$key] = clean_text((string) ($input[$key] ?? ''), $key === 'notes' ? 1000 : 190);
}"""
new_loop = """ foreach ($data as $key => $_value) {
if ($key === 'subjects') {
$data[$key] = is_array($input[$key] ?? []) ? $input[$key] : [];
continue;
}
$data[$key] = clean_text((string) ($input[$key] ?? ''), $key === 'notes' ? 1000 : 190);
}"""
c = c.replace(old_loop, new_loop)
old_val = """ if ($startDate !== '' && $endDate !== '' && strtotime($endDate) < strtotime($startDate)) {
$errors['end_date'] = 'يجب أن يكون تاريخ النهاية بعد البداية.';
}"""
new_val = old_val + "\n\n if (empty($data['subjects'])) {
$errors['subjects'] = 'يرجى اختيار مادة واحدة على الأقل.';
} else {
$data['subjects'] = array_map('intval', $data['subjects']);
}"
c = c.replace(old_val, new_val)
old_insert_sql = " 'INSERT INTO center_applications (
center_name, city, center_type, gender_scope, director_name, phone, email,
expected_students, start_date, end_date, notes, status, submitted_at, updated_at
) VALUES (
:center_name, :city, :center_type, :gender_scope, :director_name, :phone, :email,
:expected_students, :start_date, :end_date, :notes, :status, NOW(), NOW()
)""
new_insert_sql = " 'INSERT INTO center_applications (
center_name, city, center_type, gender_scope, director_name, phone, email,
expected_students, start_date, end_date, notes, subjects, status, submitted_at, updated_at
) VALUES (
:center_name, :city, :center_type, :gender_scope, :director_name, :phone, :email,
:expected_students, :start_date, :end_date, :notes, :subjects, :status, NOW(), NOW()
)""
c = c.replace(old_insert_sql, new_insert_sql)
old_execute = """ ':notes' => $data['notes'],
':status' => 'submitted',
]);"""
new_execute = """ ':notes' => $data['notes'],
':subjects' => json_encode($data['subjects']),
':status' => 'submitted',
]);"""
c = c.replace(old_execute, new_execute)
if "function get_enabled_subjects" not in c:
c += "\nfunction get_enabled_subjects(): array\n{\n $pdo = db_connection();\n $stmt = $pdo->query(\"SELECT id, name, description FROM subjects WHERE status = 'enabled' ORDER BY name ASC\");\n $result = $stmt->fetchAll(PDO::FETCH_ASSOC);\n return $result !== false ? $result : [];\n}\n"
with open("includes/app.php", "w") as f:
f.write(c)
print("Patch applied.")