702 lines
34 KiB
PHP
702 lines
34 KiB
PHP
<?= $this->extend('layout/management_layout') ?>
|
||
<?= $this->section('content') ?>
|
||
|
||
<div class="container mt-5">
|
||
<h3>Event Charges</h3>
|
||
|
||
<?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>
|
||
<p class="mb-1 text-muted small">
|
||
<?= esc($selectedEvent['description'] ?: 'No description provided for this event.') ?>
|
||
</p>
|
||
<?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">
|
||
<button type="button" class="btn btn-outline-primary" data-bs-toggle="modal" data-bs-target="#externalParticipantModal">
|
||
Add non-school participant
|
||
</button>
|
||
<div class="form-text">After adding participants, click Submit to save them to the database.</div>
|
||
<div id="externalParticipantsHidden"></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 foreach ($grouped as $eventId => $data): ?>
|
||
<?php $eventLabel = $data['label']; ?>
|
||
<?php $rows = $data['rows']; ?>
|
||
<div class="card mb-4 event-card" data-event-id="<?= esc($eventId) ?>">
|
||
<div class="card-header bg-light fw-semibold">
|
||
<?= esc($eventLabel) ?>
|
||
</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">
|
||
<thead class="table-light">
|
||
<tr>
|
||
<th>ID</th>
|
||
<th>Parent Name</th>
|
||
<th>Student Name</th>
|
||
<th>External Info</th>
|
||
<th>Class Section</th>
|
||
<th>Charged Amount</th>
|
||
<th>Created</th>
|
||
<th>Fees Paid</th>
|
||
<th>Payment</th>
|
||
<th>Actions</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<?php foreach ($rows as $charge): ?>
|
||
<?php
|
||
$isParticipating = ($charge['participation'] === 'yes');
|
||
$isEventPaid = !empty($charge['event_paid']);
|
||
?>
|
||
<tr>
|
||
<td><?= 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'] ?? '';
|
||
$standardParentName = trim($charge['parent_firstname'] . ' ' . $charge['parent_lastname']);
|
||
$parentColumnName = $externalParentLabel ?: ($standardParentName ?: '—');
|
||
?>
|
||
<td><?= esc($parentColumnName) ?></td>
|
||
<td>
|
||
<?= esc($displayName) ?>
|
||
<?php if ($externalNote): ?>
|
||
<small class="text-muted d-block"><?= esc($externalNote) ?></small>
|
||
<?php endif; ?>
|
||
</td>
|
||
<td>
|
||
<?php if ($externalParentPhone): ?>
|
||
<div class="text-muted small"><?= esc($externalParentPhone) ?></div>
|
||
<?php else: ?>
|
||
<span class="text-muted small">—</span>
|
||
<?php endif; ?>
|
||
</td>
|
||
<td>
|
||
<?= esc($classSectionNames[$charge['class_section_id'] ?? ''] ?? '—') ?>
|
||
</td>
|
||
<td>$<?= esc(number_format($charge['event_amount'] ?? 0, 2)) ?></td>
|
||
<td><?= 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;
|
||
$feeIsPaid = $isParticipating && ($isEventPaid || ($hasBalanceRecord && $parentBalance <= 0));
|
||
?>
|
||
<td class="text-center">
|
||
<?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">
|
||
<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="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() ?>
|
||
<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="5">
|
||
<span class="badge bg-secondary">
|
||
<?= esc($totalParticipants) ?> participating
|
||
</span>
|
||
</th>
|
||
</tr>
|
||
</tfoot>
|
||
</table>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<?php endforeach; ?>
|
||
<?php endif; ?>
|
||
</div>
|
||
|
||
<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 student’s 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>
|
||
|
||
<?= $this->endSection() ?>
|
||
|
||
<?= $this->section('scripts') ?>
|
||
<script>
|
||
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 $externalHidden = $('#externalParticipantsHidden');
|
||
const storageKey = 'eventChargesExternalParticipants';
|
||
let externalDrafts = [];
|
||
let editingKey = null;
|
||
|
||
const escapeHtml = (value) => {
|
||
const s = String(value ?? '');
|
||
return s.replace(/&/g, '&')
|
||
.replace(/</g, '<')
|
||
.replace(/>/g, '>')
|
||
.replace(/"/g, '"')
|
||
.replace(/'/g, ''');
|
||
};
|
||
|
||
function buildExternalRow(entry) {
|
||
const key = entry.key;
|
||
const isPaid = !!entry.paid;
|
||
const firstEsc = escapeHtml(entry.firstname);
|
||
const lastEsc = escapeHtml(entry.lastname);
|
||
const noteEsc = escapeHtml(entry.note);
|
||
const parentFirstEsc = escapeHtml(entry.parentFirstname);
|
||
const parentLastEsc = escapeHtml(entry.parentLastname);
|
||
const parentPhoneEsc = escapeHtml(entry.parentPhone);
|
||
const parentEmailEsc = escapeHtml(entry.parentEmail);
|
||
const label = (firstEsc || lastEsc)
|
||
? `${firstEsc || ''} ${lastEsc || ''}`.trim()
|
||
: 'Unnamed participant';
|
||
const parentLabel = (parentFirstEsc || parentLastEsc)
|
||
? `<div class="text-muted small">Parent: ${parentFirstEsc} ${parentLastEsc}</div>`
|
||
: '';
|
||
const phoneLabel = parentPhoneEsc
|
||
? `<div class="text-muted small">${parentPhoneEsc}</div>`
|
||
: '';
|
||
const parentNameForRow = entry.parentDisplayName || '—';
|
||
const amountDisplay = ((entry.eventAmount ?? 0) || 0.0).toFixed(2);
|
||
|
||
const $wrapper = $(`
|
||
<div data-key="${key}">
|
||
<input type="hidden" name="external_participants[${key}][firstname]" value="${firstEsc}">
|
||
<input type="hidden" name="external_participants[${key}][lastname]" value="${lastEsc}">
|
||
<input type="hidden" name="external_participants[${key}][note]" value="${noteEsc}">
|
||
<input type="hidden" name="external_participants[${key}][parent_firstname]" value="${parentFirstEsc}">
|
||
<input type="hidden" name="external_participants[${key}][parent_lastname]" value="${parentLastEsc}">
|
||
<input type="hidden" name="external_participants[${key}][parent_phone]" value="${parentPhoneEsc}">
|
||
<input type="hidden" name="external_participants[${key}][parent_email]" value="${parentEmailEsc}">
|
||
<input type="hidden" name="external_participants[${key}][paid]" value="${isPaid ? '1' : '0'}">
|
||
</div>
|
||
`);
|
||
$externalHidden.append($wrapper);
|
||
|
||
const $tableBody = $(`#eventTable_${entry.eventId} tbody`);
|
||
if (!$tableBody.length) {
|
||
return;
|
||
}
|
||
const createdDisplay = new Date().toLocaleString();
|
||
const $previewRow = $(`
|
||
<tr class="external-preview table-warning" data-key="${key}" data-event-id="${entry.eventId}">
|
||
<td>—</td>
|
||
<td>${escapeHtml(parentNameForRow)}</td>
|
||
<td>
|
||
<strong>${label}</strong>
|
||
${noteEsc ? `<div class="text-muted small">${noteEsc}</div>` : ''}
|
||
</td>
|
||
<td>
|
||
${phoneLabel}
|
||
${phoneLabel ? '' : '<span class="text-muted small">—</span>'}
|
||
</td>
|
||
<td>External</td>
|
||
<td>$${amountDisplay}</td>
|
||
<td>${createdDisplay}</td>
|
||
<td class="text-center">
|
||
<span class="badge bg-warning text-dark">Pending</span>
|
||
${isPaid ? '<span class="badge bg-success ms-1 external-paid-badge">Paid on save</span>' : '<span class="badge bg-secondary ms-1 external-paid-badge d-none">Paid on save</span>'}
|
||
</td>
|
||
<td class="text-center">
|
||
<div class="form-check d-inline-flex align-items-center gap-2 justify-content-center m-0">
|
||
<input class="form-check-input external-paid-toggle" type="checkbox" data-key="${key}" ${isPaid ? 'checked' : ''}>
|
||
<span class="small text-muted">On save</span>
|
||
</div>
|
||
</td>
|
||
<td class="text-end d-flex gap-2">
|
||
<button type="button" class="btn btn-outline-secondary btn-sm edit-external-participant" data-key="${key}">
|
||
Edit
|
||
</button>
|
||
<button type="button" class="btn btn-outline-danger btn-sm remove-external-participant" data-key="${key}">
|
||
Remove
|
||
</button>
|
||
</td>
|
||
</tr>
|
||
`);
|
||
$tableBody.append($previewRow);
|
||
}
|
||
|
||
function persistDrafts() {
|
||
localStorage.setItem(storageKey, JSON.stringify(externalDrafts));
|
||
}
|
||
|
||
function saveExternalDraft(entry) {
|
||
entry.key = entry.key ?? `${Date.now()}_${Math.random().toString(36).slice(2)}`;
|
||
entry.paid = !!entry.paid;
|
||
removeExternalEntryFromDom(entry.key);
|
||
externalDrafts = externalDrafts.filter((existing) => existing.key !== entry.key);
|
||
externalDrafts.push(entry);
|
||
persistDrafts();
|
||
buildExternalRow(entry);
|
||
}
|
||
|
||
function removeExternalEntryFromDom(key) {
|
||
$(`tr.external-preview[data-key="${key}"]`).remove();
|
||
$externalHidden.find(`div[data-key="${key}"]`).remove();
|
||
}
|
||
|
||
function removeDraft(key) {
|
||
externalDrafts = externalDrafts.filter((entry) => entry.key !== key);
|
||
persistDrafts();
|
||
}
|
||
|
||
function loadDrafts() {
|
||
const raw = localStorage.getItem(storageKey);
|
||
if (!raw) {
|
||
return;
|
||
}
|
||
try {
|
||
const saved = JSON.parse(raw);
|
||
if (!Array.isArray(saved)) {
|
||
return;
|
||
}
|
||
externalDrafts = saved;
|
||
const currentEvent = $('#event_id').val();
|
||
externalDrafts.forEach((entry) => {
|
||
if (String(entry.eventId) === String(currentEvent)) {
|
||
buildExternalRow(entry);
|
||
}
|
||
});
|
||
} catch (e) {
|
||
console.error('Failed to load external drafts', e);
|
||
}
|
||
}
|
||
|
||
function getDraftByKey(key) {
|
||
return externalDrafts.find((entry) => entry.key === key);
|
||
}
|
||
|
||
function openExternalModalForEntry(key) {
|
||
const entry = getDraftByKey(key);
|
||
if (!entry) {
|
||
return;
|
||
}
|
||
editingKey = key;
|
||
$('#modalExternalFirstName').val(entry.firstname);
|
||
$('#modalExternalLastName').val(entry.lastname);
|
||
$('#modalExternalNote').val(entry.note);
|
||
$('#modalParentFirstName').val(entry.parentFirstname);
|
||
$('#modalParentLastName').val(entry.parentLastname);
|
||
$('#modalParentPhone').val(entry.parentPhone);
|
||
$('#modalParentEmail').val(entry.parentEmail);
|
||
if (modalInstance) {
|
||
modalInstance.show();
|
||
}
|
||
}
|
||
|
||
const modalElement = document.getElementById('externalParticipantModal');
|
||
const modalInstance = modalElement ? new bootstrap.Modal(modalElement) : null;
|
||
if (modalElement) {
|
||
modalElement.addEventListener('hidden.bs.modal', () => {
|
||
editingKey = null;
|
||
});
|
||
}
|
||
function clearModalInputs() {
|
||
$('#modalExternalFirstName').val('');
|
||
$('#modalExternalLastName').val('');
|
||
$('#modalExternalNote').val('');
|
||
$('#modalParentFirstName').val('');
|
||
$('#modalParentLastName').val('');
|
||
$('#modalParentPhone').val('');
|
||
$('#modalParentEmail').val('');
|
||
}
|
||
|
||
$('#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 eventAmount = parseFloat($('#event_id option:selected').data('amount')) || 0;
|
||
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;
|
||
}
|
||
const parentName = explicitParentName;
|
||
const draftEntry = editingKey ? getDraftByKey(editingKey) : null;
|
||
const entry = {
|
||
firstname: firstName,
|
||
lastname: lastName,
|
||
note: note,
|
||
parentFirstname: parentFirst,
|
||
parentLastname: parentLast,
|
||
parentPhone: parentPhone,
|
||
parentEmail: parentEmail,
|
||
eventId: (draftEntry && draftEntry.eventId) ? draftEntry.eventId : eventId,
|
||
eventAmount: (draftEntry && draftEntry.eventAmount) ? draftEntry.eventAmount : eventAmount,
|
||
parentDisplayName: (draftEntry && draftEntry.parentDisplayName) ? draftEntry.parentDisplayName : parentName,
|
||
paid: (draftEntry && typeof draftEntry.paid !== 'undefined') ? !!draftEntry.paid : false,
|
||
};
|
||
if (editingKey) {
|
||
entry.key = editingKey;
|
||
}
|
||
saveExternalDraft(entry);
|
||
editingKey = null;
|
||
clearModalInputs();
|
||
if (modalInstance) {
|
||
modalInstance.hide();
|
||
}
|
||
});
|
||
|
||
$(document).on('click', '.remove-external-participant', function() {
|
||
const $entry = $(this).closest('[data-key]');
|
||
const key = $entry.data('key');
|
||
removeExternalEntryFromDom(key);
|
||
if (key) {
|
||
removeDraft(key);
|
||
}
|
||
});
|
||
$(document).on('click', '.edit-external-participant', function() {
|
||
const key = $(this).data('key');
|
||
if (key) {
|
||
openExternalModalForEntry(key);
|
||
}
|
||
});
|
||
$(document).on('change', '.external-paid-toggle', function() {
|
||
const key = $(this).data('key');
|
||
const paid = !!this.checked;
|
||
if (!key) {
|
||
return;
|
||
}
|
||
const entry = getDraftByKey(key);
|
||
if (entry) {
|
||
entry.paid = paid;
|
||
persistDrafts();
|
||
}
|
||
$externalHidden
|
||
.find(`div[data-key="${key}"] input[name="external_participants[${key}][paid]"]`)
|
||
.val(paid ? '1' : '0');
|
||
const $badge = $(`tr.external-preview[data-key="${key}"] .external-paid-badge`);
|
||
if (paid) {
|
||
$badge.removeClass('d-none').addClass('bg-success').text('Paid on save');
|
||
} else {
|
||
$badge.addClass('d-none');
|
||
}
|
||
});
|
||
$('#event-participant-form').on('submit', function() {
|
||
localStorage.removeItem(storageKey);
|
||
});
|
||
loadDrafts();
|
||
});
|
||
</script>
|
||
<?= $this->endSection() ?>
|