220 lines
9.7 KiB
PHP
220 lines
9.7 KiB
PHP
<?= $this->extend('layout/management_layout') ?>
|
|
<?= $this->section('content') ?>
|
|
<div class="container py-3">
|
|
<a href="<?= site_url('administrator/financial-aid') ?>" class="small">← Back to queue</a>
|
|
<h2 class="mt-2">Review Financial Aid Request #<?= (int) ($requestRow['id'] ?? 0) ?></h2>
|
|
|
|
<?php if (session()->getFlashdata('error')): ?>
|
|
<div class="alert alert-danger"><?= esc(session()->getFlashdata('error')) ?></div>
|
|
<?php endif; ?>
|
|
|
|
<div class="border rounded p-3 mb-3">
|
|
<div class="row g-4">
|
|
<div class="col-md-6">
|
|
<div><strong>Parent:</strong> <?= esc(trim(($parent['firstname'] ?? '') . ' ' . ($parent['lastname'] ?? ''))) ?></div>
|
|
<div><strong>Household size:</strong> <?= esc((string) ($requestRow['household_size'] ?? 'Not provided')) ?></div>
|
|
<div><strong>Requested amount:</strong> <?= $requestRow['requested_amount'] !== null && $requestRow['requested_amount'] !== '' ? '$' . number_format((float) $requestRow['requested_amount'], 2) : 'Not specified' ?></div>
|
|
<div class="mt-2"><strong>Parent statement</strong></div>
|
|
<p class="mb-2"><?= nl2br(esc($requestRow['need_statement'] ?? '')) ?></p>
|
|
<div><strong>Students</strong></div>
|
|
<ul class="mb-0">
|
|
<?php foreach (($students ?? []) as $student): ?>
|
|
<li>
|
|
<?= esc(trim(($student['firstname'] ?? '') . ' ' . ($student['lastname'] ?? ''))) ?>
|
|
<?= student_enrollment_status_button($student, $schoolYear ?? null) ?>
|
|
</li>
|
|
<?php endforeach; ?>
|
|
</ul>
|
|
</div>
|
|
<div class="col-md-6">
|
|
<h5 class="mb-3">Aid estimate calculator</h5>
|
|
<div class="mb-3">
|
|
<label class="form-label" for="calc_household_income">Household income</label>
|
|
<input class="form-control" type="number" min="0" step="0.01" id="calc_household_income"
|
|
value="<?= esc((string) ($requestRow['household_income'] ?? '0')) ?>">
|
|
</div>
|
|
<div class="mb-3">
|
|
<label class="form-label" for="calc_household_size">Household size</label>
|
|
<input class="form-control" type="number" min="1" id="calc_household_size"
|
|
value="<?= esc((string) max(1, (int) ($requestRow['household_size'] ?? 1))) ?>">
|
|
</div>
|
|
<div class="mb-3">
|
|
<label class="form-label" for="calc_invoice_balance">Invoice balance</label>
|
|
<input class="form-control" type="number" min="0" step="0.01" id="calc_invoice_balance"
|
|
value="<?= esc(number_format((float) ($invoiceBalance ?? 0), 2, '.', '')) ?>" readonly>
|
|
</div>
|
|
<button type="button" class="btn btn-outline-primary mb-3" id="calcFinancialAidBtn">Calculate / Recalculate</button>
|
|
<div id="calcFinancialAidError" class="alert alert-danger d-none py-2"></div>
|
|
<div id="calcFinancialAidSuccess" class="alert alert-success d-none py-2">Financial aid calculation completed.</div>
|
|
<div class="border rounded p-3 bg-light">
|
|
<div class="small text-muted">Estimated aid</div>
|
|
<div class="fs-4 fw-semibold" id="calcEstimatedAid">$<?= number_format((float) ($estimatedAid ?? 0), 2) ?></div>
|
|
<div class="small text-muted mt-2">Capped by request and balance</div>
|
|
<div class="fw-semibold" id="calcFinalRebate">$<?= number_format((float) ($finalRebate ?? 0), 2) ?></div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<?php if (in_array((string) ($requestRow['status'] ?? ''), ['submitted', 'under_review'], true)): ?>
|
|
<div class="row g-3">
|
|
<div class="col-md-6">
|
|
<form method="post" action="<?= site_url('administrator/financial-aid/' . (int) $requestRow['id'] . '/approve') ?>" class="border rounded p-3">
|
|
<?= csrf_field() ?>
|
|
<h5>Approve</h5>
|
|
<label class="form-label" for="admin_amount">Amount to apply</label>
|
|
<input class="form-control mb-2" type="number" min="0.01" step="0.01" name="admin_amount" id="admin_amount" required value="<?= esc(old('admin_amount', $requestRow['requested_amount'] ?? '')) ?>">
|
|
<label class="form-label" for="admin_note">Note</label>
|
|
<textarea class="form-control mb-2" name="admin_note" id="admin_note" rows="3"><?= esc(old('admin_note')) ?></textarea>
|
|
<button class="btn btn-success" type="submit">Approve and apply to invoice</button>
|
|
</form>
|
|
</div>
|
|
<div class="col-md-6">
|
|
<form method="post" action="<?= site_url('administrator/financial-aid/' . (int) $requestRow['id'] . '/deny') ?>" class="border rounded p-3">
|
|
<?= csrf_field() ?>
|
|
<h5>Deny</h5>
|
|
<label class="form-label" for="deny_note">Reason</label>
|
|
<textarea class="form-control mb-2" name="admin_note" id="deny_note" rows="3" required><?= esc(old('admin_note')) ?></textarea>
|
|
<button class="btn btn-danger" type="submit">Deny request</button>
|
|
</form>
|
|
</div>
|
|
</div>
|
|
<?php endif; ?>
|
|
</div>
|
|
<?= $this->endSection() ?>
|
|
|
|
<?= $this->section('scripts') ?>
|
|
<script>
|
|
(function () {
|
|
const btn = document.getElementById('calcFinancialAidBtn');
|
|
const errorBox = document.getElementById('calcFinancialAidError');
|
|
const successBox = document.getElementById('calcFinancialAidSuccess');
|
|
const estimatedEl = document.getElementById('calcEstimatedAid');
|
|
const finalEl = document.getElementById('calcFinalRebate');
|
|
const csrfName = <?= json_encode(csrf_token()) ?>;
|
|
const csrfHeaderName = <?= json_encode(csrf_header()) ?>;
|
|
let csrfHash = <?= json_encode(csrf_hash()) ?>;
|
|
const csrfCookieNames = <?= json_encode(array_values(array_unique(array_filter([
|
|
config('Security')->cookieName ?? null,
|
|
config('Security')->csrfCookieName ?? null,
|
|
'csrf_cookie_name',
|
|
])))) ?>;
|
|
|
|
function fmtUSD(value) {
|
|
return '$' + Number(value || 0).toLocaleString(undefined, { minimumFractionDigits: 2, maximumFractionDigits: 2 });
|
|
}
|
|
|
|
function getCookie(name) {
|
|
return document.cookie.split(';')
|
|
.map((value) => value.trim())
|
|
.find((value) => value.startsWith(name + '='))?.split('=')[1] || '';
|
|
}
|
|
|
|
function refreshCsrfFromCookie() {
|
|
for (const cookieName of csrfCookieNames) {
|
|
const value = decodeURIComponent(getCookie(cookieName) || '');
|
|
if (value) {
|
|
csrfHash = value;
|
|
syncCsrfFields();
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
function syncCsrfFields() {
|
|
document.querySelectorAll('input[name="' + csrfName + '"]').forEach((input) => {
|
|
input.value = csrfHash;
|
|
});
|
|
}
|
|
|
|
function captureCsrf(resp, data) {
|
|
const headerValue = resp.headers.get(csrfHeaderName) || resp.headers.get('X-CSRF-HASH') || '';
|
|
if (headerValue) {
|
|
csrfHash = headerValue;
|
|
syncCsrfFields();
|
|
return;
|
|
}
|
|
|
|
if (data?.csrf_hash) {
|
|
csrfHash = data.csrf_hash;
|
|
syncCsrfFields();
|
|
return;
|
|
}
|
|
if (data?.csrfHash) {
|
|
csrfHash = data.csrfHash;
|
|
syncCsrfFields();
|
|
return;
|
|
}
|
|
|
|
refreshCsrfFromCookie();
|
|
}
|
|
|
|
document.querySelectorAll(
|
|
'form[action$="/approve"], form[action$="/deny"]'
|
|
).forEach((form) => {
|
|
form.addEventListener('submit', () => {
|
|
refreshCsrfFromCookie();
|
|
syncCsrfFields();
|
|
});
|
|
});
|
|
|
|
btn?.addEventListener('click', async function () {
|
|
if (errorBox) {
|
|
errorBox.classList.add('d-none');
|
|
errorBox.textContent = '';
|
|
}
|
|
if (successBox) {
|
|
successBox.classList.add('d-none');
|
|
}
|
|
|
|
refreshCsrfFromCookie();
|
|
|
|
const body = new URLSearchParams();
|
|
body.set('household_income', document.getElementById('calc_household_income')?.value ?? '');
|
|
body.set('household_size', document.getElementById('calc_household_size')?.value ?? '');
|
|
body.set(csrfName, csrfHash);
|
|
|
|
try {
|
|
const resp = await fetch(<?= json_encode(site_url('administrator/financial-aid/' . (int) ($requestRow['id'] ?? 0) . '/estimate')) ?>, {
|
|
method: 'POST',
|
|
headers: {
|
|
'Content-Type': 'application/x-www-form-urlencoded',
|
|
[csrfHeaderName]: csrfHash,
|
|
'X-Requested-With': 'XMLHttpRequest',
|
|
},
|
|
credentials: 'same-origin',
|
|
body: body.toString(),
|
|
});
|
|
const text = await resp.text();
|
|
let data = {};
|
|
try {
|
|
data = JSON.parse(text);
|
|
} catch (parseErr) {
|
|
throw new Error('Unable to calculate estimated aid.');
|
|
}
|
|
captureCsrf(resp, data);
|
|
if (!resp.ok) {
|
|
throw new Error(data.error || data.message || 'Unable to calculate estimated aid.');
|
|
}
|
|
|
|
if (estimatedEl) estimatedEl.textContent = fmtUSD(data.estimated_aid);
|
|
if (finalEl) finalEl.textContent = fmtUSD(data.final_rebate);
|
|
const balanceInput = document.getElementById('calc_invoice_balance');
|
|
if (balanceInput && data.invoice_balance !== undefined) {
|
|
balanceInput.value = Number(data.invoice_balance).toFixed(2);
|
|
}
|
|
if (successBox) {
|
|
successBox.textContent = 'Financial aid calculation completed.';
|
|
successBox.classList.remove('d-none');
|
|
}
|
|
} catch (err) {
|
|
if (errorBox) {
|
|
errorBox.textContent = err.message || 'Unable to calculate estimated aid.';
|
|
errorBox.classList.remove('d-none');
|
|
}
|
|
}
|
|
});
|
|
})();
|
|
</script>
|
|
<?= $this->endSection() ?>
|