Publier Globinours 1.0.0-rc.3

This commit is contained in:
Alexandre NOEL 2026-09-03 12:39:15 +02:00
commit 9a2b4068da
325 changed files with 38230 additions and 20 deletions

591
app/Views/dashboard.php Normal file
View file

@ -0,0 +1,591 @@
<?php
$stats = $stats ?? [];
$activity = $activity ?? [];
$quality = $quality ?? [];
$vaccines_overdue = $vaccines_overdue ?? [];
$vaccines_7d = $vaccines_7d ?? [];
$vaccines_30d = $vaccines_30d ?? [];
$treatments = $treatments ?? [];
$treatments_overdue = $treatments_overdue ?? [];
$recent_notes = $recent_notes ?? [];
$quarantine = $quarantine ?? [];
$careToday = $careToday ?? [];
$careAlertsToday = $careAlertsToday ?? [];
$grantAlerts = $grantAlerts ?? [];
$agendaDue = $agendaDue ?? [];
function badgeKind(string $kind): string
{
return match ($kind) {
'consult' => 'bg-blue-lt',
'controle' => 'bg-green-lt',
'symptome' => 'bg-yellow-lt',
'diagnostic' => 'bg-purple-lt',
'acte' => 'bg-orange-lt',
'hosp' => 'bg-red-lt',
default => 'bg-secondary-lt',
};
}
function badgeStatus(string $status): array
{
// [class, label]
return match ($status) {
'refuge' => ['bg-blue-lt',t('status.refuge')],
'fa' => ['bg-cyan-lt',t('status.fa')],
'fa_permanente' => ['bg-indigo-lt',t('status.fa_permanente')],
'quarantaine' => ['bg-yellow-lt',t('status.quarantaine')],
'soin' => ['bg-red-lt',t('status.soin')],
'isolation' => ['bg-orange-lt',t('status.isolation')],
'hospitalise', 'hospitalisé' => ['bg-red-lt',t('status.hospitalise')],
'reserve', 'réservé' => ['bg-purple-lt',t('status.reserve')],
'adopte', 'adopté' => ['bg-green-lt',t('status.adopte')],
'decede', 'décédé' => ['bg-dark-lt',t('status.decede')],
'enfui' => ['bg-secondary-lt',t('status.enfui')],
default => ['bg-secondary-lt', $status],
};
}
?>
<?php
$expiredQuarantines = array_filter($quarantine, static fn (array $q): bool => $q['days_left'] !== null && (int)$q['days_left'] <= 0);
$weightLosses = array_filter($weight_alerts ?? [], static fn (array $w): bool => (float)$w['pct'] < 0);
$urgentCount = count($vaccines_overdue) + count($treatments_overdue) + count($expiredQuarantines) + count($weightLosses) + count($careAlertsToday) + count($agendaDue);
$morning = $careToday['morning'] ?? ['expected' => 0,'checked' => 0,'alerts' => 0];
$evening = $careToday['evening'] ?? ['expected' => 0,'checked' => 0,'alerts' => 0];
?>
<div class="card mb-3 overflow-hidden <?= $urgentCount ? 'border-danger' : '' ?>">
<div class="card-status-top <?= $urgentCount ? 'bg-red' : 'bg-green' ?>"></div>
<div class="card-body">
<div class="d-flex flex-wrap align-items-center justify-content-between gap-3 mb-3">
<div>
<div class="subheader"><?= h(t('dashboard.today_priorities')) ?></div>
<h2 class="mb-0">
<?= h($urgentCount ? tn('dashboard.watch_point', 'dashboard.watch_points', $urgentCount) : t('dashboard.controlled')) ?>
</h2>
</div><a class="btn btn-primary" href="/care-round"><?= h(t('dashboard.open_round')) ?></a>
</div>
<div class="row g-2">
<div class="col-6 col-lg"><a href="#medical-priorities"
class="card card-sm text-reset text-decoration-none h-100">
<div class="card-body">
<div class="h2 mb-1 text-danger"><?= $urgentCount ?></div>
<div class="text-muted small"><?= h(t('dashboard.active_alerts')) ?></div>
</div>
</a></div>
<div class="col-6 col-lg"><a href="#active-treatments"
class="card card-sm text-reset text-decoration-none h-100">
<div class="card-body">
<div class="h2 mb-1"><?= count($treatments) ?></div>
<div class="text-muted small"><?= h(t('dashboard.active_treatments')) ?></div>
</div>
</a></div>
<div class="col-6 col-lg"><a href="/care-round?period=morning"
class="card card-sm text-reset text-decoration-none h-100">
<div class="card-body">
<div class="h2 mb-1"><?= (int)$morning['checked'] ?>/<?= (int)$morning['expected'] ?></div>
<div class="text-muted small"><?= h(t('dashboard.morning_round')) ?></div>
</div>
</a></div>
<div class="col-6 col-lg"><a href="/care-round?period=evening"
class="card card-sm text-reset text-decoration-none h-100">
<div class="card-body">
<div class="h2 mb-1"><?= (int)$evening['checked'] ?>/<?= (int)$evening['expected'] ?></div>
<div class="text-muted small"><?= h(t('dashboard.evening_round')) ?></div>
</div>
</a></div>
<div class="col-12 col-lg"><a href="#vaccine-deadlines"
class="card card-sm text-reset text-decoration-none h-100">
<div class="card-body">
<div class="h2 mb-1"><?= count($vaccines_7d) ?></div>
<div class="text-muted small"><?= h(t('dashboard.vaccines_7')) ?></div>
</div>
</a></div>
<?php if (PermissionService::can('agenda')): ?><div class="col-12 col-lg"><a href="/agenda"
class="card card-sm text-reset text-decoration-none h-100">
<div class="card-body">
<div class="h2 mb-1 <?= $agendaDue ? 'text-danger' : '' ?>"><?= count($agendaDue) ?></div>
<div class="text-muted small"><?= h(t('dashboard.agenda_due')) ?></div>
</div>
</a></div><?php endif; ?>
</div><?php if ($careAlertsToday): ?><div class="alert alert-warning py-2 mt-3 mb-0">
<strong><?= h(t('dashboard.round_anomalies')) ?></strong>
<?php foreach ($careAlertsToday as $i => $alert): ?><?= $i ? ', ' : '' ?><a
href="/animal?id=<?= (int)$alert['id'] ?>"><?= h($alert['name']) ?></a>
(<?= h(t($alert['period'] === 'morning' ? 'dashboard.morning' : 'dashboard.evening')) ?>)<?php endforeach; ?>
</div><?php endif; ?>
</div>
</div>
<div class="row g-2 mb-3">
<div class="col">
<form method="get" action="/animals">
<div class="input-group">
<input type="text" class="form-control" name="q" placeholder="<?= h(t('dashboard.quick_search')) ?>">
<button class="btn btn-outline-primary" type="submit"><?= h(t('common.search')) ?></button>
</div>
</form>
</div>
</div>
<!-- VUE D'ENSEMBLE -->
<div class="d-flex align-items-center gap-2 mt-4 mb-3">
<h2 class="m-0"><?= h(t('dashboard.overview')) ?></h2>
<span class="text-muted"><?= h(t('dashboard.overview_help')) ?></span>
</div>
<div class="row row-cards mb-4">
<div class="col-6 col-md-3">
<div class="card">
<div class="card-body">
<div class="subheader"><?= h(t('dashboard.total')) ?></div>
<div class="h1 mb-2"><?= (int)($stats['total'] ?? 0) ?></div>
<div class="d-flex flex-wrap gap-1">
<span
class="badge bg-blue-lt"><?= h(tn('dashboard.adult', 'dashboard.adults', (int)($stats['adults'] ?? 0))) ?></span>
<span
class="badge bg-cyan-lt"><?= h(tn('dashboard.kitten', 'dashboard.kittens', (int)($stats['kittens'] ?? 0))) ?></span>
<?php if ((int)($stats['age_unknown'] ?? 0) > 0): ?>
<span class="badge bg-secondary-lt"><?= (int)$stats['age_unknown'] ?>
<?= h(t('dashboard.unknown_age')) ?></span>
<?php endif; ?>
</div>
</div>
</div>
</div>
<div class="col-6 col-md-3">
<div class="card">
<div class="card-body">
<div class="subheader"><?= h(t('dashboard.shelter')) ?></div>
<div class="h1 mb-2"><?= (int)($stats['refuge'] ?? 0) ?></div>
<div class="d-flex flex-wrap gap-1">
<?php foreach (($shelterRoomCounts ?? []) as $room):if ($room['status'] !== 'refuge') {
continue;
} ?><span class="badge"
style="color:<?= h($room['color']) ?>;background:color-mix(in srgb,<?= h($room['color']) ?> 14%,transparent)"><?= (int)$room['count'] ?>
<?= h($room['name']) ?></span><?php endforeach; ?>
<?php if ((int)($stats['refuge_unassigned'] ?? 0) > 0): ?>
<span class="badge bg-secondary-lt"><?= (int)$stats['refuge_unassigned'] ?>
<?= h(t('dashboard.unassigned')) ?></span>
<?php endif; ?>
</div>
</div>
</div>
</div>
<div class="col-6 col-md-3">
<div class="card">
<div class="card-body">
<?php $fosterTotal = (int)($stats['fa'] ?? 0) + (int)($stats['fa_permanente'] ?? 0); ?>
<div class="subheader"><?= h(t('dashboard.foster')) ?></div>
<div class="h1 mb-2"><?= $fosterTotal ?></div>
<div class="d-flex flex-wrap gap-1">
<span class="badge bg-cyan-lt"><?= (int)($stats['fa'] ?? 0) ?>
<?= h(t('dashboard.temporary')) ?></span>
<span class="badge bg-indigo-lt"><?= (int)($stats['fa_permanente'] ?? 0) ?>
<?= h(t('dashboard.permanent')) ?></span>
</div>
</div>
</div>
</div>
<div class="col-6 col-md-3">
<div class="card">
<div class="card-body">
<?php $medicalPoleTotal = (int)($stats['quarantaine'] ?? 0) + (int)($stats['soin'] ?? 0); ?>
<div class="subheader"><?= h(t('dashboard.medical_pole')) ?></div>
<div class="h1 mb-2"><?= $medicalPoleTotal ?></div>
<div class="d-flex flex-wrap gap-1">
<span class="badge bg-yellow-lt"><?= (int)($stats['quarantaine'] ?? 0) ?>
<?= h(t('dashboard.in_quarantine')) ?></span>
<span class="badge bg-red-lt"><?= (int)($stats['soin'] ?? 0) ?>
<?= h(t('dashboard.in_medical')) ?></span>
</div>
</div>
</div>
</div>
</div>
<?php $qualityItems = [['birth_date','missing_birth_date',t('quality.birth'),'🎂'],['sex','missing_sex',t('quality.sex'),'⚥'],['color','missing_color',t('quality.color'),'🎨'],['breed','missing_breed',t('quality.breed'),'🐈'],['origin','missing_origin',t('quality.origin'),'📍'],['chip','missing_chip',t('quality.chip'),'🔎'],['identification_registration','unknown_identification_registration',t('quality.registration'),'🏷️'],['sterilization_unknown','unknown_sterilization',t('quality.sterilization'),'❔'],['adoption_availability','unknown_adoption_availability',t('quality.adoption'),'🏠'],['sterilized','unsterilized',t('quality.unsterilized'),'⚕️']]; ?>
<div class="d-flex align-items-center gap-2 mt-4 mb-3">
<h2 class="m-0"><?= h(t('dashboard.data_quality')) ?></h2><span
class="text-muted"><?= h(t('dashboard.data_quality_help')) ?></span>
</div>
<div
class="card mb-4 overflow-hidden <?= (int)($quality['incomplete'] ?? 0) > 0 ? 'border-warning' : 'border-success' ?>">
<div class="card-status-top <?= (int)($quality['incomplete'] ?? 0) > 0 ? 'bg-orange' : 'bg-green' ?>"></div>
<div class="card-body">
<div class="d-flex flex-wrap align-items-center gap-3 mb-3">
<div class="me-auto">
<div class="subheader"><?= h(t('dashboard.current_records')) ?></div>
<h3 class="mb-0">
<?php if ((int)($quality['incomplete'] ?? 0) > 0): ?><?= h(tn('dashboard.record_to_complete', 'dashboard.records_to_complete', (int)$quality['incomplete'])) ?><?php else: ?><?= h(t('dashboard.records_complete')) ?><?php endif; ?>
</h3>
</div><a class="btn btn-outline-cyan" href="/statistics"><?= h(t('dashboard.see_statistics')) ?></a>
</div>
<div class="row g-2">
<?php foreach ($qualityItems as [$filter,$key,$label,$icon]): $count = (int)($quality[$key] ?? 0); ?><div
class="col-6 col-md-4 col-xl"><a
class="card card-sm h-100 text-reset text-decoration-none <?= $count ? '' : 'bg-muted-lt' ?>"
href="/animals?quality=<?= h($filter) ?>">
<div class="card-body d-flex align-items-center gap-2"><span class="fs-2"><?= $icon ?></span>
<div>
<div class="h2 mb-0 <?= $count ? 'text-orange' : 'text-success' ?>"><?= $count ?></div>
<div class="text-muted small"><?= h($label) ?></div>
</div>
</div>
</a></div><?php endforeach; ?></div>
<div class="text-muted small mt-3"><?= h(t('dashboard.quality_note')) ?></div>
</div>
</div>
<?php $grantAlertTotal = array_sum(array_map('intval', $grantAlerts));
if ($grantAlertTotal > 0): ?><div class="alert alert-warning d-flex flex-wrap align-items-center gap-3 mb-4">
<div class="fs-2">📁</div>
<div class="me-auto"><strong><?= h(t('dashboard.grants_check')) ?></strong>
<div class="small">
<?= h(t('dashboard.grants_summary', ['overdue' => (int)($grantAlerts['overdue'] ?? 0),'deadlines' => (int)($grantAlerts['deadlines'] ?? 0),'incomplete' => (int)($grantAlerts['incomplete'] ?? 0),'documents' => (int)($grantAlerts['documents'] ?? 0)])) ?>
</div>
</div><a class="btn btn-warning" href="/admin/grants"><?= h(t('dashboard.open_grants')) ?></a>
</div><?php endif; ?>
<!-- SOINS ET MEDICAL -->
<div class="d-flex align-items-center gap-2 mt-4 mb-3" id="medical-priorities">
<h2 class="m-0"><?= h(t('dashboard.medical')) ?></h2>
<span class="text-muted"><?= h(t('dashboard.medical_help')) ?></span>
</div>
<div class="row row-cards mb-3" id="vaccine-deadlines">
<div class="col-12 col-lg-4">
<div class="card h-100">
<div class="card-header">
<h3 class="card-title">🚨 <?= h(t('dashboard.vaccines_overdue')) ?></h3>
</div>
<div class="table-responsive">
<table class="table table-vcenter card-table">
<tbody>
<?php if (empty($vaccines_overdue)): ?>
<tr>
<td class="text-muted"><?= h(t('dashboard.nothing')) ?></td>
</tr>
<?php else: foreach ($vaccines_overdue as $v): ?>
<tr>
<td class="text-danger fw-bold"><?= h($v['due_date']) ?></td>
<td><a href="/animal?id=<?= (int)$v['id'] ?>"><?= h($v['name']) ?></a> <span
class="text-muted"><?= h($v['code'] ?? '') ?></span></td>
<td><?= h($v['vaccine_name']) ?></td>
</tr>
<?php endforeach; endif; ?>
</tbody>
</table>
</div>
</div>
</div>
<div class="col-12 col-lg-4">
<div class="card h-100">
<div class="card-header">
<h3 class="card-title"> <?= h(t('dashboard.vaccines_soon')) ?></h3>
</div>
<div class="table-responsive">
<table class="table table-vcenter card-table">
<tbody>
<?php if (empty($vaccines_7d)): ?>
<tr>
<td class="text-muted"><?= h(t('dashboard.nothing')) ?></td>
</tr>
<?php else: foreach ($vaccines_7d as $v): ?>
<tr>
<td class="text-warning fw-bold"><?= h($v['due_date']) ?></td>
<td><a href="/animal?id=<?= (int)$v['id'] ?>"><?= h($v['name']) ?></a></td>
<td><?= h($v['vaccine_name']) ?></td>
</tr>
<?php endforeach; endif; ?>
</tbody>
</table>
</div>
</div>
</div>
<div class="col-12 col-lg-4">
<div class="card h-100">
<div class="card-header">
<h3 class="card-title">📅 <?= h(t('dashboard.vaccines_month')) ?></h3>
</div>
<div class="table-responsive">
<table class="table table-vcenter card-table">
<tbody>
<?php if (empty($vaccines_30d)): ?>
<tr>
<td class="text-muted"><?= h(t('dashboard.nothing')) ?></td>
</tr>
<?php else: foreach ($vaccines_30d as $v): ?>
<tr>
<td class="text-primary fw-bold"><?= h($v['due_date']) ?></td>
<td><a href="/animal?id=<?= (int)$v['id'] ?>"><?= h($v['name']) ?></a> <span
class="text-muted"><?= h($v['code'] ?? '') ?></span></td>
<td><?= h($v['vaccine_name']) ?></td>
</tr>
<?php endforeach; endif; ?>
</tbody>
</table>
</div>
</div>
</div>
</div>
<!-- QUARANTAINE -->
<div class="row row-cards mb-3">
<div class="col-12">
<div class="card">
<div class="card-header">
<h3 class="card-title">🟨 <?= h(t('dashboard.quarantine_followup')) ?></h3>
</div>
<div class="table-responsive">
<table class="table table-vcenter card-table">
<thead>
<tr>
<th><?= h(t('common.animal')) ?></th>
<th><?= h(t('dashboard.entry')) ?></th>
<th><?= h(t('dashboard.expected_exit')) ?></th>
<th><?= h(t('dashboard.exit_followup')) ?></th>
</tr>
</thead>
<tbody>
<?php if (empty($quarantine)): ?>
<tr>
<td colspan="4" class="text-muted"><?= h(t('dashboard.no_quarantine')) ?></td>
</tr>
<?php else: foreach ($quarantine as $q): ?>
<?php
$days = isset($q['days_left']) ? (int)$q['days_left'] : null;
$timerClass = 'text-muted';
$timerText = '—';
if (!empty($q['quarantine_until'])) {
if ($days < 0) {
$timerClass = 'text-danger fw-bold';
$timerText = t('dashboard.overdue_days', ['days' => abs($days)]);
} elseif ($days === 0) {
$timerClass = 'text-warning fw-bold';
$timerText = t('dashboard.exit_today');
} elseif ($days === 1) {
$timerClass = 'text-warning fw-bold';
$timerText = t('dashboard.exit_tomorrow');
} elseif ($days <= 5) {
$timerClass = 'text-warning fw-bold';
$timerText = t('dashboard.exit_days', ['days' => $days]);
} else {
$timerClass = 'text-success';
$timerText = t('dashboard.exit_days', ['days' => $days]);
}
}
?>
<tr>
<td><a href="/animal?id=<?= (int)$q['id'] ?>"><?= h($q['name']) ?></a> <span
class="text-muted"><?= h($q['code'] ?? '') ?></span></td>
<td><?= h($q['intake_date'] ?? '—') ?></td>
<td><?= h($q['quarantine_until'] ?? '—') ?></td>
<td class="<?= $timerClass ?>"><?= h($timerText) ?></td>
</tr>
<?php endforeach; endif; ?>
</tbody>
</table>
</div>
<div class="card-footer text-muted small">
<?= h(t('dashboard.quarantine_tip')) ?>
</div>
</div>
</div>
</div>
<div class="row row-cards mb-3">
<div class="col-12">
<div class="card">
<div class="card-header">
<h3 class="card-title">📉📈 <?= h(t('dashboard.weight_alerts')) ?></h3>
</div>
<div class="table-responsive">
<table class="table table-vcenter card-table">
<thead>
<tr>
<th><?= h(t('common.animal')) ?></th>
<th>J-30</th>
<th><?= h(t('dashboard.current')) ?></th>
<th><?= h(t('dashboard.variation')) ?></th>
</tr>
</thead>
<tbody>
<?php if (empty($weight_alerts)): ?>
<tr>
<td colspan="4" class="text-muted"><?= h(t('dashboard.no_alert')) ?></td>
</tr>
<?php else: foreach ($weight_alerts as $w): ?>
<?php
$pct = (float)$w['pct'];
$cls = $pct < 0 ? 'text-danger fw-bold' : 'text-warning fw-bold';
?>
<tr>
<td><a href="/animal?id=<?= (int)$w['id'] ?>"><?= h($w['name']) ?></a> <span
class="text-muted"><?= h($w['code'] ?? '') ?></span></td>
<td><?= h($w['w30_weight']) ?> kg <span
class="text-muted small">(<?= h(substr((string)$w['w30_date'], 0, 10)) ?>)</span>
</td>
<td><?= h($w['last_weight']) ?> kg</td>
<td class="<?= $cls ?>"><?= ($pct > 0 ? '+' : '') . h($w['pct']) ?>%</td>
</tr>
<?php endforeach; endif; ?>
</tbody>
</table>
</div>
</div>
</div>
</div>
<!-- LISTES -->
<div class="row row-cards" id="active-treatments">
<div class="col-12 col-lg-6">
<div class="card">
<div class="card-header">
<h3 class="card-title">⚠️ <?= h(t('dashboard.treatments_close')) ?></h3>
</div>
<div class="table-responsive">
<table class="table table-vcenter card-table">
<tbody>
<?php if (empty($treatments_overdue)): ?>
<tr>
<td class="text-muted"><?= h(t('dashboard.nothing')) ?></td>
</tr>
<?php else: foreach ($treatments_overdue as $t): ?>
<tr>
<td class="text-warning fw-bold"><?= h($t['end_date']) ?></td>
<td><a href="/animal?id=<?= (int)$t['id'] ?>"><?= h($t['name']) ?></a></td>
<td><?= h($t['medication_name']) ?></td>
</tr>
<?php endforeach; endif; ?>
</tbody>
</table>
</div>
</div>
</div>
<div class="col-12 col-lg-6">
<div class="card">
<div class="card-header">
<h3 class="card-title"><?= h(t('dashboard.current_treatments')) ?></h3>
</div>
<div class="table-responsive">
<table class="table table-vcenter card-table">
<thead>
<tr>
<th><?= h(t('common.animal')) ?></th>
<th><?= h(t('dashboard.medication')) ?></th>
<th><?= h(t('dashboard.start')) ?></th>
<th><?= h(t('dashboard.end')) ?></th>
</tr>
</thead>
<tbody>
<?php if (empty($treatments)): ?>
<tr>
<td colspan="4" class="text-muted"><?= h(t('dashboard.no_treatment')) ?></td>
</tr>
<?php else: foreach ($treatments as $t): ?>
<tr>
<td><a href="/animal?id=<?= (int)$t['id'] ?>"><?= h($t['name']) ?></a></td>
<td>
<?= h($t['medication_name']) ?>
<?php if (!empty($t['dose_text']) || !empty($t['route'])): ?>
<div class="text-muted small">
<?= !empty($t['dose_text']) ? h($t['dose_text']) : '' ?>
<?= (!empty($t['dose_text']) && !empty($t['route'])) ? ' • ' : '' ?>
<?= !empty($t['route']) ? h($t['route']) : '' ?>
</div>
<?php endif; ?>
</td>
<td><?= h($t['start_date'] ?? '') ?></td>
<td><?= h($t['end_date'] ?? '') ?></td>
</tr>
<?php endforeach; endif; ?>
</tbody>
</table>
</div>
</div>
</div>
</div>
<!-- ACTIVITE RECENTE -->
<div class="d-flex align-items-center gap-2 mt-4 mb-3">
<h2 class="m-0"><?= h(t('dashboard.recent_activity')) ?></h2>
<span class="text-muted"><?= h(t('dashboard.recent_activity_help')) ?></span>
</div>
<div class="row row-cards mb-3">
<div class="col-12 col-md-4">
<div class="card">
<div class="card-body">
<div class="subheader"><?= h(t('dashboard.medical_notes_7')) ?></div>
<div class="h1 mb-0"><?= (int)($activity['notes_7d'] ?? 0) ?></div>
</div>
</div>
</div>
<div class="col-12 col-md-4">
<div class="card">
<div class="card-body">
<div class="subheader"><?= h(t('dashboard.vaccines_30')) ?></div>
<div class="h1 mb-0"><?= (int)($activity['vaccines_30d'] ?? 0) ?></div>
</div>
</div>
</div>
<div class="col-12 col-md-4">
<div class="card">
<div class="card-body">
<div class="subheader"><?= h(t('dashboard.treatments_30')) ?></div>
<div class="h1 mb-0"><?= (int)($activity['treatments_30d'] ?? 0) ?></div>
</div>
</div>
</div>
</div>
<div class="row row-cards">
<div class="col-12">
<div class="card">
<div class="card-header">
<h3 class="card-title"><?= h(t('dashboard.latest_notes')) ?></h3>
</div>
<div class="table-responsive">
<table class="table table-vcenter card-table">
<thead>
<tr>
<th><?= h(t('dashboard.date')) ?></th>
<th><?= h(t('common.animal')) ?></th>
<th><?= h(t('dashboard.type')) ?></th>
<th><?= h(t('dashboard.reason')) ?></th>
</tr>
</thead>
<tbody>
<?php if (empty($recent_notes)): ?>
<tr>
<td colspan="4" class="text-muted"><?= h(t('dashboard.no_note')) ?></td>
</tr>
<?php else: foreach ($recent_notes as $n): ?>
<?php $kind = (string)($n['kind'] ?? ''); ?>
<tr>
<td><?= h(substr((string)$n['noted_at'], 0, 16)) ?></td>
<td><a href="/animal?id=<?= (int)$n['animal_id'] ?>"><?= h($n['name']) ?></a> <span
class="text-muted"><?= h($n['code'] ?? '') ?></span></td>
<td><span class="badge <?= badgeKind($kind) ?>"><?= h($kind) ?></span></td>
<td><?= h($n['reason'] ?? '') ?></td>
</tr>
<?php endforeach; endif; ?>
</tbody>
</table>
</div>
</div>
</div>
</div>