Initial import of animal_management module

This commit is contained in:
Kelin Rescue Hub
2026-04-04 13:13:00 -04:00
commit 20adb1bd1e
65 changed files with 14004 additions and 0 deletions

948
views/animals/view.php Normal file
View File

@@ -0,0 +1,948 @@
<?php
use humhub\modules\animal_management\models\Animal;
use humhub\modules\animal_management\helpers\DateDisplayHelper;
use humhub\modules\animal_management\models\AnimalGalleryItem;
use humhub\modules\animal_management\models\AnimalMedicalVisit;
use humhub\modules\animal_management\models\AnimalProgressUpdate;
use humhub\modules\animal_management\models\AnimalTransfer;
use humhub\modules\animal_management\models\AnimalTransferEvent;
use humhub\modules\gallery\assets\Assets as GalleryAssets;
use humhub\modules\space\models\Space;
use yii\helpers\Html;
/* @var Space $space */
/* @var Animal $animal */
/* @var bool $canManage */
/* @var AnimalMedicalVisit[] $medicalVisits */
/* @var AnimalProgressUpdate[] $progressUpdates */
/* @var AnimalTransfer[] $transfers */
/* @var AnimalTransferEvent[] $transferEvents */
/* @var AnimalGalleryItem[] $galleryItems */
/* @var array $customFieldValues */
/* @var string $animalCoverImageUrl */
/* @var array $detailHeroFields */
$openMedicalEditId = (int)Yii::$app->request->get('inlineMedicalEdit', 0);
$openProgressEditId = (int)Yii::$app->request->get('inlineProgressEdit', 0);
$coverImageUrl = trim((string)$animalCoverImageUrl);
$hasCoverImage = $coverImageUrl !== '' && (preg_match('/^https?:\/\//i', $coverImageUrl) || substr($coverImageUrl, 0, 1) === '/');
$statusLabel = Animal::statusOptions()[$animal->status] ?? (string)$animal->status;
$detailFieldMap = [
'name' => (string)$animal->getDisplayName(),
'species' => (string)$animal->species,
'breed' => (string)$animal->breed,
'sex' => (string)$animal->sex,
'status' => (string)$statusLabel,
'location_name' => (string)$animal->location_name,
'animal_uid' => (string)$animal->animal_uid,
'public_summary' => trim((string)$animal->public_summary),
'last_medical' => !empty($medicalVisits) ? DateDisplayHelper::format((string)$medicalVisits[0]->visit_at) : '',
];
$heroFieldValues = [];
foreach ($detailHeroFields as $fieldKey) {
$fieldKey = trim((string)$fieldKey);
if ($fieldKey === '' || $fieldKey === 'name' || !array_key_exists($fieldKey, $detailFieldMap)) {
continue;
}
$value = trim((string)$detailFieldMap[$fieldKey]);
if ($value === '') {
continue;
}
$heroFieldValues[] = $value;
}
$customHeroCount = 0;
foreach ($customFieldValues as $customField) {
if ($customHeroCount >= 3) {
break;
}
$label = trim((string)($customField['label'] ?? ''));
$value = trim((string)($customField['value'] ?? ''));
if ($label === '' || $value === '') {
continue;
}
$heroFieldValues[] = $label . ': ' . $value;
$customHeroCount++;
}
if (class_exists(GalleryAssets::class)) {
GalleryAssets::register($this);
}
$uiGalleryId = 'animal-gallery-' . (int)$animal->id;
?>
<div class="panel panel-default">
<div style="position:relative;min-height:320px;overflow:hidden;border-radius:12px;background:#dbe3eb;">
<?php if ($hasCoverImage): ?>
<img src="<?= Html::encode($coverImageUrl) ?>" alt="<?= Html::encode($animal->getDisplayName()) ?>" style="position:absolute;inset:0;width:100%;height:100%;object-fit:cover;">
<?php else: ?>
<div style="position:absolute;inset:0;display:flex;align-items:center;justify-content:center;color:#9aa5b1;">
<i class="fa fa-paw fa-4x"></i>
</div>
<?php endif; ?>
<div style="position:absolute;inset:0;background:linear-gradient(180deg, rgba(7,10,16,0.08) 0%, rgba(7,10,16,0.6) 56%, rgba(7,10,16,0.84) 100%);"></div>
<?php if ($canManage): ?>
<div style="position:absolute;top:12px;right:12px;z-index:2;">
<a href="<?= Html::encode($space->createUrl('/animal_management/animals/edit', ['id' => $animal->id])) ?>" class="btn btn-default btn-sm" title="<?= Yii::t('AnimalManagementModule.base', 'Edit Profile') ?>" aria-label="<?= Yii::t('AnimalManagementModule.base', 'Edit Profile') ?>" style="border-radius:999px;background:rgba(255,255,255,0.92);border:0;">
<i class="fa fa-pencil"></i>
</a>
</div>
<?php endif; ?>
<div style="position:absolute;left:14px;right:14px;bottom:14px;color:#fff;z-index:1;">
<div style="font-size:30px;line-height:1.1;font-weight:800;margin-bottom:8px;text-shadow:0 3px 10px rgba(0,0,0,0.45);">
<?= Html::encode($animal->getDisplayName()) ?>
</div>
<?php if (!empty($heroFieldValues)): ?>
<div style="display:flex;flex-wrap:wrap;gap:8px;margin-bottom:8px;">
<?php foreach ($heroFieldValues as $heroFieldValue): ?>
<span style="display:inline-block;background:rgba(15,23,42,0.58);border:1px solid rgba(255,255,255,0.28);padding:3px 10px;border-radius:999px;font-size:12px;">
<?= Html::encode($heroFieldValue) ?>
</span>
<?php endforeach; ?>
</div>
<?php endif; ?>
<?php if (!empty($animal->public_summary)): ?>
<div style="font-size:13px;line-height:1.35;text-shadow:0 2px 8px rgba(0,0,0,0.45);max-width:960px;">
<?= nl2br(Html::encode((string)$animal->public_summary)) ?>
</div>
<?php endif; ?>
</div>
</div>
</div>
<?php
$this->registerJs(<<<'JS'
(function() {
function getCsrfPayload() {
var csrfParam = $('meta[name="csrf-param"]').attr('content') || '';
var csrfToken = $('meta[name="csrf-token"]').attr('content') || '';
var payload = {};
if (csrfParam && csrfToken) {
payload[csrfParam] = csrfToken;
}
return payload;
}
function scrollInlineEditorIntoView(editor) {
var $editor = $(editor);
if (!$editor.length) {
return;
}
var sideSpacing = parseFloat($editor.css('margin-left'));
if (!(sideSpacing > 0)) {
sideSpacing = parseFloat($editor.closest('.panel-body').css('padding-left'));
}
if (!(sideSpacing > 0)) {
sideSpacing = 14;
}
var fixedHeaderHeight = 0;
$('.navbar-fixed-top:visible, #topbar:visible, .topbar:visible, .layout-top-container:visible').each(function() {
var h = $(this).outerHeight() || 0;
if (h > fixedHeaderHeight) {
fixedHeaderHeight = h;
}
});
var topReserve = Math.max(sideSpacing, 14) + Math.max(fixedHeaderHeight, 64) + 28;
var top = Math.max(0, $editor.offset().top - topReserve);
$('html, body').stop(true).animate({scrollTop: top}, 220);
}
function refreshPanels(selectors) {
selectors = selectors || [];
if (!selectors.length) {
return $.Deferred().resolve().promise();
}
return $.get(window.location.href).done(function(html) {
var $doc = $('<div></div>').append($.parseHTML(html, document, true));
selectors.forEach(function(selector) {
var $next = $doc.find(selector).first();
if ($next.length) {
$(selector).replaceWith($next);
}
});
});
}
window.addEventListener('message', function(event) {
var data = event.data || {};
if (!data || typeof data !== 'object' || data.source !== 'animal-inline-editor') {
return;
}
if (data.type === 'cancel') {
if (data.collapseId) {
$('#' + data.collapseId).collapse('hide');
}
return;
}
if (data.type === 'saved') {
if (data.collapseId) {
$('#' + data.collapseId).collapse('hide');
}
var selectors = $.isArray(data.refreshSelectors) && data.refreshSelectors.length
? data.refreshSelectors
: ['#animal-medical-panel', '#animal-progress-panel', '#animal-gallery-panel'];
refreshPanels(selectors);
}
});
$(document)
.off('shown.bs.collapse.animalInlineScroll', '.animal-inline-editor')
.on('shown.bs.collapse.animalInlineScroll', '.animal-inline-editor', function() {
scrollInlineEditorIntoView(this);
});
$(document).on('submit', '#animal-gallery-upload', function(event) {
event.preventDefault();
var form = this;
var filesInput = form.querySelector('#galleryImages');
var selectedCount = filesInput && filesInput.files ? filesInput.files.length : 0;
if (selectedCount > 10) {
window.alert('You can upload up to 10 images at a time.');
return;
}
var formData = new FormData(form);
$.ajax({
url: form.action,
type: 'POST',
data: formData,
processData: false,
contentType: false
}).done(function() {
var shouldReopenModal = selectedCount > 0;
if (form && typeof form.reset === 'function') {
form.reset();
}
$('#animal-gallery-manage-modal').modal('hide');
refreshPanels(['#animal-gallery-panel']).done(function() {
if (shouldReopenModal) {
$('#animal-gallery-manage-modal').modal('show');
}
});
});
});
$(document).on('click', '.js-ajax-gallery-remove', function(event) {
event.preventDefault();
var $link = $(this);
var confirmText = $link.data('confirmMessage') || $link.data('confirm');
if (confirmText && !window.confirm(confirmText)) {
return;
}
$.post($link.attr('href'), getCsrfPayload()).done(function() {
refreshPanels(['#animal-gallery-panel']);
});
});
})();
JS
, \yii\web\View::POS_END);
?>
<div class="panel panel-default" id="animal-gallery-panel">
<div class="panel-heading" style="display:flex;justify-content:space-between;align-items:center;gap:8px;">
<span><?= Yii::t('AnimalManagementModule.base', '<strong>Gallery</strong>') ?></span>
<?php if ($canManage): ?>
<?= Html::a('<i class="fa fa-plus"></i> ' . Yii::t('AnimalManagementModule.base', 'Add'), '#animal-gallery-manage-modal', [
'class' => 'btn btn-xs btn-success',
'title' => Yii::t('AnimalManagementModule.base', 'Upload Gallery Images'),
'aria-label' => Yii::t('AnimalManagementModule.base', 'Upload Gallery Images'),
'data-toggle' => 'modal',
]) ?>
<?php endif; ?>
</div>
<div class="panel-body">
<?php if (empty($galleryItems)): ?>
<div class="text-muted" style="margin-bottom:10px;">
<?= Yii::t('AnimalManagementModule.base', 'No gallery images yet.') ?>
</div>
<?php else: ?>
<div class="row" style="margin-bottom:8px;">
<?php foreach ($galleryItems as $galleryItem): ?>
<?php $galleryUrl = trim((string)$galleryItem->getImageUrl()); ?>
<?php if ($galleryUrl === '') { continue; } ?>
<div class="col-sm-4 col-md-3" style="margin-bottom:10px;">
<div style="position:relative;border-radius:8px;overflow:hidden;background:#f3f5f7;">
<a href="<?= Html::encode($galleryUrl) ?>#.jpeg"
data-type="image"
data-toggle="lightbox"
data-parent="#animal-gallery-panel"
data-ui-gallery="<?= Html::encode($uiGalleryId) ?>"
data-pjax="0"
data-pjax-prevent
aria-label="<?= Yii::t('AnimalManagementModule.base', 'Open image') ?>">
<img src="<?= Html::encode($galleryUrl) ?>" alt="<?= Yii::t('AnimalManagementModule.base', 'Gallery image') ?>" style="width:100%;height:170px;object-fit:cover;display:block;">
</a>
<?php if ($canManage): ?>
<div style="position:absolute;top:8px;right:8px;">
<?= Html::a('<i class="fa fa-trash"></i>', $space->createUrl('/animal_management/animals/remove-gallery-image', ['id' => $animal->id, 'galleryId' => $galleryItem->id]), [
'class' => 'btn btn-xs btn-default js-ajax-gallery-remove',
'style' => 'background:rgba(255,255,255,0.92);border:0;',
'title' => Yii::t('AnimalManagementModule.base', 'Remove Image'),
'aria-label' => Yii::t('AnimalManagementModule.base', 'Remove Image'),
'data-confirm-message' => Yii::t('AnimalManagementModule.base', 'Remove this image from the gallery?'),
]) ?>
</div>
<?php endif; ?>
</div>
</div>
<?php endforeach; ?>
</div>
<?php endif; ?>
</div>
</div>
<?php if ($canManage): ?>
<div class="modal fade" id="animal-gallery-manage-modal" tabindex="-1" role="dialog" aria-hidden="true">
<div class="modal-dialog modal-lg" role="document">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-label="<?= Yii::t('AnimalManagementModule.base', 'Close') ?>"><span aria-hidden="true">&times;</span></button>
<h4 class="modal-title"><?= Yii::t('AnimalManagementModule.base', 'Add Images to Gallery') ?></h4>
</div>
<div class="modal-body">
<?php if (!empty($galleryItems)): ?>
<div class="row" style="margin-bottom:10px;max-height:260px;overflow:auto;">
<?php foreach ($galleryItems as $galleryItem): ?>
<?php $galleryUrl = trim((string)$galleryItem->getImageUrl()); ?>
<?php if ($galleryUrl === '') { continue; } ?>
<div class="col-xs-6 col-sm-3" style="margin-bottom:8px;">
<img src="<?= Html::encode($galleryUrl) ?>" alt="<?= Yii::t('AnimalManagementModule.base', 'Gallery image') ?>" style="width:100%;height:110px;object-fit:cover;border-radius:6px;">
</div>
<?php endforeach; ?>
</div>
<hr style="margin-top:0;">
<?php endif; ?>
<form id="animal-gallery-upload" method="post" action="<?= Html::encode($space->createUrl('/animal_management/animals/add-gallery-images', ['id' => $animal->id])) ?>" enctype="multipart/form-data">
<?= Html::hiddenInput(Yii::$app->request->csrfParam, Yii::$app->request->getCsrfToken()) ?>
<div class="form-group" style="margin-bottom:8px;">
<label class="control-label" for="galleryImages"><?= Yii::t('AnimalManagementModule.base', 'Upload Images') ?></label>
<input type="file" class="form-control" id="galleryImages" name="galleryImages[]" accept="image/*" multiple>
<div class="help-block" style="margin-bottom:0;"><?= Yii::t('AnimalManagementModule.base', 'You can upload up to 10 images at a time.') ?></div>
</div>
<button type="submit" class="btn btn-primary"><?= Yii::t('AnimalManagementModule.base', 'Upload to Gallery') ?></button>
</form>
</div>
</div>
</div>
</div>
<?php endif; ?>
<style>
.animal-feed-card {
position: relative;
overflow: hidden;
border: 1px solid #d5dfe8;
border-radius: 12px;
background: #223446;
min-height: 240px;
box-shadow: 0 8px 22px rgba(12, 24, 36, 0.16);
}
.animal-feed-cover {
position: absolute;
inset: 0;
width: 100%;
height: 100%;
object-fit: cover;
display: block;
}
.animal-feed-overlay {
position: absolute;
inset: 0;
background: linear-gradient(110deg, rgba(10, 18, 28, 0.28) 10%, rgba(10, 18, 28, 0.55) 52%, rgba(10, 18, 28, 0.75) 100%);
}
.animal-feed-content {
position: relative;
z-index: 1;
min-height: 240px;
padding: 14px;
display: flex;
flex-direction: column;
align-items: flex-end;
gap: 10px;
}
.animal-feed-top-row {
width: 100%;
max-width: none;
margin-left: 0;
display: flex;
align-items: center;
justify-content: space-between;
gap: 8px;
}
.animal-feed-date {
font-size: 15px;
font-weight: 700;
color: #ffffff;
margin-right: auto;
}
.animal-feed-details {
width: 55%;
max-width: 55%;
min-width: 0;
margin-left: auto;
border-radius: 10px;
border: 1px solid rgba(255, 255, 255, 0.22);
background: rgba(10, 18, 28, 0.5);
backdrop-filter: blur(2px);
padding: 12px;
color: #ecf2f8;
}
.animal-feed-head {
display: flex;
justify-content: flex-end;
align-items: flex-start;
gap: 8px;
margin-bottom: 8px;
}
.animal-feed-chips {
display: flex;
flex-wrap: wrap;
gap: 6px;
margin-bottom: 8px;
}
.animal-feed-chips--top {
margin-bottom: 0;
justify-content: flex-end;
}
.animal-feed-chip {
display: inline-block;
border-radius: 999px;
border: 1px solid rgba(255, 255, 255, 0.3);
background: rgba(255, 255, 255, 0.16);
color: #ffffff;
font-size: 12px;
padding: 4px 10px;
}
.animal-feed-label {
font-size: 11px;
text-transform: uppercase;
letter-spacing: .04em;
color: rgba(231, 241, 249, 0.78);
margin-bottom: 4px;
}
.animal-feed-copy {
color: #eff5fb;
margin-bottom: 10px;
}
.animal-inline-editor {
position: relative;
z-index: 2;
width: auto;
max-width: none;
min-width: 0;
margin: 0 0 14px 0;
padding-top: 0;
align-self: stretch;
box-sizing: border-box;
border: 1px solid rgba(255, 255, 255, 0.22);
border-radius: 12px;
background: rgba(10, 18, 28, 0.2);
padding: 8px;
}
.animal-inline-editor iframe {
display: block;
width: 100%;
border: 0;
border-radius: 10px;
background: transparent;
}
@media (max-width: 1200px) {
.animal-feed-details {
width: 55%;
max-width: 55%;
min-width: 0;
}
}
@media (max-width: 991px) {
.animal-feed-details {
width: 55%;
max-width: 55%;
min-width: 0;
}
.animal-feed-chips--top {
justify-content: flex-start;
}
}
</style>
<div class="row">
<div class="col-md-6">
<div class="panel panel-default" id="animal-medical-panel">
<div class="panel-heading" style="display:flex;justify-content:space-between;align-items:center;gap:8px;">
<a href="<?= Html::encode($space->createUrl('/animal_management/animals/medical-visits', ['id' => $animal->id])) ?>" style="font-weight:bold;">
<?= Yii::t('AnimalManagementModule.base', 'Medical Visits') ?>
</a>
<?php if ($canManage): ?>
<?= Html::a('<i class="fa fa-plus"></i> ' . Yii::t('AnimalManagementModule.base', 'Add'), $space->createUrl('/animal_management/animals/medical-visits', ['id' => $animal->id, 'inlineMedicalAdd' => 1]) . '#medical-add-inline', [
'class' => 'btn btn-xs btn-success',
'title' => Yii::t('AnimalManagementModule.base', 'Add Medical Visit'),
'aria-label' => Yii::t('AnimalManagementModule.base', 'Add Medical Visit'),
]) ?>
<?php endif; ?>
</div>
<div class="panel-body">
<?php if (empty($medicalVisits)): ?>
<div class="text-muted" style="margin-bottom:10px;"><?= Yii::t('AnimalManagementModule.base', 'No medical visits recorded.') ?></div>
<?php else: ?>
<?php
$hiddenMedicalKeys = [
'second_physician_name',
'second_physician_business_name',
'second_physician_street_address',
'second_physician_city',
'second_physician_state',
'second_physician_zip',
'second_physician_cell_phone',
'second_physician_business_phone',
'second_physician_license_number',
'previous_physicians',
];
$knownMedicalKeys = [
'weight',
'pulse',
'blood_pressure',
'oxygen',
'chronic_conditions',
'acute_conditions',
'special_needs',
'date_of_most_recent_medical_visit',
'physician_name',
'physician_business_name',
'physician_street_address',
'physician_city',
'physician_state',
'physician_zip',
'physician_cell_phone',
'physician_business_phone',
'physician_license_number',
'medical_media_reference',
'media_reference',
];
$medicalVitalLabelOverrides = [
'blood_pressure' => 'BP',
'oxygen' => 'O₂',
];
?>
<?php foreach ($medicalVisits as $visit): ?>
<?php
$visitCustomValues = $visit->getCustomFieldDisplayValues($canManage);
$visitFieldsByKey = [];
$additionalVisitFields = [];
$medicalMedia = '';
foreach ($visitCustomValues as $customField) {
$fieldKey = (string)($customField['field_key'] ?? '');
if (in_array($fieldKey, $hiddenMedicalKeys, true)) {
continue;
}
$fieldValue = trim((string)($customField['value'] ?? ''));
if ($fieldValue === '') {
continue;
}
if ($fieldKey === 'medical_media_reference' || $fieldKey === 'media_reference') {
$medicalMedia = $fieldValue;
continue;
}
if (in_array($fieldKey, $knownMedicalKeys, true)) {
$visitFieldsByKey[$fieldKey] = [
'label' => (string)($medicalVitalLabelOverrides[$fieldKey] ?? ($customField['label'] ?? $fieldKey)),
'value' => $fieldValue,
];
continue;
}
$additionalVisitFields[] = [
'label' => (string)($customField['label'] ?? $fieldKey),
'value' => $fieldValue,
];
}
$hasMedicalMedia = $medicalMedia !== '' && (preg_match('/^https?:\/\//i', $medicalMedia) || substr($medicalMedia, 0, 1) === '/');
$visitDateDisplay = DateDisplayHelper::format((string)$visit->visit_at);
?>
<div class="panel panel-default animal-feed-card" style="margin-bottom:12px;">
<?php if ($hasMedicalMedia): ?>
<img class="animal-feed-cover" src="<?= Html::encode($medicalMedia) ?>" alt="<?= Yii::t('AnimalManagementModule.base', 'Medical media') ?>">
<?php endif; ?>
<div class="animal-feed-overlay"></div>
<div class="animal-feed-content">
<?php
$vitalKeys = ['weight', 'pulse', 'blood_pressure', 'oxygen'];
$hasVitals = false;
foreach ($vitalKeys as $vitalKey) {
if (!empty($visitFieldsByKey[$vitalKey]['value'])) {
$hasVitals = true;
break;
}
}
?>
<div class="animal-feed-top-row">
<span class="animal-feed-date"><?= Html::encode($visitDateDisplay !== '' ? $visitDateDisplay : (string)$visit->visit_at) ?></span>
<?php if ($hasVitals): ?>
<div class="animal-feed-chips animal-feed-chips--top">
<?php foreach ($vitalKeys as $vitalKey): ?>
<?php if (empty($visitFieldsByKey[$vitalKey]['value'])) { continue; } ?>
<span class="animal-feed-chip"><?= Html::encode($visitFieldsByKey[$vitalKey]['label']) ?>: <?= Html::encode($visitFieldsByKey[$vitalKey]['value']) ?></span>
<?php endforeach; ?>
</div>
<?php endif; ?>
</div>
<div class="animal-feed-details">
<div class="animal-feed-head">
<?php if ($canManage): ?>
<?= Html::a(
'<i class="fa fa-pencil"></i>',
'#medical-edit-inline-' . (int)$visit->id,
[
'class' => 'btn btn-xs btn-default',
'data-toggle' => 'collapse',
'title' => Yii::t('AnimalManagementModule.base', 'Edit'),
'aria-label' => Yii::t('AnimalManagementModule.base', 'Edit'),
]
) ?>
<?php endif; ?>
</div>
<?php if (!empty($visit->provider_name)): ?>
<div class="animal-feed-copy" style="margin-bottom:8px;"><?= Yii::t('AnimalManagementModule.base', 'Provider') ?>: <?= Html::encode((string)$visit->provider_name) ?></div>
<?php endif; ?>
<?php if (!empty($visit->notes)): ?>
<div class="animal-feed-label"><?= Yii::t('AnimalManagementModule.base', 'Clinical Notes') ?></div>
<div class="animal-feed-copy"><?= nl2br(Html::encode((string)$visit->notes)) ?></div>
<?php endif; ?>
<?php if (!empty($visit->recommendations)): ?>
<div class="animal-feed-label"><?= Yii::t('AnimalManagementModule.base', 'Recommendations') ?></div>
<div class="animal-feed-copy"><?= nl2br(Html::encode((string)$visit->recommendations)) ?></div>
<?php endif; ?>
<?php
$conditionKeys = ['chronic_conditions', 'acute_conditions', 'special_needs'];
foreach ($conditionKeys as $conditionKey):
if (empty($visitFieldsByKey[$conditionKey]['value'])) {
continue;
}
?>
<div class="animal-feed-label"><?= Html::encode($visitFieldsByKey[$conditionKey]['label']) ?></div>
<div class="animal-feed-copy"><?= nl2br(Html::encode($visitFieldsByKey[$conditionKey]['value'])) ?></div>
<?php endforeach; ?>
<?php if (!empty($visitFieldsByKey['date_of_most_recent_medical_visit']['value'])): ?>
<div class="animal-feed-label"><?= Html::encode($visitFieldsByKey['date_of_most_recent_medical_visit']['label']) ?></div>
<div class="animal-feed-copy"><?= Html::encode(DateDisplayHelper::format((string)$visitFieldsByKey['date_of_most_recent_medical_visit']['value'])) ?></div>
<?php endif; ?>
<?php
$contactKeys = [
'physician_name',
'physician_business_name',
'physician_street_address',
'physician_city',
'physician_state',
'physician_zip',
'physician_cell_phone',
'physician_business_phone',
'physician_license_number',
];
$hasContact = false;
foreach ($contactKeys as $contactKey) {
if (!empty($visitFieldsByKey[$contactKey]['value'])) {
$hasContact = true;
break;
}
}
?>
<?php if ($hasContact): ?>
<?php
$contactLines = [];
if (!empty($visitFieldsByKey['physician_name']['value'])) {
$contactLines[] = (string)$visitFieldsByKey['physician_name']['value'];
}
if (!empty($visitFieldsByKey['physician_business_name']['value'])) {
$contactLines[] = (string)$visitFieldsByKey['physician_business_name']['value'];
}
if (!empty($visitFieldsByKey['physician_street_address']['value'])) {
$contactLines[] = (string)$visitFieldsByKey['physician_street_address']['value'];
}
$cityStateZip = trim(implode(', ', array_filter([
trim((string)($visitFieldsByKey['physician_city']['value'] ?? '')),
trim((string)($visitFieldsByKey['physician_state']['value'] ?? '')),
])));
$zipValue = trim((string)($visitFieldsByKey['physician_zip']['value'] ?? ''));
if ($zipValue !== '') {
$cityStateZip = trim($cityStateZip . ($cityStateZip !== '' ? ' ' : '') . $zipValue);
}
if ($cityStateZip !== '') {
$contactLines[] = $cityStateZip;
}
$phones = array_filter([
trim((string)($visitFieldsByKey['physician_cell_phone']['value'] ?? '')),
trim((string)($visitFieldsByKey['physician_business_phone']['value'] ?? '')),
]);
if (!empty($phones)) {
$contactLines[] = implode(' · ', $phones);
}
if (!empty($visitFieldsByKey['physician_license_number']['value'])) {
$contactLines[] = (string)$visitFieldsByKey['physician_license_number']['value'];
}
?>
<div class="animal-feed-label"><?= Yii::t('AnimalManagementModule.base', 'Care Contact') ?></div>
<div class="animal-feed-copy">
<?php foreach ($contactLines as $contactLine): ?>
<div><?= Html::encode($contactLine) ?></div>
<?php endforeach; ?>
</div>
<?php endif; ?>
<?php if (!empty($additionalVisitFields)): ?>
<div class="animal-feed-label"><?= Yii::t('AnimalManagementModule.base', 'Additional Fields') ?></div>
<div class="animal-feed-copy">
<?php foreach ($additionalVisitFields as $additionalField): ?>
<div><strong><?= Html::encode((string)$additionalField['label']) ?>:</strong> <?= nl2br(Html::encode((string)$additionalField['value'])) ?></div>
<?php endforeach; ?>
</div>
<?php endif; ?>
</div>
<?php if ($canManage): ?>
<div id="medical-edit-inline-<?= (int)$visit->id ?>" class="collapse animal-inline-editor<?= $openMedicalEditId === (int)$visit->id ? ' in' : '' ?>">
<iframe
src="<?= Html::encode($space->createUrl('/animal_management/animals/edit-medical-visit', ['id' => $animal->id, 'visitId' => $visit->id, 'inline' => 1, '_v' => time()])) ?>"
style="width:100%;min-height:640px;"
loading="lazy"
></iframe>
</div>
<?php endif; ?>
</div>
</div>
<?php endforeach; ?>
<?php endif; ?>
</div>
</div>
</div>
<div class="col-md-6">
<div class="panel panel-default" id="animal-progress-panel">
<div class="panel-heading" style="display:flex;justify-content:space-between;align-items:center;gap:8px;">
<a href="<?= Html::encode($space->createUrl('/animal_management/animals/progress-updates', ['id' => $animal->id])) ?>" style="font-weight:bold;">
<?= Yii::t('AnimalManagementModule.base', 'Progress Feed') ?>
</a>
<?php if ($canManage): ?>
<?= Html::a('<i class="fa fa-plus"></i> ' . Yii::t('AnimalManagementModule.base', 'Add'), $space->createUrl('/animal_management/animals/progress-updates', ['id' => $animal->id, 'inlineProgressAdd' => 1]) . '#progress-add-inline', [
'class' => 'btn btn-xs btn-success',
'title' => Yii::t('AnimalManagementModule.base', 'Add Progress Update'),
'aria-label' => Yii::t('AnimalManagementModule.base', 'Add Progress Update'),
]) ?>
<?php endif; ?>
</div>
<div class="panel-body">
<?php if (empty($progressUpdates)): ?>
<div class="text-muted" style="margin-bottom:10px;"><?= Yii::t('AnimalManagementModule.base', 'No progress updates recorded.') ?></div>
<?php else: ?>
<?php foreach ($progressUpdates as $update): ?>
<?php
$progressCustomValues = $update->getCustomFieldDisplayValues($canManage);
$mediaReference = '';
$progressCustomDisplayValues = [];
foreach ($progressCustomValues as $customField) {
if ((string)($customField['field_key'] ?? '') === 'media_reference') {
$mediaReference = trim((string)$customField['value']);
continue;
}
$progressCustomDisplayValues[] = $customField;
}
$hasMediaImage = $mediaReference !== '' && (preg_match('/^https?:\/\//i', $mediaReference) || substr($mediaReference, 0, 1) === '/');
?>
<div class="panel panel-default animal-feed-card" style="margin-bottom:12px;">
<?php if ($hasMediaImage): ?>
<img class="animal-feed-cover" src="<?= Html::encode($mediaReference) ?>" alt="<?= Yii::t('AnimalManagementModule.base', 'Progress media') ?>">
<?php endif; ?>
<div class="animal-feed-overlay"></div>
<div class="animal-feed-content">
<div class="animal-feed-top-row">
<span class="animal-feed-date"><?= Html::encode(DateDisplayHelper::format((string)$update->update_at)) ?></span>
<?php if (!empty($update->weight) || !empty($update->vitals)): ?>
<div class="animal-feed-chips animal-feed-chips--top">
<?php if (!empty($update->weight)): ?><span class="animal-feed-chip"><?= Yii::t('AnimalManagementModule.base', 'Weight') ?>: <?= Html::encode((string)$update->weight) ?></span><?php endif; ?>
<?php if (!empty($update->vitals)): ?><span class="animal-feed-chip"><?= Yii::t('AnimalManagementModule.base', 'Vitals') ?></span><?php endif; ?>
</div>
<?php endif; ?>
</div>
<div class="animal-feed-details">
<div class="animal-feed-head">
<?php if ($canManage): ?>
<?= Html::a(
'<i class="fa fa-pencil"></i>',
'#progress-edit-inline-' . (int)$update->id,
[
'class' => 'btn btn-xs btn-default',
'data-toggle' => 'collapse',
'title' => Yii::t('AnimalManagementModule.base', 'Edit'),
'aria-label' => Yii::t('AnimalManagementModule.base', 'Edit'),
]
) ?>
<?php endif; ?>
</div>
<?php if (!empty($update->vitals)): ?><div class="animal-feed-label"><?= Yii::t('AnimalManagementModule.base', 'Vitals') ?></div><div class="animal-feed-copy"><?= nl2br(Html::encode((string)$update->vitals)) ?></div><?php endif; ?>
<?php if (!empty($update->behavior_notes)): ?><div class="animal-feed-label"><?= Yii::t('AnimalManagementModule.base', 'Behavior') ?></div><div class="animal-feed-copy"><?= nl2br(Html::encode((string)$update->behavior_notes)) ?></div><?php endif; ?>
<?php if (!empty($update->meal_plan_changes)): ?><div class="animal-feed-label"><?= Yii::t('AnimalManagementModule.base', 'Meal Plan') ?></div><div class="animal-feed-copy"><?= nl2br(Html::encode((string)$update->meal_plan_changes)) ?></div><?php endif; ?>
<?php if (!empty($update->housing_changes)): ?><div class="animal-feed-label"><?= Yii::t('AnimalManagementModule.base', 'Housing') ?></div><div class="animal-feed-copy"><?= nl2br(Html::encode((string)$update->housing_changes)) ?></div><?php endif; ?>
<?php if (!empty($update->medical_concerns)): ?><div class="animal-feed-label"><?= Yii::t('AnimalManagementModule.base', 'Medical Concerns') ?></div><div class="animal-feed-copy"><?= nl2br(Html::encode((string)$update->medical_concerns)) ?></div><?php endif; ?>
<?php if (!empty($progressCustomDisplayValues)): ?>
<div class="animal-feed-label"><?= Yii::t('AnimalManagementModule.base', 'Additional Fields') ?></div>
<div class="animal-feed-copy">
<?php foreach ($progressCustomDisplayValues as $customField): ?>
<div><strong><?= Html::encode((string)$customField['label']) ?>:</strong> <?= nl2br(Html::encode((string)$customField['value'])) ?></div>
<?php endforeach; ?>
</div>
<?php endif; ?>
</div>
<?php if ($canManage): ?>
<div id="progress-edit-inline-<?= (int)$update->id ?>" class="collapse animal-inline-editor<?= $openProgressEditId === (int)$update->id ? ' in' : '' ?>">
<iframe
src="<?= Html::encode($space->createUrl('/animal_management/animals/edit-progress-update', ['id' => $animal->id, 'updateId' => $update->id, 'inline' => 1, '_v' => time()])) ?>"
style="width:100%;min-height:760px;"
loading="lazy"
></iframe>
</div>
<?php endif; ?>
</div>
</div>
<?php endforeach; ?>
<?php endif; ?>
</div>
</div>
</div>
</div>
<div class="panel panel-default">
<div class="panel-heading" style="display:flex;justify-content:space-between;align-items:center;gap:8px;">
<span><?= Yii::t('AnimalManagementModule.base', '<strong>Transfer Timeline</strong>') ?></span>
<?php if ($canManage): ?>
<?= Html::a('<i class="fa fa-plus"></i> ' . Yii::t('AnimalManagementModule.base', 'Add'), $space->createUrl('/animal_management/animals/transfer', ['id' => $animal->id]), [
'class' => 'btn btn-xs btn-success',
'title' => Yii::t('AnimalManagementModule.base', 'Request Transfer'),
'aria-label' => Yii::t('AnimalManagementModule.base', 'Request Transfer'),
]) ?>
<?php endif; ?>
</div>
<div class="panel-body">
<?php if (empty($transfers)): ?>
<div class="text-muted"><?= Yii::t('AnimalManagementModule.base', 'No transfers yet.') ?></div>
<?php else: ?>
<table class="table table-condensed table-hover" style="margin-bottom:0;">
<thead>
<tr>
<th><?= Yii::t('AnimalManagementModule.base', 'From') ?></th>
<th><?= Yii::t('AnimalManagementModule.base', 'To') ?></th>
<th><?= Yii::t('AnimalManagementModule.base', 'Status') ?></th>
<th><?= Yii::t('AnimalManagementModule.base', 'Updated') ?></th>
</tr>
</thead>
<tbody>
<?php foreach ($transfers as $transfer): ?>
<?php $fromSpace = $transfer->getFromSpace(); $toSpace = $transfer->getToSpace(); ?>
<tr>
<td><?= Html::encode($fromSpace ? $fromSpace->name : Yii::t('AnimalManagementModule.base', 'Unknown')) ?></td>
<td><?= Html::encode($toSpace ? $toSpace->name : Yii::t('AnimalManagementModule.base', 'Unknown')) ?></td>
<td><?= Html::encode(AnimalTransfer::statusOptions()[$transfer->status] ?? $transfer->status) ?></td>
<td><?= Html::encode(DateDisplayHelper::format((string)$transfer->updated_at)) ?></td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
<?php endif; ?>
</div>
</div>
<div class="panel panel-default">
<div class="panel-heading"><?= Yii::t('AnimalManagementModule.base', '<strong>Transfer Audit Log</strong>') ?></div>
<div class="panel-body">
<?php if (empty($transferEvents)): ?>
<div class="text-muted"><?= Yii::t('AnimalManagementModule.base', 'No transfer events recorded yet.') ?></div>
<?php else: ?>
<table class="table table-condensed table-hover" style="margin-bottom:0;">
<thead>
<tr>
<th><?= Yii::t('AnimalManagementModule.base', 'When') ?></th>
<th><?= Yii::t('AnimalManagementModule.base', 'By') ?></th>
<th><?= Yii::t('AnimalManagementModule.base', 'Event') ?></th>
<th><?= Yii::t('AnimalManagementModule.base', 'Status Change') ?></th>
<th><?= Yii::t('AnimalManagementModule.base', 'Message') ?></th>
</tr>
</thead>
<tbody>
<?php foreach ($transferEvents as $event): ?>
<?php $actor = $event->createdByUser; ?>
<tr>
<td><?= Html::encode(DateDisplayHelper::format((string)$event->created_at)) ?></td>
<td><?= Html::encode($actor ? $actor->displayName : Yii::t('AnimalManagementModule.base', 'System')) ?></td>
<td><?= Html::encode((string)$event->event_type) ?></td>
<td><?= Html::encode((string)($event->from_status ?: '-') . ' -> ' . (string)($event->to_status ?: '-')) ?></td>
<td><?= Html::encode((string)$event->message) ?></td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
<?php endif; ?>
</div>
</div>