recreate project

This commit is contained in:
root
2026-02-10 22:11:06 -05:00
commit 663c0cdbda
10149 changed files with 1379710 additions and 0 deletions
+189
View File
@@ -0,0 +1,189 @@
<?= $this->extend('layout/management_layout') ?>
<?= $this->section('content') ?>
<div class="container-fluid mt-4">
<h2 class="text-center mt-4 mb-3">Expense / Purchase</h2>
<?= $this->include('partials/academic_filter') ?>
<div class="d-flex gap-2 mb-3 justify-content-end">
<a href="<?= base_url('expenses/create') ?>" class="btn btn-success mb-3">Add New</a>
</div>
<?php if (session()->getFlashdata('success')): ?>
<div class="alert alert-success">
<?= session()->getFlashdata('success') ?>
</div>
<?php endif; ?>
<?php $totalAmount = 0.0; ?>
<div class="table-responsive">
<table id="expensesTable" class="table table-bordered table-striped align-middle w-100 no-mgmt-sticky" style="table-layout:auto;">
<thead class="table-light">
<tr>
<th>Category</th>
<th>Amount</th>
<th>Vendor</th>
<th>Purchased By</th>
<th>Date of Purchase</th>
<th>Description</th>
<th>Status</th>
<th>Approved By</th>
<th>Receipt</th>
<th>Added On</th>
<th>Action</th>
</tr>
</thead>
<tbody>
<?php foreach ($expenses as $e): ?>
<?php $totalAmount += (float)($e['amount'] ?? 0); ?>
<tr>
<td>
<?= esc($e['category']) ?>
<?php if (strcasecmp($e['category'], 'Donation') === 0): ?>
<span class="badge bg-info text-dark ms-1">Non-reimbursable</span>
<?php endif; ?>
</td>
<td>$<?= number_format($e['amount'], 2) ?></td>
<td>
<?php
$retail = $e['retailor'] ?? ($e['retailer'] ?? null);
echo esc($retail ?: 'N/A');
?>
</td>
<td>
<?= $e['purchaser_firstname'] || $e['purchaser_lastname']
? esc($e['purchaser_firstname'] . ' ' . $e['purchaser_lastname'])
: 'N/A' ?>
</td>
<td>
<?php
$purchaseDate = $e['date_of_purchase']
?? ($e['purchase_date']
?? ($e['purchased_at']
?? ($e['date_purchased']
?? null)));
if (!empty($purchaseDate)) {
$purchaseDisp = local_date($purchaseDate, 'm-d-Y');
} elseif (!empty($e['created_at'])) {
$purchaseDisp = local_date($e['created_at'], 'm-d-Y');
} else {
$purchaseDisp = '-';
}
echo esc($purchaseDisp);
?>
</td>
<td><?= esc($e['description']) ?></td>
<td>
<span class="badge bg-<?= $e['status'] === 'approved' ? 'success' : ($e['status'] === 'denied' ? 'danger' : 'secondary') ?>">
<?= ucfirst($e['status']) ?>
</span>
<?php if (strcasecmp($e['category'], 'Donation') === 0): ?>
<span class="badge bg-info text-dark ms-1">Donation</span>
<?php endif; ?>
</td>
<td><?= esc($e['approver_firstname'] . ' ' . $e['approver_lastname'] ?? '-') ?></td>
<!--td><--?= esc($e['status_reason'] ?? '-') ?></td-->
<td>
<?php if ($e['receipt_path']): ?>
<!--a href="<--?= base_url('writable/' . $e['receipt_path']) ?>" target="_blank">View</a-->
<a href="<?= site_url('receipts/' . $e['receipt_path']) ?>" target="_blank">View receipt</a>
<?php else: ?>
N/A
<?php endif; ?>
</td>
<td><?= esc(!empty($e['created_at']) ? local_datetime($e['created_at'], 'm-d-Y H:i') : '') ?></td>
<td class="d-flex gap-2">
<a class="btn btn-outline-secondary btn-sm" href="<?= site_url('expenses/edit/' . $e['id']) ?>">Edit</a>
<?php if (strcasecmp($e['category'], 'Donation') === 0): ?>
<span class="text-muted small align-self-center">Donation no reimbursement actions</span>
<?php elseif ($e['status'] === 'pending'): ?>
<button class="btn btn-success btn-sm" onclick="handleExpenseAction(<?= $e['id'] ?>, 'approved')">Approve</button>
<button class="btn btn-danger btn-sm" onclick="handleExpenseAction(<?= $e['id'] ?>, 'denied')">Deny</button>
<?php endif; ?>
</td>
</tr>
<?php endforeach; ?>
</tbody>
<tfoot class="table-light">
<tr>
<th>Total</th>
<th>$<?= number_format($totalAmount, 2) ?></th>
<th></th>
<th></th>
<th></th>
<th></th>
<th></th>
<th></th>
<th></th>
<th></th>
<th></th>
</tr>
</tfoot>
</table>
</div>
<!-- Removed local sticky CSS; DataTables FixedHeader handles header pinning -->
</div>
<?= $this->endSection() ?>
<?= $this->section('scripts') ?>
<script>
(function(){
const CSRF_COOKIE_NAME = <?= json_encode(config('Security')->csrfCookieName ?? (config('Security')->cookieName ?? 'csrf_cookie_name')) ?>;
function getCookie(name){
return document.cookie.split(';').map(v=>v.trim()).find(v=>v.startsWith(name+'='))?.split('=')[1] || '';
}
window.handleExpenseAction = function(id, status) {
if (!confirm(`Are you sure you want to ${status} this expense?`)) return;
const csrf = decodeURIComponent(getCookie(CSRF_COOKIE_NAME) || '');
fetch("<?= base_url('expenses/updateStatus') ?>", {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-CSRF-TOKEN': csrf || '<?= csrf_hash() ?>'
},
// No 'reason' sent
body: JSON.stringify({
id,
status
})
})
.then(res => res.json())
.then(response => {
if (response.success) {
location.reload();
} else {
alert(response.error || 'Action failed.');
}
})
.catch(() => alert('Server error.'));
}
})();
</script>
<script>
// Attach DataTables FixedHeader for sticky header reliability
(function(){
function getFixedHeaderOffset(){
var total = 0;
try {
var sels = ['header.navbar.sticky-top','header.navbar.fixed-top','#navbarManagement.navbar.sticky-top','#navbarManagement.navbar.fixed-top'];
var seen = [];
sels.forEach(function(s){ document.querySelectorAll(s).forEach(function(el){ if (seen.indexOf(el) === -1) seen.push(el); }); });
document.querySelectorAll('.navbar.sticky-top, .navbar.fixed-top').forEach(function(el){ if (seen.indexOf(el) === -1) seen.push(el); });
seen.forEach(function(el){ var h = el.offsetHeight || (el.getBoundingClientRect && el.getBoundingClientRect().height) || 0; total += Math.max(0, Math.round(h)); });
} catch(_) {}
return total;
}
document.addEventListener('DOMContentLoaded', function(){
if (window.jQuery && jQuery.fn && jQuery.fn.DataTable) {
try {
jQuery('#expensesTable').DataTable({ paging:false, searching:false, info:false, order: [], fixedHeader: { header: true, headerOffset: getFixedHeaderOffset() } });
} catch(_) {}
}
// No polyfill needed when DataTables FixedHeader is active
});
})();
</script>
<?= $this->endSection() ?>