fix enrollment and registration email send
Deploy to Shared Hosting / Shared hosting deploy (push) Failing after 49s
Tests / PHPUnit (push) Failing after 1m20s

This commit is contained in:
root
2026-08-28 16:16:59 -04:00
parent 3133b3c584
commit d2cb159451
6 changed files with 118 additions and 73 deletions
@@ -246,6 +246,7 @@ foreach (($activeExceptions ?? []) as $exceptionRow) {
}
}
$emailExampleCount = count($emailExamples ?? []);
$failedEmailCount = count(array_filter($emailExamples ?? [], static fn ($example): bool => ($example['delivery_status'] ?? '') === 'failed'));
$launchReady = empty($launchState['missing']);
$launchApproved = !empty($launchState['approved']);
$defaultTab = 'work';
@@ -726,7 +727,15 @@ $reasonCodes = is_array($exceptionReasonCodes ?? null) ? $exceptionReasonCodes :
<input class="form-check-input" type="checkbox" id="force_resend" name="force_resend" value="1">
<label class="form-check-label small" for="force_resend">Also resend emails already sent</label>
</div>
<button type="submit" class="btn btn-danger" <?= !$launchApproved || !$launchReady || $emailExampleCount === 0 ? 'disabled' : '' ?>>Send real emails</button>
<div class="d-flex flex-wrap gap-2">
<button type="submit" class="btn btn-danger" <?= !$launchApproved || !$launchReady || $emailExampleCount === 0 ? 'disabled' : '' ?>>Send real emails</button>
</div>
</form>
<form class="mt-2" method="post" action="<?= site_url('administrator/enrollment-admin/send-registration-emails') ?>" onsubmit="return confirm('Retry only failed registration emails for <?= esc((string) ($schoolYear ?? ''), 'js') ?>?');">
<?= csrf_field() ?>
<input type="hidden" name="school_year" value="<?= esc($schoolYear ?? '') ?>">
<input type="hidden" name="failed_only" value="1">
<button type="submit" class="btn btn-outline-danger" <?= !$launchApproved || !$launchReady || $failedEmailCount === 0 ? 'disabled' : '' ?>>Send failed emails only<?= $failedEmailCount > 0 ? ' (' . (int) $failedEmailCount . ')' : '' ?></button>
</form>
</div>
</div>
+14 -64
View File
@@ -23,7 +23,7 @@
<div id="assignRoleAlert" class="alert alert-danger d-none" role="alert"></div>
<div class="table-responsive">
<table id="usersTable" class="display table table-bordered table-striped align-middle">
<table id="usersTable" class="display table table-bordered table-striped align-middle no-dt-fixedheader" data-no-dt-fixedheader>
<thead class="table-dark">
<tr>
<th>User ID</th>
@@ -78,53 +78,6 @@
<?= $this->section('scripts') ?>
<script>
document.addEventListener('DOMContentLoaded', () => {
// --- FixedHeader helpers ---
function getFixedHeaderOffset() {
let total = 0;
const stack = [];
const header = document.querySelector('header.navbar.sticky-top, header.navbar.fixed-top');
if (header) stack.push(header);
const mgmt = document.getElementById('navbarManagement');
if (mgmt && (mgmt.classList.contains('sticky-top') || mgmt.classList.contains('fixed-top'))) stack.push(mgmt);
document.querySelectorAll('.navbar.sticky-top, .navbar.fixed-top').forEach(el => { if (!stack.includes(el)) stack.push(el); });
stack.forEach(el => { const h = el.offsetHeight || el.getBoundingClientRect().height || 0; total += Math.max(0, Math.round(h)); });
return total;
}
function loadScript(src, id) {
return new Promise((resolve, reject) => {
if (id && document.getElementById(id)) return resolve();
const s = document.createElement('script');
if (id) s.id = id;
s.src = src;
s.onload = resolve;
s.onerror = reject;
document.head.appendChild(s);
});
}
function loadCss(href, id) {
return new Promise((resolve) => {
if (id && document.getElementById(id)) return resolve();
const l = document.createElement('link');
if (id) l.id = id;
l.rel = 'stylesheet';
l.href = href;
l.onload = resolve;
document.head.appendChild(l);
});
}
function ensureFixedHeaderAssets() {
const hasFH = !!(window.jQuery && window.jQuery.fn && window.jQuery.fn.dataTable && window.jQuery.fn.dataTable.FixedHeader);
if (hasFH) return Promise.resolve();
return Promise.all([
loadScript('https://cdn.jsdelivr.net/npm/datatables.net-fixedheader@3.4.0/js/dataTables.fixedHeader.min.js', 'dt-fixedheader'),
loadCss('https://cdn.jsdelivr.net/npm/datatables.net-fixedheader-bs5@3.4.0/css/fixedHeader.bootstrap5.min.css', 'dt-fixedheader-css')
]).catch(() => {});
}
// Start loading FixedHeader assets early
ensureFixedHeaderAssets();
const container = document.querySelector('[data-users-endpoint]');
if (!container) {
return;
@@ -149,8 +102,18 @@
const destroyDataTable = () => {
if (!hasDataTables()) return;
if (window.jQuery.fn.DataTable.isDataTable('#usersTable')) {
window.jQuery('#usersTable').DataTable().clear().destroy();
const table = document.getElementById('usersTable');
if (!table || !table.parentNode) return;
if (window.jQuery.fn.DataTable.isDataTable(table)) {
try {
const dt = window.jQuery(table).DataTable();
if (dt.fixedHeader && typeof dt.fixedHeader.destroy === 'function') {
dt.fixedHeader.destroy();
}
dt.clear().destroy();
} catch (error) {
console.warn('Unable to destroy users DataTable cleanly.', error);
}
}
};
@@ -164,20 +127,7 @@
],
};
if (window.jQuery.fn.dataTable && window.jQuery.fn.dataTable.FixedHeader) {
opts.fixedHeader = { header: true, headerOffset: getFixedHeaderOffset() };
}
const dt = window.jQuery('#usersTable').DataTable(opts);
// If plugin finished loading slightly after init, attach FixedHeader instance
if (!opts.fixedHeader) {
ensureFixedHeaderAssets().then(() => {
if (window.jQuery.fn.dataTable && window.jQuery.fn.dataTable.FixedHeader) {
try { new window.jQuery.fn.dataTable.FixedHeader(dt, { header: true, headerOffset: getFixedHeaderOffset() }); } catch (_) {}
}
});
}
window.jQuery('#usersTable').DataTable(opts);
};
const showError = (message) => {