
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
// 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() ?>
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() ?>