fix the event print and certificate qr

This commit is contained in:
root
2026-05-21 17:00:38 -04:00
parent 3333e71c94
commit 239d35f8ff
7 changed files with 502 additions and 276 deletions
+67 -110
View File
@@ -224,23 +224,51 @@
<?php if (empty($grouped)): ?>
<div class="alert alert-info">No charges found.</div>
<?php else: ?>
<?php
$printBaseParams = array_filter([
'school_year' => $school_year,
'semester' => $semester,
'parent_id' => $filterParentId > 0 ? $filterParentId : null,
'event_id' => $filterEventId > 0 ? $filterEventId : null,
], static fn ($value) => $value !== null && $value !== '');
$printAllUrl = site_url('administrator/event-charges/pdf');
if (!empty($printBaseParams)) {
$printAllUrl .= '?' . http_build_query($printBaseParams);
}
?>
<div class="d-flex justify-content-between align-items-center flex-wrap gap-2 mb-3 no-print">
<p class="text-muted mb-0">Click a column header to sort the participants list.</p>
<button type="button" class="btn btn-outline-secondary" id="printAllEventCharges">
<a href="<?= esc($printAllUrl) ?>"
class="btn btn-outline-secondary pdf-print-link"
data-base-href="<?= esc($printAllUrl) ?>"
data-print-scope="all">
Print All Participant Lists
</button>
</a>
</div>
<div id="eventChargeCards">
<?php foreach ($grouped as $eventId => $data): ?>
<?php $eventLabel = $data['label']; ?>
<?php $rows = $data['rows']; ?>
<?php
$singlePrintParams = array_filter([
'school_year' => $school_year,
'semester' => $semester,
'parent_id' => $filterParentId > 0 ? $filterParentId : null,
'event_id' => $eventId,
], static fn ($value) => $value !== null && $value !== '');
$singlePrintUrl = site_url('administrator/event-charges/pdf') . '?' . http_build_query($singlePrintParams);
?>
<div class="card mb-4 event-card" data-event-id="<?= esc($eventId) ?>">
<div class="card-header bg-light fw-semibold event-card-header">
<span><?= esc($eventLabel) ?></span>
<div class="event-card-header-actions no-print">
<button type="button" class="btn btn-outline-secondary btn-sm print-event-card" data-event-id="<?= esc($eventId) ?>">
<a href="<?= esc($singlePrintUrl) ?>"
class="btn btn-outline-secondary btn-sm pdf-print-link"
data-base-href="<?= esc($singlePrintUrl) ?>"
data-print-scope="single"
data-event-id="<?= esc($eventId) ?>">
Print This List
</button>
</a>
</div>
</div>
<div class="card-body p-0">
@@ -287,7 +315,7 @@
. (!empty($returnParams) ? '?' . http_build_query($returnParams) : '')
. '#eventTable_' . (int) ($charge['event_id'] ?? 0);
?>
<tr>
<tr data-charge-id="<?= esc((string) ($charge['id'] ?? '')) ?>">
<td data-sort-value="<?= esc((string) ($charge['id'] ?? '')) ?>"><?= esc($charge['id']) ?></td>
<?php
$studentName = $charge['student_firstname']
@@ -593,15 +621,6 @@ function loadStudentsWithCharges() {
form.appendChild(input);
}
function escapeHtml(value) {
const s = String(value ?? '');
return s.replace(/&/g, '&amp;')
.replace(/</g, '&lt;')
.replace(/>/g, '&gt;')
.replace(/"/g, '&quot;')
.replace(/'/g, '&#39;');
}
function submitExternalParticipant(payload) {
const form = document.createElement('form');
form.method = 'post';
@@ -690,89 +709,41 @@ function loadStudentsWithCharges() {
);
}
function buildPrintableCard(card) {
const clone = card.cloneNode(true);
clone.querySelectorAll('.no-print').forEach((node) => node.remove());
clone.querySelectorAll('.sortable-header').forEach((button) => {
const headerText = button.textContent || '';
const textNode = document.createTextNode(headerText);
button.parentNode.replaceChild(textNode, button);
});
clone.querySelectorAll('form').forEach((form) => {
const cell = form.closest('td');
if (!cell) {
form.remove();
return;
}
const label = form.querySelector('.form-check-label');
const checkbox = form.querySelector('input[type="checkbox"]');
const text = label
? label.textContent.trim()
: (checkbox && checkbox.checked ? 'Paid' : 'Unpaid');
cell.textContent = text || '—';
});
clone.querySelectorAll('tr').forEach((row) => {
if (row.children.length > 10) {
row.removeChild(row.lastElementChild);
}
});
return clone.outerHTML;
}
function openPrintWindow(title, contentHtml) {
const printWindow = window.open('', '_blank', 'noopener,noreferrer,width=1200,height=900');
if (!printWindow) {
alert('Unable to open the print preview window. Please allow pop-ups for this site.');
return;
function getTableRowOrder(table) {
if (!table || !table.tBodies.length) {
return [];
}
const style = `
<style>
body { font-family: Arial, sans-serif; margin: 24px; color: #212529; }
h1 { font-size: 1.5rem; margin-bottom: 0.25rem; }
p { margin-top: 0; color: #6c757d; }
.card { border: 1px solid #dee2e6; border-radius: 0.5rem; margin-bottom: 1.5rem; overflow: hidden; }
.card-header { background: #f8f9fa; padding: 0.9rem 1rem; font-weight: 700; }
.table { width: 100%; border-collapse: collapse; }
.table th, .table td { border: 1px solid #dee2e6; padding: 0.55rem 0.7rem; text-align: left; vertical-align: top; }
.table thead th, .table tfoot th { background: #f8f9fa; }
.badge { display: inline-block; padding: 0.2rem 0.45rem; border-radius: 999px; font-size: 0.8rem; font-weight: 700; }
.bg-success { background: #198754; color: #fff; }
.bg-danger { background: #dc3545; color: #fff; }
.bg-secondary { background: #6c757d; color: #fff; }
.text-muted { color: #6c757d; }
.small { font-size: 0.875rem; }
@media print {
body { margin: 0; }
}
</style>
`;
printWindow.document.open();
printWindow.document.write(`
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>${escapeHtml(title)}</title>
${style}
</head>
<body>
<h1>${escapeHtml(title)}</h1>
<p>Generated on ${escapeHtml(new Date().toLocaleString())}</p>
${contentHtml}
<script>
window.onload = function() {
window.print();
};
<\/script>
</body>
</html>
`);
printWindow.document.close();
return Array.from(table.tBodies[0].rows)
.map((row) => String(row.dataset.chargeId || '').trim())
.filter(Boolean);
}
function printEventCards(cards, title) {
const printable = cards.map((card) => buildPrintableCard(card)).join('');
openPrintWindow(title, printable);
function buildPdfUrl(link) {
const baseHref = String(link.dataset.baseHref || link.getAttribute('href') || '');
const url = new URL(baseHref, window.location.origin);
const scope = String(link.dataset.printScope || 'single');
if (scope === 'all') {
document.querySelectorAll('.event-card').forEach((card) => {
const eventId = String(card.dataset.eventId || '').trim();
const table = card.querySelector('.event-charges-table');
const rowOrder = getTableRowOrder(table);
if (eventId && rowOrder.length) {
url.searchParams.set(`row_order[${eventId}]`, rowOrder.join(','));
}
});
return url.toString();
}
const eventId = String(link.dataset.eventId || '').trim();
const table = eventId ? document.getElementById(`eventTable_${eventId}`) : null;
const rowOrder = getTableRowOrder(table);
if (eventId && rowOrder.length) {
url.searchParams.set(`row_order[${eventId}]`, rowOrder.join(','));
}
return url.toString();
}
$('.sortable-header').on('click', function() {
@@ -786,22 +757,8 @@ function loadStudentsWithCharges() {
sortEventTable(table, columnIndex, sortType, direction);
});
$('.print-event-card').on('click', function() {
const eventId = String($(this).data('eventId'));
const card = document.querySelector(`.event-card[data-event-id="${eventId}"]`);
if (!card) {
return;
}
const title = $(card).find('.card-header span').first().text().trim() || 'Event Participants';
printEventCards([card], `${title} Participant List`);
});
$('#printAllEventCharges').on('click', function() {
const cards = Array.from(document.querySelectorAll('.event-card'));
if (!cards.length) {
return;
}
printEventCards(cards, 'All Event Participant Lists');
$('.pdf-print-link').on('click', function() {
this.href = buildPdfUrl(this);
});
$('#externalParticipantSave').on('click', function() {
@@ -0,0 +1,207 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Event Participant Lists</title>
<style>
@page {
margin: 24px;
}
body {
font-family: DejaVu Sans, sans-serif;
font-size: 11px;
color: #1f2933;
}
h1 {
margin: 0 0 6px;
font-size: 20px;
}
.meta {
margin: 0 0 18px;
color: #52606d;
font-size: 10px;
}
.event-card {
margin-bottom: 22px;
page-break-inside: avoid;
}
.event-title {
margin: 0 0 8px;
padding: 8px 10px;
background: #f5f7fa;
border: 1px solid #d9e2ec;
font-size: 14px;
font-weight: 700;
}
table {
width: 100%;
border-collapse: collapse;
table-layout: fixed;
}
th,
td {
border: 1px solid #d9e2ec;
padding: 6px 7px;
vertical-align: top;
word-wrap: break-word;
}
th {
background: #f5f7fa;
text-align: left;
font-weight: 700;
}
tfoot th {
background: #f5f7fa;
}
.small {
color: #52606d;
font-size: 10px;
}
.status-paid {
color: #166534;
font-weight: 700;
}
.status-unpaid {
color: #b42318;
font-weight: 700;
}
.status-neutral {
color: #52606d;
font-weight: 700;
}
</style>
</head>
<body>
<?php
$title = !empty($filterEventId) ? 'Event Participant List' : 'All Event Participant Lists';
if (!empty($filterParentId)) {
$title .= ' for Selected Parent';
}
?>
<h1><?= esc($title) ?></h1>
<p class="meta">
Generated on <?= esc($generatedAt) ?>
| School Year: <?= esc($school_year ?: 'N/A') ?>
| Semester: <?= esc($semester ?: 'N/A') ?>
</p>
<?php if (empty($groupedCharges)): ?>
<p>No charges found for the selected filters.</p>
<?php else: ?>
<?php foreach ($groupedCharges as $eventId => $data): ?>
<?php
$rows = $data['rows'] ?? [];
$totalParticipants = 0;
$totalCharged = 0.0;
?>
<section class="event-card">
<h2 class="event-title"><?= esc($data['label'] ?? 'N/A') ?></h2>
<table>
<thead>
<tr>
<th style="width: 4%;">ID</th>
<th style="width: 15%;">Parent Name</th>
<th style="width: 14%;">Student Name</th>
<th style="width: 15%;">External Info</th>
<th style="width: 11%;">Class Section</th>
<th style="width: 8%;">Charged Amount</th>
<th style="width: 10%;">Created</th>
<th style="width: 7%;">Waiver</th>
<th style="width: 7%;">Fees Paid</th>
<th style="width: 9%;">Payment</th>
</tr>
</thead>
<tbody>
<?php foreach ($rows as $charge): ?>
<?php
$isParticipating = ($charge['participation'] ?? '') === 'yes';
$isEventPaid = !empty($charge['event_paid']);
$feeAmount = (float) ($charge['event_amount'] ?? 0);
$hasBalanceRecord = array_key_exists((int) ($charge['parent_id'] ?? 0), $parentBalances);
$parentBalance = $hasBalanceRecord ? (float) $parentBalances[(int) $charge['parent_id']] : null;
$waiverSigned = !empty($charge['waiver_signed']);
$feeIsPaid = $isParticipating && ($isEventPaid || ($hasBalanceRecord && $parentBalance <= 0));
$studentName = !empty($charge['student_firstname'])
? trim(($charge['student_firstname'] ?? '') . ' ' . ($charge['student_lastname'] ?? ''))
: '';
$externalName = trim(($charge['external_firstname'] ?? '') . ' ' . ($charge['external_lastname'] ?? ''));
$displayName = $studentName ?: ($externalName ?: '—');
$externalNote = trim((string) ($charge['external_note'] ?? ''));
$externalParentLabel = trim(($charge['external_parent_firstname'] ?? '') . ' ' . ($charge['external_parent_lastname'] ?? ''));
$externalParentPhone = trim((string) ($charge['external_parent_phone'] ?? ''));
$externalParentEmail = trim((string) ($charge['external_parent_email'] ?? ''));
$parentPhone = trim((string) ($charge['parent_cellphone'] ?? ''));
$standardParentName = trim(($charge['parent_firstname'] ?? '') . ' ' . ($charge['parent_lastname'] ?? ''));
$parentColumnName = $externalParentLabel ?: ($standardParentName ?: '—');
$isExternalParticipant = $externalName !== '';
$infoPhone = $isExternalParticipant ? $externalParentPhone : $parentPhone;
$infoEmail = $isExternalParticipant ? $externalParentEmail : '';
$classSectionName = $classSectionNames[$charge['class_section_id'] ?? ''] ?? '—';
if ($isParticipating) {
$totalParticipants++;
$totalCharged += $feeAmount;
}
?>
<tr>
<td><?= esc($charge['id'] ?? '') ?></td>
<td><?= esc($parentColumnName) ?></td>
<td>
<?= esc($displayName) ?>
<?php if ($externalNote !== ''): ?>
<div class="small"><?= esc($externalNote) ?></div>
<?php endif; ?>
</td>
<td>
<?php if ($infoPhone !== ''): ?>
<div class="small"><?= esc($infoPhone) ?></div>
<?php endif; ?>
<?php if ($infoEmail !== ''): ?>
<div class="small"><?= esc($infoEmail) ?></div>
<?php endif; ?>
<?php if ($infoPhone === '' && $infoEmail === ''): ?>
<span class="small">—</span>
<?php endif; ?>
</td>
<td><?= esc($classSectionName) ?></td>
<td>$<?= esc(number_format($feeAmount, 2)) ?></td>
<td><?= esc(!empty($charge['created_at']) ? local_datetime($charge['created_at'], 'm-d-Y H:i') : '') ?></td>
<td class="<?= $waiverSigned ? 'status-paid' : 'status-neutral' ?>">
<?= $waiverSigned ? 'Signed' : 'Unsigned' ?>
</td>
<td class="<?= $feeIsPaid ? 'status-paid' : 'status-unpaid' ?>">
<?= $feeIsPaid ? 'Paid' : 'Unpaid' ?>
</td>
<td class="<?= $isEventPaid ? 'status-paid' : 'status-unpaid' ?>">
<?= $isEventPaid ? 'Paid' : 'Unpaid' ?>
</td>
</tr>
<?php endforeach; ?>
</tbody>
<tfoot>
<tr>
<th colspan="5">Totals</th>
<th>$<?= esc(number_format($totalCharged, 2)) ?></th>
<th colspan="4"><?= esc($totalParticipants) ?> participating</th>
</tr>
</tfoot>
</table>
</section>
<?php endforeach; ?>
<?php endif; ?>
</body>
</html>