521 lines
28 KiB
PHP
521 lines
28 KiB
PHP
<?php
|
|
declare(strict_types=1);
|
|
require_once __DIR__ . '/includes/app.php';
|
|
|
|
function center_score_display(?float $value): string
|
|
{
|
|
if ($value === null) {
|
|
return '';
|
|
}
|
|
|
|
return rtrim(rtrim(number_format($value, 2, '.', ''), '0'), '.');
|
|
}
|
|
|
|
$flash = consume_flash();
|
|
$applicationId = filter_input(INPUT_GET, 'id', FILTER_VALIDATE_INT) ?: 0;
|
|
$requestedCycleId = filter_input(INPUT_GET, 'cycle', FILTER_VALIDATE_INT) ?: 0;
|
|
$requestedAssessmentId = filter_input(INPUT_GET, 'assessment_id', FILTER_VALIDATE_INT) ?: 0;
|
|
$application = $applicationId > 0 ? get_application($applicationId) : null;
|
|
$isApprovedCenter = $application && (string) ($application['status'] ?? '') === 'approved';
|
|
$errors = [];
|
|
$cycleContext = ['cycles' => [], 'selected' => null, 'active' => null, 'read_only' => false];
|
|
$selectedCycle = null;
|
|
$selectedCycleId = 0;
|
|
$isCycleReadOnly = false;
|
|
$cycleLabel = 'لا توجد دورة بعد';
|
|
$values = [
|
|
'assessment_type_id' => '',
|
|
'assessed_on' => date('Y-m-d'),
|
|
'status' => 'completed',
|
|
'assessment_max_score' => 0.0,
|
|
'has_criteria' => false,
|
|
'criteria' => [],
|
|
'criteria_scores' => [],
|
|
'score' => null,
|
|
'score_raw' => '',
|
|
'notes' => '',
|
|
'should_save' => false,
|
|
];
|
|
|
|
$buildCenterAssessmentsUrl = static function (int $targetApplicationId = 0, int $targetCycleId = 0): string {
|
|
$params = [];
|
|
if ($targetApplicationId > 0) {
|
|
$params['id'] = $targetApplicationId;
|
|
}
|
|
if ($targetCycleId > 0) {
|
|
$params['cycle'] = $targetCycleId;
|
|
}
|
|
return 'center_assessments.php' . ($params !== [] ? '?' . http_build_query($params) : '');
|
|
};
|
|
|
|
$buildCenterAssessmentCriteriaUrl = static function (int $targetApplicationId = 0, int $targetCycleId = 0, int $targetAssessmentId = 0): string {
|
|
$params = [];
|
|
if ($targetApplicationId > 0) {
|
|
$params['id'] = $targetApplicationId;
|
|
}
|
|
if ($targetCycleId > 0) {
|
|
$params['cycle'] = $targetCycleId;
|
|
}
|
|
if ($targetAssessmentId > 0) {
|
|
$params['assessment_id'] = $targetAssessmentId;
|
|
}
|
|
return 'center_assessment_criteria.php' . ($params !== [] ? '?' . http_build_query($params) : '');
|
|
};
|
|
|
|
$buildCenterAssessmentScoreUrl = static function (int $targetApplicationId = 0, int $targetCycleId = 0, int $targetAssessmentId = 0): string {
|
|
$params = [];
|
|
if ($targetApplicationId > 0) {
|
|
$params['id'] = $targetApplicationId;
|
|
}
|
|
if ($targetCycleId > 0) {
|
|
$params['cycle'] = $targetCycleId;
|
|
}
|
|
if ($targetAssessmentId > 0) {
|
|
$params['assessment_id'] = $targetAssessmentId;
|
|
}
|
|
return 'center_assessment_score_sheet.php' . ($params !== [] ? '?' . http_build_query($params) : '');
|
|
};
|
|
|
|
$buildCenterAssessmentReportUrl = static function (int $targetApplicationId = 0, int $targetCycleId = 0): string {
|
|
$params = [];
|
|
if ($targetApplicationId > 0) {
|
|
$params['id'] = $targetApplicationId;
|
|
}
|
|
if ($targetCycleId > 0) {
|
|
$params['cycle'] = $targetCycleId;
|
|
}
|
|
return 'center_assessment_report.php' . ($params !== [] ? '?' . http_build_query($params) : '');
|
|
};
|
|
|
|
$buildCenterAssessmentPrintUrl = static function (int $targetApplicationId = 0, int $targetCycleId = 0, int $targetAssessmentId = 0): string {
|
|
$params = [];
|
|
if ($targetApplicationId > 0) {
|
|
$params['id'] = $targetApplicationId;
|
|
}
|
|
if ($targetCycleId > 0) {
|
|
$params['cycle'] = $targetCycleId;
|
|
}
|
|
if ($targetAssessmentId > 0) {
|
|
$params['assessment_id'] = $targetAssessmentId;
|
|
}
|
|
return 'center_assessment_print_sheet.php' . ($params !== [] ? '?' . http_build_query($params) : '');
|
|
};
|
|
|
|
if ($applicationId <= 0 || $requestedCycleId <= 0) {
|
|
set_flash('error', 'اختر المركز والدورة أولاً ثم افتح صفحة رصد التقييم من شاشة تقييم المراكز.');
|
|
header('Location: ' . $buildCenterAssessmentsUrl($applicationId, $requestedCycleId));
|
|
exit;
|
|
}
|
|
|
|
if ($isApprovedCenter) {
|
|
$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'] ?? false);
|
|
$cycleLabel = $selectedCycle ? (string) ($selectedCycle['cycle_name'] ?? $cycleLabel) : $cycleLabel;
|
|
}
|
|
|
|
$assessmentOptions = $isApprovedCenter && $selectedCycleId > 0
|
|
? center_assessment_type_options_by_cycle((int) $application['id'], $selectedCycleId, false)
|
|
: [];
|
|
$selectedAssessmentId = $requestedAssessmentId;
|
|
if ($selectedAssessmentId <= 0 && $assessmentOptions !== []) {
|
|
$keys = array_keys($assessmentOptions);
|
|
$selectedAssessmentId = (int) ($keys[0] ?? 0);
|
|
}
|
|
if ($selectedAssessmentId > 0) {
|
|
$values['assessment_type_id'] = (string) $selectedAssessmentId;
|
|
}
|
|
|
|
if ($_SERVER['REQUEST_METHOD'] === 'POST' && $application) {
|
|
if (!$isApprovedCenter) {
|
|
$errors['form'] = 'لا يمكن رصد تقييم المركز قبل اعتماد المركز.';
|
|
} elseif ($selectedCycleId <= 0) {
|
|
$errors['form'] = 'يرجى إنشاء دورة موسمية أولاً من صفحة المركز.';
|
|
} elseif ($isCycleReadOnly) {
|
|
$errors['form'] = 'هذه الدورة مؤرشفة للقراءة فقط. اختر دورة نشطة لإدخال تقييم جديد.';
|
|
} else {
|
|
[$values, $errors, $selectedAssessmentMeta] = validate_center_assessment_score_input((int) $application['id'], $selectedCycleId, $_POST);
|
|
$selectedAssessmentId = (int) ($values['assessment_type_id'] ?? 0);
|
|
if ($errors === []) {
|
|
try {
|
|
save_center_assessment_score_in_cycle((int) $application['id'], $selectedCycleId, $values);
|
|
set_flash('success', 'تم حفظ رصد تقييم المركز بنجاح.');
|
|
$returnUrl = filter_input(INPUT_GET, 'return_url', FILTER_SANITIZE_URL); header('Location: ' . ($returnUrl ?: $buildCenterAssessmentScoreUrl((int) $application['id'], $selectedCycleId, $selectedAssessmentId)));
|
|
exit;
|
|
} catch (Throwable $exception) {
|
|
$errors['form'] = 'تعذر حفظ الرصد حالياً. يرجى المحاولة مرة أخرى.';
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
$selectedAssessment = $selectedAssessmentId > 0 ? ($assessmentOptions[$selectedAssessmentId] ?? null) : null;
|
|
$criteria = $isApprovedCenter && $selectedCycleId > 0 && $selectedAssessmentId > 0
|
|
? list_center_assessment_criteria_by_assessment((int) $application['id'], $selectedCycleId, $selectedAssessmentId, true)
|
|
: [];
|
|
$hasCriteria = $criteria !== [];
|
|
if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
|
|
$values['has_criteria'] = $hasCriteria;
|
|
$values['criteria'] = $criteria;
|
|
if ($hasCriteria) {
|
|
$values['assessment_max_score'] = round(array_reduce($criteria, static function (float $carry, array $criterion): float {
|
|
return $carry + (float) ($criterion['max_score'] ?? 0);
|
|
}, 0.0), 2);
|
|
} elseif ($selectedAssessment) {
|
|
$values['assessment_max_score'] = (float) ($selectedAssessment['max_score'] ?? 0);
|
|
}
|
|
}
|
|
|
|
$existingBundle = $isApprovedCenter && $selectedCycleId > 0 && $selectedAssessmentId > 0
|
|
? center_assessment_score_bundle_by_assessment((int) $application['id'], $selectedCycleId, $selectedAssessmentId)
|
|
: ['score' => null, 'criteria_scores' => []];
|
|
$existingScore = $existingBundle['score'] ?? null;
|
|
$existingCriteriaScores = $existingBundle['criteria_scores'] ?? [];
|
|
|
|
if ($_SERVER['REQUEST_METHOD'] !== 'POST' && is_array($existingScore) && $existingScore !== []) {
|
|
if (!empty($existingScore['assessed_on'])) {
|
|
$values['assessed_on'] = (string) $existingScore['assessed_on'];
|
|
}
|
|
$values['status'] = center_assessment_normalize_status((string) ($existingScore['status'] ?? 'completed'));
|
|
$values['notes'] = (string) ($existingScore['notes'] ?? '');
|
|
|
|
if ($hasCriteria) {
|
|
foreach ($criteria as $criterion) {
|
|
$criterionId = (int) ($criterion['id'] ?? 0);
|
|
if ($criterionId <= 0) {
|
|
continue;
|
|
}
|
|
$existingCriterion = $existingCriteriaScores[$criterionId] ?? [];
|
|
$values['criteria_scores'][$criterionId] = [
|
|
'criterion_id' => $criterionId,
|
|
'score' => isset($existingCriterion['score']) ? (float) $existingCriterion['score'] : null,
|
|
'score_raw' => isset($existingCriterion['score']) && $existingCriterion['score'] !== null
|
|
? center_score_display((float) $existingCriterion['score'])
|
|
: '',
|
|
'max_score' => (float) ($criterion['max_score'] ?? 0),
|
|
];
|
|
}
|
|
} else {
|
|
$values['score'] = isset($existingScore['score']) ? (float) $existingScore['score'] : null;
|
|
$values['score_raw'] = isset($existingScore['score']) && $existingScore['score'] !== null
|
|
? center_score_display((float) $existingScore['score'])
|
|
: '';
|
|
}
|
|
}
|
|
|
|
$criteriaMetrics = $isApprovedCenter && $selectedCycleId > 0 && $selectedAssessmentId > 0
|
|
? center_assessment_criteria_metrics((int) $application['id'], $selectedCycleId, $selectedAssessmentId)
|
|
: ['total' => 0, 'active' => 0, 'active_max_score' => 0.0];
|
|
$scoreMetrics = $isApprovedCenter && $selectedCycleId > 0 && $selectedAssessmentId > 0
|
|
? center_assessment_score_metrics_by_cycle((int) $application['id'], $selectedCycleId, $selectedAssessmentId)
|
|
: ['total' => 0, 'completed' => 0, 'pending' => 0, 'waived' => 0, 'average_score' => 0.0, 'latest_date' => ''];
|
|
|
|
$pageTitle = $application && $isApprovedCenter
|
|
? 'رصد تقييم المركز: ' . (string) ($application['center_name'] ?? '') . ($selectedAssessment ? ' — ' . (string) ($selectedAssessment['title'] ?? '') : '')
|
|
: 'رصد تقييم المركز';
|
|
$pageDescription = 'إدخال درجة تقييم المركز نفسه داخل الدورة المختارة، مع دعم البنود التفصيلية والتقرير النهائي.';
|
|
|
|
if (!$application && $applicationId > 0) {
|
|
http_response_code(404);
|
|
}
|
|
|
|
$assessmentsUrl = $application ? $buildCenterAssessmentsUrl((int) $application['id'], $selectedCycleId) : 'center_assessments.php';
|
|
$criteriaUrl = $application && $selectedAssessmentId > 0 ? $buildCenterAssessmentCriteriaUrl((int) $application['id'], $selectedCycleId, $selectedAssessmentId) : 'center_assessment_criteria.php';
|
|
$reportUrl = $application ? $buildCenterAssessmentReportUrl((int) $application['id'], $selectedCycleId) : 'center_assessment_report.php';
|
|
$maxScoreLabel = center_score_display((float) ($values['assessment_max_score'] ?? 0));
|
|
$existingStatus = is_array($existingScore) ? center_assessment_normalize_status((string) ($existingScore['status'] ?? 'pending')) : '';
|
|
$existingPercentage = (is_array($existingScore) && isset($existingScore['score']) && $existingScore['score'] !== null && (float) ($existingScore['max_score'] ?? 0) > 0)
|
|
? round(((float) $existingScore['score'] / (float) $existingScore['max_score']) * 100, 2)
|
|
: null;
|
|
|
|
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="admin-layout row g-4 align-items-start">
|
|
<div class="col-lg-3 layout-sidebar-column">
|
|
<?php require __DIR__ . '/includes/sidebar.php'; ?>
|
|
</div>
|
|
<div class="col-lg-9 layout-content-column">
|
|
<?php if (!$application): ?>
|
|
<div class="app-card text-center py-5">
|
|
<div class="empty-title mb-2">المركز غير موجود</div>
|
|
<p class="text-muted mb-3">تحقق من الرابط أو ارجع إلى قائمة المراكز المعتمدة.</p>
|
|
<a class="btn btn-primary" href="applications.php?status=approved">المراكز المعتمدة</a>
|
|
</div>
|
|
<?php elseif (!$isApprovedCenter): ?>
|
|
<div class="app-card text-center py-5">
|
|
<div class="empty-title mb-2">الرصد يُفتح بعد الاعتماد</div>
|
|
<p class="text-muted mb-3">اعتمد المركز أولاً حتى يظهر رصد تقييمه الإشرافي.</p>
|
|
<a class="btn btn-outline-secondary" href="application_detail.php?id=<?= e((string) $application['id']) ?>">ملف الاعتماد</a>
|
|
</div>
|
|
<?php else: ?>
|
|
<div class="page-banner approved-hero mb-4">
|
|
<div class="row g-4 align-items-center">
|
|
<div class="col-lg-8">
|
|
<span class="approved-kicker mb-3">رصد المركز</span>
|
|
<h1 class="page-title mb-3">إدخال تقييم إشرافي للمركز</h1>
|
|
<p class="page-copy mb-3">اختر التقييم، ثم أدخل النتيجة مباشرة أو عبر البنود التفصيلية داخل دورة <strong><?= e($cycleLabel) ?></strong>.</p>
|
|
<div class="hero-meta">
|
|
<span><?= e((string) count($assessmentOptions)) ?> تقييمات متاحة</span>
|
|
<span><?= e($cycleLabel) ?></span>
|
|
<?php if ($selectedAssessment): ?><span><?= e((string) ($selectedAssessment['title'] ?? '')) ?></span><?php endif; ?>
|
|
</div>
|
|
</div>
|
|
<div class="col-lg-4">
|
|
<div class="app-card h-100">
|
|
<div class="section-title mb-2">تنقّل سريع</div>
|
|
<div class="cta-stack">
|
|
<?php if (!empty($_GET['return_url'])): ?>
|
|
<a class="btn btn-outline-primary" href="<?= e($_GET['return_url']) ?>">العودة للقائمة</a>
|
|
<?php endif; ?>
|
|
<a class="btn btn-outline-secondary" href="<?= e($assessmentsUrl) ?>">كل التقييمات</a>
|
|
<a class="btn btn-outline-secondary" href="<?= e($reportUrl) ?>">تقرير الدورة</a>
|
|
<?php if ($selectedAssessment): ?>
|
|
<a class="btn btn-outline-secondary" href="<?= e($buildCenterAssessmentPrintUrl((int) $application['id'], $selectedCycleId, (int) ($selectedAssessment['id'] ?? 0))) ?>" target="_blank" rel="noopener">طباعة النموذج الرسمي</a>
|
|
<a class="btn btn-outline-secondary" href="<?= e($criteriaUrl) ?>">إدارة البنود</a>
|
|
<?php endif; ?>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<?php if ($selectedCycleId <= 0): ?>
|
|
<div class="app-card">
|
|
<div class="alert alert-warning mb-0">لا توجد دورة متاحة لهذا المركز بعد. أنشئ دورة أولاً من صفحة المركز.</div>
|
|
</div>
|
|
<?php elseif ($assessmentOptions === []): ?>
|
|
<div class="app-card text-center py-5">
|
|
<div class="empty-title mb-2">لا توجد تقييمات مراكز جاهزة للرصد</div>
|
|
<p class="text-muted mb-3">أضف نوع تقييم أولاً من صفحة تقييم المراكز ثم ارجع هنا لإدخال الدرجة.</p>
|
|
<a class="btn btn-primary" href="<?= e($assessmentsUrl) ?>">إضافة تقييم</a>
|
|
</div>
|
|
<?php else: ?>
|
|
<div class="app-card mb-4">
|
|
<form method="get" class="row g-2 align-items-center">
|
|
<input type="hidden" name="id" value="<?= e((string) $application['id']) ?>">
|
|
<input type="hidden" name="cycle" value="<?= e((string) $selectedCycleId) ?>">
|
|
<?php if (!empty($_GET['return_url'])): ?><input type="hidden" name="return_url" value="<?= e($_GET['return_url']) ?>"><?php endif; ?>
|
|
<div class="col-md-8">
|
|
<label class="form-label" for="assessment_id">التقييم</label>
|
|
<select class="form-select" name="assessment_id" id="assessment_id" onchange="this.form.submit()">
|
|
<?php foreach ($assessmentOptions as $assessmentId => $assessment): ?>
|
|
<option value="<?= e((string) $assessmentId) ?>" <?= $selectedAssessmentId === (int) $assessmentId ? 'selected' : '' ?>><?= e((string) ($assessment['label'] ?? '')) ?></option>
|
|
<?php endforeach; ?>
|
|
</select>
|
|
</div>
|
|
<div class="col-md-4 d-grid">
|
|
<button type="submit" class="btn btn-primary">فتح الرصد</button>
|
|
</div>
|
|
</form>
|
|
</div>
|
|
|
|
<?php if ($selectedAssessment): ?>
|
|
<div class="row g-3 mb-4">
|
|
<div class="col-md-3"><div class="app-card h-100"><div class="section-title mb-2">الدرجة النهائية</div><div class="display-6 mb-1"><?= e($maxScoreLabel !== '' ? $maxScoreLabel : '0') ?></div><div class="section-subtle">المجموع المعتمد</div></div></div>
|
|
<div class="col-md-3"><div class="app-card h-100"><div class="section-title mb-2">البنود النشطة</div><div class="display-6 mb-1"><?= e((string) ($criteriaMetrics['active'] ?? 0)) ?></div><div class="section-subtle">من أصل <?= e((string) ($criteriaMetrics['total'] ?? 0)) ?></div></div></div>
|
|
<div class="col-md-3"><div class="app-card h-100"><div class="section-title mb-2">حالة الرصد</div><div class="display-6 mb-1" style="font-size:1.05rem;"><?= $existingStatus !== '' ? center_assessment_status_badge($existingStatus) : '<span class="text-muted">غير مرصود</span>' ?></div><div class="section-subtle">آخر حفظ <?= e($scoreMetrics['latest_date'] !== '' ? (string) $scoreMetrics['latest_date'] : '—') ?></div></div></div>
|
|
<div class="col-md-3"><div class="app-card h-100"><div class="section-title mb-2">آخر نسبة</div><div class="display-6 mb-1"><?= e($existingPercentage !== null ? center_score_display((float) $existingPercentage) . '%' : '—') ?></div><div class="section-subtle">للتقييم الحالي</div></div></div>
|
|
</div>
|
|
<?php endif; ?>
|
|
|
|
<?php if (!empty($errors['form'])): ?>
|
|
<div class="alert alert-danger mb-4"><?= e($errors['form']) ?></div>
|
|
<?php endif; ?>
|
|
<?php if ($isCycleReadOnly): ?>
|
|
<div class="alert alert-warning mb-4">هذه الدورة مؤرشفة، لذلك الصفحة معروضة للقراءة فقط.</div>
|
|
<?php endif; ?>
|
|
|
|
<?php if ($selectedAssessment): ?>
|
|
<div class="app-card">
|
|
<form method="post" novalidate>
|
|
<input type="hidden" name="assessment_type_id" value="<?= e((string) $selectedAssessmentId) ?>">
|
|
<div class="row g-3 align-items-end mb-4">
|
|
<div class="col-md-4">
|
|
<label class="form-label" for="assessed_on">تاريخ الرصد</label>
|
|
<input type="date" class="form-control <?= isset($errors['assessed_on']) ? 'is-invalid' : '' ?>" id="assessed_on" name="assessed_on" value="<?= e((string) $values['assessed_on']) ?>" <?= $isCycleReadOnly ? 'disabled' : '' ?>>
|
|
<?php if (isset($errors['assessed_on'])): ?><div class="invalid-feedback"><?= e($errors['assessed_on']) ?></div><?php endif; ?>
|
|
</div>
|
|
<div class="col-md-4">
|
|
<label class="form-label" for="status">الحالة</label>
|
|
<select class="form-select" id="status" name="status" data-center-status <?= $isCycleReadOnly ? 'disabled' : '' ?>>
|
|
<?php foreach (center_assessment_status_map() as $statusKey => $statusMeta): ?>
|
|
<option value="<?= e($statusKey) ?>" <?= (string) $values['status'] === (string) $statusKey ? 'selected' : '' ?>><?= e((string) $statusMeta['label']) ?></option>
|
|
<?php endforeach; ?>
|
|
</select>
|
|
</div>
|
|
<div class="col-md-4">
|
|
<div class="school-data-item h-100">
|
|
<strong>الوزن</strong>
|
|
<span><?= e(center_score_display((float) ($selectedAssessment['weight_percentage'] ?? 0)) ?: '0') ?>٪</span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<?php if (!empty($errors['score'])): ?>
|
|
<div class="alert alert-danger mb-4"><?= e($errors['score']) ?></div>
|
|
<?php endif; ?>
|
|
|
|
<?php if ($hasCriteria): ?>
|
|
<div class="table-responsive mb-4">
|
|
<table class="table app-table align-middle mb-0">
|
|
<thead>
|
|
<tr>
|
|
<th>البند</th>
|
|
<th>الدرجة القصوى</th>
|
|
<th>الدرجة المرصودة</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
<?php foreach ($criteria as $criterion): ?>
|
|
<?php
|
|
$criterionId = (int) ($criterion['id'] ?? 0);
|
|
$criterionValue = (string) (($values['criteria_scores'][$criterionId]['score_raw'] ?? ''));
|
|
?>
|
|
<tr>
|
|
<td>
|
|
<div class="fw-semibold"><?= e((string) ($criterion['title'] ?? '')) ?></div>
|
|
<?php if (!empty($criterion['notes'])): ?><div class="text-muted small"><?= e((string) ($criterion['notes'] ?? '')) ?></div><?php endif; ?>
|
|
</td>
|
|
<td><?= e(center_score_display((float) ($criterion['max_score'] ?? 0))) ?></td>
|
|
<td>
|
|
<input
|
|
class="form-control"
|
|
type="number"
|
|
step="0.01"
|
|
min="0"
|
|
max="<?= e(center_score_display((float) ($criterion['max_score'] ?? 0))) ?>"
|
|
name="criteria[<?= e((string) $criterionId) ?>]"
|
|
value="<?= e($criterionValue) ?>"
|
|
placeholder="<?= e(center_score_display((float) ($criterion['max_score'] ?? 0))) ?>"
|
|
data-center-criterion
|
|
<?= $isCycleReadOnly ? 'disabled' : '' ?>
|
|
>
|
|
</td>
|
|
</tr>
|
|
<?php endforeach; ?>
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
<div class="row g-3 mb-4">
|
|
<div class="col-md-4"><div class="school-data-item"><strong>المجموع الحالي</strong><span data-center-total><?= e($values['score_raw'] !== '' ? center_score_display((float) $values['score_raw']) : '—') ?></span></div></div>
|
|
<div class="col-md-4"><div class="school-data-item"><strong>المجموع الكلي</strong><span><?= e($maxScoreLabel !== '' ? $maxScoreLabel : '0') ?></span></div></div>
|
|
<div class="col-md-4"><div class="school-data-item"><strong>نسبة الإنجاز</strong><span data-center-percentage><?= e(($values['score_raw'] !== '' && (float) ($values['assessment_max_score'] ?? 0) > 0) ? center_score_display(((float) $values['score_raw'] / (float) $values['assessment_max_score']) * 100) . '%' : '—') ?></span></div></div>
|
|
</div>
|
|
<?php else: ?>
|
|
<div class="mb-4">
|
|
<label class="form-label" for="score">الدرجة المرصودة</label>
|
|
<input class="form-control <?= isset($errors['score']) ? 'is-invalid' : '' ?>" type="number" step="0.01" min="0" max="<?= e($maxScoreLabel !== '' ? $maxScoreLabel : '0') ?>" id="score" name="score" value="<?= e((string) $values['score_raw']) ?>" placeholder="من <?= e($maxScoreLabel !== '' ? $maxScoreLabel : '0') ?>" <?= $isCycleReadOnly ? 'disabled' : '' ?>>
|
|
<?php if (isset($errors['score'])): ?><div class="invalid-feedback"><?= e($errors['score']) ?></div><?php endif; ?>
|
|
</div>
|
|
<?php endif; ?>
|
|
|
|
<div class="mb-4">
|
|
<label class="form-label" for="notes">ملاحظات</label>
|
|
<textarea class="form-control" id="notes" name="notes" rows="4" placeholder="اختياري" <?= $isCycleReadOnly ? 'disabled' : '' ?>><?= e((string) $values['notes']) ?></textarea>
|
|
</div>
|
|
|
|
<?php if (is_array($existingScore) && $existingScore !== []): ?>
|
|
<div class="alert alert-light border mb-4">
|
|
<strong>آخر حفظ:</strong>
|
|
<?= center_assessment_status_badge((string) ($existingScore['status'] ?? 'pending')) ?>
|
|
<span class="ms-2">بتاريخ <?= e((string) ($existingScore['assessed_on'] ?? '—')) ?></span>
|
|
<?php if ($existingPercentage !== null): ?><span class="ms-2">— <?= e(center_score_display((float) $existingPercentage)) ?>%</span><?php endif; ?>
|
|
</div>
|
|
<?php endif; ?>
|
|
|
|
<?php if (!$isCycleReadOnly): ?>
|
|
<div class="d-flex justify-content-between align-items-center pt-3 border-top mt-3 flex-wrap gap-2">
|
|
<div class="section-subtle">يمكنك تعديل نفس التقييم لاحقاً، وسيتم تحديث الدرجة والبنود الحالية بدلاً من إنشاء نسخة جديدة.</div>
|
|
<button class="btn btn-primary" type="submit">حفظ الرصد</button>
|
|
</div>
|
|
<?php endif; ?>
|
|
</form>
|
|
</div>
|
|
<?php endif; ?>
|
|
<?php endif; ?>
|
|
<?php endif; ?>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</section>
|
|
<?php if (!$isCycleReadOnly): ?>
|
|
<script>
|
|
document.addEventListener('DOMContentLoaded', () => {
|
|
const statusField = document.querySelector('[data-center-status]');
|
|
const criterionInputs = Array.from(document.querySelectorAll('[data-center-criterion]'));
|
|
const totalEl = document.querySelector('[data-center-total]');
|
|
const percentageEl = document.querySelector('[data-center-percentage]');
|
|
const directScore = document.getElementById('score');
|
|
const maxScore = <?= json_encode(center_score_display((float) ($values['assessment_max_score'] ?? 0))) ?>;
|
|
const maxScoreValue = <?= (float) ($values['assessment_max_score'] ?? 0) ?>;
|
|
|
|
const parsedMax = <?= (float) ($values['assessment_max_score'] ?? 0) ?>;
|
|
|
|
const updateCriteriaState = () => {
|
|
if (!statusField || !criterionInputs.length) return;
|
|
const enabled = statusField.value === 'completed';
|
|
let total = 0;
|
|
let hasValue = false;
|
|
criterionInputs.forEach((input) => {
|
|
if (enabled) {
|
|
input.removeAttribute('disabled');
|
|
} else {
|
|
input.setAttribute('disabled', 'disabled');
|
|
input.value = '';
|
|
}
|
|
const raw = input.value.trim();
|
|
if (enabled && raw !== '' && !Number.isNaN(Number(raw))) {
|
|
total += Number(raw);
|
|
hasValue = true;
|
|
}
|
|
});
|
|
|
|
if (totalEl) {
|
|
totalEl.textContent = enabled && hasValue ? total.toFixed(2).replace(/\.00$/, '').replace(/(\.\d)0$/, '$1') : '—';
|
|
}
|
|
if (percentageEl) {
|
|
const maxValue = Number(percentageEl.getAttribute('data-max-score') || '0');
|
|
percentageEl.textContent = enabled && hasValue && maxValue > 0
|
|
? ((total / maxValue) * 100).toFixed(2).replace(/\.00$/, '').replace(/(\.\d)0$/, '$1') + '%'
|
|
: '—';
|
|
}
|
|
};
|
|
|
|
if (percentageEl) {
|
|
percentageEl.setAttribute('data-max-score', '<?= e((string) ((float) ($values['assessment_max_score'] ?? 0))) ?>');
|
|
}
|
|
|
|
const updateDirectScoreState = () => {
|
|
if (!statusField || !directScore) return;
|
|
const enabled = statusField.value === 'completed';
|
|
if (enabled) {
|
|
directScore.removeAttribute('disabled');
|
|
} else {
|
|
directScore.setAttribute('disabled', 'disabled');
|
|
directScore.value = '';
|
|
}
|
|
};
|
|
|
|
if (statusField) {
|
|
statusField.addEventListener('change', () => {
|
|
updateDirectScoreState();
|
|
updateCriteriaState();
|
|
});
|
|
}
|
|
|
|
criterionInputs.forEach((input) => {
|
|
input.addEventListener('input', updateCriteriaState);
|
|
input.addEventListener('change', updateCriteriaState);
|
|
});
|
|
|
|
updateDirectScoreState();
|
|
updateCriteriaState();
|
|
});
|
|
</script>
|
|
<?php endif; ?>
|
|
<?php render_page_end();
|