39669-vm/global_center_assessments.php
2026-04-17 14:22:06 +00:00

432 lines
26 KiB
PHP

<?php
declare(strict_types=1);
require_once __DIR__ . '/includes/app.php';
if (!is_super_admin()) {
http_response_code(403);
render_page_start('صلاحيات غير كافية', 'admin', 'هذه الصفحة مخصصة للمشرف العام لإدارة قوالب تقييم المراكز.');
?>
<section class="py-5 text-center">
<div class="container-xxl">
<h1 class="mb-3">عذراً</h1>
<p>هذه الصفحة مخصصة للمشرف العام فقط.</p>
<a href="admin.php" class="btn btn-primary mt-3">العودة إلى لوحة الإدارة</a>
</div>
</section>
<?php
render_page_end();
exit;
}
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$action = $_POST['action'] ?? 'create';
$assessmentId = filter_input(INPUT_POST, 'assessment_id', FILTER_VALIDATE_INT) ?: 0;
if ($action === 'delete') {
if ($assessmentId > 0 && delete_global_center_assessment_type($assessmentId)) {
set_flash('success', 'تم حذف قالب التقييم بنجاح.');
} else {
set_flash('error', 'حدث خطأ أثناء محاولة حذف قالب التقييم.');
}
header('Location: global_center_assessments.php');
exit;
} else {
[$values, $errors] = validate_assessment_input($_POST);
if ($errors === []) {
try {
if ($action === 'edit' && $assessmentId > 0) {
update_global_center_assessment_type($assessmentId, $values);
set_flash('success', 'تم تحديث قالب تقييم المراكز بنجاح.');
} else {
$assessmentId = create_global_center_assessment_type($values);
set_flash('success', 'تم إنشاء قالب تقييم جديد للمراكز. يمكنك الآن إضافة البنود الخاصة به.');
}
header('Location: global_center_assessments.php' . ($assessmentId > 0 && $action !== 'edit' ? '?highlight=' . $assessmentId : ''));
exit;
} catch (Throwable $exception) {
set_flash('error', 'تعذر حفظ قالب التقييم حالياً. يرجى المحاولة مرة أخرى.');
header('Location: global_center_assessments.php');
exit;
}
} else {
$errorMsg = implode(' ', $errors);
set_flash('error', $errorMsg);
header('Location: global_center_assessments.php');
exit;
}
}
}
$filters = [
'search' => clean_text($_GET['search'] ?? '', 255),
'category' => clean_text($_GET['category'] ?? '', 80),
];
$page = filter_input(INPUT_GET, 'page', FILTER_VALIDATE_INT) ?: 1;
$limit = 20;
$offset = ($page - 1) * $limit;
$assessments = list_global_center_assessments($filters, $limit, $offset);
$totalAssessments = count_global_center_assessments($filters);
$metrics = global_center_assessment_metrics();
$highlightAssessmentId = filter_input(INPUT_GET, 'highlight', FILTER_VALIDATE_INT) ?: 0;
$activeWeight = round((float) $metrics['active_weight'], 2);
$weightGap = round(100 - $activeWeight, 2);
$flash = consume_flash();
render_page_start(
'قوالب تقييم المراكز',
'admin',
'إدارة القوالب العامة لتقييم المراكز وبنودها حتى يتمكن المشرف العام أو المقيمون المكلّفون من استخدامها لاحقاً عند تقييم كل مركز.'
);
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">
<div class="page-banner mb-4">
<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-3">من هنا ينشئ المشرف العام التقييمات العامة للمراكز مرة واحدة، ثم تُستخدم لاحقاً من قبل المشرف العام أو أي مقيّم مكلّف لتقييم المراكز واحداً تلو الآخر.</p>
<div class="hero-meta">
<span>قوالب نشطة <?= e((string) $metrics['active']) ?></span>
<span>بنفس المنهجية السابقة للبنود والرصد</span>
<span>إجمالي الوزن النشط <?= e(rtrim(rtrim(number_format($activeWeight, 2, '.', ''), '0'), '.')) ?>٪</span>
</div>
<div class="cta-stack mt-4">
<button class="btn btn-primary" 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>
<a class="btn btn-outline-secondary" href="center_assessments.php">فتح تقييم المراكز الحالي</a>
</div>
</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(rtrim(rtrim(number_format($weightGap, 2, '.', ''), '0'), '.')) ?>٪</div>
<div class="mini-stat-copy mb-3">كلما اقتربت القوالب النشطة من 100٪ صار توزيع التقييم أوضح للمقيمين عند تقييم المراكز ميدانياً.</div>
</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) $metrics['total']) ?></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) $metrics['active']) ?></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(rtrim(rtrim(number_format((float) $metrics['average_max_score'], 2, '.', ''), '0'), '.')) ?></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) ($metrics['percentage'] + $metrics['points'] + $metrics['rubric'])) ?></div><div class="mini-stat-copy">نسبة: <?= e((string) $metrics['percentage']) ?> • نقاط: <?= e((string) $metrics['points']) ?> • Rubric: <?= e((string) $metrics['rubric']) ?></div></div></div>
</div>
<div class="app-card mb-4">
<div class="section-head mb-3">
<div class="d-flex justify-content-between align-items-center mb-0 flex-wrap gap-3">
<div>
<div class="section-title">إدارة قوالب التقييم</div>
<div class="section-copy">كل قالب هنا يمثل نموذج تقييم إشرافي يمكن لاحقاً استخدامه عند تقييم أي مركز.</div>
</div>
</div>
</div>
<?php render_search_bar($filters['search'], 'ابحث باسم القالب أو الفئة أو الملاحظات...', 'global_center_assessments.php', $_GET); ?>
<form method="get" class="row g-3 align-items-end mb-4">
<input type="hidden" name="search" value="<?= e($filters['search']) ?>">
<div class="col-md-8">
<label class="form-label" for="categoryFilter">الفئة</label>
<select class="form-select" id="categoryFilter" name="category">
<option value="">كل الفئات</option>
<?php foreach (assessment_category_options() as $categoryOption): ?>
<option value="<?= e($categoryOption) ?>" <?= $filters['category'] === $categoryOption ? 'selected' : '' ?>><?= e($categoryOption) ?></option>
<?php endforeach; ?>
</select>
</div>
<div class="col-md-4 d-grid">
<button class="btn btn-outline-secondary" type="submit">تطبيق الفلترة</button>
</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>
<th>الحالة</th>
<th>الإجراءات</th>
</tr>
</thead>
<tbody>
<?php if ($assessments === []): ?>
<tr>
<td colspan="7" class="text-center py-4 text-muted">لا توجد قوالب تقييم مسجلة أو لم يتم العثور على نتائج.</td>
</tr>
<?php else: ?>
<?php foreach ($assessments as $assessment): ?>
<?php $isHighlighted = (int) ($assessment['id'] ?? 0) === $highlightAssessmentId; ?>
<tr<?= $isHighlighted ? ' class="table-warning"' : '' ?>>
<td>
<div class="fw-semibold"><?= e((string) ($assessment['title'] ?? '')) ?></div>
<div class="text-muted small" style="max-width: 250px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis;" title="<?= e((string) ($assessment['notes'] ?? '')) ?>"><?= e((string) ($assessment['notes'] ?? 'بدون ملاحظات إضافية')) ?></div>
</td>
<td><?= e((string) ($assessment['category'] ?? '')) ?></td>
<td><?= assessment_scale_type_badge((string) ($assessment['scale_type'] ?? '')) ?></td>
<td>
<div class="fw-semibold"><?= e(rtrim(rtrim(number_format((float) ($assessment['weight_percentage'] ?? 0), 2, '.', ''), '0'), '.')) ?>٪</div>
<div class="text-muted small">الدرجة: <?= e(rtrim(rtrim(number_format((float) ($assessment['max_score'] ?? 0), 2, '.', ''), '0'), '.')) ?></div>
</td>
<td>
<div class="fw-semibold"><?= e((string) ((int) ($assessment['criteria_count'] ?? 0))) ?></div>
<div class="text-muted small">مجموع النشط: <?= e(rtrim(rtrim(number_format((float) ($assessment['criteria_total_max_score'] ?? 0), 2, '.', ''), '0'), '.')) ?></div>
</td>
<td><?= assessment_active_badge((int) ($assessment['is_active'] ?? 0)) ?></td>
<td>
<div class="d-flex gap-2">
<button class="btn btn-sm btn-outline-secondary" title="تعديل"
data-bs-toggle="modal" data-bs-target="#editModal"
data-id="<?= e((string) ($assessment['id'] ?? 0)) ?>"
data-title="<?= e((string) ($assessment['title'] ?? '')) ?>"
data-category="<?= e((string) ($assessment['category'] ?? '')) ?>"
data-scale_type="<?= e((string) ($assessment['scale_type'] ?? '')) ?>"
data-max_score="<?= e(rtrim(rtrim(number_format((float) ($assessment['max_score'] ?? 0), 2, '.', ''), '0'), '.')) ?>"
data-weight_percentage="<?= e(rtrim(rtrim(number_format((float) ($assessment['weight_percentage'] ?? 0), 2, '.', ''), '0'), '.')) ?>"
data-is_active="<?= (int) ($assessment['is_active'] ?? 0) === 1 ? '1' : '0' ?>"
data-notes="<?= e((string) ($assessment['notes'] ?? '')) ?>"
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>
<button class="btn btn-sm btn-outline-danger" title="حذف"
data-bs-toggle="modal" data-bs-target="#deleteModal"
data-id="<?= e((string) ($assessment['id'] ?? 0)) ?>"
data-title="<?= e((string) ($assessment['title'] ?? '')) ?>"
onclick="fillDeleteModal(this)">
<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>
<a class="btn btn-primary btn-sm" href="global_center_assessment_criteria.php?assessment_id=<?= e((string) ($assessment['id'] ?? 0)) ?>" title="البنود">
<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" fill="currentColor" viewBox="0 0 16 16">
<path d="M14.5 3a.5.5 0 0 1 .5.5v9a.5.5 0 0 1-.5.5h-13a.5.5 0 0 1-.5-.5v-9a.5.5 0 0 1 .5-.5h13zm-13-1A1.5 1.5 0 0 0 0 3.5v9A1.5 1.5 0 0 0 1.5 14h13a1.5 1.5 0 0 0 1.5-1.5v-9A1.5 1.5 0 0 0 14.5 2h-13z"/>
<path d="M3 5.5a.5.5 0 0 1 .5-.5h9a.5.5 0 0 1 0 1h-9a.5.5 0 0 1-.5-.5zM3 8.5a.5.5 0 0 1 .5-.5h9a.5.5 0 0 1 0 1h-9a.5.5 0 0 1-.5-.5zM3 11.5a.5.5 0 0 1 .5-.5h6a.5.5 0 0 1 0 1h-6a.5.5 0 0 1-.5-.5z"/>
</svg>
</a>
<a class="btn btn-success btn-sm" href="execute_global_assessment.php?id=<?= e((string) ($assessment['id'] ?? 0)) ?>" title="تطبيق التقييم">
<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" fill="currentColor" viewBox="0 0 16 16">
<path d="M10.97 4.97a.75.75 0 0 1 1.07 1.05l-3.99 4.99a.75.75 0 0 1-1.08.02L4.324 8.384a.75.75 0 1 1 1.06-1.06l2.094 2.093 3.473-4.425a.267.267 0 0 1 .02-.022z"/>
</svg> تطبيق
</a>
</div>
</td>
</tr>
<?php endforeach; ?>
<?php endif; ?>
</tbody>
</table>
</div>
<?php if ($assessments !== []): ?>
<div class="mt-4">
<?php render_pagination($totalAssessments, $limit, $page, $_GET); ?>
</div>
<?php endif; ?>
</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 modal-lg">
<div class="modal-content">
<form method="POST" action="global_center_assessments.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="row g-3">
<div class="col-12">
<label class="form-label" for="create_title">اسم التقييم</label>
<input class="form-control" id="create_title" name="title" placeholder="مثال: الزيارة الإشرافية العامة" required>
</div>
<div class="col-md-6">
<label class="form-label" for="create_category">الفئة</label>
<select class="form-select" id="create_category" name="category">
<?php foreach (assessment_category_options() as $categoryOption): ?>
<option value="<?= e($categoryOption) ?>"><?= e($categoryOption) ?></option>
<?php endforeach; ?>
</select>
</div>
<div class="col-md-6">
<label class="form-label" for="create_scale_type">مقياس التقييم</label>
<select class="form-select" id="create_scale_type" name="scale_type">
<?php foreach (assessment_scale_type_map() as $scaleKey => $scaleMeta): ?>
<option value="<?= e($scaleKey) ?>"><?= e((string) $scaleMeta['label']) ?></option>
<?php endforeach; ?>
</select>
</div>
<div class="col-md-6">
<label class="form-label" for="create_max_score">الدرجة القصوى</label>
<input class="form-control" id="create_max_score" type="number" min="0.01" max="1000" step="0.01" name="max_score" value="100">
</div>
<div class="col-md-6">
<label class="form-label" for="create_weight_percentage">الوزن النسبي ٪</label>
<input class="form-control" id="create_weight_percentage" type="number" min="0" max="100" step="0.01" name="weight_percentage" value="100">
</div>
<div class="col-md-6">
<label class="form-label" for="create_is_active">الحالة</label>
<select class="form-select" id="create_is_active" name="is_active">
<option value="1" selected>مفعّل</option>
<option value="0">مؤرشف</option>
</select>
</div>
<div class="col-12">
<label class="form-label" for="create_notes">ملاحظات داخلية</label>
<textarea class="form-control" id="create_notes" name="notes" rows="3" placeholder="مثلاً: يستخدم في الزيارات الإشرافية الفصلية..."></textarea>
</div>
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">إلغاء</button>
<button type="submit" class="btn btn-primary">إنشاء القالب</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 modal-lg">
<div class="modal-content">
<form method="POST" action="global_center_assessments.php">
<input type="hidden" name="action" value="edit">
<input type="hidden" name="assessment_id" id="edit_assessment_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="row g-3">
<div class="col-12">
<label class="form-label" for="edit_title">اسم التقييم</label>
<input class="form-control" id="edit_title" name="title" required>
</div>
<div class="col-md-6">
<label class="form-label" for="edit_category">الفئة</label>
<select class="form-select" id="edit_category" name="category">
<?php foreach (assessment_category_options() as $categoryOption): ?>
<option value="<?= e($categoryOption) ?>"><?= e($categoryOption) ?></option>
<?php endforeach; ?>
</select>
</div>
<div class="col-md-6">
<label class="form-label" for="edit_scale_type">مقياس التقييم</label>
<select class="form-select" id="edit_scale_type" name="scale_type">
<?php foreach (assessment_scale_type_map() as $scaleKey => $scaleMeta): ?>
<option value="<?= e($scaleKey) ?>"><?= e((string) $scaleMeta['label']) ?></option>
<?php endforeach; ?>
</select>
</div>
<div class="col-md-6">
<label class="form-label" for="edit_max_score">الدرجة القصوى</label>
<input class="form-control" id="edit_max_score" type="number" min="0.01" max="1000" step="0.01" name="max_score">
</div>
<div class="col-md-6">
<label class="form-label" for="edit_weight_percentage">الوزن النسبي ٪</label>
<input class="form-control" id="edit_weight_percentage" type="number" min="0" max="100" step="0.01" name="weight_percentage">
</div>
<div class="col-md-6">
<label class="form-label" for="edit_is_active">الحالة</label>
<select class="form-select" id="edit_is_active" name="is_active">
<option value="1">مفعّل</option>
<option value="0">مؤرشف</option>
</select>
</div>
<div class="col-12">
<label class="form-label" for="edit_notes">ملاحظات داخلية</label>
<textarea class="form-control" id="edit_notes" name="notes" rows="3"></textarea>
</div>
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">إلغاء</button>
<button type="submit" class="btn btn-primary">حفظ التعديلات</button>
</div>
</form>
</div>
</div>
</div>
<!-- Delete Modal -->
<div class="modal fade" id="deleteModal" tabindex="-1" aria-labelledby="deleteModalLabel" aria-hidden="true">
<div class="modal-dialog">
<div class="modal-content">
<form method="POST" action="global_center_assessments.php">
<input type="hidden" name="action" value="delete">
<input type="hidden" name="assessment_id" id="delete_assessment_id">
<div class="modal-header">
<h5 class="modal-title" id="deleteModalLabel">تأكيد حذف القالب</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body">
<p>هل أنت متأكد أنك تريد حذف القالب <strong id="delete_assessment_title"></strong>؟</p>
<p class="text-danger small">ملاحظة: سيتم حذف جميع البنود المرتبطة بهذا القالب أيضاً. هذا الإجراء لا يمكن التراجع عنه.</p>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">إلغاء</button>
<button type="submit" class="btn btn-danger">تأكيد الحذف</button>
</div>
</form>
</div>
</div>
</div>
<script>
function fillEditModal(btn) {
document.getElementById('edit_assessment_id').value = btn.getAttribute('data-id');
document.getElementById('edit_title').value = btn.getAttribute('data-title');
document.getElementById('edit_category').value = btn.getAttribute('data-category');
document.getElementById('edit_scale_type').value = btn.getAttribute('data-scale_type');
document.getElementById('edit_max_score').value = btn.getAttribute('data-max_score');
document.getElementById('edit_weight_percentage').value = btn.getAttribute('data-weight_percentage');
document.getElementById('edit_is_active').value = btn.getAttribute('data-is_active');
document.getElementById('edit_notes').value = btn.getAttribute('data-notes');
}
function fillDeleteModal(btn) {
document.getElementById('delete_assessment_id').value = btn.getAttribute('data-id');
document.getElementById('delete_assessment_title').textContent = btn.getAttribute('data-title');
}
</script>
<?php render_page_end(); ?>