Files
alrahma_sunday_school/app/Views/attendance/early_dismissals_add.php
T
2026-02-10 22:11:06 -05:00

205 lines
7.3 KiB
PHP

<?= $this->extend('layout/management_layout') ?>
<?= $this->section('content') ?>
<?= csrf_meta() ?>
<div class="container-xxl py-4">
<div class="d-flex align-items-center justify-content-between flex-wrap gap-2 mb-3">
<h2 class="mb-0">Add Early Dismissal</h2>
<a href="<?= site_url('attendance/early-dismissals') ?>" class="btn btn-outline-secondary">Back to List</a>
</div>
<?php if (session()->getFlashdata('error')): ?>
<div class="alert alert-danger"><?= session()->getFlashdata('error') ?></div>
<?php endif; ?>
<?php if (session()->getFlashdata('message')): ?>
<div class="alert alert-success"><?= session()->getFlashdata('message') ?></div>
<?php endif; ?>
<div class="card shadow-sm">
<div class="card-body">
<form method="post" action="<?= site_url('attendance/early-dismissals') ?>">
<?= csrf_field() ?>
<div class="row gy-3">
<div class="col-md-5 position-relative">
<label class="form-label">Student</label>
<input type="text" id="studentSearch" class="form-control" autocomplete="off" placeholder="Type student name to search…" value="<?= esc(old('student_label', '')) ?>">
<input type="hidden" name="student_id" id="studentId" value="<?= esc(old('student_id', '')) ?>">
<div id="studentSuggestions" class="list-group position-absolute w-100" style="z-index:1000; max-height: 240px; overflow:auto;"></div>
<div class="form-text">Start typing first or last name, then pick from the list.</div>
</div>
<div class="col-md-3">
<label class="form-label">Date (Sunday)</label>
<input type="date" name="date" class="form-control" value="<?= esc(old('date', $defaultDate ?? local_date(utc_now(), 'Y-m-d'))) ?>" required>
</div>
<div class="col-md-2">
<label class="form-label">Dismissal Time</label>
<input type="time" name="dismiss_time" class="form-control" value="<?= esc(old('dismiss_time', '')) ?>" required>
</div>
<div class="col-12">
<label class="form-label">Reason (optional)</label>
<textarea name="reason" class="form-control" rows="2" placeholder="e.g., Family appointment"><?= esc(old('reason', '')) ?></textarea>
</div>
<div class="col-12 d-flex gap-2">
<button type="submit" class="btn btn-primary">Save Early Dismissal</button>
<a href="<?= site_url('attendance/early-dismissals') ?>" class="btn btn-outline-secondary">Cancel</a>
</div>
</div>
</form>
</div>
</div>
<div class="mt-3 small text-muted">
Note: Early dismissals do not change daily present/absent status; they are shown for staff awareness and pickup coordination.
</div>
</div>
<?= $this->endSection() ?>
<?= $this->section('scripts') ?>
<?php
// Prepare a minimal dataset for client-side search
$jsStudents = array_map(static function($s) {
return [
'id' => (int)($s['id'] ?? 0),
'firstname'=> (string)($s['firstname'] ?? ''),
'lastname' => (string)($s['lastname'] ?? ''),
'section' => (string)($s['class_section_name'] ?? ''),
];
}, (array)($students ?? []));
?>
<script>
(function() {
const STUDENTS = <?= json_encode($jsStudents, JSON_HEX_TAG|JSON_HEX_APOS|JSON_HEX_AMP|JSON_HEX_QUOT) ?>;
const input = document.getElementById('studentSearch');
const hiddenId = document.getElementById('studentId');
const list = document.getElementById('studentSuggestions');
if (!input || !hiddenId || !list) return;
const norm = (s) => (s || '').toLowerCase().trim();
const buildKey = (o) => {
const first = norm(o.firstname);
const last = norm(o.lastname);
const sec = norm(o.section);
return `${first} ${last} ${last} ${first} ${sec}`;
};
// Build enriched search dataset
const DATA = STUDENTS.map(s => ({
...s,
key: buildKey(s),
label: `${s.lastname}, ${s.firstname}${s.section ? ' — ' + s.section : ''}`,
display: `${s.firstname} ${s.lastname}${s.section ? ' — ' + s.section : ''}`
}));
let activeIndex = -1;
function render(q) {
const query = norm(q);
list.innerHTML = '';
list.style.display = 'none';
activeIndex = -1;
if (query.length === 0) {
// If user clears, clear selected student
hiddenId.value = '';
return;
}
// Clear selection whenever user types
hiddenId.value = '';
const matches = DATA.filter(s => s.key.includes(query)).slice(0, 20);
if (matches.length === 0) return;
for (let i = 0; i < matches.length; i++) {
const m = matches[i];
const btn = document.createElement('button');
btn.type = 'button';
btn.className = 'list-group-item list-group-item-action';
btn.textContent = m.display;
btn.dataset.id = String(m.id);
btn.addEventListener('click', () => selectStudent(m));
list.appendChild(btn);
}
list.style.display = '';
}
function selectStudent(s) {
hiddenId.value = s.id;
input.value = s.display;
list.innerHTML = '';
list.style.display = 'none';
}
input.addEventListener('input', () => render(input.value));
// Keyboard navigation
input.addEventListener('keydown', (e) => {
const items = Array.from(list.querySelectorAll('.list-group-item'));
if (e.key === 'ArrowDown') {
e.preventDefault();
if (items.length === 0) return;
activeIndex = (activeIndex + 1) % items.length;
updateActive(items);
} else if (e.key === 'ArrowUp') {
e.preventDefault();
if (items.length === 0) return;
activeIndex = (activeIndex - 1 + items.length) % items.length;
updateActive(items);
} else if (e.key === 'Enter') {
if (activeIndex >= 0 && items[activeIndex]) {
e.preventDefault();
const id = parseInt(items[activeIndex].dataset.id, 10);
const s = DATA.find(x => x.id === id);
if (s) selectStudent(s);
}
} else if (e.key === 'Escape') {
list.innerHTML = '';
list.style.display = 'none';
}
});
function updateActive(items) {
items.forEach((el, idx) => {
if (idx === activeIndex) {
el.classList.add('active');
el.scrollIntoView({ block: 'nearest' });
} else {
el.classList.remove('active');
}
});
}
// Hide suggestions on outside click
document.addEventListener('click', (e) => {
if (!list.contains(e.target) && e.target !== input) {
list.innerHTML = '';
list.style.display = 'none';
}
});
// Client-side submit validation: ensure a student was selected
const form = input.closest('form');
if (form) {
form.addEventListener('submit', (e) => {
if (!hiddenId.value) {
e.preventDefault();
render(input.value);
input.classList.add('is-invalid');
if (!list.style.display || list.innerHTML === '') {
// No matches; show simple feedback
alert('Please select a student from the list.');
}
}
});
input.addEventListener('input', () => input.classList.remove('is-invalid'));
}
})();
</script>
<?= $this->endSection() ?>