Files
alrahma_sunday_school/app/Views/administrator/events/event_charges.php
T
2026-05-21 17:00:38 -04:00

805 lines
42 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<?= $this->extend('layout/management_layout') ?>
<?= $this->section('content') ?>
<div class="container mt-5">
<h3>Event Charges</h3>
<style>
.event-card-header {
display: flex;
align-items: center;
justify-content: space-between;
gap: 1rem;
}
.event-card-header-actions {
display: flex;
align-items: center;
gap: 0.5rem;
flex-wrap: wrap;
}
.sortable-header {
display: inline-flex;
align-items: center;
gap: 0.35rem;
padding: 0;
border: 0;
background: transparent;
color: inherit;
font-weight: 600;
text-decoration: none;
}
.sortable-header:hover,
.sortable-header:focus {
color: var(--bs-primary);
text-decoration: none;
}
.sortable-header::after {
content: '↕';
font-size: 0.85rem;
opacity: 0.45;
}
.sortable-header.sorted-asc::after {
content: '↑';
opacity: 1;
}
.sortable-header.sorted-desc::after {
content: '↓';
opacity: 1;
}
@media print {
.no-print {
display: none !important;
}
}
</style>
<?php if (session()->getFlashdata('success')): ?>
<div class="alert alert-success"><?= session()->getFlashdata('success') ?></div>
<?php endif; ?>
<?php if (session()->getFlashdata('error')): ?>
<div class="alert alert-danger"><?= session()->getFlashdata('error') ?></div>
<?php endif; ?>
<?php
$classSectionNames = $classSectionNames ?? [];
$semesterOptions = $semesterOptions ?? [];
$schoolYearOptions = $schoolYearOptions ?? [];
?>
<?php
$filterParentId = $filterParentId ?? 0;
$parentBalances = $parentBalances ?? [];
$selectedEvent = null;
if (!empty($filterEventId)) {
foreach ($events as $event) {
if ((int)$event['id'] === (int)$filterEventId) {
$selectedEvent = $event;
break;
}
}
}
?>
<div class="card mb-4">
<div class="card-body">
<form method="get" action="<?= site_url('administrator/event-charges') ?>" class="row g-3 align-items-end">
<?php if ($filterEventId > 0): ?>
<input type="hidden" name="event_id" value="<?= esc($filterEventId) ?>">
<?php endif; ?>
<?php if ($filterParentId > 0): ?>
<input type="hidden" name="parent_id" value="<?= esc($filterParentId) ?>">
<?php endif; ?>
<div class="col-md-4">
<label for="semester_filter" class="form-label">Semester</label>
<select id="semester_filter" name="semester" class="form-select">
<option value="">-- All semesters --</option>
<?php foreach ($semesterOptions as $option): ?>
<option value="<?= esc($option) ?>" <?= $semester === $option ? 'selected' : '' ?>>
<?= esc($option) ?>
</option>
<?php endforeach; ?>
</select>
</div>
<div class="col-md-4">
<label for="school_year_filter" class="form-label">School Year</label>
<select id="school_year_filter" name="school_year" class="form-select">
<option value="">-- All school years --</option>
<?php foreach ($schoolYearOptions as $option): ?>
<option value="<?= esc($option) ?>" <?= $school_year === $option ? 'selected' : '' ?>>
<?= esc($option) ?>
</option>
<?php endforeach; ?>
</select>
</div>
<div class="col-auto">
<button type="submit" class="btn btn-outline-primary">Filter</button>
</div>
</form>
</div>
</div>
<!-- Add New Charge Form -->
<div class="card mb-4">
<div class="card-body">
<form id="event-participant-form" action="<?= site_url('payment/event_charges') ?>" method="post">
<?= csrf_field() ?>
<input type="hidden" name="school_year" value="<?= esc($school_year) ?>">
<input type="hidden" name="semester" value="<?= esc($semester) ?>">
<div class="row g-3">
<div class="col-md-4">
<label for="event_id" class="form-label">Select Event</label>
<select name="event_id" id="event_id" class="form-select" required>
<option value="">-- All events --</option>
<?php foreach ($events as $event): ?>
<option value="<?= esc($event['id']) ?>" data-amount="<?= esc(number_format($event['amount'] ?? 0, 2, '.', '')) ?>" <?= isset($filterEventId) && $filterEventId == $event['id'] ? 'selected' : '' ?>>
<?= esc($event['event_name']) ?>
</option>
<?php endforeach; ?>
</select>
</div>
<?php if ($selectedEvent): ?>
<div class="col-md-8">
<div class="border rounded bg-light p-3 h-100">
<div class="d-flex justify-content-between align-items-center mb-2">
<h6 class="m-0"><?= esc($selectedEvent['event_name']) ?></h6>
<span class="badge bg-info text-dark">
$<?= esc(number_format($selectedEvent['amount'] ?? 0, 2)) ?> fee
</span>
</div>
<?php $eventDescription = trim((string) ($selectedEvent['description'] ?? '')); ?>
<div class="mb-1">
<button type="button"
class="btn btn-link btn-sm px-0 py-0 text-decoration-none event-description-toggle"
data-bs-toggle="collapse"
data-bs-target="#selectedEventDescription"
aria-expanded="false"
aria-controls="selectedEventDescription"
data-label-collapsed="Show description"
data-label-expanded="Hide description">
Show description
</button>
<div class="collapse mt-2" id="selectedEventDescription">
<div class="text-muted small" style="white-space: pre-line;">
<?= esc($eventDescription !== '' ? $eventDescription : 'No description provided for this event.') ?>
</div>
</div>
</div>
<?php if (!empty($selectedEvent['expiration_date'])): ?>
<small class="text-secondary">
Expires: <?= esc(local_date($selectedEvent['expiration_date'], 'm-d-Y')) ?>
</small>
<?php endif; ?>
</div>
</div>
<?php endif; ?>
<div class="col-md-4">
<label for="parent_id" class="form-label">Parents List</label>
<select id="parent_id" name="parent_id" class="form-select">
<option value="">-- Select Parent --</option>
<?php foreach ($parents as $parent): ?>
<option value="<?= esc($parent['id']) ?>" <?= $filterParentId && $filterParentId == $parent['id'] ? 'selected' : '' ?>>
<?= esc($parent['firstname'] . ' ' . $parent['lastname']) ?> (<?= esc($parent['school_id']) ?>)
</option>
<?php endforeach; ?>
</select>
</div>
</div>
<div class="mt-4" id="studentCheckboxContainer" style="display: none;">
<label class="form-label">Select Students:</label>
<div id="studentList" class="row g-3"></div>
</div>
<div class="mt-4">
<?php if (!in_array(strtolower((string)(session()->get('role') ?? '')), ['parent', 'teacher', 'teacher_assistant'], true)): ?>
<button type="button" class="btn btn-outline-primary" data-bs-toggle="modal" data-bs-target="#externalParticipantModal">
Add non-school participant
</button>
<?php endif; ?>
<div class="form-text">Enrolled students are saved when you click Submit. Non-school participants are saved immediately and will then appear in all browsers.</div>
</div>
<div class="d-flex flex-wrap gap-2 mt-3">
<button type="submit" class="btn btn-primary">Submit</button>
<a href="<?= site_url('administrator/events') ?>" class="btn btn-success">Event List</a>
</div>
</form>
</div>
</div>
<!-- Charge Tables grouped by event -->
<?php
$grouped = [];
foreach ($charges as $charge) {
$eventId = (int)($charge['event_id'] ?? 0);
if (!isset($grouped[$eventId])) {
$grouped[$eventId] = [
'label' => $charge['event_name'] ?? 'N/A',
'rows' => [],
];
}
$grouped[$eventId]['rows'][] = $charge;
}
?>
<?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>
<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
</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">
<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
</a>
</div>
</div>
<div class="card-body p-0">
<?php
$totalParticipants = 0;
$totalCharged = 0.0;
?>
<div class="table-responsive">
<table id="eventTable_<?= esc($eventId) ?>" class="table table-bordered table-striped mb-0 no-mgmt-sticky event-charges-table">
<thead class="table-light">
<tr>
<th><button type="button" class="sortable-header" data-sort-index="0" data-sort-type="number">ID</button></th>
<th><button type="button" class="sortable-header" data-sort-index="1" data-sort-type="text">Parent Name</button></th>
<th><button type="button" class="sortable-header" data-sort-index="2" data-sort-type="text">Student Name</button></th>
<th><button type="button" class="sortable-header" data-sort-index="3" data-sort-type="text">External Info</button></th>
<th><button type="button" class="sortable-header" data-sort-index="4" data-sort-type="text">Class Section</button></th>
<th><button type="button" class="sortable-header" data-sort-index="5" data-sort-type="number">Charged Amount</button></th>
<th><button type="button" class="sortable-header" data-sort-index="6" data-sort-type="date">Created</button></th>
<th><button type="button" class="sortable-header" data-sort-index="7" data-sort-type="text">Waiver</button></th>
<th><button type="button" class="sortable-header" data-sort-index="8" data-sort-type="text">Fees Paid</button></th>
<th><button type="button" class="sortable-header" data-sort-index="9" data-sort-type="text">Payment</button></th>
<th>Actions</th>
</tr>
</thead>
<tbody>
<?php foreach ($rows as $charge): ?>
<?php
$isParticipating = ($charge['participation'] === 'yes');
$isEventPaid = !empty($charge['event_paid']);
$returnParams = [];
if (!empty($semester)) {
$returnParams['semester'] = $semester;
}
if (!empty($school_year)) {
$returnParams['school_year'] = $school_year;
}
if (!empty($filterEventId)) {
$returnParams['event_id'] = $filterEventId;
}
if (!empty($filterParentId)) {
$returnParams['parent_id'] = $filterParentId;
}
$returnTo = site_url('administrator/event-charges')
. (!empty($returnParams) ? '?' . http_build_query($returnParams) : '')
. '#eventTable_' . (int) ($charge['event_id'] ?? 0);
?>
<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']
? trim($charge['student_firstname'] . ' ' . $charge['student_lastname'])
: '';
$externalName = trim(
($charge['external_firstname'] ?? '') . ' ' .
($charge['external_lastname'] ?? '')
);
$displayName = $studentName ?: ($externalName ?: '—');
$externalNote = $charge['external_note'] ?? '';
$externalParentLabel = trim(
($charge['external_parent_firstname'] ?? '') . ' ' .
($charge['external_parent_lastname'] ?? '')
);
$externalParentPhone = $charge['external_parent_phone'] ?? '';
$externalParentEmail = $charge['external_parent_email'] ?? '';
$parentPhone = $charge['parent_cellphone'] ?? '';
$standardParentName = trim($charge['parent_firstname'] . ' ' . $charge['parent_lastname']);
$parentColumnName = $externalParentLabel ?: ($standardParentName ?: '—');
$isExternalParticipant = $externalName !== '';
$infoPhone = $isExternalParticipant ? $externalParentPhone : $parentPhone;
$infoEmail = $isExternalParticipant ? $externalParentEmail : '';
$externalInfoValue = trim(implode(' ', array_filter([
$infoPhone,
$infoEmail,
$externalNote,
])));
?>
<td data-sort-value="<?= esc(strtolower($parentColumnName)) ?>"><?= esc($parentColumnName) ?></td>
<td data-sort-value="<?= esc(strtolower($displayName)) ?>">
<?= esc($displayName) ?>
<?php if ($externalNote): ?>
<small class="text-muted d-block"><?= esc($externalNote) ?></small>
<?php endif; ?>
</td>
<td data-sort-value="<?= esc(strtolower($externalInfoValue)) ?>">
<?php if ($infoPhone): ?>
<div class="text-muted small"><?= esc($infoPhone) ?></div>
<?php endif; ?>
<?php if ($infoEmail): ?>
<div class="text-muted small"><?= esc($infoEmail) ?></div>
<?php endif; ?>
<?php if (!$infoPhone && !$infoEmail): ?>
<span class="text-muted small">—</span>
<?php endif; ?>
</td>
<td data-sort-value="<?= esc(strtolower((string) ($classSectionNames[$charge['class_section_id'] ?? ''] ?? ''))) ?>">
<?= esc($classSectionNames[$charge['class_section_id'] ?? ''] ?? '—') ?>
</td>
<td data-sort-value="<?= esc(number_format((float) ($charge['event_amount'] ?? 0), 2, '.', '')) ?>">$<?= esc(number_format($charge['event_amount'] ?? 0, 2)) ?></td>
<td data-sort-value="<?= esc((string) ($charge['created_at'] ?? '')) ?>"><?= esc(!empty($charge['created_at']) ? local_datetime($charge['created_at'], 'm-d-Y H:i') : '') ?></td>
<?php
$feeAmount = (float) ($charge['event_amount'] ?? 0);
$hasBalanceRecord = array_key_exists((int)$charge['parent_id'], $parentBalances);
$parentBalance = $hasBalanceRecord ? (float)$parentBalances[$charge['parent_id']] : null;
$waiverSigned = !empty($charge['waiver_signed']);
$feeIsPaid = $isParticipating && ($isEventPaid || ($hasBalanceRecord && $parentBalance <= 0));
?>
<td class="text-center" data-sort-value="<?= $waiverSigned ? 'signed' : 'unsigned' ?>">
<form method="post" action="<?= site_url('administrator/event-charges/waiver/' . esc($charge['id'])) ?>" class="d-inline-flex align-items-center">
<?= csrf_field() ?>
<input type="hidden" name="return_to" value="<?= esc($returnTo) ?>">
<input type="hidden" name="waiver_signed" value="<?= $waiverSigned ? '1' : '0' ?>">
<input type="checkbox" class="form-check-input" id="eventWaiver_<?= esc($charge['id']) ?>"
<?= $waiverSigned ? 'checked' : '' ?>
onchange="this.form.waiver_signed.value = this.checked ? 1 : 0; this.form.submit();">
<label class="form-check-label ms-2" for="eventWaiver_<?= esc($charge['id']) ?>" aria-hidden="true">
<?= $waiverSigned ? 'Signed' : 'Unsigned' ?>
</label>
</form>
</td>
<td class="text-center" data-sort-value="<?= $feeIsPaid ? 'paid' : 'unpaid' ?>">
<?php if ($feeIsPaid): ?>
<span class="badge bg-success">Paid</span>
<?php else: ?>
<span class="badge bg-danger">Unpaid</span>
<?php endif; ?>
</td>
<td class="text-center" data-sort-value="<?= $isEventPaid ? 'paid' : 'unpaid' ?>">
<form method="post" action="<?= site_url('administrator/event-charges/payment/' . esc($charge['id'])) ?>" class="d-inline-flex align-items-center">
<?= csrf_field() ?>
<input type="hidden" name="return_to" value="<?= esc($returnTo) ?>">
<input type="hidden" name="paid" value="<?= $feeIsPaid ? '1' : '0' ?>">
<input type="checkbox" class="form-check-input" id="eventPayment_<?= esc($charge['id']) ?>"
<?= $isEventPaid ? 'checked' : '' ?>
onchange="this.form.paid.value = this.checked ? 1 : 0; this.form.submit();">
<label class="form-check-label ms-2" for="eventPayment_<?= esc($charge['id']) ?>" aria-hidden="true">Paid</label>
</form>
</td>
<td class="text-nowrap">
<div class="d-flex gap-1 align-items-center">
<a href="<?= site_url('administrator/event-charges') ?>?event_id=<?= esc($charge['event_id']) ?>&parent_id=<?= esc($charge['parent_id']) ?>#event-participant-form" class="btn btn-outline-primary btn-sm">
Edit
</a>
<form action="<?= site_url('administrator/event-charges/remove/' . esc($charge['id'])) ?>" method="post" onsubmit="return confirm('Remove this participation and update the charge?');">
<?= csrf_field() ?>
<input type="hidden" name="return_to" value="<?= esc($returnTo) ?>">
<button type="submit" class="btn btn-outline-danger btn-sm">Remove</button>
</form>
</div>
</td>
</tr>
<?php
if ($isParticipating) {
$totalParticipants++;
$totalCharged += (float) ($charge['event_amount'] ?? 0);
}
?>
<?php endforeach; ?>
</tbody>
<tfoot class="table-light">
<tr>
<th colspan="4">Totals</th>
<th>$<?= esc(number_format($totalCharged, 2)) ?></th>
<th colspan="6">
<span class="badge bg-secondary">
<?= esc($totalParticipants) ?> participating
</span>
</th>
</tr>
</tfoot>
</table>
</div>
</div>
</div>
<?php endforeach; ?>
</div>
<?php endif; ?>
</div>
<?php if (!in_array(strtolower((string)(session()->get('role') ?? '')), ['parent', 'teacher', 'teacher_assistant'], true)): ?>
<div class="modal fade" id="externalParticipantModal" tabindex="-1" aria-labelledby="externalParticipantModalLabel" aria-hidden="true">
<div class="modal-dialog">
<div class="modal-content">
<form id="externalParticipantForm" class="needs-validation" novalidate>
<?= csrf_field() ?>
<div class="modal-header">
<h5 class="modal-title" id="externalParticipantModalLabel">Add Non-School Participant</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body">
<p class="small text-muted">Provide the students name and the accompanying guardian/contact details.</p>
<div class="mb-3">
<label class="form-label">Student / Kid Info</label>
<div class="row g-3">
<div class="col-md-6">
<input type="text" class="form-control" id="modalExternalFirstName" placeholder="First name" required>
</div>
<div class="col-md-6">
<input type="text" class="form-control" id="modalExternalLastName" placeholder="Last name" required>
</div>
</div>
</div>
<div class="mb-3">
<label class="form-label">Parent / Contact Info</label>
<div class="row g-3">
<div class="col-md-6">
<input type="text" class="form-control" id="modalParentFirstName" placeholder="Parent first name" required>
</div>
<div class="col-md-6">
<input type="text" class="form-control" id="modalParentLastName" placeholder="Parent last name" required>
</div>
<div class="col-12">
<input type="text" class="form-control" id="modalParentPhone" placeholder="Parent phone or contact">
</div>
<div class="col-12">
<input type="email" class="form-control" id="modalParentEmail" placeholder="Parent email" required>
</div>
</div>
</div>
<div class="mb-3">
<label class="form-label" for="modalExternalNote">Note <span class="text-muted small">(optional)</span></label>
<textarea rows="2" class="form-control" id="modalExternalNote" placeholder="Additional info (e.g., guest, sibling, etc.)"></textarea>
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Cancel</button>
<button type="button" class="btn btn-primary" id="externalParticipantSave">
Add to list
</button>
</div>
</form>
</div>
</div>
</div>
<?php endif; ?>
<?= $this->endSection() ?>
<?= $this->section('scripts') ?>
<script>
document.addEventListener('DOMContentLoaded', function () {
document.querySelectorAll('.event-description-toggle').forEach(function (button) {
const targetSelector = button.getAttribute('data-bs-target');
const target = targetSelector ? document.querySelector(targetSelector) : null;
if (!target) {
return;
}
target.addEventListener('show.bs.collapse', function () {
button.textContent = button.dataset.labelExpanded || 'Hide description';
});
target.addEventListener('hide.bs.collapse', function () {
button.textContent = button.dataset.labelCollapsed || 'Show description';
});
});
});
function loadStudentsWithCharges() {
let parentId = $('#parent_id').val();
let eventId = $('#event_id').val();
let semester = '<?= esc($semester) ?>';
let schoolYear = '<?= esc($school_year) ?>';
if (parentId && eventId) {
$.get('<?= base_url('administrator/get-students-with-charges') ?>', {
parent_id: parentId,
event_id: eventId,
semester: semester,
school_year: schoolYear
}, function(data) {
let container = $('#studentList');
container.empty();
if (data.length > 0) {
$('#studentCheckboxContainer').show();
data.forEach(student => {
container.append(`
<div class="col-md-4">
<div class="form-check">
<input type="hidden" name="participation[${student.id}]" value="no">
<input class="form-check-input" type="checkbox" name="participation[${student.id}]" value="yes" id="student_${student.id}" ${student.charged ? 'checked' : ''}>
<label class="form-check-label" for="student_${student.id}">
${student.name}
</label>
</div>
</div>
`);
});
} else {
$('#studentCheckboxContainer').hide();
}
}, 'json');
} else {
$('#studentCheckboxContainer').hide();
$('#studentList').empty();
}
}
const capitalizeName = (value) => {
if (!value) return '';
return value
.split(' ')
.map(part => {
const trimmed = part.trim();
if (!trimmed) return '';
return trimmed.charAt(0).toUpperCase() + trimmed.slice(1).toLowerCase();
})
.filter(Boolean)
.join(' ');
};
$(function() {
$('#parent_id').on('change', loadStudentsWithCharges);
$('#event_id').on('change', function() {
let eventId = $(this).val();
let url = new URL(window.location.href);
if (eventId) {
url.searchParams.set('event_id', eventId);
} else {
url.searchParams.delete('event_id');
}
window.location.href = url.toString();
});
if ($('#event_id').val() && $('#parent_id').val()) {
loadStudentsWithCharges();
}
const $externalSaveButton = $('#externalParticipantSave');
const modalElement = document.getElementById('externalParticipantModal');
const modalInstance = modalElement ? new bootstrap.Modal(modalElement) : null;
function clearModalInputs() {
$('#modalExternalFirstName').val('');
$('#modalExternalLastName').val('');
$('#modalExternalNote').val('');
$('#modalParentFirstName').val('');
$('#modalParentLastName').val('');
$('#modalParentPhone').val('');
$('#modalParentEmail').val('');
}
function appendHiddenField(form, name, value) {
const input = document.createElement('input');
input.type = 'hidden';
input.name = name;
input.value = value;
form.appendChild(input);
}
function submitExternalParticipant(payload) {
const form = document.createElement('form');
form.method = 'post';
form.action = '<?= site_url('payment/event_charges') ?>';
appendHiddenField(form, '<?= csrf_token() ?>', '<?= csrf_hash() ?>');
appendHiddenField(form, 'school_year', '<?= esc($school_year) ?>');
appendHiddenField(form, 'semester', '<?= esc($semester) ?>');
appendHiddenField(form, 'event_id', payload.eventId);
appendHiddenField(form, 'external_participants[new][firstname]', payload.firstname);
appendHiddenField(form, 'external_participants[new][lastname]', payload.lastname);
appendHiddenField(form, 'external_participants[new][note]', payload.note);
appendHiddenField(form, 'external_participants[new][parent_firstname]', payload.parentFirstname);
appendHiddenField(form, 'external_participants[new][parent_lastname]', payload.parentLastname);
appendHiddenField(form, 'external_participants[new][parent_phone]', payload.parentPhone);
appendHiddenField(form, 'external_participants[new][parent_email]', payload.parentEmail);
appendHiddenField(form, 'external_participants[new][waiver_signed]', '0');
appendHiddenField(form, 'external_participants[new][paid]', '0');
document.body.appendChild(form);
form.submit();
}
function getSortValue(row, columnIndex, sortType) {
const cell = row.children[columnIndex];
if (!cell) {
return sortType === 'number' ? 0 : '';
}
const rawValue = cell.getAttribute('data-sort-value');
const fallbackValue = (cell.textContent || '').trim();
const value = rawValue !== null ? rawValue : fallbackValue;
if (sortType === 'number') {
const parsed = parseFloat(String(value).replace(/[^0-9.-]/g, ''));
return Number.isNaN(parsed) ? 0 : parsed;
}
if (sortType === 'date') {
const time = Date.parse(value);
return Number.isNaN(time) ? 0 : time;
}
return String(value).toLowerCase();
}
function updateSortIndicators(table, columnIndex, direction) {
$(table).find('.sortable-header').each(function() {
const isCurrent = Number($(this).data('sortIndex')) === Number(columnIndex);
$(this)
.toggleClass('sorted-asc', isCurrent && direction === 'asc')
.toggleClass('sorted-desc', isCurrent && direction === 'desc')
.attr('aria-sort', isCurrent ? direction : 'none');
});
}
function sortEventTable(table, columnIndex, sortType, direction) {
if (!table || !table.tBodies.length) {
return;
}
const tbody = table.tBodies[0];
const rows = Array.from(tbody.rows);
rows.sort((a, b) => {
const first = getSortValue(a, columnIndex, sortType);
const second = getSortValue(b, columnIndex, sortType);
if (first < second) {
return direction === 'asc' ? -1 : 1;
}
if (first > second) {
return direction === 'asc' ? 1 : -1;
}
return 0;
});
rows.forEach((row) => tbody.appendChild(row));
table.dataset.sortColumn = String(columnIndex);
table.dataset.sortType = sortType;
table.dataset.sortDirection = direction;
updateSortIndicators(table, columnIndex, direction);
}
function applyActiveSort(table) {
if (!table || !table.dataset || !table.dataset.sortColumn) {
return;
}
sortEventTable(
table,
Number(table.dataset.sortColumn),
table.dataset.sortType || 'text',
table.dataset.sortDirection || 'asc'
);
}
function getTableRowOrder(table) {
if (!table || !table.tBodies.length) {
return [];
}
return Array.from(table.tBodies[0].rows)
.map((row) => String(row.dataset.chargeId || '').trim())
.filter(Boolean);
}
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() {
const $button = $(this);
const table = $button.closest('table').get(0);
const columnIndex = Number($button.data('sortIndex'));
const sortType = String($button.data('sortType') || 'text');
const currentColumn = Number(table.dataset.sortColumn);
const currentDirection = table.dataset.sortDirection || 'asc';
const direction = currentColumn === columnIndex && currentDirection === 'asc' ? 'desc' : 'asc';
sortEventTable(table, columnIndex, sortType, direction);
});
$('.pdf-print-link').on('click', function() {
this.href = buildPdfUrl(this);
});
$('#externalParticipantSave').on('click', function() {
const firstName = capitalizeName($('#modalExternalFirstName').val().trim());
const lastName = capitalizeName($('#modalExternalLastName').val().trim());
const note = $('#modalExternalNote').val().trim();
if (!firstName && !lastName) {
alert('Please provide at least a first or last name for the non-school participant.');
return;
}
const parentFirst = capitalizeName($('#modalParentFirstName').val().trim());
const parentLast = capitalizeName($('#modalParentLastName').val().trim());
const parentPhone = $('#modalParentPhone').val().trim();
const parentEmail = $('#modalParentEmail').val().trim();
const eventId = $('#event_id').val();
if (!eventId) {
alert('Select an event before adding a non-school participant.');
return;
}
const explicitParentName = `${parentFirst} ${parentLast}`.trim();
if (!explicitParentName) {
alert('Please enter the external kid parent name.');
return;
}
if (!parentEmail) {
alert('Please enter the external kid parent email.');
return;
}
submitExternalParticipant({
firstname: firstName,
lastname: lastName,
note: note,
parentFirstname: parentFirst,
parentLastname: parentLast,
parentPhone: parentPhone,
parentEmail: parentEmail,
eventId: eventId
});
clearModalInputs();
});
});
</script>
<?= $this->endSection() ?>