$type, 'message' => $message, ]; } function consume_flash(): ?array { if (empty($_SESSION['flash']) || !is_array($_SESSION['flash'])) { return null; } $flash = $_SESSION['flash']; unset($_SESSION['flash']); return $flash; } function status_map(): array { return [ 'submitted' => ['label' => 'قيد الاستلام', 'class' => 'status-submitted'], 'under_review' => ['label' => 'تحت المراجعة', 'class' => 'status-review'], 'approved' => ['label' => 'معتمد', 'class' => 'status-approved'], 'rejected' => ['label' => 'بحاجة إلى استكمال', 'class' => 'status-rejected'], ]; } function status_meta(string $status): array { $map = status_map(); return $map[$status] ?? ['label' => 'غير معروف', 'class' => 'status-muted']; } function status_badge(string $status): string { $meta = status_meta($status); return '' . e($meta['label']) . ''; } function db_connection(): PDO { static $pdo = null; static $bootstrapped = false; if (!$pdo instanceof PDO) { $pdo = db(); } if (!$bootstrapped) { ensure_center_application_schema($pdo); seed_center_application_demo_data($pdo); $bootstrapped = true; } return $pdo; } function ensure_center_application_schema(PDO $pdo): void { static $done = false; if ($done) { return; } $migrationPath = __DIR__ . '/../db/migrations/20260416_center_applications.sql'; if (is_file($migrationPath)) { $sql = file_get_contents($migrationPath); if (is_string($sql) && trim($sql) !== '') { $pdo->exec($sql); } } $done = true; } function seed_center_application_demo_data(PDO $pdo): void { $count = (int) $pdo->query('SELECT COUNT(*) FROM center_applications')->fetchColumn(); if ($count > 0) { return; } $rows = [ [ 'مركز النور الصيفي', 'العاصمة', 'بنين', 'طلاب', 'أ. خالد السالمي', '0501234567', 'alnoor@example.com', 180, '2026-06-15', '2026-08-15', 'يركز على القرآن والمهارات الرقمية والأنشطة الرياضية المسائية.', 'submitted', 'بانتظار زيارة ميدانية أولية.', null, ], [ 'مركز الواحة للفتيات', 'الزور', 'بنات', 'طالبات', 'أ. نورة الشيبانية', '0507654321', 'alwaha@example.com', 140, '2026-06-20', '2026-08-10', 'طلب تجهيز معمل حاسب وقاعة أنشطة فنية.', 'under_review', 'تمت مراجعة الوثائق والمطلوب استكمال خطة الأمن والسلامة.', 82, ], [ 'مركز الريادة المجتمعي', 'الساحل', 'مختلط', 'طلاب وطالبات', 'أ. سيف الحارثي', '0509988776', 'riyadah@example.com', 220, '2026-06-18', '2026-08-20', 'يشمل برنامجاً علمياً ومساراً للابتكار ومتابعة أسرية.', 'approved', 'المركز مستوفٍ للاشتراطات ويُنصح ببدء التسجيل.', 94, ], ]; $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, admin_notes, evaluation_score, submitted_at, updated_at ) VALUES ( :center_name, :city, :center_type, :gender_scope, :director_name, :phone, :email, :expected_students, :start_date, :end_date, :notes, :status, :admin_notes, :evaluation_score, NOW(), NOW() )' ); foreach ($rows as $row) { $stmt->execute([ ':center_name' => $row[0], ':city' => $row[1], ':center_type' => $row[2], ':gender_scope' => $row[3], ':director_name' => $row[4], ':phone' => $row[5], ':email' => $row[6], ':expected_students' => $row[7], ':start_date' => $row[8], ':end_date' => $row[9], ':notes' => $row[10], ':status' => $row[11], ':admin_notes' => $row[12], ':evaluation_score' => $row[13], ]); } } function clean_text(string $value, int $limit = 255): string { $normalized = preg_replace('/\s+/u', ' ', trim($value)); if (!is_string($normalized)) { $normalized = trim($value); } if (function_exists('mb_substr')) { return mb_substr($normalized, 0, $limit); } return substr($normalized, 0, $limit); } function application_defaults(): array { return [ 'center_name' => '', 'city' => '', 'center_type' => '', 'gender_scope' => '', 'director_name' => '', 'phone' => '', 'email' => '', 'expected_students' => '', 'start_date' => '', 'end_date' => '', 'notes' => '', ]; } function validate_application_input(array $input): array { $data = application_defaults(); foreach ($data as $key => $_value) { $data[$key] = clean_text((string) ($input[$key] ?? ''), $key === 'notes' ? 1000 : 190); } $errors = []; if ($data['center_name'] === '') { $errors['center_name'] = 'يرجى إدخال اسم المركز.'; } if ($data['city'] === '') { $errors['city'] = 'يرجى اختيار المدينة أو الولاية الفرعية.'; } if ($data['center_type'] === '') { $errors['center_type'] = 'يرجى تحديد نوع المركز.'; } if ($data['gender_scope'] === '') { $errors['gender_scope'] = 'يرجى تحديد الفئة المستهدفة.'; } if ($data['director_name'] === '') { $errors['director_name'] = 'يرجى إدخال اسم مدير أو مديرة المركز.'; } if ($data['phone'] === '') { $errors['phone'] = 'يرجى إدخال رقم الهاتف.'; } if ($data['email'] === '' || !filter_var($data['email'], FILTER_VALIDATE_EMAIL)) { $errors['email'] = 'يرجى إدخال بريد إلكتروني صحيح.'; } $expectedStudents = filter_var($input['expected_students'] ?? null, FILTER_VALIDATE_INT, [ 'options' => ['min_range' => 10, 'max_range' => 2000], ]); if ($expectedStudents === false) { $errors['expected_students'] = 'أدخل عدداً صحيحاً بين 10 و2000.'; } else { $data['expected_students'] = (string) $expectedStudents; } $startDate = clean_text((string) ($input['start_date'] ?? ''), 20); $endDate = clean_text((string) ($input['end_date'] ?? ''), 20); $data['start_date'] = $startDate; $data['end_date'] = $endDate; if ($startDate === '') { $errors['start_date'] = 'حدد تاريخ بداية البرنامج.'; } if ($endDate === '') { $errors['end_date'] = 'حدد تاريخ نهاية البرنامج.'; } if ($startDate !== '' && $endDate !== '' && strtotime($endDate) < strtotime($startDate)) { $errors['end_date'] = 'يجب أن يكون تاريخ النهاية بعد البداية.'; } return [$data, $errors]; } function create_application(array $data): int { $pdo = db_connection(); $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 ) VALUES ( :center_name, :city, :center_type, :gender_scope, :director_name, :phone, :email, :expected_students, :start_date, :end_date, :notes, :status, NOW(), NOW() )' ); $stmt->execute([ ':center_name' => $data['center_name'], ':city' => $data['city'], ':center_type' => $data['center_type'], ':gender_scope' => $data['gender_scope'], ':director_name' => $data['director_name'], ':phone' => $data['phone'], ':email' => $data['email'], ':expected_students' => (int) $data['expected_students'], ':start_date' => $data['start_date'], ':end_date' => $data['end_date'], ':notes' => $data['notes'], ':status' => 'submitted', ]); return (int) $pdo->lastInsertId(); } function list_applications(string $status = 'all'): array { $pdo = db_connection(); if ($status === 'all' || !array_key_exists($status, status_map())) { $stmt = $pdo->query('SELECT * FROM center_applications ORDER BY submitted_at DESC, id DESC'); return $stmt->fetchAll(); } $stmt = $pdo->prepare('SELECT * FROM center_applications WHERE status = :status ORDER BY submitted_at DESC, id DESC'); $stmt->execute([':status' => $status]); return $stmt->fetchAll(); } function get_application(int $id): ?array { $pdo = db_connection(); $stmt = $pdo->prepare('SELECT * FROM center_applications WHERE id = :id LIMIT 1'); $stmt->execute([':id' => $id]); $row = $stmt->fetch(); return $row ?: null; } function update_application_review(int $id, string $status, string $adminNotes, ?int $evaluationScore): void { $allowed = array_keys(status_map()); if (!in_array($status, $allowed, true)) { throw new InvalidArgumentException('Invalid status value.'); } $pdo = db_connection(); $stmt = $pdo->prepare( 'UPDATE center_applications SET status = :status, admin_notes = :admin_notes, evaluation_score = :evaluation_score, updated_at = NOW() WHERE id = :id' ); $stmt->bindValue(':status', $status, PDO::PARAM_STR); $stmt->bindValue(':admin_notes', $adminNotes !== '' ? $adminNotes : null, $adminNotes !== '' ? PDO::PARAM_STR : PDO::PARAM_NULL); $stmt->bindValue(':evaluation_score', $evaluationScore, $evaluationScore !== null ? PDO::PARAM_INT : PDO::PARAM_NULL); $stmt->bindValue(':id', $id, PDO::PARAM_INT); $stmt->execute(); } function dashboard_metrics(): array { $pdo = db_connection(); $totals = [ 'all' => 0, 'submitted' => 0, 'under_review' => 0, 'approved' => 0, 'rejected' => 0, 'expected_students' => 0, ]; $summary = $pdo->query('SELECT status, COUNT(*) AS total FROM center_applications GROUP BY status')->fetchAll(); foreach ($summary as $row) { $status = (string) ($row['status'] ?? ''); $count = (int) ($row['total'] ?? 0); if (array_key_exists($status, $totals)) { $totals[$status] = $count; $totals['all'] += $count; } } $totals['expected_students'] = (int) $pdo->query('SELECT COALESCE(SUM(expected_students), 0) FROM center_applications')->fetchColumn(); return $totals; } function latest_applications(int $limit = 5): array { $pdo = db_connection(); $stmt = $pdo->prepare('SELECT * FROM center_applications ORDER BY submitted_at DESC, id DESC LIMIT :limit'); $stmt->bindValue(':limit', $limit, PDO::PARAM_INT); $stmt->execute(); return $stmt->fetchAll(); } function render_page_start(string $pageTitle, string $active = 'home', string $pageDescription = ''): void { $projectName = project_name(); $description = $pageDescription !== '' ? $pageDescription : project_description(); $projectImageUrl = env_value('PROJECT_IMAGE_URL'); ?> <?= e($pageTitle) ?> | <?= e($projectName) ?>
'text-bg-success', 'error' => 'text-bg-danger', default => 'text-bg-dark', }; ?>