Publier Globinours 1.0.0-rc.3
This commit is contained in:
parent
ea8c24d622
commit
9a2b4068da
325 changed files with 38230 additions and 20 deletions
918
app/Views/animals_list.php
Normal file
918
app/Views/animals_list.php
Normal file
|
|
@ -0,0 +1,918 @@
|
|||
<?php
|
||||
/** @var array $animals */
|
||||
/** @var string $q */
|
||||
/** @var string $status */
|
||||
/** @var array $statuses */
|
||||
/** @var string $sort */
|
||||
/** @var string $dir */
|
||||
$quality = $quality ?? '';
|
||||
$qualityLabel = $qualityLabel ?? '';
|
||||
$species = $species ?? '';
|
||||
$speciesOptions = $speciesOptions ?? [];
|
||||
$viewMode = $viewMode ?? 'table';
|
||||
$speciesByCode = [];
|
||||
foreach ($speciesOptions as $speciesOption) {
|
||||
$speciesByCode[SpeciesService::normalize((string)$speciesOption['code'])] = $speciesOption;
|
||||
}
|
||||
$shortContactName = static function (?string $name): string {
|
||||
$name = trim((string)$name);
|
||||
if ($name === '') {
|
||||
return '';
|
||||
}$parts = preg_split('/\s+/u', $name) ?: [];
|
||||
if (count($parts) < 2) {
|
||||
return $name;
|
||||
}$first = (string)array_shift($parts);
|
||||
$last = (string)array_pop($parts);
|
||||
$initial = mb_strtoupper(mb_substr($last, 0, 1));
|
||||
return $first . ($initial !== '' ? ' ' . $initial . '.' : '');
|
||||
};
|
||||
$tableQs = $_GET;
|
||||
$tableQs['view'] = 'table';
|
||||
$mosaicQs = $_GET;
|
||||
$mosaicQs['view'] = 'mosaic';
|
||||
|
||||
|
||||
?>
|
||||
|
||||
|
||||
|
||||
<div class="card">
|
||||
<div class="card-body">
|
||||
<form class="row g-2 align-items-center" method="get" action="/animals">
|
||||
<?php if ($quality !== ''): ?><input type="hidden" name="quality" value="<?= h($quality) ?>"><?php endif; ?>
|
||||
<input type="hidden" name="sort" value="<?= h($sort) ?>">
|
||||
<input type="hidden" name="dir" value="<?= h(strtolower($dir)) ?>">
|
||||
<div class="col-12">
|
||||
<input class="form-control" name="q" value="<?= htmlspecialchars($q, ENT_QUOTES) ?>"
|
||||
placeholder="<?= h(t('animals.search_placeholder')) ?>">
|
||||
</div>
|
||||
|
||||
<div class="col-12 col-md-auto animal-filter animal-filter-status">
|
||||
<select class="form-select" name="status" onchange="this.form.requestSubmit()">
|
||||
<?php foreach ($statuses as $k => $label): ?>
|
||||
<option value="<?= htmlspecialchars($k, ENT_QUOTES) ?>" <?= $status === $k ? 'selected' : '' ?>>
|
||||
<?= htmlspecialchars($label, ENT_QUOTES) ?>
|
||||
</option>
|
||||
<?php endforeach; ?>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div class="col-12 col-md-auto animal-filter animal-filter-species"><select class="form-select"
|
||||
name="species" onchange="this.form.requestSubmit()">
|
||||
<option value=""><?= h(t('animals.all_species')) ?></option>
|
||||
<?php foreach ($speciesOptions as $option): ?><option value="<?= h($option['code']) ?>"
|
||||
<?= $species === $option['code'] ? 'selected' : '' ?>>
|
||||
<?= h($option['icon'] . ' ' . $option['name']) ?></option><?php endforeach; ?>
|
||||
</select></div>
|
||||
<div class="col-12 col-md-auto animal-filter animal-filter-availability"><select class="form-select"
|
||||
name="availability" onchange="this.form.requestSubmit()">
|
||||
<option value=""><?= h(t('animals.all_availability')) ?></option>
|
||||
<option value="available" <?= (($availability ?? '') === 'available') ? 'selected' : '' ?>>
|
||||
<?= h(t('animals.available')) ?></option>
|
||||
<option value="not_available" <?= (($availability ?? '') === 'not_available') ? 'selected' : '' ?>>
|
||||
<?= h(t('animals.unavailable')) ?></option>
|
||||
<option value="unknown" <?= (($availability ?? '') === 'unknown') ? 'selected' : '' ?>>
|
||||
<?= h(t('animals.to_define')) ?></option>
|
||||
</select></div>
|
||||
|
||||
<div class="col-auto">
|
||||
<a class="btn btn-outline-primary" href="/animal/new">🐈 <?= h(t('animals.add')) ?></a>
|
||||
</div>
|
||||
<div class="col-auto">
|
||||
<a class="btn btn-outline-primary" href="/litter/new">🐾 <?= h(t('animals.add_litter')) ?></a>
|
||||
</div>
|
||||
<div class="col-auto ms-auto d-flex align-items-center gap-2">
|
||||
<div class="btn-group" role="group" aria-label="<?= h(t('animals.table_view')) ?>"><a
|
||||
class="btn px-3 <?= $viewMode === 'table' ? 'btn-dark' : 'btn-outline-secondary' ?>"
|
||||
href="/animals?<?= h(http_build_query($tableQs)) ?>" title="<?= h(t('animals.table_view')) ?>"
|
||||
aria-label="<?= h(t('animals.table_view')) ?>">☷</a><a
|
||||
class="btn px-3 <?= $viewMode === 'mosaic' ? 'btn-dark' : 'btn-outline-secondary' ?>"
|
||||
href="/animals?<?= h(http_build_query($mosaicQs)) ?>" title="<?= h(t('animals.mosaic_view')) ?>"
|
||||
aria-label="<?= h(t('animals.mosaic_view')) ?>">▦</a></div>
|
||||
<div class="dropdown flex-shrink-0">
|
||||
<button class="btn btn-outline-secondary dropdown-toggle" type="button" data-bs-toggle="dropdown"
|
||||
aria-expanded="false" aria-label="<?= h(t('animals.removed_management')) ?>"
|
||||
title="<?= h(t('animals.removed_management')) ?>">
|
||||
⚙️
|
||||
</button>
|
||||
<div class="dropdown-menu dropdown-menu-end shadow">
|
||||
<a class="dropdown-item" href="/animals/archived">📦 <?= h(t('animals.archived')) ?></a>
|
||||
<a class="dropdown-item text-danger" href="/animals/trash">🗑 <?= h(t('animals.trash')) ?></a>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<?php if ($quality !== ''): ?><div
|
||||
class="alert alert-warning mx-3 mt-3 mb-0 d-flex flex-wrap align-items-center gap-2"><span>🔎
|
||||
<?= h(t('animals.quality_filter')) ?> : <strong><?= h($qualityLabel) ?></strong></span><span
|
||||
class="badge bg-orange-lt"><?= h(t('animals.records', ['count' => count($animals)])) ?></span><a
|
||||
class="btn btn-sm btn-outline-secondary ms-auto"
|
||||
href="/animals?view=<?= h($viewMode) ?>"><?= h(t('animals.remove_filter')) ?></a></div><?php endif; ?>
|
||||
|
||||
|
||||
<style>
|
||||
@media(min-width:768px) {
|
||||
.animal-filter-status {
|
||||
width: 12.5rem
|
||||
}
|
||||
|
||||
.animal-filter-species {
|
||||
width: 10.5rem
|
||||
}
|
||||
|
||||
.animal-filter-availability {
|
||||
width: 12.5rem
|
||||
}
|
||||
}
|
||||
|
||||
.litter-kitten-row td:nth-child(2) {
|
||||
padding-left: 1.5rem;
|
||||
}
|
||||
|
||||
.litter-member-row td:first-child {
|
||||
padding-left: 2.75rem;
|
||||
}
|
||||
|
||||
.litter-member-row>td {
|
||||
background-color: rgba(169, 127, 69, .045);
|
||||
}
|
||||
|
||||
.bonded-member-row>td {
|
||||
background-color: rgba(111, 66, 193, .045);
|
||||
}
|
||||
|
||||
.litter-member-row.bonded-member-row>td {
|
||||
background: linear-gradient(90deg, rgba(169, 127, 69, .045), rgba(111, 66, 193, .045));
|
||||
}
|
||||
|
||||
.litter-parent-placeholder td {
|
||||
background: var(--tblr-bg-surface-secondary, #f6f8fb);
|
||||
border-top-width: 2px;
|
||||
}
|
||||
|
||||
.bonded-group-placeholder td {
|
||||
background: rgba(111, 66, 193, .06);
|
||||
border-top: 2px solid rgba(111, 66, 193, .2);
|
||||
}
|
||||
|
||||
.litter-parent-placeholder-icon {
|
||||
display: inline-flex;
|
||||
width: 44px;
|
||||
height: 44px;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
border-radius: 10px;
|
||||
background: rgba(98, 105, 118, .12);
|
||||
color: var(--tblr-secondary);
|
||||
font-size: 1.25rem;
|
||||
}
|
||||
|
||||
.animal-alert-icon {
|
||||
display: inline-flex;
|
||||
width: 1.4rem;
|
||||
height: 1.4rem;
|
||||
flex: 0 0 1.4rem;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
border-radius: 50%;
|
||||
}
|
||||
|
||||
.animal-alert-icon svg {
|
||||
width: .9rem;
|
||||
height: .9rem;
|
||||
}
|
||||
|
||||
.animal-photo-trigger {
|
||||
display: inline-flex;
|
||||
border-radius: 10px;
|
||||
outline: none;
|
||||
cursor: zoom-in;
|
||||
}
|
||||
|
||||
.animal-photo-trigger:focus-visible {
|
||||
box-shadow: 0 0 0 3px rgba(32, 107, 196, .25);
|
||||
}
|
||||
|
||||
.animal-photo-zoom {
|
||||
position: fixed;
|
||||
z-index: 1090;
|
||||
display: none;
|
||||
pointer-events: none;
|
||||
padding: .35rem;
|
||||
background: #fff;
|
||||
border: 1px solid rgba(98, 105, 118, .25);
|
||||
border-radius: 14px;
|
||||
box-shadow: 0 1rem 3rem rgba(31, 41, 55, .28);
|
||||
}
|
||||
|
||||
.animal-photo-zoom.is-visible {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.animal-photo-zoom img {
|
||||
display: block;
|
||||
width: auto;
|
||||
height: auto;
|
||||
max-width: min(320px, 70vw);
|
||||
max-height: 60vh;
|
||||
border-radius: 10px;
|
||||
object-fit: contain;
|
||||
}
|
||||
|
||||
.animal-mosaic-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(7, minmax(0, 1fr));
|
||||
gap: .85rem;
|
||||
padding: 1rem;
|
||||
}
|
||||
|
||||
.animal-mosaic-card {
|
||||
min-width: 0;
|
||||
overflow: hidden;
|
||||
transition: transform .15s ease, box-shadow .15s ease;
|
||||
}
|
||||
|
||||
.animal-mosaic-card:hover {
|
||||
transform: translateY(-2px);
|
||||
box-shadow: 0 .55rem 1.5rem rgba(31, 41, 55, .12);
|
||||
}
|
||||
|
||||
.animal-mosaic-photo {
|
||||
display: block;
|
||||
width: 100%;
|
||||
aspect-ratio: 1/1;
|
||||
object-fit: cover;
|
||||
border-radius: 0;
|
||||
}
|
||||
|
||||
.animal-mosaic-card .card-body {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.animal-mosaic-name {
|
||||
min-width: 0;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.animal-mosaic-footer {
|
||||
margin-top: auto;
|
||||
}
|
||||
|
||||
.animal-mosaic-status {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.animal-mosaic-icons {
|
||||
min-height: 1.4rem;
|
||||
justify-content: center;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
@media(max-width:1199.98px) {
|
||||
.animal-mosaic-grid {
|
||||
grid-template-columns: repeat(5, minmax(0, 1fr));
|
||||
}
|
||||
}
|
||||
|
||||
@media(max-width:767.98px) {
|
||||
.animal-mosaic-grid {
|
||||
grid-template-columns: repeat(3, minmax(0, 1fr));
|
||||
gap: .65rem;
|
||||
padding: .75rem;
|
||||
}
|
||||
}
|
||||
|
||||
@media(max-width:479.98px) {
|
||||
.animal-mosaic-grid {
|
||||
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||
}
|
||||
}
|
||||
|
||||
.animal-sex-icon {
|
||||
display: inline-flex;
|
||||
width: 1.25rem;
|
||||
height: 1.25rem;
|
||||
flex: 0 0 1.25rem;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
border-radius: 50%;
|
||||
font-size: .8rem;
|
||||
font-weight: 700;
|
||||
line-height: 1;
|
||||
}
|
||||
|
||||
.animal-weight-column {
|
||||
width: 7.5rem;
|
||||
min-width: 7.5rem;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.animal-age-column {
|
||||
width: 5.75rem;
|
||||
min-width: 5.75rem;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.animal-species-column {
|
||||
width: 7rem;
|
||||
min-width: 7rem;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.life-profile-column {
|
||||
width: 7.25rem;
|
||||
min-width: 7.25rem;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.life-profile-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(4, 1fr);
|
||||
align-items: center;
|
||||
gap: .15rem;
|
||||
}
|
||||
|
||||
.life-profile-grid span {
|
||||
display: inline-flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
min-width: 1.35rem;
|
||||
line-height: 1.2;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.life-profile-sort {
|
||||
display: inline-flex;
|
||||
justify-content: center;
|
||||
color: inherit;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.life-profile-sort:hover,
|
||||
.life-profile-sort:focus {
|
||||
color: inherit;
|
||||
text-decoration: none;
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
.life-profile-yes {
|
||||
color: var(--tblr-success);
|
||||
}
|
||||
|
||||
.life-profile-no {
|
||||
color: var(--tblr-danger);
|
||||
}
|
||||
|
||||
.life-profile-unknown {
|
||||
color: var(--tblr-secondary);
|
||||
font-weight: 400 !important;
|
||||
}
|
||||
|
||||
/* Conservé pour une éventuelle réactivation de la colonne Prochain vaccin.
|
||||
.animal-vaccine-column {
|
||||
width: 6.5rem;
|
||||
max-width: 6.5rem;
|
||||
}
|
||||
*/
|
||||
|
||||
</style>
|
||||
<?php if ($viewMode === 'mosaic'): ?>
|
||||
<div class="animal-mosaic-grid">
|
||||
<?php foreach ($animals as $a):
|
||||
$mosaicSrc = !empty($a['primary_photo']) ? '/media/animals/' . (int)$a['id'] . '/' . rawurlencode($a['primary_photo']) : '/assets/cat_placeholder.svg';
|
||||
$mosaicSpeciesCode = SpeciesService::normalize((string)($a['species'] ?? ''));
|
||||
$mosaicSpecies = $speciesByCode[$mosaicSpeciesCode] ?? ['name' => species_label($mosaicSpeciesCode),'icon' => '🐾','color' => '#6c757d'];
|
||||
[$mosaicStatusClass,$mosaicStatusLabel] = status_badge((string)($a['status'] ?? ''));
|
||||
[$mosaicSexSymbol,$mosaicSexClass,$mosaicSexLabel] = match((string)($a['sex'] ?? 'U')) {
|
||||
'M' => ['♂','bg-blue-lt text-blue',t('sex.male')],'F' => ['♀','bg-purple-lt text-purple',t('sex.female')],default => ['?','bg-secondary-lt text-secondary',t('sex.unknown')]
|
||||
};
|
||||
$mosaicFoster = in_array((string)($a['status'] ?? ''), ['fa','fa_permanente'], true) ? $shortContactName($a['foster_contact_name'] ?? null) : '';
|
||||
$mosaicFreeCat = (string)($a['status'] ?? '') === 'chat_libre' ? trim((string)($a['free_cat_site_name'] ?: $a['free_cat_city'])) : '';
|
||||
?>
|
||||
<article class="card animal-mosaic-card">
|
||||
<a href="/animal?id=<?= (int)$a['id'] ?>"
|
||||
class="animal-photo-trigger d-flex w-100 text-reset text-decoration-none"
|
||||
data-zoom-src="<?= h($mosaicSrc) ?>"
|
||||
aria-label="<?= h(t('animals.open_record', ['name' => $a['name']])) ?>"><img class="animal-mosaic-photo"
|
||||
src="<?= h($mosaicSrc) ?>" alt="<?= h(t('animals.photo_of', ['name' => $a['name']])) ?>"></a>
|
||||
<div class="card-body p-2">
|
||||
<div class="d-flex align-items-center gap-1 mb-1"><a
|
||||
class="fw-bold text-reset text-decoration-none animal-mosaic-name me-auto"
|
||||
href="/animal?id=<?= (int)$a['id'] ?>"
|
||||
title="<?= h($a['name']) ?>"><?= h($a['name']) ?></a><span
|
||||
class="animal-sex-icon <?= h($mosaicSexClass) ?>"
|
||||
title="<?= h($mosaicSexLabel) ?>"><?= h($mosaicSexSymbol) ?></span></div>
|
||||
<div class="d-flex justify-content-center mb-1"><span class="badge"
|
||||
style="background:<?= h($mosaicSpecies['color']) ?>18;color:<?= h($mosaicSpecies['color']) ?>"><?= h($mosaicSpecies['icon'] . ' ' . $mosaicSpecies['name']) ?></span>
|
||||
</div>
|
||||
<div class="animal-mosaic-footer">
|
||||
<?php if ($mosaicFoster !== ''): ?><div class="small text-muted text-truncate text-center mb-1"
|
||||
title="<?= h(t('animals.foster')) ?> : <?= h($a['foster_contact_name']) ?>">
|
||||
<?= h($mosaicFoster) ?></div><?php endif; ?>
|
||||
<?php if ($mosaicFreeCat !== ''): ?><div class="small text-muted text-truncate text-center mb-1"
|
||||
title="<?= h(t('animals.free_cat_site')) ?> : <?= h($mosaicFreeCat) ?>">🌿
|
||||
<?= h($mosaicFreeCat) ?></div><?php endif; ?>
|
||||
<div class="animal-mosaic-status mb-1"><span class="badge <?= h($mosaicStatusClass) ?>"
|
||||
title="<?= h($mosaicStatusLabel) ?>"><?= h($mosaicStatusLabel) ?></span></div>
|
||||
<div class="animal-mosaic-icons d-flex align-items-center gap-2 small">
|
||||
<?php foreach ([['compatibility_dogs','🐶',t('animals.dogs')],['compatibility_cats','🐱',t('animals.cats')],['compatibility_children','🧒',t('animals.children')],['house_trained','🧼',t('animals.cleanliness')]] as [$field,$icon,$label]): $value = (string)($a[$field] ?? 'unknown'); ?><span
|
||||
title="<?= h($label . ' : ' . t($value === 'yes' ? 'common.yes' : ($value === 'no' ? 'common.no' : 'common.unknown'))) ?>"
|
||||
class="<?= $value === 'yes' ? 'text-success' : ($value === 'no' ? 'text-danger' : 'text-muted') ?>"><?= $icon ?><?= $value === 'yes' ? '✓' : ($value === 'no' ? '✕' : '—') ?></span><?php endforeach; ?>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</article>
|
||||
<?php endforeach; ?>
|
||||
<?php if (!$animals): ?><div class="text-center text-muted py-5" style="grid-column:1/-1">
|
||||
<?= h(t('animals.no_results')) ?></div><?php endif; ?>
|
||||
</div>
|
||||
<?php else: ?>
|
||||
<div class="table-responsive mt-2">
|
||||
<table class="table table-vcenter card-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th><?= h(t('animals.photo')) ?></th>
|
||||
<!--<th>Code</th>-->
|
||||
<th>
|
||||
<?= sort_th(t('animals.name'), 'name', $sort, $dir) ?>
|
||||
<span class="text-muted mx-1">·</span>
|
||||
<?= sort_th(t('animals.sex'), 'sex', $sort, $dir) ?>
|
||||
</th>
|
||||
<th class="animal-species-column text-center">
|
||||
<?= sort_th(t('animals.species'), 'species', $sort, $dir) ?></th>
|
||||
<th class="animal-age-column text-center"><?= sort_th(t('animals.age'), 'age', $sort, $dir) ?></th>
|
||||
<th class="text-center"><?= sort_th(t('animals.status'), 'status', $sort, $dir) ?></th>
|
||||
<th class="life-profile-column">
|
||||
<div class="life-profile-grid" aria-label="<?= h(t('animals.life_profile')) ?>"><?php foreach (['compatibility_dogs' => ['🐶',t('animals.compatible_dogs')],'compatibility_cats' => ['🐱',t('animals.compatible_cats')],'compatibility_children' => ['🧒',t('animals.compatible_children')],'house_trained' => ['🧼',t('animals.cleanliness')]] as $profileSortKey => $profileHeader): $profileQs = $_GET;
|
||||
$profileQs['sort'] = $profileSortKey;
|
||||
$profileQs['dir'] = ($sort === $profileSortKey && strtoupper($dir) === 'ASC') ? 'desc' : 'asc';
|
||||
$profileArrow = $sort === $profileSortKey ? (strtoupper($dir) === 'ASC' ? '↑' : '↓') : ''; ?><a
|
||||
class="life-profile-sort text-reset"
|
||||
href="/animals?<?= h(http_build_query($profileQs)) ?>"
|
||||
title="<?= h(t('animals.sort_by', ['label' => mb_strtolower($profileHeader[1])])) ?>"
|
||||
aria-label="<?= h(t('animals.sort_by', ['label' => mb_strtolower($profileHeader[1])])) ?>"><?= $profileHeader[0] ?><small><?= h($profileArrow) ?></small></a><?php endforeach; ?>
|
||||
</div>
|
||||
</th>
|
||||
<th class="text-nowrap text-center" style="min-width:9rem"><?= h(t('animals.fiv_felv')) ?></th>
|
||||
<!--<th class="animal-weight-column"><?= sort_th('Poids', 'weight', $sort, $dir) ?></th>-->
|
||||
<!--<th><?= sort_th('Dernier médical', 'updated_at', $sort, $dir) ?></th>-->
|
||||
<!--<th>Traitements</th>-->
|
||||
<!--<th class="animal-vaccine-column"><?= h(t('animals.next_vaccine')) ?></th>-->
|
||||
<th><?= sort_th(t('animals.location'), 'rescue_location_name', $sort, $dir) ?></th>
|
||||
<th class="w-1 text-center"><?= h(t('animals.preview')) ?></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<?php foreach ($animals as $a): ?>
|
||||
<?php
|
||||
$src = !empty($a['primary_photo'])
|
||||
? '/media/animals/' . (int)$a['id'] . '/' . rawurlencode($a['primary_photo'])
|
||||
: '/assets/cat_placeholder.svg';
|
||||
$animalSpecies = SpeciesService::normalize((string)($a['species'] ?? ''));
|
||||
$speciesMeta = $speciesByCode[$animalSpecies] ?? ['name' => species_label($animalSpecies),'icon' => '🐾','color' => '#6c757d'];
|
||||
$isCat = SpeciesService::isCat($animalSpecies);
|
||||
|
||||
$fiv = $a['fiv'] ?? null;
|
||||
$felv = $a['felv'] ?? null;
|
||||
$fivStatus = (string)(($a['fiv_status'] ?? '') ?: ((int)$fiv === 1 ? 'pos' : 'neg'));
|
||||
$felvStatus = (string)(($a['felv_status'] ?? '') ?: ((int)$felv === 1 ? 'pos' : 'neg'));
|
||||
$shortStatusLabels = ['unknown' => mb_strtolower(t('animals.test_unknown')), 'neg' => mb_strtolower(t('animals.test_negative')), 'pos' => mb_strtolower(t('animals.test_positive')), 'doubtful' => mb_strtolower(t('animals.test_doubtful'))];
|
||||
$healthLabel = $isCat ? ('FIV ' . ($shortStatusLabels[$fivStatus] ?? mb_strtolower(t('animals.test_unknown')))
|
||||
. ' · FeLV ' . ($shortStatusLabels[$felvStatus] ?? mb_strtolower(t('animals.test_unknown')))) : t('animals.tests_not_applicable');
|
||||
$testBadgeMeta = [
|
||||
'unknown' => ['?', 'bg-secondary-lt', t('animals.test_unknown')],
|
||||
'neg' => ['−', 'bg-green-lt', t('animals.test_negative')],
|
||||
'pos' => ['+', 'bg-red-lt', t('animals.test_positive')],
|
||||
'doubtful' => ['?', 'bg-yellow-lt', t('animals.test_doubtful')],
|
||||
];
|
||||
[$fivSymbol, $fivBadgeClass, $fivBadgeTitle] = $testBadgeMeta[$fivStatus] ?? $testBadgeMeta['unknown'];
|
||||
[$felvSymbol, $felvBadgeClass, $felvBadgeTitle] = $testBadgeMeta[$felvStatus] ?? $testBadgeMeta['unknown'];
|
||||
|
||||
$ageLabel = '—';
|
||||
$ageTitle = t('animals.age_unknown');
|
||||
if (!empty($a['birth_date'])) {
|
||||
try {
|
||||
$birth = new DateTimeImmutable((string)$a['birth_date']);
|
||||
$age = $birth->diff(new DateTimeImmutable('today'));
|
||||
if ($age->y > 0) {
|
||||
$ageLabel = $age->y . ' a';
|
||||
if ($age->m > 0) {
|
||||
$ageLabel .= ' · ' . $age->m . ' m';
|
||||
}
|
||||
$ageTitle = $age->y . ' an' . ($age->y > 1 ? 's' : '');
|
||||
if ($age->m > 0) {
|
||||
$ageTitle .= ' et ' . $age->m . ' mois';
|
||||
}
|
||||
} else {
|
||||
$ageLabel = $age->m . ' m';
|
||||
$ageTitle = $age->m . ' mois';
|
||||
}
|
||||
if (!empty($a['birth_is_estimated'])) {
|
||||
$ageLabel = '~ ' . $ageLabel;
|
||||
$ageTitle .= ' (' . t('animals.age_estimated') . ')';
|
||||
}
|
||||
} catch (Throwable) {
|
||||
$ageLabel = '—';
|
||||
}
|
||||
}
|
||||
|
||||
$sexLabel = match ((string)($a['sex'] ?? 'U')) {
|
||||
'M' => t('sex.male'),
|
||||
'F' => t('sex.female'),
|
||||
default => t('animals.sex_not_provided'),
|
||||
};
|
||||
|
||||
$hairLabel = match ((string)($a['hair_type'] ?? '')) {
|
||||
'court' => t('animals.short_hair'),
|
||||
'mi-long' => t('animals.medium_hair'),
|
||||
'long' => t('animals.long_hair'),
|
||||
'frise' => t('animals.curly_hair'),
|
||||
'sans-poil' => t('animals.hairless'),
|
||||
default => t('animals.hair_unknown'),
|
||||
};
|
||||
$appearance = implode(' · ', [
|
||||
(string)($a['breed'] ?: t('animals.breed_unknown')),
|
||||
(string)($a['color'] ?: t('animals.coat_unknown')),
|
||||
$hairLabel,
|
||||
]);
|
||||
|
||||
$weightLabel = t('animals.weight_unknown');
|
||||
if ($a['last_weight'] !== null) {
|
||||
$trend = '→';
|
||||
if ($a['prev_weight'] !== null) {
|
||||
if ((float)$a['last_weight'] > (float)$a['prev_weight']) {
|
||||
$trend = '↑';
|
||||
} elseif ((float)$a['last_weight'] < (float)$a['prev_weight']) {
|
||||
$trend = '↓';
|
||||
}
|
||||
}
|
||||
$weightLabel = number_format((float)$a['last_weight'], 2, ',', ' ') . ' kg ' . $trend;
|
||||
}
|
||||
|
||||
$vaccineLabel = !empty($a['next_vaccine_due'])
|
||||
? (($a['next_vaccine_name'] ?: t('animal.vaccine_booster')) . ' · ' . $a['next_vaccine_due'])
|
||||
: t('animals.no_vaccine');
|
||||
$treatmentLabel = $a['ongoing_treatment_names'] ?: t('animals.no_current_treatment');
|
||||
$medicalPoint = $a['latest_medical_point'] ?: t('animals.no_recent_medical');
|
||||
$profileMark = static function (string $value, string $label): string {
|
||||
[$symbol,$class,$state] = match($value) {
|
||||
'yes' => ['✓','life-profile-yes',t('common.yes')],
|
||||
'no' => ['✕','life-profile-no',t('common.no')],
|
||||
default => ['—','life-profile-unknown',t('common.unknown')],
|
||||
};
|
||||
return '<span class="' . h($class) . '" title="' . h($label . ' : ' . $state) . '" aria-label="' . h($label . ' : ' . $state) . '">' . h($symbol) . '</span>';
|
||||
};
|
||||
?>
|
||||
<?php if (!empty($a['_bonded_group'])): ?>
|
||||
<tr class="bonded-group-placeholder">
|
||||
<td><span class="litter-parent-placeholder-icon text-purple" aria-hidden="true">🔗</span></td>
|
||||
<td>
|
||||
<div class="fw-bold text-purple"><?= h($a['_bonded_group']['label']) ?></div>
|
||||
<div class="small text-muted"><?= h($a['_bonded_group']['details']) ?></div>
|
||||
<?php if ($a['_bonded_group']['notes'] !== ''): ?><div class="small text-muted">
|
||||
<?= h($a['_bonded_group']['notes']) ?></div><?php endif; ?>
|
||||
</td>
|
||||
<td></td>
|
||||
<td></td>
|
||||
<td><span class="badge bg-purple-lt"><?= h(t('animals.bonded')) ?></span></td>
|
||||
<td></td>
|
||||
<td></td>
|
||||
<td></td>
|
||||
<td></td>
|
||||
</tr>
|
||||
<?php endif; ?>
|
||||
<?php if (!empty($a['_litter_group'])): ?>
|
||||
<tr class="litter-parent-placeholder">
|
||||
<td><span class="litter-parent-placeholder-icon" aria-hidden="true">🐾</span></td>
|
||||
<td>
|
||||
<div class="fw-bold text-muted"><?= h($a['_litter_group']['label']) ?></div>
|
||||
<div class="small text-muted"><?= h($a['_litter_group']['details']) ?></div>
|
||||
</td>
|
||||
<td></td>
|
||||
<td></td>
|
||||
<td><span class="badge bg-secondary-lt"><?= h(t('animals.litter')) ?></span></td>
|
||||
<td></td>
|
||||
<td></td>
|
||||
<td></td>
|
||||
<td></td>
|
||||
</tr>
|
||||
<?php endif; ?>
|
||||
<tr
|
||||
class="<?= (!empty($a['_litter_branch']) || !empty($a['_bonded_branch'])) ? 'litter-kitten-row ' : '' ?><?= !empty($a['_litter_branch']) ? 'litter-member-row ' : '' ?><?= !empty($a['_bonded_branch']) ? 'bonded-member-row' : '' ?>">
|
||||
<td>
|
||||
<span class="animal-photo-trigger" tabindex="0" data-zoom-src="<?= h($src) ?>"
|
||||
aria-label="<?= h(t('animals.enlarge_photo', ['name' => $a['name']])) ?>">
|
||||
<img src="<?= htmlspecialchars($src, ENT_QUOTES) ?>"
|
||||
style="width:44px;height:44px;object-fit:cover;border-radius:10px" alt="">
|
||||
</span>
|
||||
</td>
|
||||
|
||||
<!--<td class="text-muted"><php echo //htmlspecialchars($a['internal_code'], ENT_QUOTES) ?></td>-->
|
||||
<td>
|
||||
<div class="d-flex align-items-center gap-2">
|
||||
<?php $treeBranch = $a['_bonded_branch'] ?? $a['_litter_branch'] ?? ''; ?>
|
||||
<?php if ($treeBranch !== ''): ?>
|
||||
<span class="text-muted font-monospace" aria-hidden="true"><?= h($treeBranch) ?></span>
|
||||
<?php endif; ?>
|
||||
<span class="coat-swatch" style="<?= h(coat_swatch_style($a['color'] ?? '')) ?>"
|
||||
title="<?= h($a['color'] ?: t('animals.coat_unknown')) ?>" aria-hidden="true"></span>
|
||||
<a href="/animal?id=<?= (int)$a['id'] ?>" class="fw-bold">
|
||||
<?= htmlspecialchars($a['name'], ENT_QUOTES) ?>
|
||||
</a>
|
||||
<?php
|
||||
[$sexSymbol, $sexIconClass, $sexIconLabel] = match ((string)($a['sex'] ?? 'U')) {
|
||||
'M' => ['♂', 'bg-blue-lt text-blue', t('sex.male')],
|
||||
'F' => ['♀', 'bg-purple-lt text-purple', t('sex.female')],
|
||||
default => ['?', 'bg-secondary-lt text-secondary', t('sex.unknown')],
|
||||
};
|
||||
?>
|
||||
<span class="animal-sex-icon <?= h($sexIconClass) ?>" title="<?= h($sexIconLabel) ?>"
|
||||
aria-label="<?= h($sexIconLabel) ?>"><?= h($sexSymbol) ?></span>
|
||||
<?php if (!empty($a['_bonded_branch'])): ?><span
|
||||
title="<?= h(t('animals.adopt_together')) ?>"
|
||||
aria-label="<?= h(t('animals.adopt_together')) ?>">🔗</span><?php endif; ?>
|
||||
<?php $sterilizationState = $a['sterilization_status'] ?? (!empty($a['sterilized']) ? 'yes' : 'unknown');
|
||||
if ($sterilizationState !== 'yes'): ?>
|
||||
<span
|
||||
class="animal-alert-icon <?= $sterilizationState === 'no' ? 'bg-yellow-lt text-yellow' : 'bg-secondary-lt text-secondary' ?>"
|
||||
title="<?= $sterilizationState === 'no' ? t('animals.not_sterilized') : t('animals.sterilization_unknown') ?>"
|
||||
aria-label="<?= $sterilizationState === 'no' ? t('animals.not_sterilized') : t('animals.sterilization_unknown') ?>">
|
||||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"
|
||||
stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
|
||||
<circle cx="6" cy="7" r="3"></circle>
|
||||
<circle cx="6" cy="17" r="3"></circle>
|
||||
<path d="M8.7 8.3L20 14"></path>
|
||||
<path d="M8.7 15.7L20 10"></path>
|
||||
</svg>
|
||||
</span>
|
||||
<?php endif; ?>
|
||||
<?php if (trim((string)($a['chip_id'] ?? '')) === ''): ?>
|
||||
<span class="animal-alert-icon bg-orange-lt text-orange"
|
||||
title="<?= h(t('animals.not_chipped')) ?>"
|
||||
aria-label="<?= h(t('animals.not_chipped')) ?>">
|
||||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"
|
||||
stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
|
||||
<rect x="6" y="6" width="12" height="12" rx="2"></rect>
|
||||
<path d="M9 1v3M15 1v3M9 20v3M15 20v3M20 9h3M20 14h3M1 9h3M1 14h3"></path>
|
||||
<circle cx="12" cy="12" r="2"></circle>
|
||||
</svg>
|
||||
</span>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
</td>
|
||||
<td class="animal-species-column"><span class="badge"
|
||||
style="background:<?= h($speciesMeta['color']) ?>18;color:<?= h($speciesMeta['color']) ?>"
|
||||
title="<?= h(t('animals.species_label', ['name' => $speciesMeta['name']])) ?>"><?= h($speciesMeta['icon'] . ' ' . $speciesMeta['name']) ?></span>
|
||||
</td>
|
||||
<td class="animal-age-column" title="<?= h($ageTitle) ?>"><?= h($ageLabel) ?></td>
|
||||
<td>
|
||||
<?php [$stCls, $stLbl] = status_badge((string)($a['status'] ?? '')); ?>
|
||||
<span class="badge <?= $stCls ?>"><?= htmlspecialchars($stLbl, ENT_QUOTES) ?></span>
|
||||
<?php $availability = $a['adoption_availability'] ?? 'unknown';
|
||||
if ($availability === 'available'): ?><span class="badge bg-green-lt ms-1"
|
||||
title="<?= h(t('animals.available_title')) ?>"><?= h(t('animals.adoptable')) ?></span><?php elseif ($availability === 'not_available'): ?><span
|
||||
class="badge bg-orange-lt ms-1"
|
||||
title="<?= h($a['adoption_unavailability_reason'] ?: t('animals.unavailable_title')) ?>"><?= h(t('animals.not_adoptable')) ?></span><?php endif; ?>
|
||||
<?php $fosterDisplay = in_array((string)($a['status'] ?? ''), ['fa','fa_permanente'], true) ? $shortContactName($a['foster_contact_name'] ?? null) : '';
|
||||
if ($fosterDisplay !== ''): ?><div class="small text-muted mt-1"
|
||||
title="<?= h(t('animals.foster')) ?> : <?= h($a['foster_contact_name']) ?>">
|
||||
<?= h($fosterDisplay) ?></div><?php endif; ?>
|
||||
<?php if ((string)($a['status'] ?? '') === 'chat_libre' && (!empty($a['free_cat_site_name']) || !empty($a['free_cat_city']))): ?>
|
||||
<div class="small text-muted mt-1">🌿 <?= h($a['free_cat_site_name'] ?: $a['free_cat_city']) ?>
|
||||
</div><?php endif; ?>
|
||||
</td>
|
||||
|
||||
<td class="life-profile-column">
|
||||
<div class="life-profile-grid">
|
||||
<?= $profileMark((string)($a['compatibility_dogs'] ?? 'unknown'), t('animals.dogs')) ?><?= $profileMark((string)($a['compatibility_cats'] ?? 'unknown'), t('animals.cats')) ?><?= $profileMark((string)($a['compatibility_children'] ?? 'unknown'), t('animals.children')) ?><?= $profileMark((string)($a['house_trained'] ?? 'unknown'), t('animals.cleanliness')) ?>
|
||||
</div>
|
||||
</td>
|
||||
|
||||
<td class="text-nowrap">
|
||||
<?php if ($isCat): ?>
|
||||
<div class="d-flex flex-nowrap gap-1">
|
||||
<span class="badge <?= $fivBadgeClass ?>" title="FIV : <?= h($fivBadgeTitle) ?>">FIV
|
||||
<?= h($fivSymbol) ?></span>
|
||||
<span class="badge <?= $felvBadgeClass ?>" title="FeLV : <?= h($felvBadgeTitle) ?>">FeLV
|
||||
<?= h($felvSymbol) ?></span>
|
||||
</div>
|
||||
<?php else: ?><span class="text-muted">—</span><?php endif; ?>
|
||||
</td>
|
||||
|
||||
<!--<td class="animal-weight-column">
|
||||
<?php
|
||||
$lw = $a['last_weight'];
|
||||
$pw = $a['prev_weight'];
|
||||
|
||||
if ($lw === null) {
|
||||
echo '—';
|
||||
} else {
|
||||
$arrow = '→';
|
||||
if ($pw !== null) {
|
||||
if ((float)$lw > (float)$pw) {
|
||||
$arrow = '↑';
|
||||
} elseif ((float)$lw < (float)$pw) {
|
||||
$arrow = '↓';
|
||||
}
|
||||
}
|
||||
echo htmlspecialchars(number_format((float)$lw, 2), ENT_QUOTES) . " kg <span class='text-muted'>$arrow</span>";
|
||||
}
|
||||
?>
|
||||
</td>-->
|
||||
|
||||
<!--<td><?= $a['last_med_at'] ? htmlspecialchars($a['last_med_at'], ENT_QUOTES) : '—' ?></td>-->
|
||||
<!--<td><?= (int)($a['ongoing_treatments'] ?? 0) ?></td>-->
|
||||
<!--<td class="animal-vaccine-column"><?= $a['next_vaccine_due'] ? htmlspecialchars($a['next_vaccine_due'], ENT_QUOTES) : '—' ?></td>-->
|
||||
<td>
|
||||
<div class="fw-bold">
|
||||
<?= h(($a['rescue_location_name'] ?? '') ?: ($a['rescue_address'] ?? '')) ?></div>
|
||||
</td>
|
||||
<td class="text-center">
|
||||
<button class="btn btn-outline-primary btn-sm" data-bs-toggle="modal"
|
||||
data-bs-target="#medPreview" data-id="<?= (int)$a['id'] ?>" data-name="<?= h($a['name']) ?>"
|
||||
data-photo="<?= h($src) ?>" data-identity="<?= h($sexLabel . ' · ' . $ageLabel) ?>"
|
||||
data-appearance="<?= h($appearance) ?>" data-coat="<?= h($a['color'] ?? '') ?>"
|
||||
data-coat-style="<?= h(coat_swatch_style($a['color'] ?? '')) ?>"
|
||||
data-health="<?= h($healthLabel) ?>" data-weight="<?= h($weightLabel) ?>"
|
||||
data-vaccine="<?= h($vaccineLabel) ?>" data-treatment="<?= h($treatmentLabel) ?>"
|
||||
data-deworming="<?= h($a['last_deworming_date'] ? (t('animals.latest') . ' : ' . (new DateTimeImmutable($a['last_deworming_date']))->format('d/m/Y') . ($a['next_deworming_due'] ? ' · ' . t('animals.next') . ' : ' . (new DateTimeImmutable($a['next_deworming_due']))->format('d/m/Y') : '')) : t('animals.never_provided')) ?>"
|
||||
data-medical="<?= h($medicalPoint) ?>"
|
||||
data-medical-date="<?= h($a['last_med_at'] ?: '') ?>">
|
||||
<?= h(t('animals.medical_preview')) ?>
|
||||
</button>
|
||||
</td>
|
||||
</tr>
|
||||
<?php endforeach; ?>
|
||||
<?php if (!$animals): ?>
|
||||
<tr>
|
||||
<td colspan="9" class="text-center text-muted py-4"><?= h(t('animals.no_results')) ?></td>
|
||||
</tr>
|
||||
<?php endif; ?>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
|
||||
<div class="animal-photo-zoom" id="animalPhotoZoom" aria-hidden="true"><img src=""
|
||||
alt="<?= h(t('animals.enlarged_preview')) ?>"></div>
|
||||
|
||||
<div class="modal modal-blur fade" id="medPreview" tabindex="-1" aria-hidden="true">
|
||||
<div class="modal-dialog modal-lg modal-dialog-centered" role="document">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title"><?= h(t('animals.medical_preview')) ?></h5>
|
||||
<button type="button" class="btn-close" data-bs-dismiss="modal"
|
||||
aria-label="<?= h(t('common.close')) ?>"></button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<div class="d-flex align-items-center gap-3 mb-4">
|
||||
<img id="medPreviewPhoto" src="/assets/cat_placeholder.svg" alt=""
|
||||
style="width:76px;height:76px;object-fit:cover;border-radius:14px">
|
||||
<div>
|
||||
<div id="medPreviewName" class="h2 mb-1"></div>
|
||||
<div id="medPreviewIdentity" class="text-muted"></div>
|
||||
<div class="d-flex align-items-center gap-1 text-muted small mt-1">
|
||||
<span id="medPreviewCoatSwatch" class="coat-swatch"></span>
|
||||
<span id="medPreviewAppearance"></span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row row-cards">
|
||||
<div class="col-12 col-md-6">
|
||||
<div class="card card-sm h-100">
|
||||
<div class="card-body">
|
||||
<div class="subheader mb-1"><?= h(t('animals.fiv_felv')) ?></div>
|
||||
<div id="medPreviewHealth" class="fw-bold"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-12 col-md-6">
|
||||
<div class="card card-sm h-100">
|
||||
<div class="card-body">
|
||||
<div class="subheader mb-1"><?= h(t('animals.last_weight')) ?></div>
|
||||
<div id="medPreviewWeight" class="fw-bold"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-12 col-md-6">
|
||||
<div class="card card-sm h-100">
|
||||
<div class="card-body">
|
||||
<div class="subheader mb-1"><?= h(t('animals.next_vaccine')) ?></div>
|
||||
<div id="medPreviewVaccine" class="fw-bold"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-12 col-md-6">
|
||||
<div class="card card-sm h-100">
|
||||
<div class="card-body">
|
||||
<div class="subheader mb-1"><?= h(t('animals.current_treatment')) ?></div>
|
||||
<div id="medPreviewTreatment" class="fw-bold"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-12 col-md-6">
|
||||
<div class="card card-sm h-100">
|
||||
<div class="card-body">
|
||||
<div class="subheader mb-1"><?= h(t('animals.dewormer')) ?></div>
|
||||
<div id="medPreviewDeworming" class="fw-bold"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-12">
|
||||
<div class="card card-sm">
|
||||
<div class="card-body">
|
||||
<div class="subheader mb-1"><?= h(t('animals.latest_diagnosis')) ?></div>
|
||||
<div id="medPreviewMedical" class="fw-bold"></div>
|
||||
<div id="medPreviewMedicalDate" class="text-muted small mt-1"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-link link-secondary"
|
||||
data-bs-dismiss="modal"><?= h(t('common.close')) ?></button>
|
||||
<a id="medPreviewLink" class="btn btn-primary"
|
||||
href="/animals"><?= h(t('animals.open_full_record')) ?></a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
const animalListTranslations = < ? = json_encode(['medicalPreview' => t('animals.medical_preview'),
|
||||
'neverProvided' => t('animals.never_provided'), 'lastUpdate' => t('animals.last_update', ['date' =>
|
||||
'__DATE__'
|
||||
])
|
||||
], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES) ? > ;
|
||||
(() => {
|
||||
const zoom = document.getElementById('animalPhotoZoom');
|
||||
const image = zoom?.querySelector('img');
|
||||
if (!zoom || !image) return;
|
||||
const position = (trigger) => {
|
||||
const rect = trigger.getBoundingClientRect();
|
||||
const width = Math.min(332, window.innerWidth * .7 + 12);
|
||||
let left = rect.right + 12;
|
||||
if (left + width > window.innerWidth - 12) left = Math.max(12, rect.left - width - 12);
|
||||
let top = rect.top + rect.height / 2 - Math.min(320, window.innerHeight * .6) / 2;
|
||||
top = Math.max(12, Math.min(top, window.innerHeight - Math.min(332, window.innerHeight * .6) - 12));
|
||||
zoom.style.left = left + 'px';
|
||||
zoom.style.top = top + 'px';
|
||||
};
|
||||
const show = (trigger) => {
|
||||
image.src = trigger.dataset.zoomSrc || '';
|
||||
position(trigger);
|
||||
zoom.classList.add('is-visible');
|
||||
zoom.setAttribute('aria-hidden', 'false');
|
||||
};
|
||||
const hide = () => {
|
||||
zoom.classList.remove('is-visible');
|
||||
zoom.setAttribute('aria-hidden', 'true');
|
||||
image.removeAttribute('src');
|
||||
};
|
||||
document.querySelectorAll('.animal-photo-trigger').forEach((trigger) => {
|
||||
trigger.addEventListener('mouseenter', () => show(trigger));
|
||||
trigger.addEventListener('mouseleave', hide);
|
||||
trigger.addEventListener('focus', () => show(trigger));
|
||||
trigger.addEventListener('blur', hide);
|
||||
});
|
||||
window.addEventListener('scroll', hide, true);
|
||||
window.addEventListener('resize', hide);
|
||||
})();
|
||||
document.getElementById('medPreview')?.addEventListener('show.bs.modal', (ev) => {
|
||||
const btn = ev.relatedTarget;
|
||||
const name = btn?.getAttribute('data-name') || '';
|
||||
document.querySelector('#medPreview .modal-title').textContent = animalListTranslations.medicalPreview +
|
||||
' — ' + name;
|
||||
document.getElementById('medPreviewName').textContent = name;
|
||||
document.getElementById('medPreviewIdentity').textContent = btn?.getAttribute('data-identity') || '—';
|
||||
document.getElementById('medPreviewAppearance').textContent = btn?.getAttribute('data-appearance') ||
|
||||
'—';
|
||||
const coatSwatch = document.getElementById('medPreviewCoatSwatch');
|
||||
const coat = btn?.getAttribute('data-coat') || '';
|
||||
coatSwatch.style.cssText = btn?.getAttribute('data-coat-style') || 'background:#d9dde3';
|
||||
coatSwatch.style.display = coat ? 'inline-block' : 'none';
|
||||
document.getElementById('medPreviewHealth').textContent = btn?.getAttribute('data-health') || '—';
|
||||
document.getElementById('medPreviewWeight').textContent = btn?.getAttribute('data-weight') || '—';
|
||||
document.getElementById('medPreviewVaccine').textContent = btn?.getAttribute('data-vaccine') || '—';
|
||||
document.getElementById('medPreviewTreatment').textContent = btn?.getAttribute('data-treatment') || '—';
|
||||
document.getElementById('medPreviewDeworming').textContent = btn?.getAttribute('data-deworming') ||
|
||||
animalListTranslations.neverProvided;
|
||||
document.getElementById('medPreviewMedical').textContent = btn?.getAttribute('data-medical') || '—';
|
||||
|
||||
const medicalDate = btn?.getAttribute('data-medical-date') || '';
|
||||
document.getElementById('medPreviewMedicalDate').textContent = medicalDate ? animalListTranslations
|
||||
.lastUpdate.replace('__DATE__', medicalDate) : '';
|
||||
document.getElementById('medPreviewPhoto').src = btn?.getAttribute('data-photo') ||
|
||||
'/assets/cat_placeholder.svg';
|
||||
document.getElementById('medPreviewLink').href = '/animal?id=' + encodeURIComponent(btn?.getAttribute(
|
||||
'data-id') || '');
|
||||
});
|
||||
|
||||
</script>
|
||||
Loading…
Add table
Add a link
Reference in a new issue