diff --git a/approved_school.php b/approved_school.php index 0744080..04308ad 100644 --- a/approved_school.php +++ b/approved_school.php @@ -4,7 +4,11 @@ require_once __DIR__ . '/includes/app.php'; $flash = consume_flash(); $applicationId = filter_input(INPUT_GET, 'id', FILTER_VALIDATE_INT) ?: 0; +$requestedCycleId = filter_input(INPUT_GET, 'cycle', FILTER_VALIDATE_INT) ?: 0; $application = $applicationId > 0 ? get_application($applicationId) : null; +$cycleValues = school_cycle_defaults($application ?: null); +$cycleErrors = []; +$cycleRollover = school_cycle_rollover_defaults(); if (!$application) { http_response_code(404); @@ -26,6 +30,91 @@ if (!$application) { } $isApproved = (string) $application['status'] === 'approved'; +$cycleContext = ['cycles' => [], 'selected' => null, 'active' => null, 'read_only' => false]; +$selectedCycle = null; +$selectedCycleId = 0; +$isCycleReadOnly = false; +$cycleLabel = 'لا توجد دورة بعد'; + +if ($isApproved) { + $cycleContext = resolve_school_cycle_context((int) $application['id'], $application, $requestedCycleId); + $selectedCycle = $cycleContext['selected']; + $selectedCycleId = $selectedCycle ? (int) ($selectedCycle['id'] ?? 0) : 0; + $isCycleReadOnly = (bool) $cycleContext['read_only']; + $cycleLabel = $selectedCycle ? (string) $selectedCycle['cycle_name'] : $cycleLabel; + $cycleRollover = school_cycle_rollover_defaults($selectedCycle); +} + +if ($_SERVER['REQUEST_METHOD'] === 'POST' && $isApproved) { + $cycleAction = clean_text((string) ($_POST['cycle_action'] ?? ''), 30); + if ($cycleAction === 'create_cycle') { + [$cycleValues, $cycleErrors] = validate_school_cycle_input($_POST, $application); + $cycleRollover = school_cycle_rollover_input((int) $application['id'], $_POST, $selectedCycle); + $wantsRollover = !empty($cycleRollover['copy_teachers']) || !empty($cycleRollover['copy_assessments']) || !empty($cycleRollover['copy_students']); + if ($wantsRollover && (int) $cycleRollover['source_cycle_id'] <= 0) { + $cycleErrors['form'] = 'اختر دورة مصدر إذا كنت تريد نسخ الفريق أو التقييمات أو الطلاب إلى الدورة الجديدة.'; + } + if ($cycleErrors === []) { + try { + $cycleCreation = create_school_cycle((int) $application['id'], $cycleValues, $cycleRollover); + $newCycleId = (int) ($cycleCreation['cycle_id'] ?? 0); + $rolloverSummary = (array) ($cycleCreation['rollover'] ?? []); + $flashMessage = 'تم إنشاء الدورة الموسمية الجديدة بنجاح. يمكنك الآن العمل داخل ' . format_school_cycle_name($cycleValues['season'], (int) $cycleValues['year']) . '.'; + $rolloverParts = []; + if (!empty($rolloverSummary['teachers'])) { + $rolloverParts[] = 'ترحيل ' . (int) $rolloverSummary['teachers'] . ' من أعضاء الفريق'; + } + if (!empty($rolloverSummary['assessments'])) { + $rolloverParts[] = 'نسخ ' . (int) $rolloverSummary['assessments'] . ' من بنود التقييم'; + } + if (!empty($rolloverSummary['students'])) { + $rolloverParts[] = 'نقل ' . (int) $rolloverSummary['students'] . ' من الطلاب المستمرين'; + } + if ($rolloverParts !== []) { + $sourceCycleName = (string) ($cycleCreation['source_cycle_name'] ?? 'الدورة السابقة'); + $flashMessage .= ' تم أيضاً ' . implode('، ', $rolloverParts) . ' من ' . $sourceCycleName . '.'; + } + set_flash('success', $flashMessage); + header('Location: ' . school_page_url('approved_school.php', (int) $application['id'], $newCycleId)); + exit; + } catch (PDOException $exception) { + $duplicateCode = isset($exception->errorInfo[1]) && (int) $exception->errorInfo[1] === 1062; + if ($duplicateCode) { + $cycleErrors['year'] = 'هذه الدورة موجودة مسبقاً لهذا المركز. اختر موسماً أو سنة مختلفة.'; + } else { + $cycleErrors['form'] = 'تعذر إنشاء الدورة حالياً. يرجى المحاولة مرة أخرى.'; + } + } catch (InvalidArgumentException $exception) { + $cycleErrors['form'] = 'تعذر ترحيل البيانات من الدورة المحددة. اختر دورة صحيحة ثم حاول مرة أخرى.'; + } catch (Throwable $exception) { + $cycleErrors['form'] = 'تعذر إنشاء الدورة حالياً. يرجى المحاولة مرة أخرى.'; + } + } + } elseif ($cycleAction === 'archive_cycle') { + $postedCycleId = (int) ($_POST['cycle_id'] ?? 0); + $cycleToArchive = $postedCycleId > 0 ? get_school_cycle((int) $application['id'], $postedCycleId) : null; + if (!$cycleToArchive) { + $cycleErrors['form'] = 'تعذر العثور على الدورة المطلوب أرشفتها.'; + } elseif ((string) $cycleToArchive['status'] === 'archived') { + $cycleErrors['form'] = 'هذه الدورة مؤرشفة بالفعل.'; + } else { + archive_school_cycle((int) $application['id'], $postedCycleId); + set_flash('success', 'تمت أرشفة الدورة ' . (string) $cycleToArchive['cycle_name'] . ' بنجاح. يمكنك فتح دورة جديدة متى شئت.'); + header('Location: ' . school_page_url('approved_school.php', (int) $application['id'])); + exit; + } + } + + $cycleContext = resolve_school_cycle_context((int) $application['id'], $application, $requestedCycleId); + $selectedCycle = $cycleContext['selected']; + $selectedCycleId = $selectedCycle ? (int) ($selectedCycle['id'] ?? 0) : 0; + $isCycleReadOnly = (bool) $cycleContext['read_only']; + $cycleLabel = $selectedCycle ? (string) $selectedCycle['cycle_name'] : $cycleLabel; + if ($cycleAction !== 'create_cycle') { + $cycleRollover = school_cycle_rollover_defaults($selectedCycle); + } +} + $scoreValue = $application['evaluation_score'] !== null ? max(0, min(100, (int) $application['evaluation_score'])) : null; $durationDays = 0; $startTs = strtotime((string) $application['start_date']); @@ -34,9 +123,19 @@ if ($startTs !== false && $endTs !== false && $endTs >= $startTs) { $durationDays = (int) floor(($endTs - $startTs) / 86400) + 1; } -$pageTitle = $isApproved ? 'صفحة المركز المعتمد: ' . (string) $application['center_name'] : 'صفحة المركز بعد الاعتماد'; +$studentsUrl = school_page_url('students.php', (int) $application['id'], $selectedCycleId); +$teachersUrl = school_page_url('teachers.php', (int) $application['id'], $selectedCycleId); +$assessmentsUrl = school_page_url('assessments.php', (int) $application['id'], $selectedCycleId); +$attendanceUrl = school_page_url('attendance.php', (int) $application['id'], $selectedCycleId); +$approvedSchoolUrl = school_page_url('approved_school.php', (int) $application['id'], $selectedCycleId); +$studentCycleMetrics = $isApproved && $selectedCycleId > 0 ? school_student_metrics_by_cycle((int) $application['id'], $selectedCycleId) : ['total' => 0, 'active' => 0]; +$teacherCycleMetrics = $isApproved && $selectedCycleId > 0 ? school_teacher_metrics_by_cycle((int) $application['id'], $selectedCycleId) : ['total' => 0, 'active' => 0]; +$assessmentCycleMetrics = $isApproved && $selectedCycleId > 0 ? school_assessment_metrics_by_cycle((int) $application['id'], $selectedCycleId) : ['total' => 0, 'active' => 0, 'active_weight' => 0]; +$attendanceCycleMetrics = $isApproved && $selectedCycleId > 0 ? school_attendance_metrics_by_cycle((int) $application['id'], $selectedCycleId) : ['total' => 0, 'today_count' => 0, 'latest_date' => '']; + +$pageTitle = $isApproved ? 'صفحة المركز المعتمد: ' . (string) $application['center_name'] . ($selectedCycle ? ' — ' . $cycleLabel : '') : 'صفحة المركز بعد الاعتماد'; $pageDescription = $isApproved - ? 'صفحة هبوط تشغيلية للمركز المعتمد تعرض الجاهزية، بيانات التواصل، والخطوات التالية بعد الموافقة.' + ? 'صفحة تشغيلية للمركز المعتمد تعرض الجاهزية، الدورات الموسمية، والخطوات التالية بعد الموافقة.' : 'هذه الصفحة تصبح متاحة بعد اعتماد طلب المركز من المشرف العام.'; render_page_start($pageTitle, 'approved', $pageDescription); @@ -76,8 +175,10 @@ render_flash($flash);
استخدم هذا النموذج عند نهاية الصيف أو الشتاء لبدء دورة جديدة باسم تلقائي مثل Summer 2026 أو Winter 2026.
+صفحة الفريق أصبحت جاهزة الآن لإضافة المعلمين والمشرفين وربط أدوارهم التشغيلية مباشرة بالمركز المعتمد.
بعد الانطلاق يمكن توسيع هذه الصفحة لاحقاً بمؤشرات حضور يومية، تقييمات، وتنبيهات تشغيلية.
+ 3) ضبط التقييمات +صفحة التقييمات أصبحت جاهزة الآن لتحديد نوع التقييم، المقياس، والوزن قبل بدء الرصد والمتابعة.
+صفحة غياب الطلاب أصبحت جاهزة الآن لتسجيل الغياب اليومي، الأعذار، وحالات التأخر لكل طالب داخل المركز.
تحقق من رابط المدرسة أو ارجع إلى قائمة المراكز المعتمدة.
+ المراكز المعتمدة +تم تجهيز صفحة أنواع التقييم، لكن استخدامها مرتبط بتحويل حالة المركز إلى معتمد أولاً حتى تبقى الخطة الأكاديمية مرتبطة فقط بالمراكز الجاهزة للتشغيل.
+هنا يتم تعريف أنواع التقييم الخاصة بالمركز بعد تجهيز الطلاب والمعلمين: اسم التقييم، فئته، المقياس المستخدم، والوزن التشغيلي ضمن الخطة الأكاديمية.
+افتح نفس صفحة التقييمات لأي موسم سابق أو حالي حتى تراجع الأوزان والخطط التاريخية بسرعة.
+ +أدخل نوع التقييم مرة واحدة، ثم استخدم القائمة في الأسفل كمرجع للخطة الأكاديمية الخاصة بالمركز.
+ + +ابدأ من النموذج في الجانب الأيمن لإضافة أول نوع تقييم إلى خطة المركز.
+| التقييم | +الفئة | +المقياس | +الدرجة | +الوزن | +الحالة | +الإضافة | +
|---|---|---|---|---|---|---|
| + = e((string) $assessment['title']) ?> + = e((string) $assessment['notes']) ?> + | += e((string) $assessment['category']) ?> | += assessment_scale_type_badge((string) $assessment['scale_type']) ?> | += e(number_format((float) $assessment['max_score'], 2, '.', '')) ?> | += e(number_format((float) $assessment['weight_percentage'], 2, '.', '')) ?>% | += assessment_active_badge((int) $assessment['is_active']) ?> | += e(substr((string) $assessment['created_at'], 0, 10)) ?> | +
تحقق من رابط المدرسة أو ارجع إلى قائمة المراكز المعتمدة.
+ المراكز المعتمدة +تم تجهيز صفحة غياب الطلاب، لكن استخدامها مرتبط بتحويل حالة المركز إلى معتمد أولاً حتى يبقى السجل اليومي محصوراً بالمراكز الجاهزة للتشغيل.
+هنا يتم تسجيل الغياب اليومي، الأعذار، وحالات التأخر في صفحة تشغيلية منفصلة. كل سجل يرتبط مباشرة بطالب محدد داخل هذا المركز فقط.
+افتح سجل الغياب لنفس المركز في أي موسم سابق أو حالي مباشرة من هذه الصفحة.
+ +اختر الطالب والتاريخ والحالة، ثم أضف سبباً مختصراً وملاحظة متابعة عند الحاجة.
+ + +ابدأ من النموذج في الجانب الأيمن لإضافة أول سجل غياب أو تأخر لهذا المركز.
+| الطالب | +التاريخ | +الحالة | +السبب | +المتابعة | +
|---|---|---|---|---|
| + = e((string) $record['full_name']) ?> + = e((string) $record['student_code']) ?> — = e((string) $record['grade_level']) ?> + | += e((string) $record['attendance_date']) ?> | += attendance_status_badge((string) $record['attendance_status']) ?> | += e((string) ($record['absence_reason'] ?: '—')) ?> | ++ = e((string) ($record['guardian_phone'] ?: '—')) ?> + = e((string) $record['notes']) ?> + | +
يمكنك فتح نفس صفحة الطلاب لأي موسم سابق أو حالي مباشرة من هنا بدون الرجوع إلى صفحة المركز.
+ +بدّل مباشرة إلى نفس صفحة الفريق في أي دورة سابقة أو حالية لمراجعة الكادر بدون الرجوع إلى صفحة المركز.
+ +