From 61e8c6788b9f55e5507604453ae7ae33818e0a3c Mon Sep 17 00:00:00 2001 From: Flatlogic Bot Date: Thu, 16 Apr 2026 17:02:27 +0000 Subject: [PATCH] update center page --- assessments.php | 534 ++++++++---------- center_subjects.php | 1 - .../20260416_alter_assessments_subject.sql | 18 + includes/app.php | 1 + includes/cycles.php | 73 ++- 5 files changed, 332 insertions(+), 295 deletions(-) create mode 100644 db/migrations/20260416_alter_assessments_subject.sql diff --git a/assessments.php b/assessments.php index 86965aa..3ecce3f 100644 --- a/assessments.php +++ b/assessments.php @@ -15,6 +15,15 @@ $selectedCycleId = 0; $isCycleReadOnly = false; $cycleLabel = 'لا توجد دورة بعد'; +$available_subjects = []; +if ($application) { + $center_subjects_ids = is_string($application['subjects']) ? json_decode($application['subjects'], true) : []; + if (!is_array($center_subjects_ids)) $center_subjects_ids = []; + $center_subjects_ids = array_map('strval', $center_subjects_ids); + $all_subjects = get_enabled_subjects(); + $available_subjects = array_filter($all_subjects, fn($s) => in_array((string)$s['id'], $center_subjects_ids, true)); +} + if ($application && $isApprovedSchool) { $cycleContext = resolve_school_cycle_context((int) $application['id'], $application, $requestedCycleId); $selectedCycle = $cycleContext['selected']; @@ -24,6 +33,9 @@ if ($application && $isApprovedSchool) { } if ($_SERVER['REQUEST_METHOD'] === 'POST' && $application) { + $action = $_POST['action'] ?? 'add'; + $assessmentId = filter_input(INPUT_POST, 'assessment_id', FILTER_VALIDATE_INT) ?: 0; + [$values, $errors] = validate_assessment_input($_POST); if (!$isApprovedSchool) { @@ -36,50 +48,37 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST' && $application) { if ($errors === []) { try { - create_assessment_type_in_cycle((int) $application['id'], $selectedCycleId, $values); - set_flash('success', 'تم حفظ نوع التقييم داخل الدورة الموسمية المحددة.'); - header('Location: ' . school_page_url('assessments.php', (int) $application['id'], $selectedCycleId)); + if ($action === 'edit' && $assessmentId > 0) { + update_assessment_type_in_cycle((int) $application['id'], $selectedCycleId, $assessmentId, $values); + set_flash('success', 'تم تحديث التقييم بنجاح.'); + } else { + create_assessment_type_in_cycle((int) $application['id'], $selectedCycleId, $values); + set_flash('success', 'تم حفظ التقييم داخل الدورة الموسمية المحددة.'); + } + header('Location: ' . school_page_url('assessments.php', (int) $application['id'], $selectedCycleId) . '&' . http_build_query(array_intersect_key($_GET, array_flip(['search', 'category', 'subject_id', 'page'])))); exit; } catch (Throwable $exception) { - $errors['form'] = 'تعذر حفظ نوع التقييم حالياً. يرجى المحاولة مرة أخرى.'; + $errors['form'] = 'تعذر حفظ التقييم حالياً. يرجى المحاولة مرة أخرى.'; } } } -$search = clean_text($_GET['search'] ?? '', 255); +$filters = [ + 'search' => clean_text($_GET['search'] ?? '', 255), + 'category' => clean_text($_GET['category'] ?? '', 80), + 'subject_id' => clean_text($_GET['subject_id'] ?? '', 20) +]; + $page = filter_input(INPUT_GET, 'page', FILTER_VALIDATE_INT) ?: 1; -$limit = 10; +$limit = 20; $offset = ($page - 1) * $limit; -$assessments = $isApprovedSchool && $selectedCycleId > 0 ? list_school_assessments_by_cycle((int) $application['id'], $selectedCycleId, $search, $limit, $offset) : []; -$totalAssessments = $isApprovedSchool && $selectedCycleId > 0 ? count_school_assessments_by_cycle((int) $application['id'], $selectedCycleId, $search) : 0; +$assessments = $isApprovedSchool && $selectedCycleId > 0 ? list_school_assessments_by_cycle((int) $application['id'], $selectedCycleId, $filters, $limit, $offset) : []; +$totalAssessments = $isApprovedSchool && $selectedCycleId > 0 ? count_school_assessments_by_cycle((int) $application['id'], $selectedCycleId, $filters) : 0; $metrics = $isApprovedSchool && $selectedCycleId > 0 ? school_assessment_metrics_by_cycle((int) $application['id'], $selectedCycleId) : [ - 'total' => 0, - 'active' => 0, - 'inactive' => 0, - 'total_weight' => 0.0, - 'active_weight' => 0.0, - 'average_max_score' => 0.0, - 'percentage' => 0, - 'points' => 0, - 'rubric' => 0, -]; -$studentMetrics = $isApprovedSchool && $selectedCycleId > 0 ? school_student_metrics_by_cycle((int) $application['id'], $selectedCycleId) : [ - 'total' => 0, - 'boys' => 0, - 'girls' => 0, - 'active' => 0, - 'waiting' => 0, - 'withdrawn' => 0, -]; -$teacherMetrics = $isApprovedSchool && $selectedCycleId > 0 ? school_teacher_metrics_by_cycle((int) $application['id'], $selectedCycleId) : [ - 'total' => 0, - 'active' => 0, - 'pending' => 0, - 'inactive' => 0, - 'email_ready' => 0, - 'supervisors' => 0, + 'total' => 0, 'active' => 0, 'inactive' => 0, 'total_weight' => 0.0, 'active_weight' => 0.0, + 'average_max_score' => 0.0, 'percentage' => 0, 'points' => 0, 'rubric' => 0, ]; $activeWeight = round((float) $metrics['active_weight'], 2); @@ -87,10 +86,6 @@ $weightGap = round(100 - $activeWeight, 2); $pageTitle = $application ? 'التقييمات والأوزان: ' . (string) $application['center_name'] . ($selectedCycle ? ' — ' . $cycleLabel : '') : 'التقييمات والأوزان'; $pageDescription = 'صفحة مستقلة لتعريف أنواع التقييم، المقاييس، والأوزان لكل مدرسة معتمدة داخل دورة موسمية محددة.'; $approvedSchoolUrl = $application ? school_page_url('approved_school.php', (int) $application['id'], $selectedCycleId) : 'approved_school.php'; -$studentsUrl = $application ? school_page_url('students.php', (int) $application['id'], $selectedCycleId) : 'students.php'; -$teachersUrl = $application ? school_page_url('teachers.php', (int) $application['id'], $selectedCycleId) : 'teachers.php'; -$attendanceUrl = $application ? school_page_url('attendance.php', (int) $application['id'], $selectedCycleId) : 'attendance.php'; -$applicationDetailUrl = $application ? 'application_detail.php?id=' . urlencode((string) $application['id']) : 'application_detail.php'; if (!$application) { http_response_code(404); @@ -101,11 +96,11 @@ render_flash($flash); ?>
-
-
+
+
-
+
@@ -114,279 +109,246 @@ render_flash($flash); المراكز المعتمدة
-
+
التقييمات تبدأ بعد الاعتماد

-

تم تجهيز صفحة أنواع التقييم، لكن استخدامها مرتبط بتحويل حالة المركز إلى معتمد أولاً حتى تبقى الخطة الأكاديمية مرتبطة فقط بالمراكز الجاهزة للتشغيل.

-
- الحالة الحالية: - المدينة: -
- +

يجب اعتماد المركز أولاً.

+ صفحة المركز
-
-
-
- صفحة مستقلة للتقييمات والأوزان -

خطة التقييم —

-

هنا يتم تعريف أنواع التقييم الخاصة بالمركز بعد تجهيز الطلاب والمعلمين: اسم التقييم، فئته، المقياس المستخدم، والوزن التشغيلي ضمن الخطة الأكاديمية.

-
- - تقييمات مفعلة - إجمالي الأوزان المفعلة % -
- + +
+
+
+

التقييمات:

+

إدارة أنواع التقييم وتوزيع الدرجات ضمن الخطة الأكاديمية للدورة .

-
-
-
توازن الأوزان
- -
ممتاز — الأوزان المفعلة تساوي 100% وجاهزة للاستخدام.
- 100): ?> -
مجموع الأوزان المفعلة يتجاوز 100% بمقدار %. يحتاج إلى إعادة موازنة.
- -
المجموع الحالي للأوزان المفعلة هو %، والمتبقي % لاستكمال الخطة.
- -
-
التقييمات المفعلة
-
متوسط الدرجة القصوى
-
فريق التدريس عضو
-
-
+
+ + +
- - -
-
-
-
-
-
الدورة الموسمية الحالية
-
كل عناصر خطة التقييم في هذه الصفحة تخص الدورة . يمكنك مراجعة مواسمك السابقة أو الحالية مباشرة من هنا.
-
- -
-
-
اسم الدورة
-
الفترة
-
عدد الدورات دورة للمركز
-
التقييمات كلها
-
التقييمات المفعلة
-
الأوزان المفعلة%
-
- - -
هذه الدورة مؤرشفة، لذلك تبقى خطة التقييم للقراءة فقط حالياً.
- -
-
- -
- - -
-
+ +
+ + +
هذه الدورة مؤرشفة. التقييمات معروضة للقراءة فقط.
-
-
-
- diff --git a/center_subjects.php b/center_subjects.php index 9f3190e..0e07f4e 100644 --- a/center_subjects.php +++ b/center_subjects.php @@ -67,7 +67,6 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['action']) && $_POST[' $errors['form'] = 'تعذر حفظ البيانات. يرجى المحاولة لاحقاً.'; } } -} render_page_start('إدارة المواد الدراسية: ' . (string) $application['center_name'], 'profile', 'تحديد المواد الدراسية التي يتم تدريسها في المركز.'); render_flash($flash); diff --git a/db/migrations/20260416_alter_assessments_subject.sql b/db/migrations/20260416_alter_assessments_subject.sql new file mode 100644 index 0000000..9b696c3 --- /dev/null +++ b/db/migrations/20260416_alter_assessments_subject.sql @@ -0,0 +1,18 @@ +-- Safely add the column. +SET @dbname = DATABASE(); +SET @tablename = 'school_assessment_types'; +SET @columnname = 'subject_id'; +SET @preparedStatement = (SELECT IF( + ( + SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS + WHERE + (table_name = @tablename) + AND (table_schema = @dbname) + AND (column_name = @columnname) + ) > 0, + "SELECT 1", + CONCAT("ALTER TABLE ", @tablename, " ADD ", @columnname, " INT UNSIGNED NULL AFTER cycle_id;") +)); +PREPARE alterIfNotExists FROM @preparedStatement; +EXECUTE alterIfNotExists; +DEALLOCATE PREPARE alterIfNotExists; diff --git a/includes/app.php b/includes/app.php index bf88acd..8cf0e35 100644 --- a/includes/app.php +++ b/includes/app.php @@ -995,6 +995,7 @@ function assessment_active_badge(int $isActive): string function validate_assessment_input(array $input): array { $data = assessment_defaults(); + $data['subject_id'] = clean_text((string) ($input['subject_id'] ?? ''), 20); $data['title'] = clean_text((string) ($input['title'] ?? ''), 150); $data['category'] = clean_text((string) ($input['category'] ?? ''), 80); $data['scale_type'] = clean_text((string) ($input['scale_type'] ?? ''), 40); diff --git a/includes/cycles.php b/includes/cycles.php index 0acfa7a..dbfe051 100644 --- a/includes/cycles.php +++ b/includes/cycles.php @@ -406,12 +406,12 @@ function copy_school_cycle_rollover(PDO $pdo, int $centerApplicationId, int $sou if (!empty($rollover['copy_assessments'])) { $stmt = $pdo->prepare( 'INSERT INTO school_assessment_types ( - center_application_id, cycle_id, title, category, scale_type, + center_application_id, cycle_id, subject_id, title, category, scale_type, max_score, weight_percentage, is_active, notes, created_at, updated_at ) SELECT - center_application_id, :target_cycle_id, title, category, scale_type, + center_application_id, :target_cycle_id, subject_id, title, category, scale_type, max_score, weight_percentage, is_active, notes, NOW(), NOW() FROM school_assessment_types @@ -833,11 +833,11 @@ function create_assessment_type_in_cycle(int $centerApplicationId, int $cycleId, $pdo = db_connection(); $stmt = $pdo->prepare( 'INSERT INTO school_assessment_types ( - center_application_id, cycle_id, title, category, scale_type, + center_application_id, cycle_id, subject_id, title, category, scale_type, max_score, weight_percentage, is_active, notes, created_at, updated_at ) VALUES ( - :center_application_id, :cycle_id, :title, :category, :scale_type, + :center_application_id, :cycle_id, :subject_id, :title, :category, :scale_type, :max_score, :weight_percentage, :is_active, :notes, NOW(), NOW() )' @@ -845,6 +845,7 @@ function create_assessment_type_in_cycle(int $centerApplicationId, int $cycleId, $stmt->execute([ ':center_application_id' => $centerApplicationId, ':cycle_id' => $cycleId, + ':subject_id' => !empty($data['subject_id']) ? (int) $data['subject_id'] : null, ':title' => $data['title'], ':category' => $data['category'], ':scale_type' => $data['scale_type'], @@ -856,7 +857,7 @@ function create_assessment_type_in_cycle(int $centerApplicationId, int $cycleId, return (int) $pdo->lastInsertId(); } -function list_school_assessments_by_cycle(int $centerApplicationId, int $cycleId, string $search = '', int $limit = 0, int $offset = 0): array +function list_school_assessments_by_cycle(int $centerApplicationId, int $cycleId, array $filters = [], int $limit = 0, int $offset = 0): array { $pdo = db_connection(); $query = 'SELECT * FROM school_assessment_types WHERE center_application_id = :center_application_id AND cycle_id = :cycle_id'; @@ -865,12 +866,25 @@ function list_school_assessments_by_cycle(int $centerApplicationId, int $cycleId ':cycle_id' => $cycleId, ]; + $search = $filters['search'] ?? ''; if ($search !== '') { - $query .= ' AND (assessment_name LIKE :search1 OR assessment_category LIKE :search2)'; + $query .= ' AND (title LIKE :search1 OR category LIKE :search2)'; $params[':search1'] = "%$search%"; $params[':search2'] = "%$search%"; } + $subject_id = $filters['subject_id'] ?? ''; + if ($subject_id !== '') { + $query .= ' AND subject_id = :subject_id'; + $params[':subject_id'] = (int) $subject_id; + } + + $category = $filters['category'] ?? ''; + if ($category !== '') { + $query .= ' AND category = :category'; + $params[':category'] = $category; + } + $query .= ' ORDER BY is_active DESC, created_at DESC, id DESC'; if ($limit > 0) { @@ -882,7 +896,7 @@ function list_school_assessments_by_cycle(int $centerApplicationId, int $cycleId return $stmt->fetchAll(); } -function count_school_assessments_by_cycle(int $centerApplicationId, int $cycleId, string $search = ''): int +function count_school_assessments_by_cycle(int $centerApplicationId, int $cycleId, array $filters = []): int { $pdo = db_connection(); $query = 'SELECT COUNT(*) FROM school_assessment_types WHERE center_application_id = :center_application_id AND cycle_id = :cycle_id'; @@ -891,12 +905,25 @@ function count_school_assessments_by_cycle(int $centerApplicationId, int $cycleI ':cycle_id' => $cycleId, ]; + $search = $filters['search'] ?? ''; if ($search !== '') { - $query .= ' AND (assessment_name LIKE :search1 OR assessment_category LIKE :search2)'; + $query .= ' AND (title LIKE :search1 OR category LIKE :search2)'; $params[':search1'] = "%$search%"; $params[':search2'] = "%$search%"; } + $subject_id = $filters['subject_id'] ?? ''; + if ($subject_id !== '') { + $query .= ' AND subject_id = :subject_id'; + $params[':subject_id'] = (int) $subject_id; + } + + $category = $filters['category'] ?? ''; + if ($category !== '') { + $query .= ' AND category = :category'; + $params[':category'] = $category; + } + $stmt = $pdo->prepare($query); $stmt->execute($params); return (int)$stmt->fetchColumn(); @@ -1126,3 +1153,33 @@ function update_student_in_cycle(int $centerApplicationId, int $cycleId, int $st $stmt->bindValue(':notes', $data['notes'] !== '' ? $data['notes'] : null, $data['notes'] !== '' ? PDO::PARAM_STR : PDO::PARAM_NULL); $stmt->execute(); } + +function update_assessment_type_in_cycle(int $centerApplicationId, int $cycleId, int $assessmentId, array $data): bool +{ + $pdo = db_connection(); + $stmt = $pdo->prepare( + 'UPDATE school_assessment_types SET + subject_id = :subject_id, + title = :title, + category = :category, + scale_type = :scale_type, + max_score = :max_score, + weight_percentage = :weight_percentage, + is_active = :is_active, + notes = :notes + WHERE id = :id AND center_application_id = :center_application_id AND cycle_id = :cycle_id' + ); + return $stmt->execute([ + ':id' => $assessmentId, + ':center_application_id' => $centerApplicationId, + ':cycle_id' => $cycleId, + ':subject_id' => !empty($data['subject_id']) ? (int) $data['subject_id'] : null, + ':title' => $data['title'], + ':category' => $data['category'], + ':scale_type' => $data['scale_type'], + ':max_score' => (float) $data['max_score'], + ':weight_percentage' => (float) $data['weight_percentage'], + ':is_active' => (int) $data['is_active'], + ':notes' => $data['notes'] !== '' ? $data['notes'] : null, + ]); +}