367 lines
18 KiB
PHP
367 lines
18 KiB
PHP
<?php
|
|
// Expects: $f (array) — single family enriched as in FamilyAdminController
|
|
// Quick helpers
|
|
$gCount = count($f['guardians'] ?? []);
|
|
$sCount = count($f['students'] ?? []);
|
|
$sum = $f['finance_summary'] ?? ['invoices_count'=>0,'total_amount'=>0,'paid_amount'=>0,'balance'=>0];
|
|
|
|
// Title: prefer provided household_name unless it's generic like "Family of User 53" or empty.
|
|
$titleRaw = trim((string)($f['household_name'] ?? ''));
|
|
$title = $titleRaw;
|
|
if ($title === '' || preg_match('/^\s*Family\s+of\s+User\s*\d+\s*$/i', $title)) {
|
|
// Derive from primary guardian last name, else first guardian's last name
|
|
$lastName = '';
|
|
$primary = null;
|
|
foreach (($f['guardians'] ?? []) as $g) {
|
|
if (!empty($g['is_primary'])) { $primary = $g; break; }
|
|
}
|
|
$pick = $primary ?: (($f['guardians'][0] ?? null) ?: null);
|
|
if ($pick) {
|
|
$ln = trim((string)($pick['lastname'] ?? ''));
|
|
if ($ln !== '') $lastName = $ln;
|
|
}
|
|
$title = $lastName !== '' ? ('Family of ' . $lastName) : 'Family';
|
|
}
|
|
?>
|
|
|
|
<div class="family-card-root" data-family-title="<?= esc($title) ?>">
|
|
<style>
|
|
/* Scoped styles for Family Card only */
|
|
.family-card-root { font-size: 1rem; color: #12344d; }
|
|
.family-card-root .fc-header {
|
|
background: linear-gradient(135deg, #2d89ec, #2161a7);
|
|
color: #fff;
|
|
border-bottom: 0;
|
|
border-radius: .5rem .5rem 0 0;
|
|
}
|
|
.family-card-root .fc-title { font-size: 1.3rem; letter-spacing: .2px; }
|
|
.family-card-root .fc-name { font-size: 1.08rem; font-weight: 600; color: #0b5ed7; }
|
|
.family-card-root .fc-name:hover { color: #084298; text-decoration: underline; }
|
|
.family-card-root .fc-badges .badge { background: rgba(255,255,255,.18); color: #fff; font-weight: 500; }
|
|
.family-card-root .nav-tabs { padding-left: .5rem; padding-right: .5rem; }
|
|
.family-card-root .nav-tabs .nav-link { color: #2161a7; font-weight: 600; }
|
|
.family-card-root .nav-tabs .nav-link:hover { color: #0b5ed7; }
|
|
.family-card-root .nav-tabs .nav-link.active { color: #0b5ed7; border-color: #2d89ec #2d89ec #fff; }
|
|
.family-card-root h6 { font-size: 1.05rem; color: #143b63; }
|
|
.family-card-root .table thead th { background: #f1f5fb; color: #143b63; font-size: .9rem; }
|
|
.family-card-root .table tbody td { font-size: 1rem; }
|
|
.family-card-root .list-group-item { font-size: 1rem; }
|
|
.family-card-root .badge { font-size: .75rem; }
|
|
.family-card-root .nav-tabs::-webkit-scrollbar { height: 6px; }
|
|
.family-card-root .nav-tabs { overflow-x: auto; overflow-y: hidden; white-space: nowrap; }
|
|
.family-card-root .fc-table-stack thead { display: table-header-group; }
|
|
.family-card-root .fc-table-stack td { vertical-align: top; }
|
|
.family-card-root .fc-table-stack td[data-label] { word-break: break-word; }
|
|
|
|
/* Mobile stacking */
|
|
@media (max-width: 576px) {
|
|
.family-card-root { font-size: .98rem; }
|
|
.family-card-root .fc-header { border-radius: .5rem .5rem 0 0; }
|
|
.family-card-root .fc-header .d-flex { flex-direction: column; align-items: flex-start !important; }
|
|
.family-card-root .fc-badges { width: 100%; }
|
|
.family-card-root .fc-badges .badge { width: 100%; text-align: left; }
|
|
.family-card-root .nav-tabs { gap: .25rem; }
|
|
.family-card-root .nav-tabs .nav-link { padding: .45rem .65rem; font-size: .9rem; }
|
|
.family-card-root .table-responsive { margin: 0 -0.5rem; padding: 0 .5rem; }
|
|
.family-card-root .fc-table-stack thead { display: none; }
|
|
.family-card-root .fc-table-stack tbody tr { display: block; border: 1px solid #e5e7eb; border-radius: .65rem; padding: .75rem; margin-bottom: .75rem; }
|
|
.family-card-root .fc-table-stack tbody tr:last-child { margin-bottom: 0; }
|
|
.family-card-root .fc-table-stack tbody td { display: block; width: 100%; border: 0 !important; padding: .15rem 0; }
|
|
.family-card-root .fc-table-stack tbody td[data-label]::before {
|
|
content: attr(data-label);
|
|
display: block;
|
|
font-size: .78rem;
|
|
text-transform: uppercase;
|
|
letter-spacing: .5px;
|
|
color: #6b7280;
|
|
margin-bottom: .05rem;
|
|
font-weight: 600;
|
|
}
|
|
}
|
|
</style>
|
|
<!-- Header strip -->
|
|
<div class="p-3 fc-header">
|
|
<div class="d-flex flex-wrap justify-content-between align-items-center gap-2">
|
|
<div class="me-3">
|
|
<div class="fw-semibold fc-title"><?= esc($title) ?></div>
|
|
</div>
|
|
<div class="d-flex flex-wrap gap-2 fc-badges">
|
|
<span class="badge">Guardians: <?= (int)$gCount ?></span>
|
|
<span class="badge">Students: <?= (int)$sCount ?></span>
|
|
<span class="badge">Invoices: <?= (int)($sum['invoices_count'] ?? 0) ?></span>
|
|
<span class="badge">
|
|
Balance: $<?= number_format((float)($sum['balance'] ?? 0), 2) ?>
|
|
</span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Tabs -->
|
|
<ul class="nav nav-tabs small px-3 pt-2" role="tablist">
|
|
<li class="nav-item" role="presentation">
|
|
<button class="nav-link active" id="fc-tab-overview" data-bs-toggle="tab" data-bs-target="#fc-overview" type="button" role="tab">Students</button>
|
|
</li>
|
|
<li class="nav-item" role="presentation">
|
|
<button class="nav-link" id="fc-tab-guardians" data-bs-toggle="tab" data-bs-target="#fc-guardians" type="button" role="tab">Parents/Guardians</button>
|
|
</li>
|
|
<li class="nav-item" role="presentation">
|
|
<button class="nav-link" id="fc-tab-ec" data-bs-toggle="tab" data-bs-target="#fc-ec" type="button" role="tab">Emergency</button>
|
|
</li>
|
|
<!--li class="nav-item" role="presentation">
|
|
<button class="nav-link" id="fc-tab-fin" data-bs-toggle="tab" data-bs-target="#fc-fin" type="button" role="tab">Financials</button>
|
|
</li-->
|
|
</ul>
|
|
|
|
<div class="tab-content">
|
|
<!-- Overview -->
|
|
<div class="tab-pane fade show active" id="fc-overview" role="tabpanel" aria-labelledby="fc-tab-overview">
|
|
<div class="p-3">
|
|
<div class="row g-3">
|
|
<div class="col-12">
|
|
<h6 class="mb-2">Students</h6>
|
|
<?php if (empty($f['students'])): ?>
|
|
<div class="text-muted small">No students linked.</div>
|
|
<?php else: ?>
|
|
<ul class="list-group">
|
|
<?php foreach ($f['students'] as $s): ?>
|
|
<li class="list-group-item d-flex justify-content-between align-items-center">
|
|
<div>
|
|
<a href="#" class="text-decoration-none fc-name" data-family-student-id="<?= (int)($s['id'] ?? 0) ?>">
|
|
<?= esc(($s['firstname'] ?? '').' '.($s['lastname'] ?? '')) ?>
|
|
</a>
|
|
</div>
|
|
<?php if (!empty($s['grade'])): ?>
|
|
<span class="badge text-bg-secondary"><?= esc($s['grade']) ?></span>
|
|
<?php endif; ?>
|
|
</li>
|
|
<?php endforeach; ?>
|
|
</ul>
|
|
<?php endif; ?>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Guardians -->
|
|
<div class="tab-pane fade" id="fc-guardians" role="tabpanel" aria-labelledby="fc-tab-guardians">
|
|
<div class="p-3">
|
|
<div class="d-flex justify-content-between align-items-center mb-2">
|
|
<h6 class="mb-0">Guardians</h6>
|
|
<span class="text-muted small">Total: <?= (int)$gCount ?></span>
|
|
</div>
|
|
<?php if (empty($f['guardians'])): ?>
|
|
<div class="alert alert-light border">No guardians yet.</div>
|
|
<?php else: ?>
|
|
<div class="table-responsive">
|
|
<table class="table table-sm align-middle guardians-table fc-table-stack">
|
|
<thead class="table-light">
|
|
<tr>
|
|
<th style="min-width: 220px;">Guardian</th>
|
|
<th style="min-width: 220px;">Contact</th>
|
|
<th style="min-width: 260px;">Address</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
<?php foreach ($f['guardians'] as $g): ?>
|
|
<tr>
|
|
<td data-label="Guardian">
|
|
<div>
|
|
<strong>
|
|
<a href="#" class="text-decoration-none fc-name" data-family-guardian-id="<?= (int)($g['user_id'] ?? 0) ?>">
|
|
<?= esc(($g['firstname'] ?? '').' '.($g['lastname'] ?? '')) ?>
|
|
</a>
|
|
</strong>
|
|
<?php if (!empty($g['is_primary'])): ?>
|
|
<span class="badge text-bg-primary ms-1"><i class="bi bi-star-fill"></i> Primary</span>
|
|
<?php endif; ?>
|
|
</div>
|
|
<?php
|
|
$relRaw = (string)($g['relation'] ?? '');
|
|
$rel = strtolower(trim($relRaw));
|
|
if ($rel === 'guardian') $relLabel = 'second parent';
|
|
elseif ($rel === 'primary') $relLabel = 'first parent';
|
|
else $relLabel = $relRaw;
|
|
?>
|
|
<div class="text-muted small">Relation: <?= esc($relLabel) ?></div>
|
|
</td>
|
|
<td class="small" data-label="Contact">
|
|
<div>
|
|
<i class="bi bi-envelope me-1 text-muted"></i>
|
|
<?= !empty($g['email']) ? ('<a href="mailto:'.esc($g['email']).'">'.esc($g['email']).'</a>') : '<span class="text-muted">—</span>' ?>
|
|
</div>
|
|
<div class="mt-1">
|
|
<i class="bi bi-telephone me-1 text-muted"></i>
|
|
<?= !empty($g['cellphone']) ? ('<a href="tel:'.esc($g['cellphone']).'">'.esc($g['cellphone']).'</a>') : '<span class="text-muted">—</span>' ?>
|
|
</div>
|
|
</td>
|
|
<td class="small" data-label="Address">
|
|
<i class="bi bi-geo-alt me-1 text-muted"></i>
|
|
<?php
|
|
$addrParts = array_filter([
|
|
$g['address_street'] ?? '',
|
|
trim(($g['city'] ?? '').' '.($g['state'] ?? '')),
|
|
$g['zip'] ?? ''
|
|
], fn($x)=>trim((string)$x) !== '');
|
|
?>
|
|
<?= esc(implode(', ', $addrParts) ?: '—') ?>
|
|
</td>
|
|
|
|
</tr>
|
|
<?php endforeach; ?>
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
<?php endif; ?>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Emergency Contacts -->
|
|
<div class="tab-pane fade" id="fc-ec" role="tabpanel" aria-labelledby="fc-tab-ec">
|
|
<div class="p-3">
|
|
<div class="d-flex justify-content-between align-items-center mb-2">
|
|
<h6 class="mb-0">Emergency Contacts</h6>
|
|
<span class="text-muted small">Linked to parents/guardians</span>
|
|
</div>
|
|
<?php $ecs = $f['emergency_contacts'] ?? []; ?>
|
|
<?php if (empty($ecs)): ?>
|
|
<div class="alert alert-light border">No emergency contacts on file.</div>
|
|
<?php else: ?>
|
|
<div class="table-responsive">
|
|
<table class="table table-sm table-striped table-hover align-middle fc-table-stack">
|
|
<thead class="table-light">
|
|
<tr>
|
|
<th>Parent</th>
|
|
<th>Contact Name</th>
|
|
<th>Relation</th>
|
|
<th>Phone</th>
|
|
<th>Email</th>
|
|
<th>Updated</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
<?php foreach ($ecs as $ec): ?>
|
|
<tr>
|
|
<td data-label="Parent">
|
|
<?php $pid = (int)($ec['parent_id'] ?? 0); $pl = trim((string)($ec['parent_label'] ?? '')); ?>
|
|
<a href="#" class="text-decoration-none fc-name" data-family-guardian-id="<?= $pid ?>">
|
|
<?= esc($pl ?: ('Parent #'.$pid)) ?>
|
|
</a>
|
|
</td>
|
|
<td data-label="Contact Name"><span class="fc-name"><?= esc($ec['emergency_contact_name'] ?? '') ?></span></td>
|
|
<td data-label="Relation"><?= esc($ec['relation'] ?? '') ?></td>
|
|
<td data-label="Phone">
|
|
<?php $ph = trim((string)($ec['cellphone'] ?? '')); ?>
|
|
<?= $ph !== '' ? ('<a href="tel:'.esc($ph).'">'.esc($ph).'</a>') : '<span class="text-muted">—</span>' ?>
|
|
</td>
|
|
<td data-label="Email">
|
|
<?php $em = trim((string)($ec['email'] ?? '')); ?>
|
|
<?= $em !== '' ? ('<a href="mailto:'.esc($em).'">'.esc($em).'</a>') : '<span class="text-muted">—</span>' ?>
|
|
</td>
|
|
<td data-label="Updated">
|
|
<?php $ud = (string)($ec['updated_at'] ?? $ec['created_at'] ?? ''); ?>
|
|
<?= esc($ud ? local_date($ud, 'm-d-Y') : '—') ?>
|
|
</td>
|
|
</tr>
|
|
<?php endforeach; ?>
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
<?php endif; ?>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Financials -->
|
|
<div class="tab-pane fade" id="fc-fin" role="tabpanel" aria-labelledby="fc-tab-fin">
|
|
<div class="p-3">
|
|
<div class="row g-3 mb-2">
|
|
<div class="col-6 col-md-3">
|
|
<div class="border rounded p-2 text-center bg-light">
|
|
<div class="text-muted small">Invoices</div>
|
|
<div class="fw-semibold"><?= (int)($sum['invoices_count'] ?? 0) ?></div>
|
|
</div>
|
|
</div>
|
|
<div class="col-6 col-md-3">
|
|
<div class="border rounded p-2 text-center bg-light">
|
|
<div class="text-muted small">Total</div>
|
|
<div class="fw-semibold">$<?= number_format((float)($sum['total_amount'] ?? 0), 2) ?></div>
|
|
</div>
|
|
</div>
|
|
<div class="col-6 col-md-3">
|
|
<div class="border rounded p-2 text-center bg-light">
|
|
<div class="text-muted small">Paid</div>
|
|
<div class="fw-semibold">$<?= number_format((float)($sum['paid_amount'] ?? 0), 2) ?></div>
|
|
</div>
|
|
</div>
|
|
<div class="col-6 col-md-3">
|
|
<div class="border rounded p-2 text-center <?= ((float)($sum['balance'] ?? 0) > 0) ? 'bg-danger-subtle' : 'bg-success-subtle' ?>">
|
|
<div class="text-muted small">Balance</div>
|
|
<div class="fw-semibold">$<?= number_format((float)($sum['balance'] ?? 0), 2) ?></div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="row g-3">
|
|
<div class="col-md-6">
|
|
<h6 class="mb-2">Invoices</h6>
|
|
<?php if (empty($f['invoices'])): ?>
|
|
<div class="alert alert-light border">No invoices on record.</div>
|
|
<?php else: ?>
|
|
<div class="table-responsive">
|
|
<table class="table table-sm table-striped table-hover align-middle fc-table-stack">
|
|
<thead class="table-light">
|
|
<tr>
|
|
<th>#</th><th>Status</th><th>Total</th><th>Paid</th><th>Balance</th><th>Date</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
<?php foreach ($f['invoices'] as $iv): ?>
|
|
<tr>
|
|
<td data-label="Invoice"><?= esc($iv['invoice_number']) ?></td>
|
|
<td data-label="Status"><?= esc($iv['status']) ?></td>
|
|
<td data-label="Total">$<?= number_format((float)($iv['total_amount'] ?? 0), 2) ?></td>
|
|
<td data-label="Paid">$<?= number_format((float)($iv['paid_amount'] ?? 0), 2) ?></td>
|
|
<td data-label="Balance">$<?= number_format((float)($iv['balance'] ?? 0), 2) ?></td>
|
|
<td data-label="Date"><?= esc(!empty($iv['issue_date']) ? local_date($iv['issue_date'], 'm-d-Y') : '') ?></td>
|
|
</tr>
|
|
<?php endforeach; ?>
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
<?php endif; ?>
|
|
</div>
|
|
|
|
<div class="col-md-6">
|
|
<h6 class="mb-2">Recent Payments</h6>
|
|
<?php if (empty($f['payments'])): ?>
|
|
<div class="alert alert-light border">No payments on record.</div>
|
|
<?php else: ?>
|
|
<div class="table-responsive">
|
|
<table class="table table-sm table-striped table-hover align-middle fc-table-stack">
|
|
<thead class="table-light">
|
|
<tr>
|
|
<th>Invoice</th><th>Amount</th><th>Balance</th><th>Method</th><th>Date</th><th>Status</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
<?php $imap = $f['invoice_map'] ?? []; ?>
|
|
<?php foreach ($f['payments'] as $p): ?>
|
|
<tr>
|
|
<td data-label="Invoice"><?php $iid = (int)($p['invoice_id'] ?? 0); echo esc($imap[$iid] ?? ('#'.$iid)); ?></td>
|
|
<td data-label="Amount">$<?= number_format((float)($p['paid_amount'] ?? 0), 2) ?></td>
|
|
<td data-label="Balance">$<?= number_format((float)($p['balance'] ?? 0), 2) ?></td>
|
|
<td data-label="Method"><?= esc($p['payment_method'] ?? '') ?></td>
|
|
<td data-label="Date"><?= esc(!empty($p['payment_date']) ? local_date($p['payment_date'], 'm-d-Y') : '') ?></td>
|
|
<td data-label="Status"><?= esc($p['status'] ?? '') ?></td>
|
|
</tr>
|
|
<?php endforeach; ?>
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
<?php endif; ?>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|