677 lines
39 KiB
PHP
677 lines
39 KiB
PHP
<?php
|
|
declare(strict_types=1);
|
|
require_once __DIR__ . '/includes/app.php';
|
|
|
|
$flash = consume_flash();
|
|
$approvedCenters = list_applications('approved');
|
|
$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;
|
|
$isApprovedCenter = $application && (string) ($application['status'] ?? '') === 'approved';
|
|
$values = assessment_defaults();
|
|
$errors = [];
|
|
$cycleContext = ['cycles' => [], 'selected' => null, 'active' => null, 'read_only' => false];
|
|
$selectedCycle = null;
|
|
$selectedCycleId = 0;
|
|
$isCycleReadOnly = false;
|
|
$cycleLabel = 'لا توجد دورة بعد';
|
|
|
|
$buildCenterAssessmentsUrl = static function (int $targetApplicationId = 0, int $targetCycleId = 0, array $extra = []): string {
|
|
$params = [];
|
|
if ($targetApplicationId > 0) {
|
|
$params['id'] = $targetApplicationId;
|
|
}
|
|
if ($targetCycleId > 0) {
|
|
$params['cycle'] = $targetCycleId;
|
|
}
|
|
foreach ($extra as $key => $value) {
|
|
if ($value === '' || $value === null) {
|
|
continue;
|
|
}
|
|
$params[$key] = $value;
|
|
}
|
|
return 'center_assessments.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) : '');
|
|
};
|
|
|
|
$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) : '');
|
|
};
|
|
|
|
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;
|
|
}
|
|
|
|
if ($_SERVER['REQUEST_METHOD'] === 'POST' && $isApprovedCenter) {
|
|
$action = $_POST['action'] ?? 'add';
|
|
$assessmentId = filter_input(INPUT_POST, 'assessment_id', FILTER_VALIDATE_INT) ?: 0;
|
|
[$values, $errors] = validate_assessment_input($_POST);
|
|
|
|
if ($selectedCycleId <= 0) {
|
|
$errors['form'] = 'يجب إنشاء دورة موسمية لهذا المركز قبل إعداد تقييمات المراكز.';
|
|
} elseif ($isCycleReadOnly) {
|
|
$errors['form'] = 'الدورة الحالية مؤرشفة للقراءة فقط. اختر دورة نشطة أو أنشئ دورة جديدة.';
|
|
}
|
|
|
|
if ($errors === []) {
|
|
try {
|
|
if ($action === 'import_global') {
|
|
$globalId = filter_input(INPUT_POST, 'global_assessment_id', FILTER_VALIDATE_INT) ?: 0;
|
|
if ($globalId > 0) {
|
|
import_global_center_assessment_to_center($globalId, (int) $application['id'], $selectedCycleId);
|
|
set_flash('success', 'تم إدراج القالب العام مع بنوده بنجاح.');
|
|
} else {
|
|
set_flash('error', 'يجب اختيار قالب صحيح.');
|
|
}
|
|
} elseif ($action === 'edit' && $assessmentId > 0) {
|
|
update_center_assessment_type_in_cycle((int) $application['id'], $selectedCycleId, $assessmentId, $values);
|
|
set_flash('success', 'تم تحديث تقييم المركز بنجاح.');
|
|
} else {
|
|
create_center_assessment_type_in_cycle((int) $application['id'], $selectedCycleId, $values);
|
|
set_flash('success', 'تم حفظ نوع تقييم جديد للمركز داخل الدورة المحددة.');
|
|
}
|
|
|
|
$redirectParams = array_intersect_key($_GET, array_flip(['search', 'category', 'page']));
|
|
header('Location: ' . $buildCenterAssessmentsUrl((int) $application['id'], $selectedCycleId, $redirectParams));
|
|
exit;
|
|
} catch (Throwable $exception) {
|
|
$errors['form'] = 'تعذر حفظ تقييم المركز حالياً. يرجى المحاولة مرة أخرى.';
|
|
}
|
|
}
|
|
}
|
|
|
|
$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 = $isApprovedCenter && $selectedCycleId > 0 ? list_center_assessments_by_cycle((int) $application['id'], $selectedCycleId, $filters, $limit, $offset) : [];
|
|
$totalAssessments = $isApprovedCenter && $selectedCycleId > 0 ? count_center_assessments_by_cycle((int) $application['id'], $selectedCycleId, $filters) : 0;
|
|
$metrics = $isApprovedCenter && $selectedCycleId > 0 ? center_assessment_metrics_by_cycle((int) $application['id'], $selectedCycleId) : [
|
|
'total' => 0,
|
|
'active' => 0,
|
|
'inactive' => 0,
|
|
'active_weight' => 0.0,
|
|
'average_max_score' => 0.0,
|
|
'percentage' => 0,
|
|
'points' => 0,
|
|
'rubric' => 0,
|
|
];
|
|
|
|
$approvedCenterCards = [];
|
|
foreach ($approvedCenters as $center) {
|
|
$centerId = (int) ($center['id'] ?? 0);
|
|
if ($centerId <= 0) {
|
|
continue;
|
|
}
|
|
|
|
$centerCycleContext = resolve_school_cycle_context($centerId, $center, 0);
|
|
$centerSelectedCycle = $centerCycleContext['selected'] ?? null;
|
|
$centerCycleId = $centerSelectedCycle ? (int) ($centerSelectedCycle['id'] ?? 0) : 0;
|
|
$approvedCenterCards[] = [
|
|
'application' => $center,
|
|
'selected_cycle' => $centerSelectedCycle,
|
|
'selected_cycle_id' => $centerCycleId,
|
|
'url' => $buildCenterAssessmentsUrl($centerId, $centerCycleId),
|
|
];
|
|
}
|
|
|
|
$pageTitle = $application && $isApprovedCenter
|
|
? 'تقييم المراكز: ' . (string) ($application['center_name'] ?? '') . ($selectedCycle ? ' — ' . $cycleLabel : '')
|
|
: 'تقييم المراكز';
|
|
$pageDescription = 'إدارة تقييمات إشرافية مستقلة للمراكز المعتمدة حسب الدورة الموسمية، مع البنود، الرصد، والتقرير المجمع.';
|
|
|
|
render_page_start($pageTitle, 'admin', $pageDescription);
|
|
render_flash($flash);
|
|
?>
|
|
<style>
|
|
.icon-actions {
|
|
display: flex;
|
|
flex-wrap: wrap;
|
|
gap: 0.5rem;
|
|
}
|
|
|
|
.icon-action {
|
|
width: 2.25rem;
|
|
height: 2.25rem;
|
|
display: inline-flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
border: 1px solid rgba(15, 23, 42, 0.12);
|
|
border-radius: 0.85rem;
|
|
color: #0f4c81;
|
|
background: #ffffff;
|
|
padding: 0;
|
|
line-height: 1;
|
|
cursor: pointer;
|
|
text-decoration: none;
|
|
transition: transform 0.18s ease, box-shadow 0.18s ease, border-color 0.18s ease, background-color 0.18s ease;
|
|
}
|
|
|
|
.icon-action:hover,
|
|
.icon-action:focus-visible {
|
|
color: #0a2f57;
|
|
border-color: rgba(15, 76, 129, 0.28);
|
|
background: #f4f9ff;
|
|
box-shadow: 0 10px 24px rgba(15, 76, 129, 0.12);
|
|
transform: translateY(-1px);
|
|
}
|
|
|
|
.icon-action svg {
|
|
width: 1rem;
|
|
height: 1rem;
|
|
}
|
|
|
|
.table-meta {
|
|
font-size: 0.85rem;
|
|
color: var(--bs-secondary-color, #6c757d);
|
|
}
|
|
</style>
|
|
<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 mb-lg-5">
|
|
<div class="row g-4 align-items-start">
|
|
<div class="col-lg-8">
|
|
<h1 class="page-title mb-3">إدارة تقييمات المراكز</h1>
|
|
<p class="page-copy mb-3">هذه الصفحة تضيف طبقة مستقلة لتقييم <strong>المراكز المعتمدة</strong> حسب <strong>الدورة الموسمية</strong>، بدون خلطها مع تقييمات الطلاب. يمكنك الآن تعريف <strong>أنواع تقييم المراكز وأوزانها</strong> ثم فتح <strong>بنود كل تقييم</strong> بنفس النمط المستخدم في تقييم الطلاب، تمهيداً لصفحة الرصد الفعلي.</p>
|
|
<div class="hero-meta">
|
|
<span>المراكز المعتمدة <?= e((string) count($approvedCenterCards)) ?></span>
|
|
<span>الإعداد الحالي <?= e((string) $metrics['active']) ?> تقييمات نشطة</span>
|
|
<span>الدورة المختارة <?= e($cycleLabel) ?></span>
|
|
</div>
|
|
<div class="cta-stack mt-4">
|
|
<a class="btn btn-primary" href="admin.php">العودة إلى لوحة الإدارة</a>
|
|
<a class="btn btn-outline-secondary" href="applications.php?status=approved">المراكز المعتمدة</a>
|
|
<?php if ($application && $isApprovedCenter): ?>
|
|
<a class="btn btn-outline-secondary" href="approved_school.php?id=<?= e((string) $application['id']) ?>&cycle=<?= e((string) $selectedCycleId) ?>">صفحة المركز</a>
|
|
<?php endif; ?>
|
|
</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((string) $metrics['total']) ?></div>
|
|
<div class="mini-stat-copy mb-3">أنواع تقييم مركزية معرفة حالياً للمركز/الدورة المحددين.</div>
|
|
<div class="d-grid gap-2">
|
|
<?php if ($application && $isApprovedCenter && $selectedCycleId > 0 && !$isCycleReadOnly): ?>
|
|
<button class="btn btn-primary btn-sm" type="button" data-bs-toggle="modal" data-bs-target="#centerAssessmentModal">إضافة تقييم جديد</button>
|
|
<button class="btn btn-outline-primary btn-sm" type="button" data-bs-toggle="modal" data-bs-target="#importGlobalModal">استيراد من القوالب العامة</button>
|
|
<?php else: ?>
|
|
<a class="btn btn-outline-secondary btn-sm" href="applications.php?status=approved">اختر مركزاً معتمداً</a>
|
|
<?php endif; ?>
|
|
<span class="small text-muted">المعيار هنا: <strong>لكل مركز معتمد</strong> و<strong>لكل دورة</strong> بشكل مستقل.</span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
|
|
<div class="row g-4 align-items-start">
|
|
<div class="col-lg-8">
|
|
<div class="app-card h-100">
|
|
<?php if (!$application || !$isApprovedCenter): ?>
|
|
<div class="section-head mb-3">
|
|
<div>
|
|
<div class="section-title">اختر مركزاً معتمداً للبدء</div>
|
|
<div class="section-copy">التقييمات المركزية لا تظهر إلا للمراكز المعتمدة، وكل مركز يُدار داخل دورته الموسمية الخاصة.</div>
|
|
</div>
|
|
</div>
|
|
<?php if ($approvedCenterCards === []): ?>
|
|
<div class="empty-state 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">العودة إلى الطلبات</a>
|
|
</div>
|
|
<?php else: ?>
|
|
<div class="row g-3">
|
|
<?php foreach ($approvedCenterCards as $card): ?>
|
|
<?php $center = $card['application']; ?>
|
|
<div class="col-md-6">
|
|
<article class="app-card h-100 border-0 shadow-sm">
|
|
<div class="section-title mb-2"><?= e((string) ($center['center_name'] ?? '')) ?></div>
|
|
<p class="text-muted mb-3"><?= e((string) ($center['city'] ?? '')) ?> — <?= e((string) ($center['director_name'] ?? '')) ?></p>
|
|
<div class="hero-meta mb-3">
|
|
<span>الدورة <?= e((string) (($card['selected_cycle']['cycle_name'] ?? 'غير متاحة'))) ?></span>
|
|
<span>السعة <?= e((string) ($center['expected_students'] ?? '0')) ?> طالب</span>
|
|
</div>
|
|
<?php if ((int) $card['selected_cycle_id'] > 0): ?>
|
|
<a class="btn btn-primary btn-sm" href="<?= e($card['url']) ?>">فتح تقييم هذا المركز</a>
|
|
<?php else: ?>
|
|
<a class="btn btn-outline-secondary btn-sm" href="approved_school.php?id=<?= e((string) ($center['id'] ?? 0)) ?>">أنشئ دورة أولاً</a>
|
|
<?php endif; ?>
|
|
</article>
|
|
</div>
|
|
<?php endforeach; ?>
|
|
</div>
|
|
<?php endif; ?>
|
|
<?php else: ?>
|
|
<div class="section-head mb-3">
|
|
<div>
|
|
<div class="section-title">تقييمات <?= e((string) $application['center_name']) ?></div>
|
|
<div class="section-copy">الدورة الحالية: <strong><?= e($cycleLabel) ?></strong>. يمكنك هنا إدارة أنواع التقييم، ثم فتح البنود، الرصد، والتقرير المجمع لكل دورة من عمود الإجراءات.</div>
|
|
</div>
|
|
<?php if (!$isCycleReadOnly && $selectedCycleId > 0): ?>
|
|
<div class="d-flex gap-2 flex-wrap">
|
|
<a class="btn btn-outline-secondary btn-sm" href="<?= e($buildCenterAssessmentReportUrl((int) $application['id'], $selectedCycleId)) ?>">تقرير الدورة</a>
|
|
<button class="btn btn-primary btn-sm" type="button" data-bs-toggle="modal" data-bs-target="#centerAssessmentModal">إضافة تقييم</button>
|
|
</div>
|
|
<?php endif; ?>
|
|
</div>
|
|
|
|
<?php if ($selectedCycleId <= 0): ?>
|
|
<div class="alert alert-warning mb-0">لا توجد دورة متاحة لهذا المركز بعد. أنشئ دورة موسمية أولاً من صفحة المركز.</div>
|
|
<?php else: ?>
|
|
<?php if ($isCycleReadOnly): ?>
|
|
<div class="alert alert-warning mb-4">هذه الدورة مؤرشفة للقراءة فقط. يمكنك المراجعة، لكن لا يمكن إضافة أو تعديل تقييمات جديدة هنا.</div>
|
|
<?php endif; ?>
|
|
|
|
<form method="get" class="row g-3 align-items-end mb-4">
|
|
<input type="hidden" name="id" value="<?= e((string) $application['id']) ?>">
|
|
<input type="hidden" name="cycle" value="<?= e((string) $selectedCycleId) ?>">
|
|
<div class="col-md-5">
|
|
<label class="form-label" for="search">بحث</label>
|
|
<input type="text" name="search" id="search" class="form-control" placeholder="ابحث باسم التقييم..." value="<?= e($filters['search']) ?>">
|
|
</div>
|
|
<div class="col-md-4">
|
|
<label class="form-label" for="category">الفئة</label>
|
|
<select class="form-select" name="category" id="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-3 d-grid gap-2">
|
|
<button class="btn btn-outline-secondary" type="submit">تصفية</button>
|
|
<a class="btn btn-link" href="<?= e($buildCenterAssessmentsUrl((int) $application['id'], $selectedCycleId)) ?>">إعادة تعيين</a>
|
|
</div>
|
|
</form>
|
|
|
|
<?php if ($assessments === []): ?>
|
|
<div class="empty-state text-center py-5">
|
|
<div class="empty-title mb-2">لا توجد تقييمات مراكز بعد</div>
|
|
<p class="text-muted mb-3">ابدأ بتعريف أول نوع تقييم للمركز مثل: الالتزام الإداري، جودة البيئة التعليمية، أو متابعة الخطة.</p>
|
|
<?php if (!$isCycleReadOnly): ?>
|
|
<button class="btn btn-primary" type="button" data-bs-toggle="modal" data-bs-target="#centerAssessmentModal">إضافة أول تقييم</button>
|
|
<?php endif; ?>
|
|
</div>
|
|
<?php else: ?>
|
|
<div class="table-responsive">
|
|
<table class="table app-table align-middle mb-0">
|
|
<thead>
|
|
<tr>
|
|
<th>التقييم</th>
|
|
<th>الفئة</th>
|
|
<th>الدرجة والوزن</th>
|
|
<th>البنود</th>
|
|
<th>الحالة</th>
|
|
<th>الإجراء</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
<?php foreach ($assessments as $assessment): ?>
|
|
<tr>
|
|
<td>
|
|
<div class="fw-semibold"><?= e((string) ($assessment['title'] ?? '')) ?></div>
|
|
</td>
|
|
<td>
|
|
<div><?= e((string) ($assessment['category'] ?? '')) ?></div>
|
|
<div class="table-meta mt-1"><?= assessment_scale_type_badge((string) ($assessment['scale_type'] ?? '')) ?></div>
|
|
</td>
|
|
<td>
|
|
<div class="fw-semibold"><?= e((string) round((float) ($assessment['max_score'] ?? 0), 2)) ?></div>
|
|
<div class="table-meta mt-1">وزن <?= e((string) round((float) ($assessment['weight_percentage'] ?? 0), 2)) ?>٪</div>
|
|
</td>
|
|
<td>
|
|
<span class="fw-semibold"><?= e((string) ((int) ($assessment['criteria_count'] ?? 0))) ?></span>
|
|
</td>
|
|
<td><?= assessment_active_badge((int) ($assessment['is_active'] ?? 0)) ?></td>
|
|
<td>
|
|
<div class="icon-actions">
|
|
<a
|
|
class="icon-action"
|
|
href="<?= e($buildCenterAssessmentCriteriaUrl((int) $application['id'], $selectedCycleId, (int) ($assessment['id'] ?? 0))) ?>"
|
|
data-bs-toggle="tooltip"
|
|
data-bs-placement="top"
|
|
data-bs-title="إدارة البنود"
|
|
title="إدارة البنود"
|
|
aria-label="إدارة البنود"
|
|
>
|
|
<svg viewBox="0 0 16 16" fill="currentColor" aria-hidden="true">
|
|
<path d="M2.5 3A1.5 1.5 0 0 1 4 1.5h8A1.5 1.5 0 0 1 13.5 3v10A1.5 1.5 0 0 1 12 14.5H4A1.5 1.5 0 0 1 2.5 13V3Zm1.5-.5a.5.5 0 0 0-.5.5v10a.5.5 0 0 0 .5.5h8a.5.5 0 0 0 .5-.5V3a.5.5 0 0 0-.5-.5H4Zm1.25 2.25a.75.75 0 0 1 .75-.75h4a.75.75 0 0 1 0 1.5h-4a.75.75 0 0 1-.75-.75Zm0 3a.75.75 0 0 1 .75-.75h4a.75.75 0 0 1 0 1.5h-4a.75.75 0 0 1-.75-.75Zm0 3a.75.75 0 0 1 .75-.75h2.5a.75.75 0 0 1 0 1.5h-2.5a.75.75 0 0 1-.75-.75Z"/>
|
|
</svg>
|
|
<span class="visually-hidden">إدارة البنود</span>
|
|
</a>
|
|
<a
|
|
class="icon-action"
|
|
href="<?= e($buildCenterAssessmentScoreUrl((int) $application['id'], $selectedCycleId, (int) ($assessment['id'] ?? 0))) ?>"
|
|
data-bs-toggle="tooltip"
|
|
data-bs-placement="top"
|
|
data-bs-title="رصد الدرجات"
|
|
title="رصد الدرجات"
|
|
aria-label="رصد الدرجات"
|
|
>
|
|
<svg viewBox="0 0 16 16" fill="currentColor" aria-hidden="true">
|
|
<path d="M11 1.5a1.5 1.5 0 0 1 1.5 1.5v9.293l-2-2V3a.5.5 0 0 0-.5-.5H4A.5.5 0 0 0 3.5 3v10a.5.5 0 0 0 .5.5h4.5l2 2H4A1.5 1.5 0 0 1 2.5 14V3A1.5 1.5 0 0 1 4 1.5h7Zm2.354 9.146a.5.5 0 0 1 .11.54l-1 3a.5.5 0 0 1-.316.316l-3 1a.5.5 0 0 1-.65-.65l1-3a.5.5 0 0 1 .12-.195l2.5-2.5a.5.5 0 0 1 .707 0l.53.53Zm-6.104-4.396a.75.75 0 0 1 .75-.75h1.5a.75.75 0 0 1 0 1.5H8a.75.75 0 0 1-.75-.75Zm0 3a.75.75 0 0 1 .75-.75h1.5a.75.75 0 0 1 0 1.5H8a.75.75 0 0 1-.75-.75Z"/>
|
|
</svg>
|
|
<span class="visually-hidden">رصد الدرجات</span>
|
|
</a>
|
|
<a
|
|
class="icon-action"
|
|
href="<?= e($buildCenterAssessmentPrintUrl((int) $application['id'], $selectedCycleId, (int) ($assessment['id'] ?? 0))) ?>"
|
|
target="_blank"
|
|
rel="noopener"
|
|
data-bs-toggle="tooltip"
|
|
data-bs-placement="top"
|
|
data-bs-title="طباعة النموذج الرسمي"
|
|
title="طباعة النموذج الرسمي"
|
|
aria-label="طباعة النموذج الرسمي"
|
|
>
|
|
<svg viewBox="0 0 16 16" fill="currentColor" aria-hidden="true">
|
|
<path d="M4 1.5A1.5 1.5 0 0 0 2.5 3v2.5a.5.5 0 0 0 1 0V3a.5.5 0 0 1 .5-.5h8a.5.5 0 0 1 .5.5v2.5a.5.5 0 0 0 1 0V3A1.5 1.5 0 0 0 12 1.5H4ZM3 6a2 2 0 0 0-2 2v3a2 2 0 0 0 2 2h1v1.5A1.5 1.5 0 0 0 5.5 16h5A1.5 1.5 0 0 0 12 14.5V13h1a2 2 0 0 0 2-2V8a2 2 0 0 0-2-2H3Zm2 7v1.5a.5.5 0 0 0 .5.5h5a.5.5 0 0 0 .5-.5V13H5Zm6-1H5V8h6v4Zm1-3.25a.75.75 0 1 1 0-1.5.75.75 0 0 1 0 1.5Z"/>
|
|
</svg>
|
|
<span class="visually-hidden">طباعة النموذج الرسمي</span>
|
|
</a>
|
|
<?php if (!$isCycleReadOnly): ?>
|
|
<button
|
|
type="button"
|
|
class="icon-action"
|
|
data-bs-toggle="modal"
|
|
data-bs-target="#centerAssessmentModal"
|
|
data-bs-placement="top"
|
|
data-bs-title="تعديل التقييم"
|
|
title="تعديل التقييم"
|
|
aria-label="تعديل التقييم"
|
|
data-action="edit"
|
|
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((string) round((float) ($assessment['max_score'] ?? 0), 2)) ?>"
|
|
data-weight-percentage="<?= e((string) round((float) ($assessment['weight_percentage'] ?? 0), 2)) ?>"
|
|
data-is-active="<?= e((string) ($assessment['is_active'] ?? 0)) ?>"
|
|
data-notes="<?= e((string) ($assessment['notes'] ?? '')) ?>"
|
|
>
|
|
<svg viewBox="0 0 16 16" fill="currentColor" aria-hidden="true">
|
|
<path d="M12.854.146a.5.5 0 0 1 .707 0l1.586 1.586a.5.5 0 0 1 0 .707l-9.5 9.5a.5.5 0 0 1-.168.11l-3.5 1.25a.5.5 0 0 1-.64-.64l1.25-3.5a.5.5 0 0 1 .11-.168l9.5-9.5ZM11.207 2 3.354 9.854l-.793 2.22 2.22-.793L12.646 3.414 11.207 2Z"/>
|
|
</svg>
|
|
<span class="visually-hidden">تعديل التقييم</span>
|
|
</button>
|
|
<?php else: ?>
|
|
<span class="table-meta align-self-center">قراءة فقط</span>
|
|
<?php endif; ?>
|
|
</div>
|
|
</td>
|
|
</tr>
|
|
<?php endforeach; ?>
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
<?php render_pagination($totalAssessments, $limit, $page, ['id' => (int) $application['id'], 'cycle' => $selectedCycleId, 'search' => $filters['search'], 'category' => $filters['category']]); ?>
|
|
<?php endif; ?>
|
|
<?php endif; ?>
|
|
<?php endif; ?>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="col-lg-4">
|
|
<div class="app-card h-100 sidebar-card">
|
|
<div class="section-title mb-3">المراكز المعتمدة</div>
|
|
<?php if ($approvedCenterCards === []): ?>
|
|
<div class="empty-state text-center py-4">
|
|
<div class="empty-title mb-2">لا توجد مراكز معتمدة</div>
|
|
<p class="text-muted mb-0">ستظهر هنا بمجرد اعتماد أول مركز.</p>
|
|
</div>
|
|
<?php else: ?>
|
|
<div class="quick-link-stack">
|
|
<?php foreach ($approvedCenterCards as $card): ?>
|
|
<?php $center = $card['application']; ?>
|
|
<a class="quick-link-item <?= $applicationId === (int) ($center['id'] ?? 0) ? 'active' : '' ?>" href="<?= e($card['url']) ?>">
|
|
<div>
|
|
<strong><?= e((string) ($center['center_name'] ?? '')) ?></strong>
|
|
<span><?= e((string) ($card['selected_cycle']['cycle_name'] ?? 'بدون دورة')) ?> — <?= e((string) ($center['city'] ?? '')) ?></span>
|
|
</div>
|
|
</a>
|
|
<?php endforeach; ?>
|
|
</div>
|
|
<?php endif; ?>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</section>
|
|
|
|
<?php if ($application && $isApprovedCenter && $selectedCycleId > 0): ?>
|
|
<!-- Import Global Assessment Modal -->
|
|
<div class="modal fade" id="importGlobalModal" tabindex="-1" aria-labelledby="importGlobalModalLabel" aria-hidden="true">
|
|
<div class="modal-dialog">
|
|
<div class="modal-content">
|
|
<form method="post" action="<?= e($buildCenterAssessmentsUrl((int) $application['id'], $selectedCycleId, ['search' => $filters['search'], 'category' => $filters['category'], 'page' => $page])) ?>">
|
|
<input type="hidden" name="action" value="import_global">
|
|
<!-- dummy fields to satisfy validate_assessment_input which expects them -->
|
|
<input type="hidden" name="title" value="import">
|
|
<input type="hidden" name="category" value="import">
|
|
<input type="hidden" name="scale_type" value="percentage">
|
|
<input type="hidden" name="max_score" value="100">
|
|
|
|
<div class="modal-header">
|
|
<h5 class="modal-title" id="importGlobalModalLabel">استيراد من القوالب العامة</h5>
|
|
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="إغلاق"></button>
|
|
</div>
|
|
<div class="modal-body">
|
|
<p>سيتم استيراد القالب المحدد مع كافة بنوده وخصائصه إلى هذا المركز في الدورة المحددة.</p>
|
|
<div class="mb-3">
|
|
<label for="global_assessment_id" class="form-label">اختر القالب</label>
|
|
<select class="form-select" id="global_assessment_id" name="global_assessment_id" required>
|
|
<option value="">-- اختر القالب --</option>
|
|
<?php foreach (global_center_assessment_type_options(true) as $globalTemplate): ?>
|
|
<option value="<?= e((string) $globalTemplate['id']) ?>"><?= e((string) $globalTemplate['title']) ?> (الوزن: <?= e(rtrim(rtrim(number_format((float) ($globalTemplate['weight_percentage'] ?? 0), 2, '.', ''), '0'), '.')) ?>٪)</option>
|
|
<?php endforeach; ?>
|
|
</select>
|
|
</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>
|
|
|
|
<div class="modal fade" id="centerAssessmentModal" tabindex="-1" aria-labelledby="centerAssessmentModalLabel" aria-hidden="true">
|
|
<div class="modal-dialog modal-lg modal-dialog-centered">
|
|
<div class="modal-content">
|
|
<form method="post" action="<?= e($buildCenterAssessmentsUrl((int) $application['id'], $selectedCycleId, ['search' => $filters['search'], 'category' => $filters['category'], 'page' => $page])) ?>">
|
|
<div class="modal-header">
|
|
<h2 class="modal-title fs-5" id="centerAssessmentModalLabel">إضافة تقييم مركز</h2>
|
|
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="إغلاق"></button>
|
|
</div>
|
|
<div class="modal-body">
|
|
<?php if (isset($errors['form'])): ?>
|
|
<div class="alert alert-danger"><?= e($errors['form']) ?></div>
|
|
<?php endif; ?>
|
|
|
|
<input type="hidden" name="action" id="centerAssessmentAction" value="add">
|
|
<input type="hidden" name="assessment_id" id="centerAssessmentId" value="0">
|
|
|
|
<div class="row g-3">
|
|
<div class="col-md-6">
|
|
<label class="form-label" for="title">اسم التقييم</label>
|
|
<input type="text" class="form-control <?= isset($errors['title']) ? 'is-invalid' : '' ?>" id="title" name="title" value="<?= e($values['title']) ?>" required>
|
|
<?php if (isset($errors['title'])): ?><div class="invalid-feedback"><?= e($errors['title']) ?></div><?php endif; ?>
|
|
</div>
|
|
<div class="col-md-6">
|
|
<label class="form-label" for="category">الفئة</label>
|
|
<select class="form-select <?= isset($errors['category']) ? 'is-invalid' : '' ?>" id="categoryField" name="category" required>
|
|
<?php foreach (assessment_category_options() as $categoryOption): ?>
|
|
<option value="<?= e($categoryOption) ?>" <?= $values['category'] === $categoryOption ? 'selected' : '' ?>><?= e($categoryOption) ?></option>
|
|
<?php endforeach; ?>
|
|
</select>
|
|
<?php if (isset($errors['category'])): ?><div class="invalid-feedback"><?= e($errors['category']) ?></div><?php endif; ?>
|
|
</div>
|
|
<div class="col-md-4">
|
|
<label class="form-label" for="scaleTypeField">المقياس</label>
|
|
<select class="form-select <?= isset($errors['scale_type']) ? 'is-invalid' : '' ?>" id="scaleTypeField" name="scale_type" required>
|
|
<?php foreach (assessment_scale_type_map() as $scaleKey => $scaleMeta): ?>
|
|
<option value="<?= e($scaleKey) ?>" <?= $values['scale_type'] === $scaleKey ? 'selected' : '' ?>><?= e((string) $scaleMeta['label']) ?></option>
|
|
<?php endforeach; ?>
|
|
</select>
|
|
<?php if (isset($errors['scale_type'])): ?><div class="invalid-feedback"><?= e($errors['scale_type']) ?></div><?php endif; ?>
|
|
</div>
|
|
<div class="col-md-4">
|
|
<label class="form-label" for="maxScoreField">الدرجة النهائية</label>
|
|
<input type="number" step="0.01" min="0.01" max="1000" class="form-control <?= isset($errors['max_score']) ? 'is-invalid' : '' ?>" id="maxScoreField" name="max_score" value="<?= e($values['max_score']) ?>" required>
|
|
<?php if (isset($errors['max_score'])): ?><div class="invalid-feedback"><?= e($errors['max_score']) ?></div><?php endif; ?>
|
|
</div>
|
|
<div class="col-md-4">
|
|
<label class="form-label" for="weightField">الوزن ٪</label>
|
|
<input type="number" step="0.01" min="0" max="100" class="form-control <?= isset($errors['weight_percentage']) ? 'is-invalid' : '' ?>" id="weightField" name="weight_percentage" value="<?= e($values['weight_percentage']) ?>" required>
|
|
<?php if (isset($errors['weight_percentage'])): ?><div class="invalid-feedback"><?= e($errors['weight_percentage']) ?></div><?php endif; ?>
|
|
</div>
|
|
<div class="col-12">
|
|
<label class="form-label" for="notesField">ملاحظات</label>
|
|
<textarea class="form-control" id="notesField" name="notes" rows="3"><?= e($values['notes']) ?></textarea>
|
|
</div>
|
|
<div class="col-12">
|
|
<input type="hidden" name="is_active" value="0">
|
|
<div class="form-check form-switch">
|
|
<input class="form-check-input" type="checkbox" role="switch" id="isActiveField" name="is_active" value="1" <?= $values['is_active'] === '1' ? 'checked' : '' ?>>
|
|
<label class="form-check-label" for="isActiveField">تفعيل هذا التقييم داخل الدورة الحالية</label>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="modal-footer">
|
|
<button type="button" class="btn btn-outline-secondary" data-bs-dismiss="modal">إلغاء</button>
|
|
<button type="submit" class="btn btn-primary">حفظ التقييم</button>
|
|
</div>
|
|
</form>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<script>
|
|
document.addEventListener('DOMContentLoaded', function () {
|
|
document.querySelectorAll('[data-bs-toggle="tooltip"]').forEach(function (element) {
|
|
new bootstrap.Tooltip(element);
|
|
});
|
|
|
|
const modal = document.getElementById('centerAssessmentModal');
|
|
if (!modal) return;
|
|
|
|
modal.addEventListener('show.bs.modal', function (event) {
|
|
const trigger = event.relatedTarget;
|
|
const actionField = document.getElementById('centerAssessmentAction');
|
|
const idField = document.getElementById('centerAssessmentId');
|
|
const titleField = document.getElementById('title');
|
|
const categoryField = document.getElementById('categoryField');
|
|
const scaleTypeField = document.getElementById('scaleTypeField');
|
|
const maxScoreField = document.getElementById('maxScoreField');
|
|
const weightField = document.getElementById('weightField');
|
|
const isActiveField = document.getElementById('isActiveField');
|
|
const notesField = document.getElementById('notesField');
|
|
const modalTitle = document.getElementById('centerAssessmentModalLabel');
|
|
|
|
if (!trigger || !trigger.dataset || !trigger.dataset.action) {
|
|
actionField.value = 'add';
|
|
idField.value = '0';
|
|
modalTitle.innerText = 'إضافة تقييم مركز';
|
|
titleField.value = '<?= e($values['title']) ?>';
|
|
categoryField.value = '<?= e($values['category']) ?>';
|
|
scaleTypeField.value = '<?= e($values['scale_type']) ?>';
|
|
maxScoreField.value = '<?= e($values['max_score']) ?>';
|
|
weightField.value = '<?= e($values['weight_percentage']) ?>';
|
|
isActiveField.checked = <?= $values['is_active'] === '1' ? 'true' : 'false' ?>;
|
|
notesField.value = '<?= e($values['notes']) ?>';
|
|
return;
|
|
}
|
|
|
|
actionField.value = trigger.dataset.action || 'edit';
|
|
idField.value = trigger.dataset.id || '0';
|
|
modalTitle.innerText = 'تعديل تقييم المركز';
|
|
titleField.value = trigger.dataset.title || '';
|
|
categoryField.value = trigger.dataset.category || 'اختبار قصير';
|
|
scaleTypeField.value = trigger.dataset.scaleType || 'percentage';
|
|
maxScoreField.value = trigger.dataset.maxScore || '100';
|
|
weightField.value = trigger.dataset.weightPercentage || '0';
|
|
isActiveField.checked = (trigger.dataset.isActive || '0') === '1';
|
|
notesField.value = trigger.dataset.notes || '';
|
|
});
|
|
|
|
<?php if ($errors !== []): ?>
|
|
const bootstrapModal = new bootstrap.Modal(modal);
|
|
bootstrapModal.show();
|
|
<?php endif; ?>
|
|
});
|
|
</script>
|
|
<?php endif; ?>
|
|
|
|
<?php render_page_end(); ?>
|