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

View file

@ -0,0 +1,436 @@
<?php
// Sous-vue de la fiche animale ; les variables sont préparées par AnimalsController::show().
?>
<!-- MÉDICAL -->
<?php if ($canViewMedical): ?>
<div class="tab-pane" id="tab-med">
<div class="card mt-3">
<div class="card-header d-flex align-items-center justify-content-between">
<div>
<h3 class="card-title m-0"><?= h(t('animal.medical_status_global')) ?></h3>
<div class="text-muted small"><?= h(t('animal.current_health_help')) ?></div>
</div>
<button class="btn btn-sm btn-outline-primary" type="button" data-bs-toggle="modal"
data-bs-target="#modalMedicalStatus"><?= h(t('common.edit')) ?></button>
</div>
<div class="card-body">
<div class="row g-3">
<?php if ($isCat): ?><div class="col-12 col-md-3">
<div class="subheader mb-1">FIV</div>
<span class="badge <?= $fivClass ?>"><?= h($fivLabel) ?></span>
</div>
<div class="col-12 col-md-3">
<div class="subheader mb-1">FeLV</div>
<span class="badge <?= $felvClass ?>"><?= h($felvLabel) ?></span>
</div><?php endif; ?>
<div class="col-12 <?= $isCat ? 'col-md-6' : '' ?>">
<div class="subheader mb-1"><?= h(t('animal.conditions_short')) ?></div>
<?php $activeConditions = array_values(array_filter($healthConditions ?? [], fn ($condition) => ($condition['status'] ?? '') === 'active')); ?>
<?php if (!$activeConditions): ?>
<span class="text-muted"><?= h(t('animal.no_active_condition')) ?></span>
<?php else: ?>
<div class="d-flex flex-wrap gap-2">
<?php foreach ($activeConditions as $condition): ?>
<span class="badge bg-red-lt"><?= h($condition['name']) ?></span>
<?php endforeach; ?>
</div>
<?php endif; ?>
</div>
</div>
</div>
</div>
<div class="row g-3 mt-0">
<div class="col-12 col-xl-5">
<div class="card h-100">
<div class="card-header d-flex justify-content-between align-items-center">
<div>
<h3 class="card-title mb-0">📄 <?= h(t('medical_document.prescriptions')) ?></h3>
<div class="text-muted small"><?= h(t('medical_document.private_documents')) ?></div>
</div><button class="btn btn-sm btn-outline-primary" data-bs-toggle="modal"
data-bs-target="#modalPrescription"><?= h(t('common.add')) ?></button>
</div>
<div class="card-body">
<?php if (empty($prescriptions)): ?><div class="text-muted">
<?= h(t('medical_document.no_prescription')) ?></div><?php else: ?><div class="divide-y">
<?php foreach ($prescriptions as $p): $statusKey = 'medical_document.status_' . $p['status']; ?>
<div class="py-2">
<div class="d-flex justify-content-between gap-2">
<div>
<strong><?= h((new DateTimeImmutable($p['prescribed_on']))->format('d/m/Y')) ?></strong>
<span
class="badge <?= $p['status'] === 'active' ? 'bg-green-lt' : 'bg-secondary-lt' ?>"><?= h(t($statusKey)) ?></span>
</div><a class="btn btn-sm btn-ghost-primary" target="_blank"
href="/animal/medical-document?kind=prescription&amp;id=<?= (int)$p['id'] ?>"><i
class="ti ti-file-search"></i> <?= h(t('common.open')) ?></a>
</div><?php if (!empty($p['clinic_name']) || !empty($p['veterinarian_name'])): ?><div
class="text-muted small">
<?= h(implode(' · ', array_filter([$p['clinic_name'],$p['veterinarian_name']]))) ?>
</div><?php endif; ?><?php if (!empty($prescriptionTreatments[$p['id']])): ?><div
class="mt-1 d-flex flex-wrap gap-1">
<?php foreach ($prescriptionTreatments[$p['id']] as $linked): ?><span
class="badge bg-orange-lt">💊
<?= h($linked['medication_name'] ?: t('animal.treatment')) ?></span><?php endforeach; ?>
</div><?php endif; ?><?php if (!empty($p['notes'])): ?><div class="small mt-1">
<?= nl2br(h($p['notes'])) ?></div><?php endif; ?>
</div><?php endforeach; ?></div><?php endif; ?>
</div>
</div>
</div>
<div class="col-12 col-xl-7">
<div class="card h-100">
<div class="card-header d-flex justify-content-between align-items-center">
<div>
<h3 class="card-title mb-0">🧪 <?= h(t('medical_document.lab_results')) ?></h3>
<div class="text-muted small"><?= h(t('medical_document.lab_help')) ?></div>
</div><button class="btn btn-sm btn-outline-primary" data-bs-toggle="modal"
data-bs-target="#modalLabReport"><?= h(t('common.add')) ?></button>
</div>
<div class="card-body">
<?php if (empty($labReports)): ?><div class="text-muted"><?= h(t('medical_document.no_analysis')) ?>
</div><?php else: ?><div class="accordion" id="labReportsAccordion">
<?php foreach ($labReports as $i => $report): $results = $labResultsByReport[$report['id']] ?? []; ?>
<div class="accordion-item">
<h2 class="accordion-header"><button class="accordion-button <?= $i ? 'collapsed' : '' ?>"
type="button" data-bs-toggle="collapse"
data-bs-target="#lab-report-<?= (int)$report['id'] ?>"><span
class="me-2"><?= h((new DateTimeImmutable($report['sampled_on']))->format('d/m/Y')) ?></span><span
class="text-muted"><?= h($report['report_type'] ?: t('medical_document.analysis')) ?></span></button>
</h2>
<div id="lab-report-<?= (int)$report['id'] ?>"
class="accordion-collapse collapse <?= !$i ? 'show' : '' ?>"
data-bs-parent="#labReportsAccordion">
<div class="accordion-body"><?php if ($report['stored_name']): ?><a
class="btn btn-sm btn-outline-primary mb-2" target="_blank"
href="/animal/medical-document?kind=analysis&amp;id=<?= (int)$report['id'] ?>"><i
class="ti ti-paperclip"></i>
<?= h(t('medical_document.open_report')) ?></a><?php endif; ?><?php if ($results): ?>
<div class="table-responsive">
<table class="table table-sm table-vcenter mb-0">
<thead>
<tr>
<th><?= h(t('medical_document.parameter')) ?></th>
<th><?= h(t('medical_document.value')) ?></th>
<th><?= h(t('medical_document.reference')) ?></th>
</tr>
</thead>
<tbody><?php foreach ($results as $result): $hasReference = $result['reference_min'] !== null || $result['reference_max'] !== null;
$low = $result['reference_min'] !== null && (float)$result['value'] < (float)$result['reference_min'];
$high = $result['reference_max'] !== null && (float)$result['value'] > (float)$result['reference_max']; ?><tr>
<td><?= h($result['parameter_name']) ?></td>
<td><strong
class="<?= !$hasReference ? '' : ($low || $high ? 'text-danger' : 'text-success') ?>"><?= h((string)$result['value'] . ' ' . ($result['unit'] ?? '')) ?></strong>
</td>
<td><?= $hasReference ? h(($result['reference_min'] ?? '…') . ' ' . ($result['reference_max'] ?? '…') . ' ' . ($result['unit'] ?? '')) : '—' ?>
</td>
</tr><?php endforeach; ?></tbody>
</table>
</div><?php endif; ?><div class="text-muted small mt-2">
<?= h(implode(' · ', array_filter([$report['laboratory_name'],$report['clinic_name'],$report['veterinarian_name']]))) ?>
</div><?php if ($report['notes']): ?><div class="small mt-1">
<?= nl2br(h($report['notes'])) ?></div><?php endif; ?></div>
</div>
</div><?php endforeach; ?></div><?php endif; ?>
</div>
</div>
</div>
</div>
<?php if (!empty($labSeries)): ?><div class="card mt-3">
<div class="card-header">
<div>
<h3 class="card-title mb-0"><?= h(t('medical_document.evolution')) ?></h3>
<div class="text-muted small"><?= h(t('medical_document.graph_help')) ?></div>
</div>
</div>
<div class="card-body">
<div class="row g-3"><?php foreach (array_values($labSeries) as $i => $series): ?><div
class="col-12 col-xl-6">
<div class="border rounded p-3">
<h4 class="mb-2">
<?= h($series['name']) ?><?= $series['unit'] ? ' <span class="text-muted fw-normal">(' . h($series['unit']) . ')</span>' : '' ?>
</h4>
<div style="height:230px"><canvas class="lab-chart"
data-series="<?= h(json_encode($series, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES)) ?>"></canvas>
</div>
</div>
</div><?php endforeach; ?></div>
</div>
</div><?php endif; ?>
<div class="card mt-3">
<div class="card-header d-flex align-items-center justify-content-between">
<div>
<h3 class="card-title m-0">🏥 Chirurgies</h3>
<div class="text-muted small">Interventions planifiées et réalisées</div>
</div><a class="btn btn-sm btn-outline-primary"
href="/animal/surgery/new?animal_id=<?= (int)$animal['id'] ?>"><?= h(t('common.add')) ?></a>
</div>
<div class="card-body"><?php if (empty($surgeries)):?><span class="text-muted">Aucune chirurgie
enregistrée.</span><?php else:?><div class="table-responsive">
<table class="table table-sm table-vcenter mb-0">
<thead>
<tr>
<th>Date</th>
<th>Intervention</th>
<th>Structure / vétérinaire</th>
<th>Statut</th>
<th class="text-end">Coût</th>
</tr>
</thead>
<tbody><?php foreach ($surgeries as $s):?><tr>
<td><?= h(date('d/m/Y', strtotime($s['surgery_date']))) ?></td>
<td><strong><?= h($s['procedure_name']) ?></strong><?php if ($s['notes']):?><div
class="small text-muted"><?= h($s['notes']) ?></div><?php endif?></td>
<td><?= h(implode(' · ', array_filter([$s['clinic_name'],$s['veterinarian_name']])) ?: '—') ?>
</td>
<td><span
class="badge <?= $s['status'] === 'completed' ? 'bg-green-lt' : ($s['status'] === 'planned' ? 'bg-blue-lt' : 'bg-secondary-lt') ?>"><?= h(['completed' => 'Réalisée','planned' => 'Planifiée','cancelled' => 'Annulée'][$s['status']] ?? $s['status']) ?></span>
</td>
<td class="text-end">
<?= $s['amount_cents'] === null ? '—' : number_format($s['amount_cents'] / 100, 2, ',', ' ') . ' €' ?>
</td>
</tr><?php endforeach?></tbody>
</table>
</div><?php endif?></div>
</div>
<div class="card mt-3">
<div class="card-header d-flex align-items-center justify-content-between">
<div>
<h3 class="card-title m-0"><?= h(t('animals.dewormer')) ?></h3>
<div class="text-muted small"><?= h(t('animal.deworming_schedule')) ?></div>
</div><button class="btn btn-sm btn-outline-primary" data-bs-toggle="modal"
data-bs-target="#modalDeworming"><?= h(t('common.add')) ?></button>
</div>
<div class="card-body"><?php if (!$dewormings): ?><span
class="text-muted"><?= h(t('animal.no_deworming')) ?></span><?php else: ?><div class="mb-3">
<strong><?= h(t('animal.latest_deworming', ['date' => (new DateTimeImmutable($lastDeworming['administered_on']))->format('d/m/Y')])) ?></strong><?php if ($lastDeworming['next_due_date']): $due = new DateTimeImmutable($lastDeworming['next_due_date']);
$late = $due < new DateTimeImmutable('today'); ?><span
class="badge ms-2 <?= $late ? 'bg-red-lt' : 'bg-blue-lt' ?>"><?= h(t('animal.next_date', ['date' => $due->format('d/m/Y')])) ?></span><?php endif; ?>
</div>
<div class="table-responsive">
<table class="table table-sm table-vcenter mb-0">
<thead>
<tr>
<th><?= h(t('common.date')) ?></th>
<th><?= h(t('animal.product')) ?></th>
<th><?= h(t('animal.dose')) ?></th>
<th><?= h(t('common.weight')) ?></th>
<th><?= h(t('animal.recorded_by')) ?></th>
</tr>
</thead>
<tbody><?php foreach ($dewormings as $d): ?><tr>
<td><?= h((new DateTimeImmutable($d['administered_on']))->format('d/m/Y')) ?></td>
<td><?= h($d['medication_name'] ?: t('common.not_provided')) ?></td>
<td><?= h($d['dose_text'] ?: '—') ?></td>
<td><?= $d['weight_kg'] !== null ? h(number_format((float)$d['weight_kg'], 2, ',', ' ')) . ' kg' : '—' ?>
</td>
<td><?= h($d['actor_name'] ?: '—') ?></td>
</tr><?php endforeach; ?></tbody>
</table>
</div><?php endif; ?>
</div>
</div>
<div class="card mt-3">
<div class="card-header">
<h3 class="card-title m-0"><?= h(t('animal.weight_evolution')) ?></h3>
</div>
<?php if (!$weights): ?>
<div class="card-body text-muted"><?= h(t('animal.no_weight')) ?></div>
<?php else: ?>
<div style="height:260px">
<canvas id="weightChart"></canvas>
</div>
<?php endif; ?>
</div>
<hr />
<?php if (!$medical_notes): ?>
<div class="text-muted"><?= h(t('animal.no_medical_note')) ?></div>
<?php else: ?>
<div class="divide-y">
<?php foreach ($medical_notes as $n): ?>
<div class="py-3">
<div class="d-flex justify-content-between">
<div>
<?php
$kind = (string)$n['kind'];
$badge = 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',
};
$stmtPhotos = DB::pdo()->prepare('
SELECT *
FROM medical_photos
WHERE medical_note_id = ?
ORDER BY created_at ASC
');
$stmtPhotos->execute([(int)$n['id']]);
$notePhotos = $stmtPhotos->fetchAll(PDO::FETCH_ASSOC);
?>
<span class="badge <?= $badge ?>"><?= ucfirst(h($kind)) ?></span>
<?php if (!empty($n['reason'])): ?>
<strong class="ms-2"><?= h($n['reason']) ?></strong>
<?php endif; ?>
</div>
<div class="text-muted"><?= h($n['noted_at']) ?></div>
</div>
<div class="mt-2">
<?php if (!empty($n['weight_kg']) || !empty($n['temperature_c'])): ?>
<div class="text-muted">
<?= !empty($n['weight_kg']) ? (h(t('pdf.weight')) . ': <strong>' . h($n['weight_kg']) . ' kg</strong>') : '' ?>
<?= (!empty($n['weight_kg']) && !empty($n['temperature_c'])) ? ' • ' : '' ?>
<?= !empty($n['temperature_c']) ? ('Temp: <strong>' . h($n['temperature_c']) . ' °C</strong>') : '' ?>
</div>
<?php endif; ?>
<?php if (!empty($n['clinic_name']) || !empty($n['vet_name'])): ?>
<div class="text-muted">
<?= !empty($n['clinic_name']) ? h($n['clinic_name']) : '' ?>
<?= (!empty($n['clinic_name']) && !empty($n['vet_name'])) ? ' • ' : '' ?>
<?= !empty($n['vet_name']) ? h($n['vet_name']) : '' ?>
</div>
<?php endif; ?>
<?php if (!empty($n['symptoms'])): ?>
<div><span class="text-muted"><?= h(t('animal.symptoms')) ?> :</span> <?= nl2br(h($n['symptoms'])) ?>
</div>
<?php endif; ?>
<?php if (!empty($n['exam'])): ?>
<div><span class="text-muted"><?= h(t('animal.exam')) ?> :</span> <?= nl2br(h($n['exam'])) ?></div>
<?php endif; ?>
<?php if (!empty($n['diagnosis'])): ?>
<div><span class="text-muted"><?= h(t('animal.diagnosis')) ?> :</span> <?= nl2br(h($n['diagnosis'])) ?>
</div>
<?php endif; ?>
<?php if (!empty($n['plan'])): ?>
<div><span class="text-muted"><?= h(t('animal.plan')) ?> :</span> <?= nl2br(h($n['plan'])) ?></div>
<?php endif; ?>
</div>
<?php if (!empty($notePhotos)): ?>
<div class="mt-3">
<div class="text-muted mb-1"><?= h(t('animal.photos')) ?> :</div>
<div class="row row-cards">
<?php foreach ($notePhotos as $p): ?>
<?php
$src = '/private-media?kind=medical&id=' . (int)$p['id'];
?>
<div class="col-6 col-sm-4 col-md-3 col-lg-2">
<a href="<?= $src ?>" target="_blank" class="d-block">
<img src="<?= $src ?>" class="img-fluid rounded border"
style="height:140px;object-fit:cover;">
</a>
</div>
<?php endforeach; ?>
</div>
</div>
<?php endif; ?>
</div>
<?php endforeach; ?>
</div>
<?php endif; ?>
</div>
<!-- TRAITEMENTS -->
<div class="tab-pane" id="tab-trt">
<?php if (!$treatments): ?>
<div class="text-muted"><?= h(t('animal.no_treatment')) ?></div>
<?php else: ?>
<div class="table-responsive">
<table class="table table-vcenter">
<thead>
<tr>
<th><?= h(t('animal.status')) ?></th>
<th><?= h(t('animal.medication')) ?></th>
<th><?= h(t('animal.route')) ?></th>
<th><?= h(t('animal.dosage')) ?></th>
<th><?= h(t('animal.start')) ?></th>
<th><?= h(t('dashboard.end')) ?></th>
<th><?= h(t('common.notes')) ?></th>
</tr>
</thead>
<tbody>
<?php foreach ($treatments as $t): ?>
<?php
$isFinished = !empty($t['end_date'])
&& $t['end_date'] < date('Y-m-d');
?>
<tr class="<?= $isFinished ? 'table-secondary text-muted' : '' ?>">
<td>
<span class="badge <?= $isFinished ? 'bg-grey-lt' : 'bg-green-lt' ?>">
<?= h(t($isFinished ? 'animal.finished' : 'animal.ongoing')) ?>
</span>
</td>
<td>
<strong><?= h($t['medication_name']) ?></strong>
<?php if (!empty($t['molecule'])): ?>
<div class="text-muted">
<?= h($t['molecule']) ?><?= !empty($t['form']) ? (' • ' . h($t['form'])) : '' ?></div>
<?php endif; ?>
</td>
<td><?= $t['route'] ? h($t['route']) : '—' ?></td>
<td><?= h($t['dose_text']) ?></td>
<td><?= h($t['start_date']) ?></td>
<td><?= $t['end_date'] ? h($t['end_date']) : '—' ?></td>
<td><?= $t['notes'] ? h($t['notes']) : '—' ?></td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
</div>
<?php endif; ?>
</div>
<!-- VACCINS -->
<div class="tab-pane" id="tab-vac">
<?php if (!$vaccinations): ?>
<div class="text-muted"><?= h(t('animal.no_vaccine_recorded')) ?></div>
<?php else: ?>
<div class="table-responsive">
<table class="table table-vcenter">
<thead>
<tr>
<th><?= h(t('animal.vaccine')) ?></th>
<th><?= h(t('animal.done_on')) ?></th>
<th><?= h(t('animal.reminder_due')) ?></th>
<th><?= h(t('animal.batch_traceability')) ?></th>
<th><?= h(t('animal.administered_by')) ?></th>
<th><?= h(t('common.notes')) ?></th>
</tr>
</thead>
<tbody>
<?php foreach ($vaccinations as $v): ?>
<tr>
<td><strong><?= h($v['vaccine_name']) ?></strong></td>
<td><?= h($v['done_date']) ?></td>
<td><?= $v['due_date'] ? h($v['due_date']) : '—' ?></td>
<td><?php if (!empty($v['manufacturer'])): ?><strong><?= h($v['manufacturer']) ?></strong><br><?php endif; ?><?= $v['lot'] ? h(t('animal.batch')) . ' ' . h($v['lot']) : '—' ?><?php if (!empty($v['batch_expires_on'])): ?>
<div class="text-muted small">
<?= h(t('animal.expires_on', ['date' => date('d/m/Y', strtotime((string)$v['batch_expires_on']))])) ?>
</div><?php endif; ?></td>
<td><?= !empty($v['administrator_name']) ? h($v['administrator_name']) : '—' ?></td>
<td><?= $v['notes'] ? h($v['notes']) : '—' ?></td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
</div>
<?php endif; ?>
</div>
<?php endif; ?>