610 lines
32 KiB
PHP
610 lines
32 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];
|
|
$canViewInvoices = !empty($f['can_view_invoices']);
|
|
$selectedStudentId = (int)($f['selected_student_id'] ?? 0);
|
|
$scoreDefaultStudentId = $selectedStudentId > 0
|
|
? $selectedStudentId
|
|
: (int)($f['students'][0]['id'] ?? 0);
|
|
|
|
$displayValue = static function ($value, string $fallback = 'Not provided'): string {
|
|
if ($value === null || trim((string)$value) === '') {
|
|
return $fallback;
|
|
}
|
|
|
|
return (string)$value;
|
|
};
|
|
|
|
$formatDate = static function ($value, bool $includeTime = false) use ($displayValue): string {
|
|
if ($value === null || trim((string)$value) === '') {
|
|
return 'Not provided';
|
|
}
|
|
|
|
try {
|
|
return $includeTime
|
|
? local_datetime((string)$value, 'm-d-Y g:i A')
|
|
: (new \DateTime((string)$value))->format('m-d-Y');
|
|
} catch (\Throwable $e) {
|
|
return $displayValue($value);
|
|
}
|
|
};
|
|
|
|
$formatScore = static function ($value): string {
|
|
if ($value === null || $value === '' || !is_numeric($value)) {
|
|
return '—';
|
|
}
|
|
|
|
return rtrim(rtrim(number_format((float)$value, 2, '.', ''), '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';
|
|
}
|
|
$returnUrl = trim((string)service('request')->getServer('HTTP_REFERER'));
|
|
if ($returnUrl === '') {
|
|
$returnUrl = site_url('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-student-details { background: #f8fafc; border-top: 1px solid #e5e7eb; }
|
|
.family-card-root .fc-detail-label { color: #64748b; font-size: .76rem; font-weight: 700; letter-spacing: .04em; text-transform: uppercase; }
|
|
.family-card-root .fc-detail-value { color: #12344d; overflow-wrap: anywhere; }
|
|
.family-card-root .fc-health-card { border-left: 4px solid #2d89ec !important; }
|
|
.family-card-root .fc-score-table th { white-space: normal; min-width: 105px; vertical-align: middle; }
|
|
.family-card-root .fc-score-table td { white-space: normal; vertical-align: top; }
|
|
.family-card-root .fc-score-table .fc-score-comment { min-width: 220px; max-width: 320px; }
|
|
.family-card-root .fc-score-student-nav { flex-wrap: nowrap; overflow-x: auto; }
|
|
.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>
|
|
<?php if ($canViewInvoices): ?>
|
|
<span class="badge">Invoices: <?= (int)($sum['invoices_count'] ?? 0) ?></span>
|
|
<span class="badge">
|
|
Balance: $<?= number_format((float)($sum['balance'] ?? 0), 2) ?>
|
|
</span>
|
|
<?php endif; ?>
|
|
</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-scores" data-bs-toggle="tab" data-bs-target="#fc-scores" type="button" role="tab">Scores History</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>
|
|
<?php if ($canViewInvoices): ?>
|
|
<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">Invoices</button>
|
|
</li>
|
|
<?php endif; ?>
|
|
</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: ?>
|
|
<div class="accordion" id="fc-students-<?= (int)($f['id'] ?? 0) ?>">
|
|
<?php foreach ($f['students'] as $s): ?>
|
|
<?php
|
|
$studentId = (int)($s['id'] ?? 0);
|
|
$detailId = 'fc-student-details-' . (int)($f['id'] ?? 0) . '-' . $studentId;
|
|
$isSelected = $selectedStudentId > 0 && $selectedStudentId === $studentId;
|
|
$allergies = array_values(array_filter(array_map('trim', (array)($s['allergies'] ?? []))));
|
|
$conditions = array_values(array_filter(array_map('trim', (array)($s['medical_conditions'] ?? []))));
|
|
?>
|
|
<div class="accordion-item">
|
|
<h2 class="accordion-header">
|
|
<button
|
|
type="button"
|
|
class="accordion-button <?= $isSelected ? '' : 'collapsed' ?>"
|
|
data-bs-toggle="collapse"
|
|
data-bs-target="#<?= esc($detailId) ?>"
|
|
aria-expanded="<?= $isSelected ? 'true' : 'false' ?>"
|
|
aria-controls="<?= esc($detailId) ?>"
|
|
>
|
|
<span class="fc-name">
|
|
<?= esc(trim(($s['firstname'] ?? '').' '.($s['lastname'] ?? ''))) ?>
|
|
</span>
|
|
<?php if (!empty($s['grade'])): ?>
|
|
<span class="badge text-bg-secondary ms-2"><?= esc($s['grade']) ?></span>
|
|
<?php endif; ?>
|
|
</button>
|
|
</h2>
|
|
|
|
<div
|
|
id="<?= esc($detailId) ?>"
|
|
class="accordion-collapse collapse <?= $isSelected ? 'show' : '' ?>"
|
|
data-bs-parent="#fc-students-<?= (int)($f['id'] ?? 0) ?>"
|
|
>
|
|
<div class="accordion-body fc-student-details">
|
|
<h6 class="mb-3">Student Details</h6>
|
|
<div class="row g-3">
|
|
<?php
|
|
$details = [
|
|
'School ID' => $displayValue($s['school_id'] ?? null),
|
|
'Date of Birth' => $formatDate($s['dob'] ?? null),
|
|
'Age' => $displayValue($s['age'] ?? null),
|
|
'Gender' => $displayValue($s['gender'] ?? null),
|
|
'Assigned Class / Section' => $displayValue($s['grade'] ?? null),
|
|
'Enrollment Status' => $displayValue($s['enrollment_status'] ?? null),
|
|
'Registration Grade' => $displayValue($s['registration_grade'] ?? null),
|
|
'Status' => ((int)($s['is_active'] ?? 0) === 1) ? 'Active' : 'Inactive',
|
|
'Photo Consent' => ((int)($s['photo_consent'] ?? 0) === 1) ? 'Yes' : 'No',
|
|
'Registration Date' => $formatDate($s['registration_date'] ?? null, true),
|
|
'Year of Registration' => $displayValue($s['year_of_registration'] ?? null),
|
|
'RFID Tag' => $displayValue($s['rfid_tag'] ?? null),
|
|
'Tuition Paid' => ((int)($s['tuition_paid'] ?? 0) === 1) ? 'Yes' : 'No',
|
|
];
|
|
?>
|
|
<?php foreach ($details as $label => $value): ?>
|
|
<div class="col-12 col-sm-6 col-lg-4">
|
|
<div class="fc-detail-label"><?= esc($label) ?></div>
|
|
<div class="fc-detail-value"><?= esc($value) ?></div>
|
|
</div>
|
|
<?php endforeach; ?>
|
|
</div>
|
|
|
|
<div class="row g-3 mt-1">
|
|
<div class="col-12 col-lg-6">
|
|
<div class="card h-100 fc-health-card">
|
|
<div class="card-body">
|
|
<div class="fc-detail-label mb-2">Medical Conditions</div>
|
|
<?php if (empty($conditions)): ?>
|
|
<div class="text-muted">None on file</div>
|
|
<?php else: ?>
|
|
<ul class="mb-0 ps-3">
|
|
<?php foreach ($conditions as $condition): ?>
|
|
<li><?= esc($condition) ?></li>
|
|
<?php endforeach; ?>
|
|
</ul>
|
|
<?php endif; ?>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="col-12 col-lg-6">
|
|
<div class="card h-100 fc-health-card">
|
|
<div class="card-body">
|
|
<div class="fc-detail-label mb-2">Allergies</div>
|
|
<?php if (empty($allergies)): ?>
|
|
<div class="text-muted">None on file</div>
|
|
<?php else: ?>
|
|
<ul class="mb-0 ps-3">
|
|
<?php foreach ($allergies as $allergy): ?>
|
|
<li><?= esc($allergy) ?></li>
|
|
<?php endforeach; ?>
|
|
</ul>
|
|
<?php endif; ?>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<?php endforeach; ?>
|
|
</div>
|
|
<?php endif; ?>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Scores History -->
|
|
<div class="tab-pane fade" id="fc-scores" role="tabpanel" aria-labelledby="fc-tab-scores">
|
|
<div class="p-3">
|
|
<?php if (empty($f['students'])): ?>
|
|
<div class="alert alert-light border mb-0">No students linked.</div>
|
|
<?php else: ?>
|
|
<ul class="nav nav-pills fc-score-student-nav gap-2 mb-3" role="tablist">
|
|
<?php foreach ($f['students'] as $s): ?>
|
|
<?php
|
|
$scoreStudentId = (int)($s['id'] ?? 0);
|
|
$scoreStudentActive = $scoreStudentId === $scoreDefaultStudentId;
|
|
$scorePaneId = 'fc-score-student-' . (int)($f['id'] ?? 0) . '-' . $scoreStudentId;
|
|
?>
|
|
<li class="nav-item" role="presentation">
|
|
<button
|
|
class="nav-link text-nowrap <?= $scoreStudentActive ? 'active' : '' ?>"
|
|
data-bs-toggle="pill"
|
|
data-bs-target="#<?= esc($scorePaneId) ?>"
|
|
type="button"
|
|
role="tab"
|
|
aria-selected="<?= $scoreStudentActive ? 'true' : 'false' ?>"
|
|
>
|
|
<?= esc(trim(($s['firstname'] ?? '') . ' ' . ($s['lastname'] ?? ''))) ?>
|
|
</button>
|
|
</li>
|
|
<?php endforeach; ?>
|
|
</ul>
|
|
|
|
<div class="tab-content">
|
|
<?php foreach ($f['students'] as $s): ?>
|
|
<?php
|
|
$scoreStudentId = (int)($s['id'] ?? 0);
|
|
$scoreStudentActive = $scoreStudentId === $scoreDefaultStudentId;
|
|
$scorePaneId = 'fc-score-student-' . (int)($f['id'] ?? 0) . '-' . $scoreStudentId;
|
|
$scoreHistory = (array)($s['score_history'] ?? []);
|
|
?>
|
|
<div class="tab-pane fade <?= $scoreStudentActive ? 'show active' : '' ?>" id="<?= esc($scorePaneId) ?>" role="tabpanel">
|
|
<?php if (empty($scoreHistory)): ?>
|
|
<div class="alert alert-light border mb-0">No score history found for this student.</div>
|
|
<?php else: ?>
|
|
<div class="table-responsive">
|
|
<table class="table table-sm table-bordered table-striped align-middle fc-score-table mb-0">
|
|
<thead class="table-light">
|
|
<tr>
|
|
<th>School Year</th>
|
|
<th>Midterm S1</th>
|
|
<th class="fc-score-comment">Midterm Comment S1</th>
|
|
<th>PTAP S1</th>
|
|
<th class="fc-score-comment">PTAP Comment S1</th>
|
|
<th>Attendance S1</th>
|
|
<th class="fc-score-comment">Attendance Comment S1</th>
|
|
<th>Final S2</th>
|
|
<th class="fc-score-comment">Final Comment S2</th>
|
|
<th>PTAP S2</th>
|
|
<th class="fc-score-comment">PTAP Comment S2</th>
|
|
<th>Attendance S2</th>
|
|
<th class="fc-score-comment">Attendance Comment S2</th>
|
|
<th>Year Score</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
<?php foreach ($scoreHistory as $history): ?>
|
|
<tr>
|
|
<td data-label="School Year" class="text-nowrap fw-semibold"><?= esc($history['school_year'] ?? '—') ?></td>
|
|
<td data-label="Midterm S1"><?= esc($formatScore($history['midterm_s1'] ?? null)) ?></td>
|
|
<td data-label="Midterm Comment S1" class="fc-score-comment"><?= esc($displayValue($history['midterm_comment_s1'] ?? null, '—')) ?></td>
|
|
<td data-label="PTAP S1"><?= esc($formatScore($history['ptap_s1'] ?? null)) ?></td>
|
|
<td data-label="PTAP Comment S1" class="fc-score-comment"><?= esc($displayValue($history['ptap_comment_s1'] ?? null, '—')) ?></td>
|
|
<td data-label="Attendance S1"><?= esc($formatScore($history['attendance_s1'] ?? null)) ?></td>
|
|
<td data-label="Attendance Comment S1" class="fc-score-comment"><?= esc($displayValue($history['attendance_comment_s1'] ?? null, '—')) ?></td>
|
|
<td data-label="Final S2"><?= esc($formatScore($history['final_s2'] ?? null)) ?></td>
|
|
<td data-label="Final Comment S2" class="fc-score-comment"><?= esc($displayValue($history['final_comment_s2'] ?? null, '—')) ?></td>
|
|
<td data-label="PTAP S2"><?= esc($formatScore($history['ptap_s2'] ?? null)) ?></td>
|
|
<td data-label="PTAP Comment S2" class="fc-score-comment"><?= esc($displayValue($history['ptap_comment_s2'] ?? null, '—')) ?></td>
|
|
<td data-label="Attendance S2"><?= esc($formatScore($history['attendance_s2'] ?? null)) ?></td>
|
|
<td data-label="Attendance Comment S2" class="fc-score-comment"><?= esc($displayValue($history['attendance_comment_s2'] ?? null, '—')) ?></td>
|
|
<td data-label="Year Score" class="fw-semibold"><?= esc($formatScore($history['year_score'] ?? null)) ?></td>
|
|
</tr>
|
|
<?php endforeach; ?>
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
<?php endif; ?>
|
|
</div>
|
|
<?php endforeach; ?>
|
|
</div>
|
|
<?php endif; ?>
|
|
</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>
|
|
<?php
|
|
$gEmail = trim((string)($g['email'] ?? ''));
|
|
$gName = trim((string)($g['firstname'] ?? '') . ' ' . (string)($g['lastname'] ?? ''));
|
|
$composeUrl = $gEmail !== ''
|
|
? site_url('family/compose-email?to=' . rawurlencode($gEmail) . '&name=' . rawurlencode($gName) . '&return_url=' . rawurlencode($returnUrl))
|
|
: '';
|
|
?>
|
|
<?= $gEmail !== '' ? ('<a href="' . esc($composeUrl) . '" target="_blank" rel="noopener">' . esc($gEmail) . '</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>
|
|
|
|
<?php if ($canViewInvoices): ?>
|
|
<!-- Invoices (administrative roles only) -->
|
|
<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">Parent 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>Parent</th><th>#</th><th>Status</th><th>Total</th><th>Paid</th><th>Balance</th><th>Issued</th><th>Due</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
<?php foreach ($f['invoices'] as $iv): ?>
|
|
<tr>
|
|
<td data-label="Parent"><?= esc($iv['parent_name'] ?? ('Parent #' . (int)($iv['parent_id'] ?? 0))) ?></td>
|
|
<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>
|
|
<td data-label="Due"><?= esc(!empty($iv['due_date']) ? local_date($iv['due_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>Parent</th><th>Invoice</th><th>Amount</th><th>Invoice Balance</th><th>Method</th><th>Date</th><th>Payment Status</th><th>Invoice Status</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
<?php $imap = $f['invoice_map'] ?? []; ?>
|
|
<?php foreach ($f['payments'] as $p): ?>
|
|
<tr>
|
|
<td data-label="Parent"><?= esc($p['parent_name'] ?? ('Parent #' . (int)($p['parent_id'] ?? 0))) ?></td>
|
|
<td data-label="Invoice"><?php $iid = (int)($p['invoice_id'] ?? 0); echo esc($p['invoice_number'] ?? $imap[$iid] ?? ('#'.$iid)); ?></td>
|
|
<td data-label="Amount">$<?= number_format((float)($p['paid_amount'] ?? 0), 2) ?></td>
|
|
<td data-label="Invoice Balance">$<?= number_format((float)($p['invoice_current_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="Payment Status"><?= esc($p['payment_status'] ?? '') ?></td>
|
|
<td data-label="Invoice Status"><?= esc($p['invoice_status'] ?? '') ?></td>
|
|
</tr>
|
|
<?php endforeach; ?>
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
<?php endif; ?>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<?php endif; ?>
|
|
</div>
|
|
</div>
|