Files
alrahma_sunday_school/app/Views/administrator/trophy_winners.php
T
2026-05-16 15:35:56 -04:00

481 lines
23 KiB
PHP

<?= $this->extend('layout/management_layout') ?>
<?= $this->section('content') ?>
<?php
$classResults = $classResults ?? [];
$selectedYear = $selectedYear ?? '';
$selectedPercentile = $selectedPercentile ?? 75;
$years = $years ?? [];
$totalWinners = array_sum(array_map(fn($c) => count($c['winners']), $classResults));
$totalStudents = array_sum(array_column($classResults, 'student_count'));
$totalBoys = array_sum(array_column($classResults, 'boys'));
$totalGirls = array_sum(array_column($classResults, 'girls'));
$totalTrophyBoys = array_sum(array_column($classResults, 'trophy_boys'));
$totalTrophyGirls = array_sum(array_column($classResults, 'trophy_girls'));
$pctWinners = $totalStudents > 0 ? round($totalWinners / $totalStudents * 100) : 0;
$pctTrophyBoys = $totalBoys > 0 ? round($totalTrophyBoys / $totalBoys * 100) : 0;
$pctTrophyGirls = $totalGirls > 0 ? round($totalTrophyGirls / $totalGirls * 100) : 0;
$pctBoys = $totalStudents > 0 ? round($totalBoys / $totalStudents * 100) : 0;
$pctGirls = $totalStudents > 0 ? round($totalGirls / $totalStudents * 100) : 0;
?>
<style>
/* ── Screen: hide print-only blocks ── */
.print-only { display: none !important; }
/* ── Print ── */
@media print {
.no-print { display: none !important; }
.print-only { display: block !important; }
.screen-only { display: none !important; }
body { font-size: 11px; }
.container-fluid { padding: 0 !important; }
h2, h3 { font-size: 14px; margin-bottom: .4rem; }
.print-page-break { break-before: page; }
table { width: 100%; border-collapse: collapse; font-size: 10px; }
table th,
table td { border: 1px solid #bbb; padding: 3px 6px; }
table thead { background: #333 !important; color: #fff !important;
-webkit-print-color-adjust: exact; print-color-adjust: exact; }
table thead th { position: static !important; top: auto !important; box-shadow: none !important; }
table tfoot { background: #eee !important;
-webkit-print-color-adjust: exact; print-color-adjust: exact; }
.badge-m { color: #4A90E2; font-weight: bold; }
.badge-f { color: #E47AB0; font-weight: bold; }
}
</style>
<div class="container-fluid">
<!-- ══ SCREEN HEADER ══════════════════════════════════════════════════════ -->
<div class="d-flex flex-wrap align-items-center justify-content-between mb-3 gap-2 no-print">
<div>
<h2 class="mb-1"><i class="bi bi-trophy-fill text-warning me-2"></i>Trophy Winners</h2>
<p class="text-muted mb-0">
<?= esc($selectedYear) ?> &mdash; <?= (int)$selectedPercentile ?>th percentile &mdash;
<strong><?= $totalWinners ?></strong> winners across <strong><?= count($classResults) ?></strong> classes
</p>
</div>
<div class="d-flex gap-2">
<button onclick="window.print()" class="btn btn-primary btn-sm">
<i class="bi bi-printer-fill me-1"></i>Print
</button>
<a href="<?= site_url('administrator/trophy?' . http_build_query(['school_year' => $selectedYear, 'percentile' => $selectedPercentile])) ?>"
class="btn btn-outline-secondary btn-sm">
<i class="bi bi-arrow-left me-1"></i>Back
</a>
</div>
</div>
<!-- Filter (screen only) -->
<form method="get" action="<?= site_url('administrator/trophy/winners') ?>"
class="row g-2 align-items-end mb-4 no-print">
<div class="col-auto">
<label class="form-label mb-1 small fw-semibold">School Year</label>
<select name="school_year" class="form-select form-select-sm" style="min-width:130px;">
<?php foreach ($years as $yr): ?>
<option value="<?= esc($yr) ?>" <?= $yr === $selectedYear ? 'selected' : '' ?>><?= esc($yr) ?></option>
<?php endforeach; ?>
</select>
</div>
<div class="col-auto">
<label class="form-label mb-1 small fw-semibold">Percentile</label>
<div class="input-group input-group-sm" style="width:110px;">
<input type="number" name="percentile" class="form-control"
min="1" max="99" step="1" value="<?= (int)$selectedPercentile ?>">
<span class="input-group-text">%</span>
</div>
</div>
<div class="col-auto">
<button type="submit" class="btn btn-primary btn-sm">Apply</button>
</div>
</form>
<?php if (empty($classResults)): ?>
<div class="alert alert-info no-print">No projected trophy winners found.</div>
<?php else: ?>
<!-- ══ PRINT VIEW ════════════════════════════════════════════════════════ -->
<div class="print-only">
<!-- Print title -->
<div style="text-align:center;margin-bottom:12px;border-bottom:2px solid #333;padding-bottom:8px;">
<h2 style="margin:0;">Trophy Winners — <?= esc($selectedYear) ?></h2>
<p style="margin:4px 0 0;font-size:10px;color:#555;">
<?= (int)$selectedPercentile ?>th percentile &mdash;
<?= $totalWinners ?> winners / <?= $totalStudents ?> students &mdash;
<?= count($classResults) ?> classes
</p>
</div>
<!-- TABLE 1: All winners -->
<h3 style="margin-bottom:4px;">Winners</h3>
<table data-no-mgmt-sticky>
<thead>
<tr>
<th>#</th>
<th>Class</th>
<th>Name</th>
<th style="text-align:center;">Gender</th>
<th style="text-align:right;">Fall Score</th>
<th style="text-align:right;">Spring Score</th>
<th style="text-align:right;">Year Score</th>
</tr>
</thead>
<tbody>
<?php
$globalRank = 0;
foreach ($classResults as $cls):
foreach ($cls['winners'] as $student):
$globalRank++;
$isMale = strtolower($student['gender'] ?? '') === 'male';
?>
<tr>
<td style="text-align:center;"><?= $globalRank ?></td>
<td><?= esc($cls['section_name']) ?></td>
<td><strong><?= esc($student['name']) ?></strong></td>
<td style="text-align:center;">
<span class="<?= $isMale ? 'badge-m' : 'badge-f' ?>">
<?= $isMale ? 'M' : 'F' ?>
</span>
</td>
<td style="text-align:right;font-weight:bold;">
<?= $student['fall_score'] !== null ? number_format($student['fall_score'], 1) : '—' ?>
</td>
<td style="text-align:right;">
<?= $student['spring_score'] !== null ? number_format($student['spring_score'], 1) : '—' ?>
</td>
<td style="text-align:right;font-weight:bold;color:#155724;">
<?= $student['year_score'] !== null ? number_format($student['year_score'], 1) : '—' ?>
</td>
</tr>
<?php endforeach; endforeach; ?>
</tbody>
<tfoot>
<tr>
<td colspan="2" style="font-weight:bold;">Total</td>
<td colspan="2" style="font-weight:bold;"><?= $totalWinners ?> winners</td>
<td colspan="3" style="text-align:right;font-weight:bold;"><?= $pctWinners ?>% of students</td>
</tr>
</tfoot>
</table>
<!-- TABLE 2: Year summary (new page) -->
<div class="print-page-break"></div>
<h3 style="margin-bottom:4px;">Year Summary — <?= esc($selectedYear) ?></h3>
<table style="margin-bottom:16px;" data-no-mgmt-sticky>
<thead>
<tr>
<th>Class</th>
<th style="text-align:center;">Students</th>
<th style="text-align:center;">Boys</th>
<th style="text-align:center;">Girls</th>
<th style="text-align:center;">Winners</th>
<th style="text-align:center;">Trophy Boys</th>
<th style="text-align:center;">Trophy Girls</th>
<th style="text-align:center;">Rate</th>
<th style="text-align:right;">Fall Threshold</th>
</tr>
</thead>
<tbody>
<?php foreach ($classResults as $cls): ?>
<tr>
<td><?= esc($cls['section_name']) ?></td>
<td style="text-align:center;"><?= $cls['student_count'] ?></td>
<td style="text-align:center;"><?= $cls['boys'] ?> (<?= $cls['pct_boys'] ?>%)</td>
<td style="text-align:center;"><?= $cls['girls'] ?> (<?= $cls['pct_girls'] ?>%)</td>
<td style="text-align:center;font-weight:bold;"><?= count($cls['winners']) ?></td>
<td style="text-align:center;">
<span class="badge-m"><?= $cls['trophy_boys'] ?></span>
<?= $cls['boys'] > 0 ? '(' . $cls['pct_trophy_boys'] . '%)' : '' ?>
</td>
<td style="text-align:center;">
<span class="badge-f"><?= $cls['trophy_girls'] ?></span>
<?= $cls['girls'] > 0 ? '(' . $cls['pct_trophy_girls'] . '%)' : '' ?>
</td>
<td style="text-align:center;"><?= $cls['pct_trophy_total'] ?>%</td>
<td style="text-align:right;"><?= $cls['threshold'] !== null ? number_format((float)$cls['threshold'], 1) : '—' ?></td>
</tr>
<?php endforeach; ?>
</tbody>
<tfoot>
<tr>
<td style="font-weight:bold;">Total</td>
<td style="text-align:center;font-weight:bold;"><?= $totalStudents ?></td>
<td style="text-align:center;font-weight:bold;"><?= $totalBoys ?> (<?= $pctBoys ?>%)</td>
<td style="text-align:center;font-weight:bold;"><?= $totalGirls ?> (<?= $pctGirls ?>%)</td>
<td style="text-align:center;font-weight:bold;"><?= $totalWinners ?></td>
<td style="text-align:center;font-weight:bold;">
<span class="badge-m"><?= $totalTrophyBoys ?></span> (<?= $pctTrophyBoys ?>%)
</td>
<td style="text-align:center;font-weight:bold;">
<span class="badge-f"><?= $totalTrophyGirls ?></span> (<?= $pctTrophyGirls ?>%)
</td>
<td style="text-align:center;font-weight:bold;"><?= $pctWinners ?>%</td>
<td></td>
</tr>
</tfoot>
</table>
</div><!-- /print-only -->
<!-- ══ SCREEN VIEW ═══════════════════════════════════════════════════════ -->
<div class="screen-only">
<!-- Per-class cards -->
<?php foreach ($classResults as $cls):
$nW = count($cls['winners']);
?>
<div class="card shadow-sm mb-4">
<div class="card-header d-flex flex-wrap align-items-center justify-content-between gap-2 py-2"
style="background:#fff8e1;">
<div class="d-flex align-items-center gap-2">
<span class="fw-bold fs-6"><?= esc($cls['section_name']) ?></span>
<span class="badge bg-warning text-dark">
<i class="bi bi-trophy-fill"></i> <?= $nW ?> winner<?= $nW !== 1 ? 's' : '' ?>
</span>
<?php if ($cls['threshold'] !== null): ?>
<span class="text-muted small">Fall &#8805; <?= number_format((float)$cls['threshold'], 1) ?></span>
<?php endif; ?>
</div>
<div class="d-flex gap-3 small">
<span>
<i class="bi bi-gender-male" style="color:#4A90E2"></i>
<strong style="color:#4A90E2"><?= $cls['trophy_boys'] ?></strong>/<?= $cls['boys'] ?> boys
<?php if ($cls['boys'] > 0): ?><span class="text-muted">(<?= $cls['pct_trophy_boys'] ?>%)</span><?php endif; ?>
</span>
<span>
<i class="bi bi-gender-female" style="color:#E47AB0"></i>
<strong style="color:#E47AB0"><?= $cls['trophy_girls'] ?></strong>/<?= $cls['girls'] ?> girls
<?php if ($cls['girls'] > 0): ?><span class="text-muted">(<?= $cls['pct_trophy_girls'] ?>%)</span><?php endif; ?>
</span>
<span class="text-muted"><?= $nW ?>/<?= $cls['student_count'] ?> (<?= $cls['pct_trophy_total'] ?>%)</span>
</div>
</div>
<div class="card-body p-0">
<table class="table table-sm table-hover mb-0 align-middle" data-no-mgmt-sticky>
<thead class="table-light">
<tr>
<th class="ps-3" style="width:2.2rem;">#</th>
<th>Name</th>
<th class="text-center" style="width:3rem;">Gender</th>
<th class="text-end">Fall Score</th>
<th class="text-end">Spring Score</th>
<th class="text-end pe-3">Year Score</th>
</tr>
</thead>
<tbody>
<?php foreach ($cls['winners'] as $i => $student):
$isMale = strtolower($student['gender'] ?? '') === 'male';
?>
<tr>
<td class="ps-3 text-muted small"><?= $i + 1 ?></td>
<td class="fw-semibold small"><?= esc($student['name']) ?></td>
<td class="text-center">
<span class="badge" style="background:<?= $isMale ? '#4A90E2' : '#E47AB0' ?>;font-size:.65rem;">
<?= $isMale ? 'M' : 'F' ?>
</span>
</td>
<td class="text-end small fw-semibold">
<?= $student['fall_score'] !== null ? number_format($student['fall_score'], 1) : '<span class="text-muted">—</span>' ?>
</td>
<td class="text-end small">
<?= $student['spring_score'] !== null
? '<span class="fw-semibold">' . number_format($student['spring_score'], 1) . '</span>'
: '<span class="text-muted">—</span>' ?>
</td>
<td class="text-end pe-3 small fw-semibold" style="color:#155724;">
<?= $student['year_score'] !== null ? number_format($student['year_score'], 1) : '<span class="text-muted">—</span>' ?>
</td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
</div>
</div>
<?php endforeach; ?>
<!-- Overall year stats -->
<div class="card shadow-sm mt-2">
<div class="card-header bg-dark text-white fw-semibold py-2">
<i class="bi bi-bar-chart-fill me-2"></i>Overall Year Summary — <?= esc($selectedYear) ?>
</div>
<div class="card-body">
<div class="row g-3 mb-3">
<?php foreach ([
[$totalWinners, 'Total Winners', 'warning', 'text-dark', $pctWinners . '% of students', null],
[$totalStudents, 'Total Students', 'secondary', 'text-white', count($classResults) . ' classes', null],
[$totalBoys, 'Total Boys', null, null, $pctBoys . '%', '#4A90E2'],
[$totalGirls, 'Total Girls', null, null, $pctGirls . '%', '#E47AB0'],
[$totalTrophyBoys, 'Trophy Boys', null, null, $pctTrophyBoys . '% of boys', '#4A90E2'],
[$totalTrophyGirls, 'Trophy Girls', null, null, $pctTrophyGirls . '% of girls', '#E47AB0'],
] as [$val, $label, $bg, $tc, $sub, $color]): ?>
<div class="col-6 col-sm-4 col-lg-2">
<div class="border rounded text-center p-3 h-100">
<div class="fs-3 fw-bold" <?= isset($color) ? 'style="color:' . $color . '"' : ($bg ? 'class="text-' . $bg . '"' : '') ?>>
<?= $val ?>
</div>
<div class="small text-muted"><?= $label ?></div>
<div class="badge mt-1 <?= $bg ? 'bg-' . $bg . ' ' . $tc : 'text-white' ?>"
<?= isset($color) && !$bg ? 'style="background:' . $color . '"' : '' ?>>
<?= $sub ?>
</div>
</div>
</div>
<?php endforeach; ?>
</div>
<div class="row g-4 mt-1">
<!-- Stacked bar: trophies per class (boys + girls) -->
<div class="col-12 col-lg-7">
<div class="border rounded p-3 h-100">
<div class="small fw-semibold text-muted mb-2">
<i class="bi bi-trophy-fill text-warning me-1"></i>Trophy Winners per Class
</div>
<canvas id="chart-per-class" style="max-height:260px;"></canvas>
</div>
</div>
<!-- Doughnut: trophy gender split + rate per class -->
<div class="col-12 col-lg-5">
<div class="row g-3 h-100">
<div class="col-12">
<div class="border rounded p-3">
<div class="small fw-semibold text-muted mb-2">
<i class="bi bi-gender-ambiguous me-1"></i>Winners by Gender
</div>
<canvas id="chart-gender" style="max-height:160px;"></canvas>
</div>
</div>
<div class="col-12">
<div class="border rounded p-3">
<div class="small fw-semibold text-muted mb-2">
<i class="bi bi-bar-chart-fill me-1"></i>Trophy Rate per Class (%)
</div>
<canvas id="chart-rate" style="max-height:130px;"></canvas>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div><!-- /screen-only -->
<?php endif; ?>
</div>
<?= $this->section('scripts') ?>
<script src="https://cdn.jsdelivr.net/npm/chart.js@4.4.3/dist/chart.umd.min.js"></script>
<script>
(function () {
<?php
$chartLabels = [];
$chartBoys = [];
$chartGirls = [];
$chartRates = [];
foreach ($classResults as $cls) {
$chartLabels[] = $cls['section_name'];
$chartBoys[] = $cls['trophy_boys'];
$chartGirls[] = $cls['trophy_girls'];
$chartRates[] = $cls['pct_trophy_total'];
}
?>
var labels = <?= json_encode($chartLabels) ?>;
var boys = <?= json_encode($chartBoys) ?>;
var girls = <?= json_encode($chartGirls) ?>;
var rates = <?= json_encode($chartRates) ?>;
var BOY_COLOR = '#4A90E2';
var GIRL_COLOR = '#E47AB0';
var RATE_COLOR = '#f0a500';
/* ── Chart 1: stacked horizontal bar per class ── */
new Chart(document.getElementById('chart-per-class'), {
type: 'bar',
data: {
labels: labels,
datasets: [
{ label: 'Boys', data: boys, backgroundColor: BOY_COLOR, stack: 'winners' },
{ label: 'Girls', data: girls, backgroundColor: GIRL_COLOR, stack: 'winners' }
]
},
options: {
indexAxis: 'y',
responsive: true,
maintainAspectRatio: true,
plugins: { legend: { position: 'bottom' } },
scales: {
x: { stacked: true, ticks: { stepSize: 1 }, grid: { color: '#f0f0f0' } },
y: { stacked: true }
}
}
});
/* ── Chart 2: doughnut gender split ── */
new Chart(document.getElementById('chart-gender'), {
type: 'doughnut',
data: {
labels: ['Trophy Boys', 'Trophy Girls'],
datasets: [{
data: [<?= $totalTrophyBoys ?>, <?= $totalTrophyGirls ?>],
backgroundColor: [BOY_COLOR, GIRL_COLOR],
borderWidth: 2
}]
},
options: {
responsive: true,
maintainAspectRatio: true,
plugins: {
legend: { position: 'bottom' },
tooltip: {
callbacks: {
label: function(ctx) {
var total = ctx.dataset.data.reduce(function(a,b){ return a+b; }, 0);
var pct = total > 0 ? Math.round(ctx.parsed / total * 100) : 0;
return ctx.label + ': ' + ctx.parsed + ' (' + pct + '%)';
}
}
}
}
}
});
/* ── Chart 3: bar chart rate per class ── */
new Chart(document.getElementById('chart-rate'), {
type: 'bar',
data: {
labels: labels,
datasets: [{
label: 'Trophy Rate %',
data: rates,
backgroundColor: rates.map(function(r) {
return r > 30 ? '#dc3545' : r > 20 ? RATE_COLOR : '#28a745';
}),
borderRadius: 3
}]
},
options: {
responsive: true,
maintainAspectRatio: true,
plugins: { legend: { display: false } },
scales: {
y: {
beginAtZero: true, max: 100,
ticks: { callback: function(v) { return v + '%'; } },
grid: { color: '#f0f0f0' }
}
}
}
});
})();
</script>
<?= $this->endSection() ?>
<?= $this->endSection() ?>