1105 lines
60 KiB
PHP
1105 lines
60 KiB
PHP
<?php
|
||
// Fenêtres modales et scripts propres à la fiche animale.
|
||
?>
|
||
<?php
|
||
/** @var array $medications */
|
||
/** @var array $ref_med_administration */
|
||
/** @var array $vaccines */
|
||
$defaultDue = date('Y-m-d', strtotime('+1 year'));
|
||
$today = date('Y-m-d');
|
||
?>
|
||
|
||
<?php if ($canViewMedical): ?>
|
||
<div class="modal modal-blur fade" id="modalTreatment" tabindex="-1" aria-hidden="true">
|
||
<div class="modal-dialog modal-lg modal-dialog-centered" role="document">
|
||
<form class="modal-content" method="post" action="/animal/add-treatment">
|
||
<div class="modal-header">
|
||
<h5 class="modal-title"><?= h(t('animal.treatment_start')) ?></h5>
|
||
<button type="button" class="btn-close" data-bs-dismiss="modal"
|
||
aria-label="<?= h(t('common.close')) ?>"></button>
|
||
</div>
|
||
|
||
<div class="modal-body">
|
||
<input type="hidden" name="animal_id" value="<?= (int)$animal['id'] ?>">
|
||
|
||
<div class="row g-2">
|
||
<div class="col-md-8">
|
||
<label class="form-label"><?= h(t('animal.medication')) ?></label>
|
||
<div class="input-group">
|
||
<select class="form-select" name="medication_id" id="treatmentMedicationSelect" required>
|
||
<option value="">— Choisir —</option>
|
||
<?php foreach ($medications as $m): ?>
|
||
<option value="<?= (int)$m['id'] ?>"><?= htmlspecialchars($m['name'], ENT_QUOTES) ?>
|
||
</option>
|
||
<?php endforeach; ?>
|
||
</select>
|
||
<button class="btn btn-outline-primary" type="button" id="treatmentMedicationAdd"
|
||
aria-expanded="false" aria-controls="treatmentMedicationNew">+
|
||
<?= h(t('common.add')) ?></button>
|
||
</div>
|
||
</div>
|
||
<div class="col-md-4">
|
||
<label class="form-label"><?= h(t('animal.route')) ?></label>
|
||
<select class="form-select" name="route" required>
|
||
<option value="">— Choisir —</option>
|
||
<?php foreach ($ref_med_administration as $rma): ?>
|
||
<option value="<?= $rma['accro'] ?>"><?= htmlspecialchars($rma['name'], ENT_QUOTES) ?>
|
||
</option>
|
||
<?php endforeach; ?>
|
||
</select>
|
||
</div>
|
||
|
||
<div class="col-12 d-none" id="treatmentMedicationNew">
|
||
<input type="hidden" name="create_medication" id="treatmentMedicationCreate" value="0">
|
||
<div class="card bg-blue-lt border-blue-lt">
|
||
<div class="card-body py-3">
|
||
<div class="row g-2">
|
||
<div class="col-md-5"><label
|
||
class="form-label"><?= h(t('animal.new_medication')) ?></label><input
|
||
class="form-control" name="new_medication_name" id="treatmentMedicationName"
|
||
maxlength="150" placeholder="Ex. Doxybactin"></div>
|
||
<div class="col-md-4"><label class="form-label"><?= h(t('animal.molecule')) ?> <span
|
||
class="text-muted">(<?= h(mb_strtolower(t('common.optional'))) ?>)</span></label><input
|
||
class="form-control" name="new_medication_molecule" maxlength="150"
|
||
placeholder="Ex. Doxycycline"></div>
|
||
<div class="col-md-3"><label class="form-label"><?= h(t('animal.form_form')) ?>
|
||
<span
|
||
class="text-muted">(<?= h(mb_strtolower(t('common.optional'))) ?>)</span></label><input
|
||
class="form-control" name="new_medication_form" maxlength="100"
|
||
placeholder="<?= h(t('animal.medication_form_placeholder')) ?>"></div>
|
||
</div>
|
||
<div class="form-hint mt-2"><?= h(t('animal.medication_saved_hint')) ?></div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="col-12">
|
||
<label class="form-label"><?= h(t('animal.dosage')) ?></label>
|
||
<input class="form-control" name="dose_text" required placeholder="Ex: 1/2 cp matin & soir">
|
||
</div>
|
||
|
||
<div class="col-12">
|
||
<label class="form-label"><?= h(t('animal.administer_during')) ?></label>
|
||
<div class="d-flex flex-wrap gap-3">
|
||
<label class="form-check"><input class="form-check-input" type="checkbox"
|
||
name="give_morning" value="1"><span class="form-check-label">☀️
|
||
<?= h(t('animal.morning_round')) ?></span></label>
|
||
<label class="form-check"><input class="form-check-input" type="checkbox"
|
||
name="give_evening" value="1"><span class="form-check-label">🌙
|
||
<?= h(t('animal.evening_round')) ?></span></label>
|
||
<label class="form-check"><input class="form-check-input" type="checkbox"
|
||
name="give_as_needed" value="1"><span
|
||
class="form-check-label"><?= h(t('animal.as_needed')) ?></span></label>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="col-md-6">
|
||
<label class="form-label"><?= h(t('animal.start')) ?></label>
|
||
<input class="form-control" type="date" name="start_date" value="<?= $today ?>">
|
||
</div>
|
||
<div class="col-md-6">
|
||
<label class="form-label"><?= h(t('animal.end_optional')) ?></label>
|
||
<input class="form-control" type="date" name="end_date">
|
||
</div>
|
||
|
||
<?php $directoryFieldId = 'treatment';
|
||
require __DIR__ . '/_directory_medical_fields.php'; ?>
|
||
<?php require __DIR__ . '/_expense_tariff_field.php'; ?>
|
||
|
||
<div class="col-12">
|
||
<label class="form-label"><?= h(t('common.notes')) ?></label>
|
||
<textarea class="form-control" name="notes" rows="2"></textarea>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="modal-footer">
|
||
<button type="button" class="btn me-auto" data-bs-dismiss="modal"><?= h(t('common.cancel')) ?></button>
|
||
<button type="submit" class="btn btn-primary"><?= h(t('common.save')) ?></button>
|
||
</div>
|
||
</form>
|
||
</div>
|
||
</div>
|
||
|
||
<script>
|
||
document.addEventListener('DOMContentLoaded', function() {
|
||
const button = document.getElementById('treatmentMedicationAdd');
|
||
const select = document.getElementById('treatmentMedicationSelect');
|
||
const panel = document.getElementById('treatmentMedicationNew');
|
||
const create = document.getElementById('treatmentMedicationCreate');
|
||
const name = document.getElementById('treatmentMedicationName');
|
||
if (!button || !select || !panel || !create || !name) return;
|
||
|
||
button.addEventListener('click', function() {
|
||
const opening = panel.classList.contains('d-none');
|
||
panel.classList.toggle('d-none', !opening);
|
||
create.value = opening ? '1' : '0';
|
||
select.disabled = opening;
|
||
select.required = !opening;
|
||
name.required = opening;
|
||
button.setAttribute('aria-expanded', opening ? 'true' : 'false');
|
||
button.textContent = opening ? < ? = json_encode(t('common.choose_from_list'),
|
||
JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES) ? > : '+ ' + < ? = json_encode(t(
|
||
'common.add'), JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES) ? > ;
|
||
if (opening) name.focus();
|
||
});
|
||
});
|
||
|
||
</script>
|
||
|
||
<div class="modal modal-blur fade" id="modalDeworming" tabindex="-1" aria-hidden="true">
|
||
<div class="modal-dialog modal-dialog-centered">
|
||
<form class="modal-content" method="post" action="/animal/add-deworming">
|
||
<div class="modal-header">
|
||
<h5 class="modal-title"><?= h(t('animal.deworming_record')) ?></h5><button type="button"
|
||
class="btn-close" data-bs-dismiss="modal" aria-label="<?= h(t('common.close')) ?>"></button>
|
||
</div>
|
||
<div class="modal-body"><input type="hidden" name="animal_id" value="<?= (int)$animal['id'] ?>">
|
||
<div class="row g-3">
|
||
<div class="col-md-6"><label class="form-label"><?= h(t('common.date')) ?></label><input
|
||
class="form-control" type="date" name="administered_on" value="<?= date('Y-m-d') ?>"
|
||
required></div>
|
||
<div class="col-md-6"><label class="form-label"><?= h(t('animal.product')) ?></label><select
|
||
class="form-select" name="dewormer_id">
|
||
<option value=""><?= h(t('common.not_provided')) ?></option>
|
||
<?php foreach ($dewormers as $m): ?><option value="<?= (int)$m['id'] ?>">
|
||
<?= h($m['name']) ?></option><?php endforeach; ?>
|
||
</select></div>
|
||
<div class="col-md-6"><label class="form-label"><?= h(t('animal.dose')) ?></label><input
|
||
class="form-control" name="dose_text" placeholder="<?= h(t('deworm.dose_example')) ?>">
|
||
</div>
|
||
<div class="col-md-6"><label class="form-label"><?= h(t('common.weight')) ?></label>
|
||
<div class="input-group"><input class="form-control" type="number" min="0" step="0.01"
|
||
name="weight_kg"><span class="input-group-text">kg</span></div>
|
||
</div>
|
||
<div class="col-md-8"><label class="form-label"><?= h(t('inventory.batch')) ?></label><select
|
||
class="form-select" name="inventory_batch_id">
|
||
<option value="">—</option><?php foreach (($inventoryBatches ?? []) as $b) {
|
||
if ($b['category'] === 'dewormer'): ?><option value="<?= (int)$b['id'] ?>">
|
||
<?= h($b['name'] . ' · ' . ($b['batch_number'] ?: t('inventory.no_batch')) . ' · ' . $b['quantity'] . ' ' . $b['unit']) ?>
|
||
</option><?php endif;
|
||
} ?>
|
||
</select></div>
|
||
<div class="col-md-4"><label class="form-label"><?= h(t('inventory.quantity')) ?></label><input
|
||
class="form-control" type="number" min="0" step="0.01" name="inventory_quantity"></div>
|
||
<div class="col-12"><label class="form-label"><?= h(t('animal.next_due')) ?> <span
|
||
class="text-muted">(<?= h(mb_strtolower(t('common.optional'))) ?>)</span></label><input
|
||
class="form-control" type="date" name="next_due_date">
|
||
<div class="form-hint"><?= h(t('animal.next_due_hint')) ?></div>
|
||
</div><?php if (!empty($litters)): ?><div class="col-12"><label class="form-check"><input
|
||
class="form-check-input" type="checkbox" name="apply_litter" value="1"><span
|
||
class="form-check-label"><?= h(t('animal.apply_litter')) ?></span></label></div>
|
||
<?php endif; ?><div class="col-12"><label
|
||
class="form-label"><?= h(t('animal.comment')) ?></label><textarea class="form-control"
|
||
name="notes" rows="2"></textarea></div>
|
||
</div>
|
||
</div>
|
||
<div class="modal-footer"><button class="btn me-auto" type="button"
|
||
data-bs-dismiss="modal"><?= h(t('common.cancel')) ?></button><button
|
||
class="btn btn-primary"><?= h(t('common.save')) ?></button></div>
|
||
</form>
|
||
</div>
|
||
</div>
|
||
|
||
|
||
<div class="modal modal-blur fade" id="modalPlacement" tabindex="-1" aria-hidden="true">
|
||
<div class="modal-dialog modal-dialog-centered">
|
||
<form class="modal-content" method="post" action="/animal/placement/save">
|
||
<div class="modal-header">
|
||
<h5 class="modal-title"><?= h(t('animal.add_placement')) ?></h5><button type="button" class="btn-close"
|
||
data-bs-dismiss="modal" aria-label="<?= h(t('common.close')) ?>"></button>
|
||
</div>
|
||
<div class="modal-body"><input type="hidden" name="animal_id" value="<?= (int)$animal['id'] ?>">
|
||
<div class="row g-3">
|
||
<div class="col-md-7"><label class="form-label"><?= h(t('animal.event')) ?></label><select
|
||
class="form-select" name="event_type" required>
|
||
<option value="reservation"><?= h(t('animal.reservation')) ?></option>
|
||
<option value="foster"><?= h(t('animal.foster_placement')) ?></option>
|
||
<option value="return"><?= h(t('animal.return_shelter')) ?></option>
|
||
<option value="cancellation"><?= h(t('animal.cancellation')) ?></option>
|
||
</select>
|
||
<div class="form-hint"><?= h(t('animal.placement_hint')) ?></div>
|
||
</div>
|
||
<div class="col-md-5"><label class="form-label"><?= h(t('common.date')) ?></label><input
|
||
class="form-control" type="date" name="event_date" value="<?= date('Y-m-d') ?>" required>
|
||
</div>
|
||
<div class="col-12"><label class="form-label"><?= h(t('animal.related_contact')) ?></label><select
|
||
class="form-select" name="contact_id">
|
||
<option value=""><?= h(t('animal.no_contact')) ?></option>
|
||
<?php foreach (($placementContacts ?? []) as $contact): ?><option
|
||
value="<?= (int)$contact['id'] ?>">
|
||
<?= h($contact['name'] . (!empty($contact['city']) ? ' · ' . $contact['city'] : '')) ?>
|
||
</option><?php endforeach; ?>
|
||
</select></div>
|
||
<div class="col-12"><label class="form-label"><?= h(t('animal.reason')) ?></label><input
|
||
class="form-control" name="reason" maxlength="255"
|
||
placeholder="<?= h(t('animal.return_reason_placeholder')) ?>"></div>
|
||
<div class="col-12"><label class="form-label"><?= h(t('common.notes')) ?></label><textarea
|
||
class="form-control" name="notes" rows="3"></textarea></div>
|
||
</div>
|
||
</div>
|
||
<div class="modal-footer"><button class="btn me-auto" type="button"
|
||
data-bs-dismiss="modal"><?= h(t('common.cancel')) ?></button><button
|
||
class="btn btn-primary"><?= h(t('common.save')) ?></button></div>
|
||
</form>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="modal modal-blur fade" id="modalVaccine" tabindex="-1" aria-hidden="true">
|
||
<div class="modal-dialog modal-lg modal-dialog-centered" role="document">
|
||
<form class="modal-content" method="post" action="/animal/add-vaccine">
|
||
<div class="modal-header">
|
||
<h5 class="modal-title"><?= h(t('animal.vaccine_record')) ?></h5>
|
||
<button type="button" class="btn-close" data-bs-dismiss="modal"
|
||
aria-label="<?= h(t('common.close')) ?>"></button>
|
||
</div>
|
||
|
||
<div class="modal-body">
|
||
<input type="hidden" name="animal_id" value="<?= (int)$animal['id'] ?>">
|
||
|
||
<div class="row g-2">
|
||
<div class="col-md-6">
|
||
<label class="form-label"><?= h(t('animal.vaccine')) ?></label>
|
||
<select class="form-select" name="vaccine_id" required>
|
||
<option value="">— Choisir —</option>
|
||
<?php foreach ($vaccines as $v): ?>
|
||
<option value="<?= (int)$v['id'] ?>"><?= htmlspecialchars($v['name'], ENT_QUOTES) ?>
|
||
</option>
|
||
<?php endforeach; ?>
|
||
</select>
|
||
</div>
|
||
|
||
|
||
<div class="col-md-3">
|
||
<label class="form-label"><?= h(t('animal.done_on')) ?></label>
|
||
<input class="form-control" type="date" name="done_date" value="<?= $today ?>">
|
||
</div>
|
||
|
||
<div class="col-md-3">
|
||
<label class="form-label"><?= h(t('animal.reminder_due')) ?></label>
|
||
<input class="form-control" type="date" name="due_date" value="<?= $defaultDue ?>">
|
||
<div class="form-hint"><?= h(t('animal.kitten_vaccine_hint')) ?></div>
|
||
</div>
|
||
|
||
<?php $directoryFieldId = 'vaccine';
|
||
require __DIR__ . '/_directory_medical_fields.php'; ?>
|
||
<?php require __DIR__ . '/_expense_tariff_field.php'; ?>
|
||
|
||
<div class="col-md-4">
|
||
<label class="form-label"><?= h(t('animal.batch')) ?></label>
|
||
<input class="form-control" name="lot" placeholder="Optionnel">
|
||
</div>
|
||
|
||
<div class="col-md-4">
|
||
<label class="form-label"><?= h(t('animal.manufacturer')) ?></label>
|
||
<input class="form-control" name="manufacturer" placeholder="Ex : Boehringer Ingelheim">
|
||
</div>
|
||
|
||
<div class="col-md-4">
|
||
<label class="form-label"><?= h(t('animal.batch_expiry')) ?></label>
|
||
<input class="form-control" type="date" name="batch_expires_on">
|
||
</div>
|
||
|
||
<div class="col-md-8"><label class="form-label"><?= h(t('inventory.batch')) ?></label><select
|
||
class="form-select" name="inventory_batch_id">
|
||
<option value="">—</option><?php foreach (($inventoryBatches ?? []) as $b) {
|
||
if ($b['category'] === 'vaccine'): ?><option value="<?= (int)$b['id'] ?>">
|
||
<?= h($b['name'] . ' · ' . ($b['batch_number'] ?: t('inventory.no_batch')) . ' · ' . $b['quantity'] . ' ' . $b['unit']) ?>
|
||
</option><?php endif;
|
||
} ?>
|
||
</select></div>
|
||
<div class="col-md-4"><label class="form-label"><?= h(t('inventory.quantity')) ?></label><input
|
||
class="form-control" type="number" min="0" step="0.01" name="inventory_quantity"></div>
|
||
|
||
<div class="col-md-6">
|
||
<label class="form-label"><?= h(t('animal.administered_by')) ?></label>
|
||
<select class="form-select" name="administered_by_user_id" id="vaccineAdministrator">
|
||
<option value=""><?= h(t('common.not_provided')) ?></option>
|
||
<?php foreach (($activeUsers ?? []) as $user): ?><option value="<?= (int)$user['id'] ?>">
|
||
<?= h($user['name']) ?></option><?php endforeach; ?><option value="other">
|
||
<?= h(t('animal.other_person')) ?></option>
|
||
</select>
|
||
</div>
|
||
|
||
<div class="col-md-6 d-none" id="vaccineAdministratorOther">
|
||
<label class="form-label"><?= h(t('animal.person_name')) ?></label>
|
||
<input class="form-control" name="administered_by_name"
|
||
placeholder="<?= h(t('animal.person_placeholder')) ?>">
|
||
</div>
|
||
|
||
<div class="col-md-12">
|
||
<label class="form-label"><?= h(t('common.notes')) ?></label>
|
||
<input class="form-control" name="notes" placeholder="Optionnel">
|
||
</div>
|
||
</div>
|
||
|
||
<script>
|
||
document.addEventListener('DOMContentLoaded', () => {
|
||
const select = document.getElementById('vaccineAdministrator');
|
||
const wrap = document.getElementById('vaccineAdministratorOther');
|
||
const sync = () => wrap?.classList.toggle('d-none', select?.value !== 'other');
|
||
select?.addEventListener('change', sync);
|
||
sync();
|
||
});
|
||
|
||
</script>
|
||
</div>
|
||
|
||
<div class="modal-footer">
|
||
<button type="button" class="btn me-auto" data-bs-dismiss="modal"><?= h(t('common.cancel')) ?></button>
|
||
<button type="submit" class="btn btn-primary"><?= h(t('common.save')) ?></button>
|
||
</div>
|
||
</form>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="modal modal-blur fade" id="modalPrescription" tabindex="-1" aria-hidden="true">
|
||
<div class="modal-dialog modal-lg modal-dialog-centered">
|
||
<form class="modal-content" method="post" action="/animal/prescription/save" enctype="multipart/form-data">
|
||
<div class="modal-header">
|
||
<h5 class="modal-title">📄 <?= h(t('medical_document.add_prescription')) ?></h5><button type="button"
|
||
class="btn-close" data-bs-dismiss="modal"></button>
|
||
</div>
|
||
<div class="modal-body"><input type="hidden" name="animal_id" value="<?= (int)$animal['id'] ?>">
|
||
<div class="alert alert-info py-2"><?= h(t('medical_document.private_hint')) ?></div>
|
||
<div class="row g-3">
|
||
<div class="col-md-4"><label
|
||
class="form-label"><?= h(t('medical_document.prescribed_on')) ?></label><input
|
||
class="form-control" type="date" name="prescribed_on" value="<?= date('Y-m-d') ?>" required>
|
||
</div>
|
||
<div class="col-md-4"><label class="form-label"><?= h(t('animal.status')) ?></label><select
|
||
class="form-select" name="status">
|
||
<option value="active"><?= h(t('medical_document.status_active')) ?></option>
|
||
<option value="finished"><?= h(t('medical_document.status_finished')) ?></option>
|
||
<option value="replaced"><?= h(t('medical_document.status_replaced')) ?></option>
|
||
</select></div>
|
||
<div class="col-md-4"><label class="form-label"><?= h(t('medical_document.file')) ?></label><input
|
||
class="form-control" type="file" name="document"
|
||
accept="application/pdf,image/jpeg,image/png,image/webp" capture="environment" required>
|
||
</div>
|
||
<div class="col-md-6"><label
|
||
class="form-label"><?= h(t('animal.veterinary_clinic')) ?></label><select
|
||
class="form-select" name="clinic_contact_id">
|
||
<option value=""><?= h(t('common.not_provided')) ?></option>
|
||
<?php foreach ($directoryClinics as $c): ?><option value="<?= (int)$c['id'] ?>">
|
||
<?= h($c['name']) ?></option><?php endforeach; ?>
|
||
</select></div>
|
||
<div class="col-md-6"><label
|
||
class="form-label"><?= h(t('animal.form.veterinarian')) ?></label><select
|
||
class="form-select" name="veterinarian_contact_id">
|
||
<option value=""><?= h(t('common.not_provided')) ?></option>
|
||
<?php foreach ($veterinarians as $v): ?><option value="<?= (int)$v['id'] ?>">
|
||
<?= h($v['name']) ?></option><?php endforeach; ?>
|
||
</select></div><?php if ($treatments): ?><div class="col-12"><label
|
||
class="form-label"><?= h(t('medical_document.link_treatments')) ?></label>
|
||
<div class="d-flex flex-wrap gap-3"><?php foreach ($treatments as $t): ?><label
|
||
class="form-check"><input class="form-check-input" type="checkbox"
|
||
name="treatment_ids[]" value="<?= (int)$t['id'] ?>"><span
|
||
class="form-check-label"><?= h($t['medication_name']) ?></span></label><?php endforeach; ?>
|
||
</div>
|
||
</div><?php endif; ?><div class="col-12"><label
|
||
class="form-label"><?= h(t('common.notes')) ?></label><textarea class="form-control"
|
||
name="notes" rows="2"></textarea></div>
|
||
</div>
|
||
</div>
|
||
<div class="modal-footer"><button type="button" class="btn me-auto"
|
||
data-bs-dismiss="modal"><?= h(t('common.cancel')) ?></button><button
|
||
class="btn btn-primary"><?= h(t('common.save')) ?></button></div>
|
||
</form>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="modal modal-blur fade" id="modalLabReport" tabindex="-1" aria-hidden="true">
|
||
<div class="modal-dialog modal-xl modal-dialog-centered modal-dialog-scrollable">
|
||
<form class="modal-content" method="post" action="/animal/lab-report/save" enctype="multipart/form-data">
|
||
<div class="modal-header">
|
||
<h5 class="modal-title">🧪 <?= h(t('medical_document.add_analysis')) ?></h5><button type="button"
|
||
class="btn-close" data-bs-dismiss="modal"></button>
|
||
</div>
|
||
<div class="modal-body"><input type="hidden" name="animal_id" value="<?= (int)$animal['id'] ?>">
|
||
<div class="row g-3">
|
||
<div class="col-md-3"><label
|
||
class="form-label"><?= h(t('medical_document.sampled_on')) ?></label><input
|
||
class="form-control" type="date" name="sampled_on" value="<?= date('Y-m-d') ?>" required>
|
||
</div>
|
||
<div class="col-md-3"><label
|
||
class="form-label"><?= h(t('medical_document.analysis_type')) ?></label><input
|
||
class="form-control" name="report_type"
|
||
placeholder="<?= h(t('medical_document.analysis_example')) ?>"></div>
|
||
<div class="col-md-3"><label
|
||
class="form-label"><?= h(t('medical_document.laboratory')) ?></label><input
|
||
class="form-control" name="laboratory_name"></div>
|
||
<div class="col-md-3"><label
|
||
class="form-label"><?= h(t('medical_document.report_optional')) ?></label><input
|
||
class="form-control" type="file" name="document"
|
||
accept="application/pdf,image/jpeg,image/png,image/webp" capture="environment"></div>
|
||
<div class="col-md-6"><label
|
||
class="form-label"><?= h(t('animal.veterinary_clinic')) ?></label><select
|
||
class="form-select" name="clinic_contact_id">
|
||
<option value=""><?= h(t('common.not_provided')) ?></option>
|
||
<?php foreach ($directoryClinics as $c): ?><option value="<?= (int)$c['id'] ?>">
|
||
<?= h($c['name']) ?></option><?php endforeach; ?>
|
||
</select></div>
|
||
<div class="col-md-6"><label
|
||
class="form-label"><?= h(t('animal.form.veterinarian')) ?></label><select
|
||
class="form-select" name="veterinarian_contact_id">
|
||
<option value=""><?= h(t('common.not_provided')) ?></option>
|
||
<?php foreach ($veterinarians as $v): ?><option value="<?= (int)$v['id'] ?>">
|
||
<?= h($v['name']) ?></option><?php endforeach; ?>
|
||
</select></div>
|
||
<div class="col-12">
|
||
<div class="d-flex flex-wrap align-items-center justify-content-between gap-2 mb-2">
|
||
<div><label
|
||
class="form-label mb-0"><?= h(t('medical_document.measured_parameters')) ?></label>
|
||
<div class="form-hint"><?= h(t('medical_document.parameters_help')) ?></div>
|
||
</div>
|
||
<div class="d-flex gap-2"><select class="form-select form-select-sm" id="labTemplate">
|
||
<option value=""><?= h(t('medical_document.template')) ?></option>
|
||
<?php foreach (($labReferenceSets ?? []) as $key => $set): ?><option
|
||
value="ref:<?= h($key) ?>"><?= h($set['label']) ?></option><?php endforeach; ?>
|
||
</select><button class="btn btn-sm btn-outline-primary" id="addLabRow" type="button">+
|
||
<?= h(t('medical_document.parameter')) ?></button></div>
|
||
</div>
|
||
<div class="table-responsive">
|
||
<table class="table table-sm align-middle">
|
||
<thead>
|
||
<tr>
|
||
<th><?= h(t('medical_document.parameter')) ?></th>
|
||
<th><?= h(t('medical_document.value')) ?></th>
|
||
<th><?= h(t('medical_document.unit')) ?></th>
|
||
<th><?= h(t('medical_document.reference_min')) ?></th>
|
||
<th><?= h(t('medical_document.reference_max')) ?></th>
|
||
<th></th>
|
||
</tr>
|
||
</thead>
|
||
<tbody id="labRows"></tbody>
|
||
</table>
|
||
</div>
|
||
</div>
|
||
<div class="col-12"><label class="form-label"><?= h(t('common.notes')) ?></label><textarea
|
||
class="form-control" name="notes" rows="2"></textarea></div>
|
||
</div>
|
||
</div>
|
||
<div class="modal-footer"><button type="button" class="btn me-auto"
|
||
data-bs-dismiss="modal"><?= h(t('common.cancel')) ?></button><button
|
||
class="btn btn-primary"><?= h(t('common.save')) ?></button></div>
|
||
</form>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="modal modal-blur fade" id="modalMedicalStatus" tabindex="-1" aria-hidden="true">
|
||
<div class="modal-dialog modal-lg modal-dialog-centered" role="document">
|
||
<form class="modal-content" method="post" action="/animal/medical-status">
|
||
<div class="modal-header">
|
||
<h5 class="modal-title"><?= h(t('animal.medical_status_global')) ?></h5>
|
||
<button type="button" class="btn-close" data-bs-dismiss="modal"
|
||
aria-label="<?= h(t('common.close')) ?>"></button>
|
||
</div>
|
||
<div class="modal-body">
|
||
<input type="hidden" name="animal_id" value="<?= (int)$animal['id'] ?>">
|
||
|
||
<?php if ($isCat): ?><div class="alert alert-info">
|
||
<?= h(t('animal.positive_test_help')) ?>
|
||
</div>
|
||
|
||
<div class="row g-3">
|
||
<div class="col-md-6">
|
||
<label class="form-label"><?= h(t('animal.fiv_status')) ?></label>
|
||
<select class="form-select" name="fiv_status">
|
||
<?php foreach (['unknown' => t('animals.test_unknown'),'neg' => t('animals.test_negative'),'pos' => t('animals.test_positive'),'doubtful' => t('animals.test_doubtful')] as $value => $label): ?>
|
||
<option value="<?= h($value) ?>" <?= $fivStatus === $value ? 'selected' : '' ?>>
|
||
<?= h($label) ?></option>
|
||
<?php endforeach; ?>
|
||
</select>
|
||
</div>
|
||
<div class="col-md-6">
|
||
<label class="form-label"><?= h(t('animal.felv_status')) ?></label>
|
||
<select class="form-select" name="felv_status">
|
||
<?php foreach (['unknown' => t('animals.test_unknown'),'neg' => t('animals.test_negative'),'pos' => t('animals.test_positive'),'doubtful' => t('animals.test_doubtful')] as $value => $label): ?>
|
||
<option value="<?= h($value) ?>" <?= $felvStatus === $value ? 'selected' : '' ?>>
|
||
<?= h($label) ?></option>
|
||
<?php endforeach; ?>
|
||
</select>
|
||
</div>
|
||
<div class="col-md-4">
|
||
<label class="form-label"><?= h(t('animal.test_date')) ?></label>
|
||
<input class="form-control" type="date" name="test_date" value="<?= date('Y-m-d') ?>">
|
||
</div>
|
||
<div class="col-md-8">
|
||
<label class="form-label"><?= h(t('animal.test_note')) ?></label>
|
||
<input class="form-control" name="test_notes"
|
||
placeholder="<?= h(t('animal.test_notes_placeholder')) ?>">
|
||
</div>
|
||
</div>
|
||
|
||
<hr class="my-4">
|
||
<?php endif; ?>
|
||
<div class="d-flex align-items-center justify-content-between mb-3">
|
||
<div>
|
||
<h4 class="m-0"><?= h(t('animal.conditions')) ?></h4>
|
||
<div class="text-muted small"><?= h(t('animal.conditions_help')) ?></div>
|
||
</div>
|
||
</div>
|
||
|
||
<?php foreach (($healthConditions ?? []) as $condition): ?>
|
||
<div class="row g-2 align-items-end mb-3">
|
||
<div class="col-md-4">
|
||
<label class="form-label"><?= h(t('animal.label')) ?></label>
|
||
<input class="form-control" name="conditions[<?= (int)$condition['id'] ?>][name]"
|
||
value="<?= h($condition['name']) ?>">
|
||
</div>
|
||
<div class="col-md-2">
|
||
<label class="form-label"><?= h(t('animal.state')) ?></label>
|
||
<select class="form-select" name="conditions[<?= (int)$condition['id'] ?>][status]">
|
||
<option value="active" <?= $condition['status'] === 'active' ? 'selected' : '' ?>>
|
||
<?= h(t('animal.active')) ?></option>
|
||
<option value="resolved" <?= $condition['status'] === 'resolved' ? 'selected' : '' ?>>
|
||
<?= h(t('animal.resolved')) ?></option>
|
||
</select>
|
||
</div>
|
||
<div class="col-md-2">
|
||
<label class="form-label"><?= h(t('animal.diagnosis')) ?></label>
|
||
<input class="form-control" type="date"
|
||
name="conditions[<?= (int)$condition['id'] ?>][diagnosed_at]"
|
||
value="<?= h($condition['diagnosed_at'] ?? '') ?>">
|
||
</div>
|
||
<div class="col-md-4">
|
||
<label class="form-label"><?= h(t('animal.note')) ?></label>
|
||
<input class="form-control" name="conditions[<?= (int)$condition['id'] ?>][notes]"
|
||
value="<?= h($condition['notes'] ?? '') ?>">
|
||
</div>
|
||
</div>
|
||
<?php endforeach; ?>
|
||
|
||
<div class="card bg-light-lt">
|
||
<div class="card-body">
|
||
<div class="subheader mb-2"><?= h(t('animal.add_condition')) ?></div>
|
||
<div class="row g-2">
|
||
<div class="col-md-4"><input class="form-control" name="conditions[new][name]"
|
||
placeholder="<?= h(t('animal.condition_placeholder')) ?>"></div>
|
||
<div class="col-md-2">
|
||
<select class="form-select" name="conditions[new][status]">
|
||
<option value="active"><?= h(t('animal.active')) ?></option>
|
||
<option value="resolved"><?= h(t('animal.resolved')) ?></option>
|
||
</select>
|
||
</div>
|
||
<div class="col-md-2"><input class="form-control" type="date"
|
||
name="conditions[new][diagnosed_at]" value="<?= date('Y-m-d') ?>"></div>
|
||
<div class="col-md-4"><input class="form-control" name="conditions[new][notes]"
|
||
placeholder="Note facultative"></div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div class="modal-footer">
|
||
<button type="button" class="btn me-auto" data-bs-dismiss="modal"><?= h(t('common.cancel')) ?></button>
|
||
<button type="submit" class="btn btn-primary"><?= h(t('animal.save_medical_status')) ?></button>
|
||
</div>
|
||
</form>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="modal modal-blur fade" id="modalMedical" tabindex="-1" aria-hidden="true">
|
||
<div class="modal-dialog modal-lg modal-dialog-centered" role="document">
|
||
<form class="modal-content" method="post" action="/animal/add-medical" enctype="multipart/form-data">
|
||
<div class="modal-header">
|
||
<h5 class="modal-title"><?= h(t('animal.add_medical_note')) ?></h5>
|
||
<button type="button" class="btn-close" data-bs-dismiss="modal"
|
||
aria-label="<?= h(t('common.close')) ?>"></button>
|
||
</div>
|
||
|
||
<div class="modal-body">
|
||
<input type="hidden" name="animal_id" value="<?= (int)$animal['id'] ?>">
|
||
|
||
<div class="row g-2">
|
||
<div class="col-md-4">
|
||
<label class="form-label"><?= h(t('animal.form.type')) ?></label>
|
||
<select class="form-select" name="kind">
|
||
<option value="consult"><?= h(t('animal.consultation')) ?></option>
|
||
<option value="controle"><?= h(t('animal.checkup')) ?></option>
|
||
<option value="symptome"><?= h(t('animal.symptom')) ?></option>
|
||
<option value="diagnostic"><?= h(t('animal.diagnosis')) ?></option>
|
||
<option value="acte"><?= h(t('animal.procedure')) ?></option>
|
||
<option value="hosp"><?= h(t('animal.hospitalization')) ?></option>
|
||
<option value="autre"><?= h(t('animal.other')) ?></option>
|
||
</select>
|
||
</div>
|
||
|
||
<div class="col-md-8">
|
||
<label class="form-label"><?= h(t('animal.reason')) ?></label>
|
||
<input class="form-control" name="reason" id="medicalReason"
|
||
placeholder="<?= h(t('animal.reason_placeholder')) ?>">
|
||
</div>
|
||
|
||
<div class="col-12">
|
||
<div class="card card-sm mb-0 <?= !empty($animal['sterilized']) ? 'bg-light-lt' : '' ?>">
|
||
<div class="card-body py-2 px-3">
|
||
<label class="form-check m-0">
|
||
<input class="form-check-input" type="checkbox" name="mark_sterilized"
|
||
id="markSterilized" value="1"
|
||
<?= !empty($animal['sterilized']) ? 'checked disabled' : '' ?>>
|
||
<span class="form-check-label">
|
||
<span class="fw-bold d-block"><?= h(t('animal.sterilization_act')) ?></span>
|
||
<span
|
||
class="form-hint mt-0"><?= h(!empty($animal['sterilized']) ? t('animal.already_sterilized') : t('animal.sterilization_auto', ['price' => AppSettings::int('adoption_fee_sterilized')])) ?></span>
|
||
</span>
|
||
</label>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<?php $directoryFieldId = 'medical';
|
||
require __DIR__ . '/_directory_medical_fields.php'; ?>
|
||
<?php require __DIR__ . '/_expense_tariff_field.php'; ?>
|
||
|
||
|
||
<div class="col-md-6">
|
||
<label class="form-label"><?= h(t('common.weight')) ?></label>
|
||
<input class="form-control" name="weight_kg" inputmode="decimal" placeholder="Ex: 4.2">
|
||
</div>
|
||
|
||
<div class="col-md-6">
|
||
<label class="form-label"><?= h(t('animal.temperature')) ?></label>
|
||
<input class="form-control" name="temperature_c" inputmode="decimal" placeholder="Ex: 38.6">
|
||
</div>
|
||
|
||
<div class="col-12">
|
||
<label class="form-label"><?= h(t('animal.symptoms')) ?></label>
|
||
<textarea class="form-control" name="symptoms" rows="2"></textarea>
|
||
</div>
|
||
|
||
<div class="col-12">
|
||
<label class="form-label"><?= h(t('animal.exam')) ?></label>
|
||
<textarea class="form-control" name="exam" rows="2"></textarea>
|
||
</div>
|
||
|
||
<div class="mb-3">
|
||
<label class="form-label"><?= h(t('animal.medical_photos')) ?></label>
|
||
<input type="file" name="medical_photos[]" class="form-control" accept="image/*" multiple>
|
||
<small class="text-muted">
|
||
<?= h(t('animal.medical_photos_hint')) ?>
|
||
</small>
|
||
</div>
|
||
|
||
<div class="col-12">
|
||
<label class="form-label"><?= h(t('animal.diagnosis')) ?></label>
|
||
<textarea class="form-control" name="diagnosis" rows="2"></textarea>
|
||
</div>
|
||
|
||
<div class="col-12">
|
||
<label class="form-label"><?= h(t('animal.plan')) ?></label>
|
||
<textarea class="form-control" name="plan" rows="2"></textarea>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="modal-footer">
|
||
<button type="button" class="btn me-auto" data-bs-dismiss="modal"><?= h(t('common.cancel')) ?></button>
|
||
<button type="submit" class="btn btn-primary"><?= h(t('common.save')) ?></button>
|
||
</div>
|
||
</form>
|
||
</div>
|
||
</div>
|
||
<?php endif; ?>
|
||
|
||
<script>
|
||
document.addEventListener('DOMContentLoaded', () => {
|
||
document.querySelectorAll('.directory-contact-select').forEach(select => {
|
||
const target = document.getElementById(select.dataset.newTarget || '');
|
||
const sync = () => {
|
||
const creating = select.value === 'new';
|
||
target?.classList.toggle('d-none', !creating);
|
||
if (target) target.required = creating;
|
||
};
|
||
select.addEventListener('change', sync);
|
||
sync();
|
||
});
|
||
document.querySelectorAll('.medical-vet-select').forEach(vet => {
|
||
const pair = vet.dataset.pair;
|
||
const clinic = document.querySelector(`.medical-clinic-select[data-pair="${pair}"]`);
|
||
if (!clinic) return;
|
||
const filterVets = () => {
|
||
const clinicId = clinic.value;
|
||
[...vet.options].forEach(option => {
|
||
if (!option.value || option.value === 'new') return;
|
||
const organization = option.dataset.organization || '';
|
||
option.hidden = Boolean(clinicId && clinicId !== 'new' &&
|
||
organization && organization !== clinicId);
|
||
});
|
||
if (vet.selectedOptions[0]?.hidden) vet.value = '';
|
||
};
|
||
vet.addEventListener('change', () => {
|
||
const organization = vet.selectedOptions[0]?.dataset.organization;
|
||
if (organization && organization !== '0') {
|
||
clinic.value = organization;
|
||
clinic.dispatchEvent(new Event('change', {
|
||
bubbles: true
|
||
}));
|
||
}
|
||
});
|
||
clinic.addEventListener('change', filterVets);
|
||
filterVets();
|
||
});
|
||
const sterilizedCheckbox = document.getElementById('markSterilized');
|
||
sterilizedCheckbox?.addEventListener('change', () => {
|
||
if (!sterilizedCheckbox.checked) return;
|
||
const kind = sterilizedCheckbox.form.querySelector('select[name="kind"]');
|
||
const reason = document.getElementById('medicalReason');
|
||
if (kind) kind.value = 'acte';
|
||
if (reason && reason.value.trim() === '') reason.value = < ? = json_encode(t(
|
||
'animal.sterilization_label'), JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES) ? > ;
|
||
});
|
||
const animalId = < ? = (int) $animal['id'] ? > ;
|
||
const legacyKey = 'animal_show_active_tab_' + animalId;
|
||
// L'ancien comportement mémorisait le dernier onglet par animal. Une
|
||
// ouverture normale doit désormais toujours revenir sur Identité.
|
||
localStorage.removeItem(legacyKey);
|
||
|
||
const tabLinks = Array.from(document.querySelectorAll('a[data-bs-toggle="tab"]'));
|
||
|
||
function showTab(target) {
|
||
if (!target) return false;
|
||
|
||
const link = document.querySelector(`a[data-bs-toggle="tab"][href="${target}"]`);
|
||
if (!link) return false;
|
||
|
||
// 1) Bootstrap API si dispo
|
||
if (window.bootstrap && bootstrap.Tab) {
|
||
bootstrap.Tab.getOrCreateInstance(link).show();
|
||
return true;
|
||
}
|
||
|
||
// 2) Fallback “manuel” (au cas où)
|
||
tabLinks.forEach(a => a.classList.remove('active'));
|
||
document.querySelectorAll('.tab-pane').forEach(p => p.classList.remove('active', 'show'));
|
||
|
||
link.classList.add('active');
|
||
document.getElementById('animalMoreTabs')?.classList.toggle('active', link.classList.contains(
|
||
'dropdown-item'));
|
||
const pane = document.querySelector(target);
|
||
if (pane) pane.classList.add('active', 'show');
|
||
|
||
return true;
|
||
}
|
||
|
||
// Le fragment reste dans l'URL afin que les actions ciblées puissent
|
||
// revenir sur Médical, Photos, Portées, etc., sans mémorisation durable.
|
||
tabLinks.forEach(a => {
|
||
a.addEventListener('shown.bs.tab', (e) => {
|
||
const href = e.target.getAttribute('href');
|
||
if (!href) return;
|
||
const moreToggle = document.getElementById('animalMoreTabs');
|
||
moreToggle?.classList.toggle('active', e.target.classList.contains(
|
||
'dropdown-item'));
|
||
history.replaceState(null, '', href);
|
||
});
|
||
|
||
// Si jamais "shown.bs.tab" ne déclenche pas (rare), on sauvegarde au click aussi
|
||
a.addEventListener('click', () => {
|
||
const href = a.getAttribute('href');
|
||
if (!href) return;
|
||
history.replaceState(null, '', href);
|
||
});
|
||
});
|
||
|
||
// Un hash explicite est respecté ; sinon Identité est systématique.
|
||
const target = window.location.hash || '#tab-ident';
|
||
|
||
// Si bootstrap n'est pas prêt à l'instant T, on retente un peu (Tabler peut charger en bas)
|
||
let tries = 0;
|
||
(function retry() {
|
||
tries++;
|
||
const ok = showTab(target);
|
||
if (!ok && tries < 20) setTimeout(retry, 50);
|
||
})();
|
||
});
|
||
|
||
</script>
|
||
<script>
|
||
(function() {
|
||
const body = document.getElementById('labRows'),
|
||
add = document.getElementById('addLabRow'),
|
||
select = document.getElementById('labTemplate');
|
||
if (!body) return;
|
||
const presets = {};
|
||
const references = < ? = json_encode($labReferenceSets ?? [], JSON_UNESCAPED_UNICODE |
|
||
JSON_UNESCAPED_SLASHES) ? > ;
|
||
Object.entries(references).forEach(([key, set]) => presets['ref:' + key] = (set.parameters || []).map(p => [
|
||
p.code + ' — ' + p.name, p.unit, p.min, p.max
|
||
]));
|
||
const esc = s => String(s ?? '').replace(/[&<>'"]/g, c => ({
|
||
'&': '&',
|
||
'<': '<',
|
||
'>': '>',
|
||
"'": ''',
|
||
'"': '"'
|
||
} [c]));
|
||
const row = (name = '', unit = '', min = null, max = null) => {
|
||
const tr = document.createElement('tr');
|
||
tr.innerHTML =
|
||
`<td><input class="form-control form-control-sm" name="parameter_name[]" value="${esc(name)}" maxlength="120"></td><td><input class="form-control form-control-sm" type="number" step="any" name="value[]"></td><td><input class="form-control form-control-sm" name="unit[]" value="${esc(unit)}" maxlength="30"></td><td><input class="form-control form-control-sm" type="number" step="any" name="reference_min[]" value="${min??''}"></td><td><input class="form-control form-control-sm" type="number" step="any" name="reference_max[]" value="${max??''}"></td><td><button class="btn btn-sm btn-ghost-danger" type="button" aria-label="<?= h(t('common.delete')) ?>">×</button></td>`;
|
||
tr.querySelector('button').addEventListener('click', () => tr.remove());
|
||
body.appendChild(tr);
|
||
};
|
||
add?.addEventListener('click', () => row());
|
||
select?.addEventListener('change', () => {
|
||
if (!presets[select.value]) return;
|
||
body.innerHTML = '';
|
||
presets[select.value].forEach(v => row(v[0], v[1], v[2], v[3]));
|
||
});
|
||
body.addEventListener('keydown', event => {
|
||
if (event.key !== 'Tab' || !event.target.matches('input[name="value[]"]')) return;
|
||
const values = [...body.querySelectorAll('input[name="value[]"]')],
|
||
index = values.indexOf(event.target),
|
||
next = values[index + (event.shiftKey ? -1 : 1)];
|
||
if (!next) return;
|
||
event.preventDefault();
|
||
next.focus();
|
||
next.select();
|
||
});
|
||
row();
|
||
})();
|
||
|
||
</script>
|
||
<script>
|
||
(function() {
|
||
document.querySelectorAll('.lab-chart').forEach(canvas => {
|
||
const s = JSON.parse(canvas.dataset.series || '{}'),
|
||
points = s.points || [];
|
||
const color = p => p.min === null && p.max === null ? '#6c757d' : (p.min !== null && p.value < p
|
||
.min ? '#206bc4' : (p.max !== null && p.value > p.max ? '#d63939' : '#2fb344'));
|
||
new Chart(canvas, {
|
||
type: 'line',
|
||
data: {
|
||
labels: points.map(p => p.date.split('-').reverse().join('/')),
|
||
datasets: [{
|
||
label: s.name,
|
||
data: points.map(p => p.value),
|
||
borderColor: '#f59f00',
|
||
backgroundColor: '#f59f00',
|
||
pointBackgroundColor: points.map(color),
|
||
pointBorderColor: points.map(color),
|
||
pointRadius: 4,
|
||
tension: .2
|
||
}, {
|
||
label: < ? = json_encode(t('medical_document.reference_min'),
|
||
JSON_UNESCAPED_UNICODE) ? > ,
|
||
data: points.map(p => p.min),
|
||
borderColor: '#74c0fc',
|
||
borderDash: [5, 5],
|
||
pointRadius: 0,
|
||
spanGaps: true
|
||
}, {
|
||
label: < ? = json_encode(t('medical_document.reference_max'),
|
||
JSON_UNESCAPED_UNICODE) ? > ,
|
||
data: points.map(p => p.max),
|
||
borderColor: '#ffa8a8',
|
||
borderDash: [5, 5],
|
||
pointRadius: 0,
|
||
spanGaps: true
|
||
}]
|
||
},
|
||
options: {
|
||
responsive: true,
|
||
maintainAspectRatio: false,
|
||
interaction: {
|
||
mode: 'index',
|
||
intersect: false
|
||
},
|
||
plugins: {
|
||
legend: {
|
||
display: true
|
||
}
|
||
},
|
||
scales: {
|
||
y: {
|
||
title: {
|
||
display: !!s.unit,
|
||
text: s.unit
|
||
}
|
||
},
|
||
x: {
|
||
title: {
|
||
display: true,
|
||
text: < ? = json_encode(t('chart.date'), JSON_UNESCAPED_UNICODE) ? >
|
||
}
|
||
}
|
||
}
|
||
}
|
||
});
|
||
});
|
||
})();
|
||
|
||
</script>
|
||
<script>
|
||
(function() {
|
||
const el = document.getElementById('weightChart');
|
||
if (!el) return;
|
||
|
||
const rows = < ? = json_encode($weights ?? [], JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE) ? > ;
|
||
|
||
// labels = dates, data = poids
|
||
const labels = rows.map(r => (r.measured_at || '').slice(0, 10)); // YYYY-MM-DD
|
||
const data = rows.map(r => Number(r.value));
|
||
|
||
// Chart.js default color (on ne fixe rien volontairement)
|
||
new Chart(el, {
|
||
type: 'line',
|
||
data: {
|
||
labels,
|
||
datasets: [{
|
||
label: < ? = json_encode(t('chart.weight'), JSON_UNESCAPED_UNICODE) ? > ,
|
||
data,
|
||
tension: 0.25,
|
||
pointRadius: 2
|
||
}]
|
||
},
|
||
options: {
|
||
responsive: true,
|
||
maintainAspectRatio: false,
|
||
plugins: {
|
||
legend: {
|
||
display: true
|
||
}
|
||
},
|
||
scales: {
|
||
y: {
|
||
title: {
|
||
display: true,
|
||
text: 'kg'
|
||
}
|
||
},
|
||
x: {
|
||
title: {
|
||
display: true,
|
||
text: < ? = json_encode(t('chart.date'), JSON_UNESCAPED_UNICODE) ? >
|
||
}
|
||
}
|
||
}
|
||
}
|
||
});
|
||
})();
|
||
|
||
</script>
|
||
<script>
|
||
(function() {
|
||
const btn = document.getElementById('btn-icad-refresh');
|
||
const body = document.getElementById('icad-card-body');
|
||
const tr = < ? = json_encode(['error' => t('animal.icad_error'), 'empty' => t('animal.no_icad'), 'date' =>
|
||
t('common.date'), 'recorded' => t('animal.recorded'), 'noEvent' => t('animal.no_event'),
|
||
'holder' => t('animal.holder_internal'), 'name' => t('common.name'), 'phone' => t(
|
||
'common.phone'), 'email' => t('common.email'), 'icadName' => t('animal.icad_name'),
|
||
'microchip' => t('animal.microchip'), 'sex' => t('animal.sex'), 'birth' => t('animal.birth'),
|
||
'appearance' => t('animal.appearance'), 'history' => t('animal.icad_history'), 'fetching' => t(
|
||
'animal.icad_fetching'), 'fetchError' => t('animal.icad_fetch_error')
|
||
], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES) ? > ;
|
||
if (!btn || !body) return;
|
||
|
||
function esc(s) {
|
||
return (s ?? '').toString()
|
||
.replaceAll('&', '&').replaceAll('<', '<')
|
||
.replaceAll('>', '>').replaceAll('"', '"')
|
||
.replaceAll("'", "'");
|
||
}
|
||
|
||
function renderIcad(payload) {
|
||
const cache = payload?.cached;
|
||
const status = cache?.status ?? 'empty';
|
||
const err = cache?.error ?? '';
|
||
const d = cache?.data ?? null;
|
||
|
||
if (status === 'error') {
|
||
return `<div class="alert alert-danger"><strong>${esc(tr.error)}</strong><br>${esc(err)}</div>`;
|
||
}
|
||
if (status !== 'ok' || !d) {
|
||
return `<div class="text-muted">${esc(tr.empty)}</div>`;
|
||
}
|
||
|
||
const owner = d.owner ?? null;
|
||
const events = Array.isArray(d.events) ? d.events : [];
|
||
|
||
const eventsHtml = events.length ?
|
||
`<ul class="list list-timeline">` + events.map(e => `
|
||
<li>
|
||
<div class="list-timeline-icon bg-blue-lt"><i class="ti ti-history"></i></div>
|
||
<div class="list-timeline-content">
|
||
<div class="fw-bold">${esc(e.label)}</div>
|
||
<div class="text-muted small">${esc(tr.date)} : ${esc(e.date)} — ${esc(tr.recorded)} : ${esc(e.recorded_at)}</div>
|
||
</div>
|
||
</li>
|
||
`).join('') + `</ul>` :
|
||
`<div class="text-muted">${esc(tr.noEvent)}</div>`;
|
||
|
||
let ownerHtml = '';
|
||
if (owner && (owner.name || owner.phone || owner.email)) {
|
||
ownerHtml = `
|
||
<hr class="my-3">
|
||
<div class="text-muted mb-2">${esc(tr.holder)}</div>
|
||
<div class="datagrid">
|
||
<div class="datagrid-item"><div class="datagrid-title">${esc(tr.name)}</div><div class="datagrid-content">${esc(owner.name)}</div></div>
|
||
<div class="datagrid-item"><div class="datagrid-title">${esc(tr.phone)}</div><div class="datagrid-content">${esc(owner.phone)}</div></div>
|
||
<div class="datagrid-item"><div class="datagrid-title">${esc(tr.email)}</div><div class="datagrid-content">${esc(owner.email)}</div></div>
|
||
</div>
|
||
`;
|
||
}
|
||
|
||
return `
|
||
<div class="row g-3">
|
||
<div class="col-md-6">
|
||
<div class="datagrid">
|
||
<div class="datagrid-item"><div class="datagrid-title">${esc(tr.icadName)}</div><div class="datagrid-content">${esc(d.name)}</div></div>
|
||
<div class="datagrid-item"><div class="datagrid-title">${esc(tr.microchip)}</div><div class="datagrid-content">${esc(d.insert)}</div></div>
|
||
<div class="datagrid-item"><div class="datagrid-title">${esc(tr.sex)}</div><div class="datagrid-content">${esc(d.sex)}</div></div>
|
||
<div class="datagrid-item"><div class="datagrid-title">${esc(tr.birth)}</div><div class="datagrid-content">${esc(d.birth_date)}</div></div>
|
||
<div class="datagrid-item"><div class="datagrid-title">${esc(tr.appearance)}</div><div class="datagrid-content">${esc(d.appearance)}</div></div>
|
||
</div>
|
||
</div>
|
||
<div class="col-md-6">
|
||
<div class="text-muted mb-2">${esc(tr.history)}</div>
|
||
${eventsHtml}
|
||
</div>
|
||
</div>
|
||
${ownerHtml}
|
||
`;
|
||
}
|
||
|
||
async function refresh() {
|
||
const id = btn.dataset.animalId;
|
||
btn.disabled = true;
|
||
const oldTxt = btn.innerText;
|
||
btn.innerText = '…';
|
||
|
||
const prev = body.innerHTML;
|
||
body.innerHTML = `<div class="text-muted">${esc(tr.fetching)}</div>`;
|
||
|
||
try {
|
||
const r = await fetch('/animal/icad', {
|
||
method: 'POST',
|
||
headers: {
|
||
'Accept': 'application/json',
|
||
'Content-Type': 'application/x-www-form-urlencoded'
|
||
},
|
||
body: new URLSearchParams({
|
||
id,
|
||
force: '1',
|
||
csrf: < ? = json_encode(Auth::csrf()) ? >
|
||
})
|
||
});
|
||
const j = await r.json();
|
||
body.innerHTML = renderIcad(j);
|
||
} catch (e) {
|
||
body.innerHTML = prev;
|
||
alert(tr.fetchError);
|
||
console.error(e);
|
||
} finally {
|
||
btn.disabled = false;
|
||
btn.innerText = oldTxt;
|
||
}
|
||
}
|
||
|
||
btn.addEventListener('click', refresh);
|
||
})();
|
||
|
||
</script>
|
||
<?php require __DIR__ . '/_deworming_modal.php'; ?>
|
||
<?php require __DIR__ . '/_expense_modal.php'; ?>
|