This commit is contained in:
@@ -1,3 +1,35 @@
|
||||
|
||||
Yesterday 11:12 PM
|
||||
write sql querry to update school_year = 2025-2026
|
||||
|
||||
UPDATE your_table_name
|
||||
SET school_year = '2025-2026'
|
||||
WHERE your_condition;
|
||||
To update every row:
|
||||
|
||||
UPDATE your_table_name
|
||||
SET school_year = '2025-2026';
|
||||
Use the second version only if you genuinely intend to rewrite the entire table, because databases are remarkably obedient when humans forget WHERE.
|
||||
|
||||
|
||||
Today 12:49 AM
|
||||
Screenshot 2026-07-14 at 12.49.07 AM.png
|
||||
|
||||
Pasted code(6).php
|
||||
File
|
||||
remove the image content from the view
|
||||
|
||||
Removed the School year / Semester filter from the view by deleting:
|
||||
|
||||
<?= $this->include('partials/academic_filter') ?>
|
||||
Pasted_code_6_without_academic_filter.php
|
||||
|
||||
|
||||
Library
|
||||
/
|
||||
Pasted_code_6_without_academic_filter.php
|
||||
|
||||
|
||||
<?= $this->extend('layout/management_layout') ?>
|
||||
<?= $this->section('content') ?>
|
||||
<?php
|
||||
@@ -28,8 +60,6 @@ $formatRole = function (?string $role): string {
|
||||
<h2 class="text-dark mb-3" style="font-family: Arial, sans-serif;">Generate Staff Badges</h2>
|
||||
</div>
|
||||
|
||||
<?= $this->include('partials/academic_filter') ?>
|
||||
|
||||
<div class="d-flex flex-column flex-md-row justify-content-md-end align-items-md-center gap-2">
|
||||
<!-- Submit button associates to form below -->
|
||||
<button type="submit" class="btn btn-success mt-3" form="badgeForm">Generate Badges</button>
|
||||
@@ -38,10 +68,7 @@ $formatRole = function (?string $role): string {
|
||||
<!-- Staff selection -->
|
||||
<form method="post" action="<?= base_url('badge') ?>" target="_blank" id="badgeForm">
|
||||
<?= csrf_field() ?>
|
||||
<input type="hidden" name="school_year" value="<?= esc($selectedYear ?? '') ?>">
|
||||
<!-- hidden container to preserve selections and their data (guaranteed inside form) -->
|
||||
<div id="selectedHiddenInputs" style="display:none"></div>
|
||||
|
||||
|
||||
<table id="staffTable" class="table table-bordered table-striped align-middle w-100">
|
||||
<thead class="table-dark">
|
||||
<tr>
|
||||
@@ -278,11 +305,7 @@ $formatRole = function (?string $role): string {
|
||||
async function refreshCsrf() {
|
||||
try {
|
||||
const params = new URLSearchParams();
|
||||
if (selectedYear) params.append('school_year', selectedYear);
|
||||
const resp = await fetch('<?= base_url('api/printables/badges/status') ?>' + (params.toString() ? ('?' + params.toString()) : ''), {
|
||||
method: 'GET',
|
||||
headers: { 'Accept': 'application/json' }
|
||||
});
|
||||
|
||||
if (!resp.ok) return;
|
||||
const json = await resp.json();
|
||||
if (json && json.csrf_token && json.csrf_hash && csrfInput && json.csrf_token === csrfName) {
|
||||
@@ -351,3 +374,352 @@ $formatRole = function (?string $role): string {
|
||||
});
|
||||
</script>
|
||||
<?= $this->endSection() ?>
|
||||
Library
|
||||
/
|
||||
Pasted_code_6_without_academic_filter.php
|
||||
|
||||
|
||||
<?= $this->extend('layout/management_layout') ?>
|
||||
<?= $this->section('content') ?>
|
||||
<?php
|
||||
// Role formatter for display + posting
|
||||
$formatRole = function (?string $role): string {
|
||||
$role = (string)$role;
|
||||
$role = str_replace(['-', '_'], ' ', $role);
|
||||
$role = preg_replace('/\s+/', ' ', trim($role));
|
||||
if ($role === '') return '';
|
||||
$out = [];
|
||||
foreach (explode(' ', $role) as $w) {
|
||||
if ($w === '') continue;
|
||||
if (preg_match('/^[A-Za-z]{1,3}$/', $w)) {
|
||||
$out[] = strtoupper($w); // TA, PTA, HR, KG...
|
||||
} else {
|
||||
$out[] = ucfirst(strtolower($w)); // Teacher, Assistant, Admin...
|
||||
}
|
||||
}
|
||||
return implode(' ', $out);
|
||||
};
|
||||
?>
|
||||
<div class="container-fluid">
|
||||
<div class="wrapper">
|
||||
<div class="content">
|
||||
|
||||
<div class="text-center mx-auto mb-5 wow" data-wow-delay="0.1s" style="max-width: 600px;">
|
||||
<br>
|
||||
<h2 class="text-dark mb-3" style="font-family: Arial, sans-serif;">Generate Staff Badges</h2>
|
||||
</div>
|
||||
|
||||
<div class="d-flex flex-column flex-md-row justify-content-md-end align-items-md-center gap-2">
|
||||
<!-- Submit button associates to form below -->
|
||||
<button type="submit" class="btn btn-success mt-3" form="badgeForm">Generate Badges</button>
|
||||
<br>
|
||||
</div>
|
||||
<!-- Staff selection -->
|
||||
<form method="post" action="<?= base_url('badge') ?>" target="_blank" id="badgeForm">
|
||||
<?= csrf_field() ?>
|
||||
|
||||
<table id="staffTable" class="table table-bordered table-striped align-middle w-100">
|
||||
<thead class="table-dark">
|
||||
<tr>
|
||||
<th style="width: 70px;">#</th>
|
||||
<th scope="col">Firstname</th>
|
||||
<th scope="col">Lastname</th>
|
||||
<th scope="col">Role</th>
|
||||
<th scope="col">Teacher Class Section</th>
|
||||
<th scope="col" style="width: 140px;">Badge Prints</th>
|
||||
<th scope="col" style="width: 150px;">
|
||||
<div class="form-check m-0">
|
||||
<input class="form-check-input" type="checkbox" id="select-all">
|
||||
<label class="form-check-label" for="select-all">Select All (page)</label>
|
||||
</div>
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<?php if (!empty($users)): ?>
|
||||
<?php $order = 1; ?>
|
||||
<?php foreach ($users as $user): ?>
|
||||
<?php
|
||||
// Pick a representative role (first of CSV or role_name/active_role)
|
||||
$roleRaw = $user['active_role'] ?? $user['role_name'] ?? ($user['roles'] ?? '');
|
||||
if (strpos((string)$roleRaw, ',') !== false) {
|
||||
$parts = array_filter(array_map('trim', explode(',', (string)$roleRaw)));
|
||||
$roleRaw = $parts[0] ?? '';
|
||||
}
|
||||
//$roleLabel = $roleRaw !== '' ? $formatRole($roleRaw) : '-';
|
||||
$roleLabel = $user['role_name'] ?? ($user['roles'] ?? '-'); // both are pre-formatted by $formatRole
|
||||
|
||||
// Normalize user id key for checkbox
|
||||
$uid = $user['user_id'] ?? $user['id'] ?? ($user['users.id'] ?? null);
|
||||
|
||||
// Class section name (optional)
|
||||
$className = !empty($user['class_section_name']) ? (string)$user['class_section_name'] : '';
|
||||
?>
|
||||
<tr
|
||||
data-user-id="<?= esc($uid ?? '') ?>"
|
||||
data-role-label="<?= esc($roleLabel) ?>"
|
||||
data-class-name="<?= esc($className) ?>">
|
||||
<td style="text-align: center;"><?= esc($order++) ?></td>
|
||||
<td><?= esc($user['firstname'] ?? '') ?></td>
|
||||
<td><?= esc($user['lastname'] ?? '') ?></td>
|
||||
<td><?= esc($roleLabel) ?></td>
|
||||
<td><?= $className !== '' ? esc($className) : '-' ?></td>
|
||||
<td><span class="badge bg-secondary prints-badge" data-user-id="<?= esc($uid ?? '') ?>">—</span></td>
|
||||
<td>
|
||||
<?php if ($uid !== null): ?>
|
||||
<div class="form-check m-0">
|
||||
<input type="checkbox" name="user_ids[]" value="<?= esc($uid) ?>" class="form-check-input user-checkbox" id="chk-<?= esc($uid) ?>">
|
||||
<label class="form-check-label" for="chk-<?= esc($uid) ?>">Select</label>
|
||||
</div>
|
||||
<?php else: ?>
|
||||
<span class="text-muted">N/A</span>
|
||||
<?php endif; ?>
|
||||
</td>
|
||||
</tr>
|
||||
<?php endforeach; ?>
|
||||
<?php else: ?>
|
||||
<tr>
|
||||
<td colspan="7" class="text-center text-muted">No staff found for the selected year.</td>
|
||||
</tr>
|
||||
<?php endif; ?>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
</form>
|
||||
<br>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<?= $this->endSection() ?>
|
||||
|
||||
<?= $this->section('scripts') ?>
|
||||
<script>
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
// Track selected user IDs across paging/filtering/sorting
|
||||
const selected = new Set();
|
||||
const formEl = document.getElementById('badgeForm');
|
||||
let clearTimerId = null; // pending auto-clear timer, if any
|
||||
let hiddenContainer = document.getElementById('selectedHiddenInputs');
|
||||
// Ensure hidden container exists inside the form (some earlier layouts had it outside)
|
||||
if (!hiddenContainer || !formEl.contains(hiddenContainer)) {
|
||||
hiddenContainer = document.createElement('div');
|
||||
hiddenContainer.id = 'selectedHiddenInputs';
|
||||
hiddenContainer.style.display = 'none';
|
||||
formEl.appendChild(hiddenContainer);
|
||||
}
|
||||
const selectAll = document.getElementById('select-all');
|
||||
const csrfName = <?= json_encode(csrf_token()) ?>;
|
||||
const csrfInput = document.querySelector(`input[name='${csrfName}']`);
|
||||
|
||||
// Build a meta map (userId -> { role, className }) from the DOM BEFORE DataTables paginates
|
||||
const rowMeta = {};
|
||||
document.querySelectorAll('#staffTable tbody tr').forEach(tr => {
|
||||
const id = tr.getAttribute('data-user-id');
|
||||
if (!id) return;
|
||||
rowMeta[id] = {
|
||||
role: tr.getAttribute('data-role-label') || '',
|
||||
className: tr.getAttribute('data-class-name') || ''
|
||||
};
|
||||
});
|
||||
|
||||
const table = $('#staffTable').DataTable({
|
||||
stateSave: true,
|
||||
pageLength: 100,
|
||||
lengthMenu: [10, 25, 50, 100],
|
||||
order: [
|
||||
[1, 'asc'],
|
||||
[2, 'asc']
|
||||
], // Firstname, Lastname
|
||||
columnDefs: [{
|
||||
targets: 0,
|
||||
searchable: false
|
||||
}, // row #
|
||||
{
|
||||
targets: 6,
|
||||
orderable: false,
|
||||
searchable: false
|
||||
} // checkbox column
|
||||
]
|
||||
});
|
||||
|
||||
// Helper: keep hidden inputs in sync so selections submit even if not on current page
|
||||
function syncHiddenInputs() {
|
||||
hiddenContainer.innerHTML = '';
|
||||
selected.forEach(function(id) {
|
||||
const meta = rowMeta[id] || {
|
||||
role: '',
|
||||
className: ''
|
||||
};
|
||||
// user_ids[]
|
||||
const i1 = document.createElement('input');
|
||||
i1.type = 'hidden';
|
||||
i1.name = 'user_ids[]';
|
||||
i1.value = id;
|
||||
hiddenContainer.appendChild(i1);
|
||||
// roles[ID]
|
||||
const i2 = document.createElement('input');
|
||||
i2.type = 'hidden';
|
||||
i2.name = `roles[${id}]`;
|
||||
i2.value = meta.role;
|
||||
hiddenContainer.appendChild(i2);
|
||||
// classes[ID]
|
||||
const i3 = document.createElement('input');
|
||||
i3.type = 'hidden';
|
||||
i3.name = `classes[${id}]`;
|
||||
i3.value = meta.className;
|
||||
hiddenContainer.appendChild(i3);
|
||||
});
|
||||
}
|
||||
|
||||
// Helper: refresh checkboxes on current page based on Set
|
||||
function syncPageCheckboxes() {
|
||||
const pageNodes = table.rows({
|
||||
page: 'current'
|
||||
}).nodes().to$();
|
||||
let allChecked = true;
|
||||
let anyChecked = false;
|
||||
pageNodes.each(function() {
|
||||
const row = this;
|
||||
const userId = row.getAttribute('data-user-id');
|
||||
const cb = row.querySelector('.user-checkbox');
|
||||
if (!cb || !userId) return;
|
||||
cb.checked = selected.has(userId);
|
||||
if (!cb.checked) allChecked = false;
|
||||
if (cb.checked) anyChecked = true;
|
||||
});
|
||||
// Update header select-all for current page
|
||||
selectAll.checked = allChecked && pageNodes.length > 0;
|
||||
selectAll.indeterminate = !allChecked && anyChecked;
|
||||
}
|
||||
|
||||
// Helper: clear all selections and reset the current page UI
|
||||
function clearSelectionsNow() {
|
||||
selected.clear();
|
||||
// Uncheck visible checkboxes on current page
|
||||
table.rows({ page: 'current' }).every(function () {
|
||||
const row = this.node();
|
||||
const cb = row.querySelector('.user-checkbox');
|
||||
if (cb) cb.checked = false;
|
||||
});
|
||||
// Reset select-all and hidden inputs
|
||||
selectAll.checked = false;
|
||||
selectAll.indeterminate = false;
|
||||
syncHiddenInputs();
|
||||
syncPageCheckboxes();
|
||||
}
|
||||
|
||||
// On draw (paging/sort/search), just refresh checkbox UI states
|
||||
table.on('draw', function() {
|
||||
syncPageCheckboxes();
|
||||
});
|
||||
|
||||
// Delegate checkbox change handling once at the table level (works across redraws)
|
||||
document.getElementById('staffTable').addEventListener('change', function (e) {
|
||||
const target = e.target;
|
||||
if (!target || !target.classList.contains('user-checkbox')) return;
|
||||
const tr = target.closest('tr[data-user-id]');
|
||||
const userId = tr ? tr.getAttribute('data-user-id') : null;
|
||||
if (!userId) return;
|
||||
if (target.checked) selected.add(userId); else selected.delete(userId);
|
||||
syncHiddenInputs();
|
||||
syncPageCheckboxes();
|
||||
// If user interacts, cancel any pending auto-clear to avoid wiping new selections
|
||||
if (clearTimerId !== null) { clearTimeout(clearTimerId); clearTimerId = null; }
|
||||
});
|
||||
|
||||
// Initial sync on first load
|
||||
table.draw(false);
|
||||
|
||||
// Header "Select All (page)" toggler
|
||||
selectAll.addEventListener('change', function() {
|
||||
const check = this.checked;
|
||||
table.rows({
|
||||
page: 'current'
|
||||
}).every(function() {
|
||||
const row = this.node();
|
||||
const userId = row.getAttribute('data-user-id');
|
||||
const cb = row.querySelector('.user-checkbox');
|
||||
if (!cb || !userId) return;
|
||||
cb.checked = check;
|
||||
if (check) selected.add(userId);
|
||||
else selected.delete(userId);
|
||||
});
|
||||
syncHiddenInputs();
|
||||
syncPageCheckboxes();
|
||||
if (clearTimerId !== null) { clearTimeout(clearTimerId); clearTimerId = null; }
|
||||
});
|
||||
|
||||
// Before submit, ensure CSRF is fresh and hidden inputs include all selections
|
||||
const form = document.getElementById('badgeForm');
|
||||
async function refreshCsrf() {
|
||||
try {
|
||||
const params = new URLSearchParams();
|
||||
|
||||
if (!resp.ok) return;
|
||||
const json = await resp.json();
|
||||
if (json && json.csrf_token && json.csrf_hash && csrfInput && json.csrf_token === csrfName) {
|
||||
csrfInput.value = json.csrf_hash;
|
||||
}
|
||||
} catch (e) {
|
||||
// No-op: if refresh fails we'll still attempt submit; server may accept existing token
|
||||
}
|
||||
}
|
||||
|
||||
form.addEventListener('submit', async function(e) {
|
||||
e.preventDefault();
|
||||
if (selected.size === 0) {
|
||||
alert('Please select at least one staff member to generate badges.');
|
||||
return;
|
||||
}
|
||||
syncHiddenInputs();
|
||||
// CSRF is excluded for this endpoint now, but keeping refresh is safe
|
||||
try { await refreshCsrf(); } catch (_) {}
|
||||
// Native submit so browser handles PDF in a new tab
|
||||
form.submit();
|
||||
// Auto-clear selections 5 seconds after generating badges
|
||||
clearTimerId = setTimeout(function() { clearSelectionsNow(); clearTimerId = null; }, 5000);
|
||||
});
|
||||
|
||||
// --- Fetch and render print status ---
|
||||
const selectedYear = <?= json_encode($selectedYear ?? '') ?>;
|
||||
|
||||
function fetchPrintStatus() {
|
||||
const ids = Object.keys(rowMeta);
|
||||
if (ids.length === 0) return;
|
||||
const params = new URLSearchParams();
|
||||
ids.forEach(id => params.append('user_ids[]', id));
|
||||
if (selectedYear) params.append('school_year', selectedYear);
|
||||
|
||||
fetch('<?= base_url('api/printables/badges/status') ?>?' + params.toString(), {
|
||||
method: 'GET',
|
||||
headers: {
|
||||
'Accept': 'application/json'
|
||||
}
|
||||
})
|
||||
.then(r => r.ok ? r.json() : Promise.reject())
|
||||
.then(json => {
|
||||
if (!json || !json.ok || !json.data) return;
|
||||
const data = json.data;
|
||||
Object.keys(rowMeta).forEach(id => {
|
||||
const info = data[id];
|
||||
const badge = document.querySelector(`.prints-badge[data-user-id="${id}"]`);
|
||||
const tr = document.querySelector(`#staffTable tbody tr[data-user-id="${id}"]`);
|
||||
if (!badge || !tr) return;
|
||||
const count = info ? (info.count || 0) : 0;
|
||||
badge.textContent = count > 0 ? `Printed ${count}` : '—';
|
||||
badge.className = 'badge prints-badge ' + (count > 0 ? 'bg-success' : 'bg-secondary');
|
||||
tr.classList.toggle('table-success', count > 0);
|
||||
});
|
||||
// Refresh CSRF for subsequent submissions (so no page reload is needed)
|
||||
if (json.csrf_token && json.csrf_hash && csrfInput && json.csrf_token === csrfName) {
|
||||
csrfInput.value = json.csrf_hash;
|
||||
}
|
||||
})
|
||||
.catch(() => {});
|
||||
}
|
||||
|
||||
fetchPrintStatus();
|
||||
window.addEventListener('focus', fetchPrintStatus);
|
||||
});
|
||||
</script>
|
||||
<?= $this->endSection() ?>
|
||||
Reference in New Issue
Block a user