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

93
views/animals/_tile.php Normal file
View File

@@ -0,0 +1,93 @@
<?php
use humhub\modules\animal_management\models\Animal;
use humhub\modules\animal_management\helpers\DateDisplayHelper;
use humhub\modules\animal_management\models\AnimalMedicalVisit;
use humhub\modules\content\components\ContentContainerActiveRecord;
use yii\helpers\Html;
/* @var Animal $animal */
/* @var ContentContainerActiveRecord $contentContainer */
/* @var AnimalMedicalVisit|null $lastMedical */
/* @var string $imageUrl */
/* @var array $tileFields */
/* @var bool $showMedicalIcon */
$showMedicalIcon = isset($showMedicalIcon) ? (bool)$showMedicalIcon : true;
$imageUrl = trim((string)$imageUrl);
$hasImage = $imageUrl !== '' && (preg_match('/^https?:\/\//i', $imageUrl) || substr($imageUrl, 0, 1) === '/');
$statusLabel = Animal::statusOptions()[$animal->status] ?? (string)$animal->status;
$fieldMap = [
'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' => $lastMedical instanceof AnimalMedicalVisit ? DateDisplayHelper::format((string)$lastMedical->visit_at) : '',
];
$tileFields = is_array($tileFields) ? $tileFields : [];
$selectedMeta = [];
foreach ($tileFields as $fieldKey) {
$fieldKey = trim((string)$fieldKey);
if ($fieldKey === '' || $fieldKey === 'name' || !array_key_exists($fieldKey, $fieldMap)) {
continue;
}
$value = trim((string)$fieldMap[$fieldKey]);
if ($value === '') {
continue;
}
$selectedMeta[] = $value;
}
$summaryText = trim((string)$fieldMap['public_summary']);
if ($summaryText !== '') {
$summaryText = substr($summaryText, 0, 160) . (strlen($summaryText) > 160 ? '...' : '');
}
$animalViewUrl = $contentContainer->createUrl('/animal_management/animals/view', ['id' => $animal->id]);
$medicalUrl = $contentContainer->createUrl('/animal_management/animals/medical-visits', ['id' => $animal->id]);
?>
<div class="panel panel-default" style="margin-bottom:0;overflow:hidden;border-radius:12px;border:0;box-shadow:0 8px 24px rgba(15,23,42,0.14);">
<div style="display:block;position:relative;aspect-ratio:4 / 3;background:#d8dee8;">
<a href="<?= Html::encode($animalViewUrl) ?>" aria-label="<?= Html::encode($animal->getDisplayName()) ?>" style="position:absolute;inset:0;z-index:1;"></a>
<?php if ($hasImage): ?>
<img src="<?= Html::encode($imageUrl) ?>" alt="<?= Html::encode($animal->getDisplayName()) ?>" style="position:absolute;inset:0;width:100%;height:100%;object-fit:cover;">
<?php endif; ?>
<div style="position:absolute;inset:0;background:linear-gradient(180deg, rgba(7,10,16,0.05) 0%, rgba(7,10,16,0.62) 58%, rgba(7,10,16,0.78) 100%);"></div>
<div style="position:absolute;left:12px;right:12px;bottom:12px;z-index:2;color:#fff;pointer-events:none;">
<div style="display:flex;align-items:center;justify-content:space-between;gap:8px;margin-bottom:6px;">
<span style="font-size:20px;font-weight:700;line-height:1.15;text-shadow:0 2px 8px rgba(0,0,0,0.4);color:#fff;">
<?= Html::encode($animal->getDisplayName()) ?>
</span>
<?php if ($showMedicalIcon): ?>
<a href="<?= Html::encode($medicalUrl) ?>" class="btn btn-xs btn-default" title="<?= Yii::t('AnimalManagementModule.base', 'Medical Visits') ?>" aria-label="<?= Yii::t('AnimalManagementModule.base', 'Medical Visits') ?>" style="border-radius:999px;padding:6px 8px;background:rgba(255,255,255,0.92);border:0;position:relative;z-index:3;pointer-events:auto;">
<i class="fa fa-heartbeat"></i>
</a>
<?php endif; ?>
</div>
<?php if (!empty($selectedMeta)): ?>
<div style="display:flex;gap:6px;flex-wrap:wrap;margin-bottom:6px;">
<?php foreach ($selectedMeta as $metaValue): ?>
<span style="display:inline-block;background:rgba(15,23,42,0.58);border:1px solid rgba(255,255,255,0.28);padding:2px 8px;border-radius:999px;font-size:12px;">
<?= Html::encode($metaValue) ?>
</span>
<?php endforeach; ?>
</div>
<?php endif; ?>
<?php if ($summaryText !== ''): ?>
<div style="font-size:12px;line-height:1.35;color:rgba(255,255,255,0.92);text-shadow:0 1px 4px rgba(0,0,0,0.5);">
<?= Html::encode($summaryText) ?>
</div>
<?php endif; ?>
</div>
</div>
</div>

View File

@@ -0,0 +1,130 @@
<?php
use humhub\modules\animal_management\models\AnimalTransfer;
use humhub\modules\space\models\Space;
use yii\helpers\Html;
/* @var AnimalTransfer $transfer */
/* @var Space $space */
/* @var string $otherRescueName */
/* @var string $otherRescueUrl */
/* @var string $animalProfileUrl */
/* @var string $imageUrl */
/* @var bool $isIncoming */
$animalName = $transfer->animal ? $transfer->animal->getDisplayName() : ('#' . (int)$transfer->animal_id);
$statusLabel = AnimalTransfer::statusOptions()[$transfer->status] ?? (string)$transfer->status;
$hasImage = $imageUrl !== '' && (preg_match('/^https?:\/\//i', $imageUrl) || substr($imageUrl, 0, 1) === '/');
$statusTextColor = '#ffffff';
switch ($transfer->status) {
case AnimalTransfer::STATUS_REQUESTED:
$statusTextColor = '#facc15';
break;
case AnimalTransfer::STATUS_ACCEPTED:
$statusTextColor = '#4ade80';
break;
case AnimalTransfer::STATUS_COMPLETED:
$statusTextColor = '#60a5fa';
break;
case AnimalTransfer::STATUS_DECLINED:
$statusTextColor = '#fca5a5';
break;
case AnimalTransfer::STATUS_CANCELLED:
$statusTextColor = '#d1d5db';
break;
}
?>
<div class="panel panel-default" style="margin-bottom:0;overflow:hidden;border-radius:12px;border:0;box-shadow:0 8px 24px rgba(15,23,42,0.14);">
<div style="position:relative;min-height:190px;background:#d8dee8;">
<?php if ($hasImage): ?>
<img src="<?= Html::encode($imageUrl) ?>" alt="<?= Html::encode($animalName) ?>" 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:#8d98a5;">
<i class="fa fa-paw fa-3x"></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) 58%, rgba(7,10,16,0.82) 100%);"></div>
<div style="position:absolute;top:12px;left:12px;z-index:2;max-width:60%;">
<?php if (!empty($otherRescueUrl)): ?>
<?= Html::a(
Html::encode($otherRescueName),
$otherRescueUrl,
[
'style' => 'display:inline-block;background:rgba(15,23,42,0.66);border:1px solid rgba(255,255,255,0.28);color:#fff;padding:4px 10px;border-radius:999px;font-size:12px;font-weight:600;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;max-width:100%;text-decoration:none;'
]
) ?>
<?php else: ?>
<span style="display:inline-block;background:rgba(15,23,42,0.66);border:1px solid rgba(255,255,255,0.28);color:#fff;padding:4px 10px;border-radius:999px;font-size:12px;font-weight:600;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;max-width:100%;">
<?= Html::encode($otherRescueName) ?>
</span>
<?php endif; ?>
</div>
<div style="position:absolute;top:12px;right:12px;z-index:2;display:flex;gap:6px;flex-wrap:wrap;justify-content:flex-end;max-width:65%;">
<?php if ($isIncoming && $transfer->status === AnimalTransfer::STATUS_REQUESTED): ?>
<?= Html::a(
Yii::t('AnimalManagementModule.base', 'Accept'),
$space->createUrl('/animal_management/animals/transfer-respond', ['id' => $transfer->id, 'decision' => 'accept']),
[
'class' => 'btn btn-xs btn-default',
'data-method' => 'post',
'style' => 'border-radius:999px;border:0;background:rgba(255,255,255,0.92);font-weight:600;'
]
) ?>
<?= Html::a(
Yii::t('AnimalManagementModule.base', 'Decline'),
$space->createUrl('/animal_management/animals/transfer-respond', ['id' => $transfer->id, 'decision' => 'decline']),
[
'class' => 'btn btn-xs btn-default',
'data-method' => 'post',
'style' => 'border-radius:999px;border:0;background:rgba(255,255,255,0.92);font-weight:600;'
]
) ?>
<?php elseif ($isIncoming && $transfer->status === AnimalTransfer::STATUS_ACCEPTED): ?>
<?= Html::a(
Yii::t('AnimalManagementModule.base', 'Complete Transfer'),
$space->createUrl('/animal_management/animals/transfer-complete', ['id' => $transfer->id]),
[
'class' => 'btn btn-xs btn-default',
'data-method' => 'post',
'style' => 'border-radius:999px;border:0;background:rgba(255,255,255,0.92);font-weight:600;'
]
) ?>
<?php elseif (!$isIncoming && in_array($transfer->status, [AnimalTransfer::STATUS_REQUESTED, AnimalTransfer::STATUS_ACCEPTED], true)): ?>
<?= Html::a(
Yii::t('AnimalManagementModule.base', 'Cancel Request'),
$space->createUrl('/animal_management/animals/transfer-cancel', ['id' => $transfer->id]),
[
'class' => 'btn btn-xs btn-default',
'data-method' => 'post',
'style' => 'border-radius:999px;border:0;background:rgba(255,255,255,0.92);font-weight:600;'
]
) ?>
<?php endif; ?>
</div>
<div style="position:absolute;left:12px;right:12px;bottom:12px;z-index:2;color:#fff;">
<div style="display:flex;align-items:flex-end;justify-content:space-between;gap:10px;">
<div style="font-size:24px;font-weight:700;line-height:1.15;text-shadow:0 2px 8px rgba(0,0,0,0.45);">
<?php if (!empty($animalProfileUrl)): ?>
<?= Html::a(
Html::encode($animalName),
$animalProfileUrl,
[
'style' => 'color:#fff;text-decoration:none;'
]
) ?>
<?php else: ?>
<?= Html::encode($animalName) ?>
<?php endif; ?>
</div>
<span style="display:inline-block;background:rgba(15,23,42,0.7);border:1px solid rgba(255,255,255,0.22);padding:5px 12px;border-radius:999px;font-size:14px;font-weight:800;letter-spacing:0.02em;color:<?= Html::encode($statusTextColor) ?>;text-transform:uppercase;white-space:nowrap;">
<?= Html::encode($statusLabel) ?>
</span>
</div>
</div>
</div>
</div>

View File

@@ -0,0 +1,518 @@
<?php
use humhub\modules\animal_management\models\Animal;
use humhub\modules\animal_management\models\AnimalGalleryItem;
use humhub\modules\animal_management\models\forms\AnimalMedicalVisitForm;
use humhub\modules\space\models\Space;
use humhub\widgets\Button;
use yii\bootstrap\ActiveForm;
use yii\helpers\Html;
use yii\helpers\Json;
/* @var Space $space */
/* @var Animal $animal */
/* @var AnimalMedicalVisitForm $model */
/* @var string $returnTo */
/* @var AnimalGalleryItem[] $galleryItems */
/* @var bool $isInline */
$isInline = isset($isInline) ? (bool)$isInline : false;
$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',
];
$renderCustomField = static function (string $fieldKey, AnimalMedicalVisitForm $formModel, array $definitions): string {
if (!isset($definitions[$fieldKey])) {
return '';
}
$definition = $definitions[$fieldKey];
$inputType = (string)$definition['input_type'];
$vitalLabelOverrides = [
'blood_pressure' => 'BP',
'oxygen' => 'O₂',
];
$label = (string)($vitalLabelOverrides[$fieldKey] ?? $definition['label']);
if ((int)$definition['required'] === 1) {
$label .= ' *';
}
$fieldName = "AnimalMedicalVisitForm[customFields][$fieldKey]";
$fieldValue = $formModel->customFields[$fieldKey] ?? '';
ob_start();
?>
<?php if ($inputType === 'textarea'): ?>
<div class="form-group">
<label class="control-label" for="animalmedicalvisitform-customfields-<?= Html::encode($fieldKey) ?>"><?= Html::encode($label) ?></label>
<?= Html::textarea($fieldName, (string)$fieldValue, ['class' => 'form-control', 'rows' => 3, 'id' => "animalmedicalvisitform-customfields-$fieldKey"]) ?>
</div>
<?php elseif ($inputType === 'boolean'): ?>
<div class="checkbox" style="margin-bottom:10px;">
<label>
<?= Html::hiddenInput($fieldName, '0') ?>
<?= Html::checkbox($fieldName, !empty($fieldValue), ['value' => '1']) ?>
<?= Html::encode($label) ?>
</label>
</div>
<?php elseif ($inputType === 'select'): ?>
<div class="form-group">
<label class="control-label" for="animalmedicalvisitform-customfields-<?= Html::encode($fieldKey) ?>"><?= Html::encode($label) ?></label>
<?= Html::dropDownList(
$fieldName,
(string)$fieldValue,
$formModel->getCustomFieldSelectOptions($fieldKey),
['class' => 'form-control', 'prompt' => Yii::t('AnimalManagementModule.base', 'Select...'), 'id' => "animalmedicalvisitform-customfields-$fieldKey"]
) ?>
</div>
<?php elseif ($inputType === 'number'): ?>
<div class="form-group">
<label class="control-label" for="animalmedicalvisitform-customfields-<?= Html::encode($fieldKey) ?>"><?= Html::encode($label) ?></label>
<?= Html::input('number', $fieldName, (string)$fieldValue, ['class' => 'form-control', 'step' => 'any', 'id' => "animalmedicalvisitform-customfields-$fieldKey"]) ?>
</div>
<?php elseif ($inputType === 'date'): ?>
<div class="form-group">
<label class="control-label" for="animalmedicalvisitform-customfields-<?= Html::encode($fieldKey) ?>"><?= Html::encode($label) ?></label>
<?= Html::input('date', $fieldName, (string)$fieldValue, ['class' => 'form-control', 'id' => "animalmedicalvisitform-customfields-$fieldKey"]) ?>
</div>
<?php elseif ($inputType === 'datetime'): ?>
<div class="form-group">
<label class="control-label" for="animalmedicalvisitform-customfields-<?= Html::encode($fieldKey) ?>"><?= Html::encode($label) ?></label>
<?= Html::input('datetime-local', $fieldName, (string)$fieldValue, ['class' => 'form-control', 'id' => "animalmedicalvisitform-customfields-$fieldKey"]) ?>
</div>
<?php else: ?>
<div class="form-group">
<label class="control-label" for="animalmedicalvisitform-customfields-<?= Html::encode($fieldKey) ?>"><?= Html::encode($label) ?></label>
<?= Html::textInput($fieldName, (string)$fieldValue, ['class' => 'form-control', 'id' => "animalmedicalvisitform-customfields-$fieldKey"]) ?>
</div>
<?php endif; ?>
<?php
return (string)ob_get_clean();
};
$customDefinitions = $model->getCustomFieldDefinitions();
$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',
];
$remainingDefinitions = [];
foreach ($customDefinitions as $fieldKey => $definition) {
if (in_array($fieldKey, $knownMedicalKeys, true) || in_array($fieldKey, $hiddenMedicalKeys, true)) {
continue;
}
$remainingDefinitions[$fieldKey] = $definition;
}
$medicalMediaPath = trim((string)($model->customFields['medical_media_reference'] ?? $model->customFields['media_reference'] ?? ''));
$hasMedicalMedia = $medicalMediaPath !== '' && (preg_match('/^https?:\/\//i', $medicalMediaPath) || substr($medicalMediaPath, 0, 1) === '/');
$medicalGalleryModalId = 'add-medical-media-gallery-modal';
$medicalFormId = 'add-medical-visit-inline-form';
$this->registerCss(<<<CSS
.inline-add-shell.panel {
position: relative;
overflow: hidden;
border: 1px solid rgba(255, 255, 255, 0.22);
border-radius: 12px;
background: rgba(10, 18, 28, 0.36);
background-size: cover;
background-position: center;
}
.inline-add-shell.panel.has-media::before {
content: '';
position: absolute;
inset: 0;
background: rgba(10, 18, 28, 0.22);
pointer-events: none;
}
.inline-add-shell > .panel-body {
position: relative;
z-index: 1;
background: rgba(10, 18, 28, 0.2);
}
.inline-add-shell .panel.panel-default {
border-color: rgba(255, 255, 255, 0.2);
background: rgba(10, 18, 28, 0.34);
}
.inline-add-shell .panel.panel-default > .panel-heading {
color: #eef5fb;
background: rgba(10, 18, 28, 0.42);
border-color: rgba(255, 255, 255, 0.2);
}
.inline-add-shell,
.inline-add-shell .panel-body,
.inline-add-shell .control-label,
.inline-add-shell .checkbox label,
.inline-add-shell .radio label,
.inline-add-shell .help-block {
color: #eef5fb;
}
.inline-add-shell .text-muted {
color: rgba(233, 242, 250, 0.78) !important;
}
.inline-add-shell .form-control {
background: rgba(10, 18, 28, 0.56);
border-color: rgba(255, 255, 255, 0.44);
color: #f3f8ff;
}
.inline-add-shell .form-control::placeholder {
color: rgba(243, 248, 255, 0.72);
}
.inline-add-shell .form-control[readonly],
.inline-add-shell .form-control[disabled] {
background: rgba(10, 18, 28, 0.42);
color: rgba(243, 248, 255, 0.72);
}
.inline-add-shell select.form-control option {
color: #0f1b2a;
}
CSS
);
if ($isInline) {
$this->registerCss(<<<CSS
html, body {
margin: 0 !important;
padding: 0 !important;
background: transparent !important;
}
body > .panel:first-child {
margin-top: 0 !important;
}
CSS
);
}
?>
<style>
.medical-media-select-thumb.is-selected {
border-color: #1f8dd6;
box-shadow: 0 0 0 2px rgba(31, 141, 214, 0.2);
}
</style>
<div class="panel panel-default inline-add-shell<?= $hasMedicalMedia ? ' has-media' : '' ?>" id="medical-inline-add-shell"<?= $hasMedicalMedia ? ' style="background-image:url(' . Html::encode($medicalMediaPath) . ');"' : '' ?>>
<div class="panel-body">
<?php
$formOptions = ['id' => $medicalFormId, 'enctype' => 'multipart/form-data'];
if (!$isInline) {
$formOptions['target'] = '_top';
}
$form = ActiveForm::begin(['options' => $formOptions]);
?>
<?= Html::hiddenInput('returnTo', (string)($returnTo ?? 'medical-visits')) ?>
<?= Html::hiddenInput('medicalMediaGalleryPath', $medicalMediaPath, ['id' => 'medical-media-gallery-path']) ?>
<?php if ($isInline): ?>
<div style="display:flex;justify-content:flex-end;gap:8px;margin-bottom:10px;">
<?= Html::submitButton('<i class="fa fa-check"></i>', [
'class' => 'btn btn-default btn-sm',
'title' => Yii::t('AnimalManagementModule.base', 'Save Medical Visit'),
'form' => $medicalFormId,
]) ?>
<?= Html::button('<i class="fa fa-times"></i>', [
'type' => 'button',
'class' => 'btn btn-default btn-sm',
'id' => 'medical-inline-add-cancel-icon',
'title' => Yii::t('AnimalManagementModule.base', 'Cancel'),
]) ?>
</div>
<?php endif; ?>
<?= $form->errorSummary($model, ['showAllErrors' => true]) ?>
<div class="panel panel-default" style="margin-bottom:12px;">
<div class="panel-heading"><strong><?= Yii::t('AnimalManagementModule.base', 'Media') ?></strong></div>
<div class="panel-body" style="padding-bottom:8px;">
<div class="row">
<div class="col-sm-4" style="margin-bottom:8px;">
<div id="medical-media-preview" style="border-radius:8px;overflow:hidden;background:#f2f4f6;height:150px;display:flex;align-items:center;justify-content:center;">
<?php if ($hasMedicalMedia): ?>
<img src="<?= Html::encode($medicalMediaPath) ?>" alt="<?= Yii::t('AnimalManagementModule.base', 'Selected medical media') ?>" style="width:100%;height:100%;object-fit:cover;">
<?php else: ?>
<i class="fa fa-image fa-2x" style="color:#a7b0b8;"></i>
<?php endif; ?>
</div>
</div>
<div class="col-sm-8">
<button type="button" class="btn btn-default" data-toggle="modal" data-target="#<?= Html::encode($medicalGalleryModalId) ?>" style="margin-bottom:8px;">
<i class="fa fa-photo"></i> <?= Yii::t('AnimalManagementModule.base', 'Choose from Gallery or Upload') ?>
</button>
<div class="checkbox" style="margin-top:0;">
<label>
<input type="checkbox" name="removeMedicalMedia" value="1">
<?= Yii::t('AnimalManagementModule.base', 'Remove selected media') ?>
</label>
</div>
</div>
</div>
</div>
</div>
<div class="panel panel-default" style="margin-bottom:12px;">
<div class="panel-heading"><strong><?= Yii::t('AnimalManagementModule.base', 'Medical Visit') ?></strong></div>
<div class="panel-body" style="padding-bottom:8px;">
<div class="row">
<div class="col-sm-6"><?= $form->field($model, 'visit_at')->input('datetime-local') ?></div>
<div class="col-sm-6"><?= $form->field($model, 'provider_name') ?></div>
</div>
<?= $form->field($model, 'notes')->textarea(['rows' => 3]) ?>
<?= $form->field($model, 'recommendations')->textarea(['rows' => 3]) ?>
</div>
</div>
<div class="panel panel-default" style="margin-bottom:12px;">
<div class="panel-heading"><strong><?= Yii::t('AnimalManagementModule.base', 'Vitals') ?></strong></div>
<div class="panel-body" style="padding-bottom:8px;">
<div class="row">
<div class="col-sm-3"><?= $renderCustomField('weight', $model, $customDefinitions) ?></div>
<div class="col-sm-3"><?= $renderCustomField('pulse', $model, $customDefinitions) ?></div>
<div class="col-sm-3"><?= $renderCustomField('blood_pressure', $model, $customDefinitions) ?></div>
<div class="col-sm-3"><?= $renderCustomField('oxygen', $model, $customDefinitions) ?></div>
</div>
</div>
</div>
<div class="panel panel-default" style="margin-bottom:12px;">
<div class="panel-heading"><strong><?= Yii::t('AnimalManagementModule.base', 'Conditions') ?></strong></div>
<div class="panel-body" style="padding-bottom:8px;">
<?= $renderCustomField('chronic_conditions', $model, $customDefinitions) ?>
<?= $renderCustomField('acute_conditions', $model, $customDefinitions) ?>
<?= $renderCustomField('special_needs', $model, $customDefinitions) ?>
</div>
</div>
<div class="panel panel-default" style="margin-bottom:12px;">
<div class="panel-heading"><strong><?= Yii::t('AnimalManagementModule.base', 'Medical Visit Detail') ?></strong></div>
<div class="panel-body" style="padding-bottom:8px;">
<?= $renderCustomField('date_of_most_recent_medical_visit', $model, $customDefinitions) ?>
</div>
</div>
<div class="panel panel-default" style="margin-bottom:12px;">
<div class="panel-heading"><strong><?= Yii::t('AnimalManagementModule.base', 'Physician') ?></strong></div>
<div class="panel-body" style="padding-bottom:8px;">
<div class="row">
<div class="col-sm-6"><?= $renderCustomField('physician_name', $model, $customDefinitions) ?></div>
<div class="col-sm-6"><?= $renderCustomField('physician_business_name', $model, $customDefinitions) ?></div>
<div class="col-sm-12"><?= $renderCustomField('physician_street_address', $model, $customDefinitions) ?></div>
<div class="col-sm-4"><?= $renderCustomField('physician_city', $model, $customDefinitions) ?></div>
<div class="col-sm-4"><?= $renderCustomField('physician_state', $model, $customDefinitions) ?></div>
<div class="col-sm-4"><?= $renderCustomField('physician_zip', $model, $customDefinitions) ?></div>
<div class="col-sm-4"><?= $renderCustomField('physician_cell_phone', $model, $customDefinitions) ?></div>
<div class="col-sm-4"><?= $renderCustomField('physician_business_phone', $model, $customDefinitions) ?></div>
<div class="col-sm-4"><?= $renderCustomField('physician_license_number', $model, $customDefinitions) ?></div>
</div>
</div>
</div>
<div class="panel panel-default" style="margin-bottom:12px;">
<div class="panel-heading"><strong><?= Yii::t('AnimalManagementModule.base', 'Social Post') ?></strong></div>
<div class="panel-body" style="padding-bottom:8px;">
<?= $form->field($model, 'post_to_space_feed')->checkbox() ?>
<?= $form->field($model, 'post_to_animal_feed')->checkbox() ?>
</div>
</div>
<?php if (!empty($remainingDefinitions)): ?>
<div class="panel panel-default" style="margin-bottom:12px;">
<div class="panel-heading"><strong><?= Yii::t('AnimalManagementModule.base', 'Additional Details') ?></strong></div>
<div class="panel-body" style="padding-bottom:8px;">
<?php foreach ($remainingDefinitions as $fieldKey => $definition): ?>
<?= $renderCustomField($fieldKey, $model, $remainingDefinitions) ?>
<?php endforeach; ?>
</div>
</div>
<?php endif; ?>
<?= Button::save(Yii::t('AnimalManagementModule.base', 'Save Medical Visit'))->submit() ?>
<?php if ($isInline): ?>
<?= Html::button(Yii::t('AnimalManagementModule.base', 'Cancel'), [
'type' => 'button',
'class' => 'btn btn-default',
'id' => 'medical-inline-add-cancel',
]) ?>
<?php else: ?>
<?= Button::asLink(Yii::t('AnimalManagementModule.base', 'Cancel'))
->link(($returnTo ?? 'medical-visits') === 'medical-visits'
? $space->createUrl('/animal_management/animals/medical-visits', ['id' => $animal->id])
: $space->createUrl('/animal_management/animals/view', ['id' => $animal->id])) ?>
<?php endif; ?>
<?php ActiveForm::end(); ?>
</div>
</div>
<div class="modal fade" id="<?= Html::encode($medicalGalleryModalId) ?>" 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', 'Select Medical Media from Gallery') ?></h4>
</div>
<div class="modal-body">
<?php if (empty($galleryItems)): ?>
<div class="text-muted" style="margin-bottom:10px;"><?= Yii::t('AnimalManagementModule.base', 'No gallery images available yet.') ?></div>
<?php else: ?>
<div class="row" style="max-height:280px;overflow:auto;margin-bottom:10px;">
<?php foreach ($galleryItems as $galleryItem): ?>
<?php $galleryUrl = trim((string)$galleryItem->getImageUrl()); ?>
<?php if ($galleryUrl === '') { continue; } ?>
<div class="col-xs-6 col-sm-4" style="margin-bottom:8px;">
<button type="button" class="btn btn-default medical-media-select-thumb<?= $medicalMediaPath === $galleryUrl ? ' is-selected' : '' ?>" data-media-url="<?= Html::encode($galleryUrl) ?>" style="width:100%;padding:3px;">
<img src="<?= Html::encode($galleryUrl) ?>" alt="<?= Yii::t('AnimalManagementModule.base', 'Gallery image') ?>" style="width:100%;height:120px;object-fit:cover;display:block;">
</button>
</div>
<?php endforeach; ?>
</div>
<?php endif; ?>
<div class="form-group" style="margin-bottom:0;">
<label class="control-label" for="medicalMediaUpload"><?= Yii::t('AnimalManagementModule.base', 'Upload New Image') ?></label>
<input type="file" class="form-control" id="medicalMediaUpload" name="medicalMediaUpload" form="<?= Html::encode($medicalFormId) ?>" accept="image/*">
</div>
</div>
</div>
</div>
</div>
<?php
$this->registerJs(<<<JS
(function(){
function escapeCssUrl(source) {
return String(source || '').replace(/"/g, '\\"');
}
function setMedicalShellBackground(source) {
var shell = $('#medical-inline-add-shell');
if (!shell.length) {
return;
}
if (source) {
shell.addClass('has-media').css('background-image', 'url("' + escapeCssUrl(source) + '")');
} else {
shell.removeClass('has-media').css('background-image', 'none');
}
}
function renderMedicalPreview(source) {
var preview = $('#medical-media-preview');
if (!preview.length) {
return;
}
if (source) {
preview.html('<img src="' + source + '" alt="Selected medical media" style="width:100%;height:100%;object-fit:cover;">');
} else {
preview.html('<i class="fa fa-image fa-2x" style="color:#a7b0b8;"></i>');
}
setMedicalShellBackground(source);
}
function markSelectedMedicalThumb(value) {
$('.medical-media-select-thumb').removeClass('is-selected');
if (!value) {
return;
}
$('.medical-media-select-thumb').each(function() {
if ($(this).data('media-url') === value) {
$(this).addClass('is-selected');
}
});
}
$(document).on('click', '.medical-media-select-thumb', function() {
var mediaUrl = $(this).data('media-url');
$('#medical-media-gallery-path').val(mediaUrl);
markSelectedMedicalThumb(mediaUrl);
$('#medicalMediaUpload').val('');
$('input[name="removeMedicalMedia"]').prop('checked', false);
if (mediaUrl) {
renderMedicalPreview(mediaUrl);
}
$('#{$medicalGalleryModalId}').modal('hide');
});
$('#medicalMediaUpload').on('change', function() {
var file = this.files && this.files[0] ? this.files[0] : null;
if (!file) {
return;
}
$('#medical-media-gallery-path').val('');
markSelectedMedicalThumb('');
$('input[name="removeMedicalMedia"]').prop('checked', false);
var reader = new FileReader();
reader.onload = function(e) {
renderMedicalPreview(e.target.result);
$('#{$medicalGalleryModalId}').modal('hide');
};
reader.readAsDataURL(file);
});
$('#{$medicalGalleryModalId}').on('shown.bs.modal', function() {
markSelectedMedicalThumb($('#medical-media-gallery-path').val());
});
})();
JS
, \yii\web\View::POS_END);
if ($isInline) {
$cancelPayload = Json::htmlEncode([
'source' => 'animal-inline-editor',
'type' => 'cancel',
'collapseId' => 'medical-add-inline',
]);
$this->registerJs(<<<JS
$(document).on('click', '#medical-inline-add-cancel, #medical-inline-add-cancel-icon', function() {
if (window.parent && window.parent !== window) {
window.parent.postMessage($cancelPayload, '*');
}
});
JS
, \yii\web\View::POS_END);
}
?>

View File

@@ -0,0 +1,445 @@
<?php
use humhub\modules\animal_management\models\Animal;
use humhub\modules\animal_management\models\AnimalGalleryItem;
use humhub\modules\animal_management\models\forms\AnimalProgressUpdateForm;
use humhub\modules\space\models\Space;
use humhub\widgets\Button;
use yii\bootstrap\ActiveForm;
use yii\helpers\Html;
use yii\helpers\Json;
/* @var Space $space */
/* @var Animal $animal */
/* @var AnimalProgressUpdateForm $model */
/* @var string $returnTo */
/* @var AnimalGalleryItem[] $galleryItems */
/* @var bool $isInline */
$isInline = isset($isInline) ? (bool)$isInline : false;
$renderCustomField = static function (string $fieldKey, AnimalProgressUpdateForm $formModel, array $definitions): string {
if (!isset($definitions[$fieldKey])) {
return '';
}
$definition = $definitions[$fieldKey];
$inputType = (string)$definition['input_type'];
$label = (string)$definition['label'];
if ((int)$definition['required'] === 1) {
$label .= ' *';
}
$fieldName = "AnimalProgressUpdateForm[customFields][$fieldKey]";
$fieldValue = $formModel->customFields[$fieldKey] ?? '';
ob_start();
?>
<?php if ($inputType === 'textarea'): ?>
<div class="form-group">
<label class="control-label" for="animalprogressupdateform-customfields-<?= Html::encode($fieldKey) ?>"><?= Html::encode($label) ?></label>
<?= Html::textarea($fieldName, (string)$fieldValue, ['class' => 'form-control', 'rows' => 3, 'id' => "animalprogressupdateform-customfields-$fieldKey"]) ?>
</div>
<?php elseif ($inputType === 'boolean'): ?>
<div class="checkbox" style="margin-bottom:10px;">
<label>
<?= Html::hiddenInput($fieldName, '0') ?>
<?= Html::checkbox($fieldName, !empty($fieldValue), ['value' => '1']) ?>
<?= Html::encode($label) ?>
</label>
</div>
<?php elseif ($inputType === 'select'): ?>
<div class="form-group">
<label class="control-label" for="animalprogressupdateform-customfields-<?= Html::encode($fieldKey) ?>"><?= Html::encode($label) ?></label>
<?= Html::dropDownList(
$fieldName,
(string)$fieldValue,
$formModel->getCustomFieldSelectOptions($fieldKey),
['class' => 'form-control', 'prompt' => Yii::t('AnimalManagementModule.base', 'Select...'), 'id' => "animalprogressupdateform-customfields-$fieldKey"]
) ?>
</div>
<?php elseif ($inputType === 'number'): ?>
<div class="form-group">
<label class="control-label" for="animalprogressupdateform-customfields-<?= Html::encode($fieldKey) ?>"><?= Html::encode($label) ?></label>
<?= Html::input('number', $fieldName, (string)$fieldValue, ['class' => 'form-control', 'step' => 'any', 'id' => "animalprogressupdateform-customfields-$fieldKey"]) ?>
</div>
<?php elseif ($inputType === 'date'): ?>
<div class="form-group">
<label class="control-label" for="animalprogressupdateform-customfields-<?= Html::encode($fieldKey) ?>"><?= Html::encode($label) ?></label>
<?= Html::input('date', $fieldName, (string)$fieldValue, ['class' => 'form-control', 'id' => "animalprogressupdateform-customfields-$fieldKey"]) ?>
</div>
<?php elseif ($inputType === 'datetime'): ?>
<div class="form-group">
<label class="control-label" for="animalprogressupdateform-customfields-<?= Html::encode($fieldKey) ?>"><?= Html::encode($label) ?></label>
<?= Html::input('datetime-local', $fieldName, (string)$fieldValue, ['class' => 'form-control', 'id' => "animalprogressupdateform-customfields-$fieldKey"]) ?>
</div>
<?php else: ?>
<div class="form-group">
<label class="control-label" for="animalprogressupdateform-customfields-<?= Html::encode($fieldKey) ?>"><?= Html::encode($label) ?></label>
<?= Html::textInput($fieldName, (string)$fieldValue, ['class' => 'form-control', 'id' => "animalprogressupdateform-customfields-$fieldKey"]) ?>
</div>
<?php endif; ?>
<?php
return (string)ob_get_clean();
};
$customDefinitions = $model->getCustomFieldDefinitions();
$knownProgressKeys = ['progress_notes', 'routine_updates', 'media_reference'];
$otherCustomDefinitions = [];
foreach ($customDefinitions as $fieldKey => $definition) {
if (in_array($fieldKey, $knownProgressKeys, true)) {
continue;
}
$otherCustomDefinitions[$fieldKey] = $definition;
}
$currentMediaReference = trim((string)($model->customFields['media_reference'] ?? ''));
$progressFormId = 'add-progress-update-inline-form';
$this->registerCss(<<<CSS
.inline-add-shell.panel {
position: relative;
overflow: hidden;
border: 1px solid rgba(255, 255, 255, 0.22);
border-radius: 12px;
background: rgba(10, 18, 28, 0.36);
background-size: cover;
background-position: center;
}
.inline-add-shell.panel.has-media::before {
content: '';
position: absolute;
inset: 0;
background: rgba(10, 18, 28, 0.22);
pointer-events: none;
}
.inline-add-shell > .panel-body {
position: relative;
z-index: 1;
background: rgba(10, 18, 28, 0.2);
}
.inline-add-shell .panel.panel-default {
border-color: rgba(255, 255, 255, 0.2);
background: rgba(10, 18, 28, 0.34);
}
.inline-add-shell .panel.panel-default > .panel-heading {
color: #eef5fb;
background: rgba(10, 18, 28, 0.42);
border-color: rgba(255, 255, 255, 0.2);
}
.inline-add-shell,
.inline-add-shell .panel-body,
.inline-add-shell .control-label,
.inline-add-shell .checkbox label,
.inline-add-shell .radio label,
.inline-add-shell .help-block {
color: #eef5fb;
}
.inline-add-shell .text-muted {
color: rgba(233, 242, 250, 0.78) !important;
}
.inline-add-shell .form-control {
background: rgba(10, 18, 28, 0.56);
border-color: rgba(255, 255, 255, 0.44);
color: #f3f8ff;
}
.inline-add-shell .form-control::placeholder {
color: rgba(243, 248, 255, 0.72);
}
.inline-add-shell .form-control[readonly],
.inline-add-shell .form-control[disabled] {
background: rgba(10, 18, 28, 0.42);
color: rgba(243, 248, 255, 0.72);
}
.inline-add-shell select.form-control option {
color: #0f1b2a;
}
CSS
);
if ($isInline) {
$this->registerCss(<<<CSS
html, body {
margin: 0 !important;
padding: 0 !important;
background: transparent !important;
}
body > .panel:first-child {
margin-top: 0 !important;
}
CSS
);
}
?>
<div class="panel panel-default inline-add-shell<?= $currentMediaReference !== '' ? ' has-media' : '' ?>" id="progress-inline-add-shell"<?= $currentMediaReference !== '' ? ' style="background-image:url(' . Html::encode($currentMediaReference) . ');"' : '' ?>>
<div class="panel-body">
<?php
$formOptions = ['id' => $progressFormId, 'enctype' => 'multipart/form-data'];
if (!$isInline) {
$formOptions['target'] = '_top';
}
$form = ActiveForm::begin(['options' => $formOptions]);
?>
<?= Html::hiddenInput('returnTo', (string)($returnTo ?? 'progress-updates')) ?>
<?php if ($isInline): ?>
<div style="display:flex;justify-content:flex-end;gap:8px;margin-bottom:10px;">
<?= Html::submitButton('<i class="fa fa-check"></i>', [
'class' => 'btn btn-default btn-sm',
'title' => Yii::t('AnimalManagementModule.base', 'Save Progress Update'),
'form' => $progressFormId,
]) ?>
<?= Html::button('<i class="fa fa-times"></i>', [
'type' => 'button',
'class' => 'btn btn-default btn-sm',
'id' => 'progress-inline-add-cancel-icon',
'title' => Yii::t('AnimalManagementModule.base', 'Cancel'),
]) ?>
</div>
<?php endif; ?>
<?= $form->errorSummary($model, ['showAllErrors' => true]) ?>
<div class="panel panel-default" style="margin-bottom:12px;">
<div class="panel-heading"><strong><?= Yii::t('AnimalManagementModule.base', 'Media') ?></strong></div>
<div class="panel-body" style="padding-bottom:8px;">
<input type="hidden" id="progress-media-gallery-path" name="progressMediaGalleryPath" value="<?= Html::encode($currentMediaReference) ?>">
<div class="row">
<div class="col-sm-4" style="margin-bottom:8px;">
<div id="progress-media-preview" style="border-radius:8px;overflow:hidden;background:#f2f4f6;height:150px;display:flex;align-items:center;justify-content:center;">
<?php if ($currentMediaReference !== '' && (preg_match('/^https?:\/\//i', $currentMediaReference) || substr($currentMediaReference, 0, 1) === '/')): ?>
<img src="<?= Html::encode($currentMediaReference) ?>" alt="<?= Yii::t('AnimalManagementModule.base', 'Selected media') ?>" style="width:100%;height:100%;object-fit:cover;">
<?php else: ?>
<i class="fa fa-image fa-2x" style="color:#a7b0b8;"></i>
<?php endif; ?>
</div>
</div>
<div class="col-sm-8">
<button type="button" class="btn btn-default" data-toggle="modal" data-target="#progress-media-modal" style="margin-bottom:8px;">
<i class="fa fa-photo"></i> <?= Yii::t('AnimalManagementModule.base', 'Choose from Gallery or Upload') ?>
</button>
<div class="checkbox" style="margin-top:0;">
<label>
<input type="checkbox" name="removeProgressMedia" value="1">
<?= Yii::t('AnimalManagementModule.base', 'Remove selected media') ?>
</label>
</div>
</div>
</div>
</div>
</div>
<div class="panel panel-default" style="margin-bottom:12px;">
<div class="panel-heading"><strong><?= Yii::t('AnimalManagementModule.base', 'Progress Update') ?></strong></div>
<div class="panel-body" style="padding-bottom:8px;">
<div class="row">
<div class="col-sm-4"><?= $form->field($model, 'weight') ?></div>
<div class="col-sm-8"><?= $form->field($model, 'vitals')->textInput(['maxlength' => 255]) ?></div>
<div class="col-sm-6"><?= $form->field($model, 'behavior_notes')->textarea(['rows' => 2]) ?></div>
<div class="col-sm-6"><?= $form->field($model, 'medical_concerns')->textarea(['rows' => 2]) ?></div>
<div class="col-sm-6"><?= $form->field($model, 'meal_plan_changes')->textarea(['rows' => 2]) ?></div>
<div class="col-sm-6"><?= $form->field($model, 'housing_changes')->textarea(['rows' => 2]) ?></div>
</div>
</div>
</div>
<div class="panel panel-default" style="margin-bottom:12px;">
<div class="panel-heading"><strong><?= Yii::t('AnimalManagementModule.base', 'Notes') ?></strong></div>
<div class="panel-body" style="padding-bottom:8px;">
<?= $renderCustomField('progress_notes', $model, $customDefinitions) ?>
<?= $renderCustomField('routine_updates', $model, $customDefinitions) ?>
</div>
</div>
<?php if (!empty($otherCustomDefinitions)): ?>
<div class="panel panel-default" style="margin-bottom:12px;">
<div class="panel-heading"><strong><?= Yii::t('AnimalManagementModule.base', 'Additional Details') ?></strong></div>
<div class="panel-body" style="padding-bottom:8px;">
<?php foreach ($otherCustomDefinitions as $fieldKey => $definition): ?>
<?= $renderCustomField($fieldKey, $model, $otherCustomDefinitions) ?>
<?php endforeach; ?>
</div>
</div>
<?php endif; ?>
<div class="panel panel-default" style="margin-bottom:12px;">
<div class="panel-heading"><strong><?= Yii::t('AnimalManagementModule.base', 'Social Post') ?></strong></div>
<div class="panel-body" style="padding-bottom:8px;">
<?= $form->field($model, 'post_to_space_feed')->checkbox() ?>
<?= $form->field($model, 'post_to_animal_feed')->checkbox() ?>
</div>
</div>
<div class="modal fade" id="progress-media-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', 'Select Progress Media') ?></h4>
</div>
<div class="modal-body">
<?php if (empty($galleryItems)): ?>
<div class="text-muted" style="margin-bottom:10px;"><?= Yii::t('AnimalManagementModule.base', 'No gallery images available yet.') ?></div>
<?php else: ?>
<div class="row" style="max-height:280px;overflow:auto;margin-bottom:10px;">
<?php foreach ($galleryItems as $galleryItem): ?>
<?php $galleryUrl = trim((string)$galleryItem->getImageUrl()); ?>
<?php if ($galleryUrl === '') { continue; } ?>
<div class="col-xs-6 col-sm-4" style="margin-bottom:8px;">
<button type="button" class="btn btn-default progress-media-select-thumb<?= $currentMediaReference === $galleryUrl ? ' is-selected' : '' ?>" data-media-url="<?= Html::encode($galleryUrl) ?>" style="width:100%;padding:3px;">
<img src="<?= Html::encode($galleryUrl) ?>" alt="<?= Yii::t('AnimalManagementModule.base', 'Gallery image') ?>" style="width:100%;height:120px;object-fit:cover;border-radius:4px;">
</button>
</div>
<?php endforeach; ?>
</div>
<?php endif; ?>
<div class="form-group" style="margin-bottom:0;">
<label class="control-label" for="progressMediaUpload"><?= Yii::t('AnimalManagementModule.base', 'Upload New Image') ?></label>
<input type="file" class="form-control" id="progressMediaUpload" name="progressMediaUpload" accept="image/*">
</div>
</div>
</div>
</div>
</div>
<?= Button::save(Yii::t('AnimalManagementModule.base', 'Save Progress Update'))->submit() ?>
<?php if ($isInline): ?>
<?= Html::button(Yii::t('AnimalManagementModule.base', 'Cancel'), [
'type' => 'button',
'class' => 'btn btn-default',
'id' => 'progress-inline-add-cancel',
]) ?>
<?php else: ?>
<?= Button::asLink(Yii::t('AnimalManagementModule.base', 'Cancel'))
->link(($returnTo ?? 'progress-updates') === 'progress-updates'
? $space->createUrl('/animal_management/animals/progress-updates', ['id' => $animal->id])
: $space->createUrl('/animal_management/animals/view', ['id' => $animal->id])) ?>
<?php endif; ?>
<?php ActiveForm::end(); ?>
</div>
</div>
<?php
$this->registerCss(<<<CSS
.progress-media-select-thumb.is-selected {
border-color: #2f7df4 !important;
box-shadow: 0 0 0 2px rgba(47, 125, 244, 0.22);
}
CSS
);
$this->registerJs(<<<JS
(function() {
function escapeCssUrl(source) {
return String(source || '').replace(/"/g, '\\"');
}
function setProgressShellBackground(source) {
var shell = $('#progress-inline-add-shell');
if (!shell.length) {
return;
}
if (source) {
shell.addClass('has-media').css('background-image', 'url("' + escapeCssUrl(source) + '")');
} else {
shell.removeClass('has-media').css('background-image', 'none');
}
}
function renderProgressPreview(source) {
var preview = $('#progress-media-preview');
if (!preview.length) {
return;
}
if (source) {
preview.html('<img src="' + source + '" alt="Selected media" style="width:100%;height:100%;object-fit:cover;">');
} else {
preview.html('<i class="fa fa-image fa-2x" style="color:#a7b0b8;"></i>');
}
setProgressShellBackground(source);
}
function markSelectedMediaThumb(value) {
$('.progress-media-select-thumb').removeClass('is-selected');
if (!value) {
return;
}
$('.progress-media-select-thumb').each(function() {
if (($(this).attr('data-media-url') || '') === value) {
$(this).addClass('is-selected');
}
});
}
$(document).off('click.addProgressMediaSelect', '.progress-media-select-thumb').on('click.addProgressMediaSelect', '.progress-media-select-thumb', function() {
var mediaUrl = $(this).attr('data-media-url') || '';
$('#progress-media-gallery-path').val(mediaUrl);
markSelectedMediaThumb(mediaUrl);
$('#progressMediaUpload').val('');
$('input[name="removeProgressMedia"]').prop('checked', false);
if (mediaUrl) {
renderProgressPreview(mediaUrl);
}
$('#progress-media-modal').modal('hide');
});
$(document).off('change.addProgressMediaUpload', '#progressMediaUpload').on('change.addProgressMediaUpload', '#progressMediaUpload', function() {
var file = this.files && this.files[0] ? this.files[0] : null;
if (!file) {
return;
}
$('#progress-media-gallery-path').val('');
markSelectedMediaThumb('');
$('input[name="removeProgressMedia"]').prop('checked', false);
var reader = new FileReader();
reader.onload = function(e) {
renderProgressPreview(e.target.result);
$('#progress-media-modal').modal('hide');
};
reader.readAsDataURL(file);
});
$(document).off('shown.bs.modal.addProgressMediaModal', '#progress-media-modal').on('shown.bs.modal.addProgressMediaModal', '#progress-media-modal', function() {
markSelectedMediaThumb($('#progress-media-gallery-path').val());
});
})();
JS
, \yii\web\View::POS_END);
if ($isInline) {
$cancelPayload = Json::htmlEncode([
'source' => 'animal-inline-editor',
'type' => 'cancel',
'collapseId' => 'progress-add-inline',
]);
$this->registerJs(<<<JS
$(document).on('click', '#progress-inline-add-cancel, #progress-inline-add-cancel-icon', function() {
if (window.parent && window.parent !== window) {
window.parent.postMessage($cancelPayload, '*');
}
});
JS
, \yii\web\View::POS_END);
}
?>

534
views/animals/create.php Normal file
View File

@@ -0,0 +1,534 @@
<?php
use humhub\modules\animal_management\models\forms\AnimalForm;
use humhub\modules\animal_management\models\forms\DisplaySettingsForm;
use humhub\modules\animal_management\models\Animal;
use humhub\modules\rescue_foundation\components\UploadStandards;
use humhub\modules\space\models\Space;
use humhub\widgets\Button;
use yii\bootstrap\ActiveForm;
use yii\helpers\Html;
/* @var AnimalForm $model */
/* @var Space $space */
/* @var bool $isEdit */
/* @var Animal|null $animal */
?>
<div class="panel panel-default">
<div class="panel-heading">
<?php if (!empty($isEdit)): ?>
<?= Yii::t('AnimalManagementModule.base', '<strong>Edit Animal</strong> Profile') ?>
<?php else: ?>
<?= Yii::t('AnimalManagementModule.base', '<strong>New Animal</strong> Intake') ?>
<?php endif; ?>
</div>
<div class="panel-body">
<?php $form = ActiveForm::begin(['options' => ['enctype' => 'multipart/form-data']]); ?>
<?= $form->errorSummary($model, ['showAllErrors' => true]) ?>
<?php
$customDefinitions = $model->getCustomFieldDefinitions();
$galleryImageOptions = $model->getGalleryImageOptions();
$galleryImageUrls = array_keys($galleryImageOptions);
$knownProfileFieldKeys = [
'dob',
'age',
'rescue',
'lineage',
'backstory',
'previous_owner_user_id',
'previous_owner_name',
'previous_owner_business_name',
'previous_owner_street_address',
'previous_owner_city',
'previous_owner_state',
'previous_owner_zip',
'previous_owner_cell_phone',
'previous_owner_business_phone',
'previous_owner_email',
];
$renderCustomField = static function (string $fieldKey, AnimalForm $model, array $definitions): string {
if (!isset($definitions[$fieldKey])) {
return '';
}
$definition = $definitions[$fieldKey];
$inputType = (string)$definition['input_type'];
$label = (string)$definition['label'];
if ((int)$definition['required'] === 1) {
$label .= ' *';
}
$fieldName = "AnimalForm[customFields][$fieldKey]";
$fieldValue = $model->customFields[$fieldKey] ?? '';
ob_start();
?>
<?php if ($inputType === 'textarea'): ?>
<div class="form-group">
<label class="control-label" for="animalform-customfields-<?= Html::encode($fieldKey) ?>"><?= Html::encode($label) ?></label>
<?= Html::textarea($fieldName, (string)$fieldValue, ['class' => 'form-control', 'rows' => 3, 'id' => "animalform-customfields-$fieldKey"]) ?>
</div>
<?php elseif ($inputType === 'boolean'): ?>
<div class="checkbox" style="margin-bottom:10px;">
<label>
<?= Html::hiddenInput($fieldName, '0') ?>
<?= Html::checkbox($fieldName, !empty($fieldValue), ['value' => '1']) ?>
<?= Html::encode($label) ?>
</label>
</div>
<?php elseif ($inputType === 'select'): ?>
<div class="form-group">
<label class="control-label" for="animalform-customfields-<?= Html::encode($fieldKey) ?>"><?= Html::encode($label) ?></label>
<?= Html::dropDownList(
$fieldName,
(string)$fieldValue,
$model->getCustomFieldSelectOptions($fieldKey),
['class' => 'form-control', 'prompt' => Yii::t('AnimalManagementModule.base', 'Select...'), 'id' => "animalform-customfields-$fieldKey"]
) ?>
</div>
<?php elseif ($inputType === 'number'): ?>
<div class="form-group">
<label class="control-label" for="animalform-customfields-<?= Html::encode($fieldKey) ?>"><?= Html::encode($label) ?></label>
<?= Html::input('number', $fieldName, (string)$fieldValue, ['class' => 'form-control', 'step' => 'any', 'id' => "animalform-customfields-$fieldKey"]) ?>
</div>
<?php elseif ($inputType === 'date'): ?>
<div class="form-group">
<label class="control-label" for="animalform-customfields-<?= Html::encode($fieldKey) ?>"><?= Html::encode($label) ?></label>
<?= Html::input('date', $fieldName, (string)$fieldValue, ['class' => 'form-control', 'id' => "animalform-customfields-$fieldKey"]) ?>
</div>
<?php elseif ($inputType === 'datetime'): ?>
<div class="form-group">
<label class="control-label" for="animalform-customfields-<?= Html::encode($fieldKey) ?>"><?= Html::encode($label) ?></label>
<?= Html::input('datetime-local', $fieldName, (string)$fieldValue, ['class' => 'form-control', 'id' => "animalform-customfields-$fieldKey"]) ?>
</div>
<?php else: ?>
<div class="form-group">
<label class="control-label" for="animalform-customfields-<?= Html::encode($fieldKey) ?>"><?= Html::encode($label) ?></label>
<?= Html::textInput($fieldName, (string)$fieldValue, ['class' => 'form-control', 'id' => "animalform-customfields-$fieldKey"]) ?>
</div>
<?php endif; ?>
<?php
return (string)ob_get_clean();
};
?>
<div class="panel panel-default" style="margin-bottom:14px;">
<div class="panel-heading"><strong><?= Yii::t('AnimalManagementModule.base', 'Profile & Cover Images') ?></strong></div>
<div class="panel-body" style="padding-bottom:8px;">
<div class="help-block" style="margin-top:0;">
<?= Yii::t('AnimalManagementModule.base', 'Tap the edit icon on either image to choose from gallery thumbnails or upload from device camera roll.') ?>
</div>
<div class="row">
<div class="col-sm-6" style="margin-bottom:10px;">
<div id="animal-cover-preview" style="position:relative;border-radius:10px;overflow:hidden;background:#eef1f4;min-height:180px;">
<?php if ($model->getExistingCoverImagePath()): ?>
<img src="<?= Html::encode($model->getExistingCoverImagePath()) ?>" alt="<?= Yii::t('AnimalManagementModule.base', 'Cover Image') ?>" style="width:100%;height:220px;object-fit:cover;display:block;">
<?php else: ?>
<div style="height:220px;display:flex;align-items:center;justify-content:center;color:#9ba5af;">
<i class="fa fa-image fa-3x"></i>
</div>
<?php endif; ?>
<button type="button" class="btn btn-default" data-toggle="modal" data-target="#animal-cover-image-manage-modal" style="position:absolute;top:10px;right:10px;border-radius:999px;">
<i class="fa fa-pencil"></i>
</button>
</div>
<div style="margin-top:6px;font-weight:600;"><?= Yii::t('AnimalManagementModule.base', 'Cover Image') ?></div>
</div>
<div class="col-sm-6" style="margin-bottom:10px;">
<div id="animal-profile-preview" style="position:relative;border-radius:10px;overflow:hidden;background:#eef1f4;min-height:180px;">
<?php if ($model->getExistingProfileImagePath()): ?>
<img src="<?= Html::encode($model->getExistingProfileImagePath()) ?>" alt="<?= Yii::t('AnimalManagementModule.base', 'Profile Image') ?>" style="width:100%;height:220px;object-fit:cover;display:block;">
<?php else: ?>
<div style="height:220px;display:flex;align-items:center;justify-content:center;color:#9ba5af;">
<i class="fa fa-user-circle fa-3x"></i>
</div>
<?php endif; ?>
<button type="button" class="btn btn-default" data-toggle="modal" data-target="#animal-profile-image-manage-modal" style="position:absolute;top:10px;right:10px;border-radius:999px;">
<i class="fa fa-pencil"></i>
</button>
</div>
<div style="margin-top:6px;font-weight:600;"><?= Yii::t('AnimalManagementModule.base', 'Profile Image') ?></div>
</div>
</div>
<div style="display:none;">
<?= $form->field($model, 'coverImageGalleryPath')->hiddenInput()->label(false) ?>
<?= $form->field($model, 'profileImageGalleryPath')->hiddenInput()->label(false) ?>
<?= $form->field($model, 'coverImageFile')->fileInput(['accept' => 'image/*']) ?>
<?= $form->field($model, 'profileImageFile')->fileInput(['accept' => 'image/*']) ?>
</div>
</div>
</div>
<div class="row">
<div class="col-md-6">
<div class="panel panel-default" style="margin-bottom:14px;">
<div class="panel-heading"><strong><?= Yii::t('AnimalManagementModule.base', 'Details') ?></strong></div>
<div class="panel-body" style="padding-bottom:8px;">
<div class="row">
<div class="col-sm-6"><?php if ($model->isFieldActive('name')) { echo $form->field($model, 'name')->textInput(['maxlength' => 190]); } ?></div>
<div class="col-sm-6"><?php if ($model->isFieldActive('species')) { echo $form->field($model, 'species')->textInput(['maxlength' => 120]); } ?></div>
<div class="col-sm-6"><?php if ($model->isFieldActive('breed')) { echo $form->field($model, 'breed')->textInput(['maxlength' => 120]); } ?></div>
<div class="col-sm-6"><?php if ($model->isFieldActive('sex')) { echo $form->field($model, 'sex')->textInput(['maxlength' => 32]); } ?></div>
<div class="col-sm-6"><?= $renderCustomField('dob', $model, $customDefinitions) ?></div>
<div class="col-sm-6"><?= $renderCustomField('age', $model, $customDefinitions) ?></div>
<div class="col-sm-6"><?php if ($model->isFieldActive('status')) { echo $form->field($model, 'status')->dropDownList($model->getStatusOptions()); } ?></div>
<div class="col-sm-6"><?php if ($model->isFieldActive('in_possession')) { echo $form->field($model, 'in_possession')->checkbox(); } ?></div>
</div>
</div>
</div>
<div class="panel panel-default" style="margin-bottom:14px;">
<div class="panel-heading"><strong><?= Yii::t('AnimalManagementModule.base', 'Location') ?></strong></div>
<div class="panel-body" style="padding-bottom:8px;">
<div class="row">
<div class="col-sm-6"><?= $renderCustomField('rescue', $model, $customDefinitions) ?></div>
<div class="col-sm-6"><?php if ($model->isFieldActive('location_name')) { echo $form->field($model, 'location_name')->textInput(['maxlength' => 120]); } ?></div>
<div class="col-sm-4"><?php if ($model->isFieldActive('city')) { echo $form->field($model, 'city')->textInput(['maxlength' => 120]); } ?></div>
<div class="col-sm-4"><?php if ($model->isFieldActive('state')) { echo $form->field($model, 'state')->textInput(['maxlength' => 2]); } ?></div>
<div class="col-sm-4"><?php if ($model->isFieldActive('zip')) { echo $form->field($model, 'zip')->textInput(['maxlength' => 10]); } ?></div>
</div>
</div>
</div>
<div class="panel panel-default" style="margin-bottom:14px;">
<div class="panel-heading"><strong><?= Yii::t('AnimalManagementModule.base', 'History') ?></strong></div>
<div class="panel-body" style="padding-bottom:8px;">
<?= $renderCustomField('lineage', $model, $customDefinitions) ?>
<?= $renderCustomField('backstory', $model, $customDefinitions) ?>
</div>
</div>
<div class="panel panel-default" style="margin-bottom:14px;">
<div class="panel-heading"><strong><?= Yii::t('AnimalManagementModule.base', 'Public') ?></strong></div>
<div class="panel-body" style="padding-bottom:8px;">
<?php if ($model->isFieldActive('public_summary')): ?>
<?= $form->field($model, 'public_summary')->textarea(['rows' => 4]) ?>
<?php endif; ?>
</div>
</div>
</div>
<div class="col-md-6">
<div class="panel panel-default" style="margin-bottom:14px;">
<div class="panel-heading"><strong><?= Yii::t('AnimalManagementModule.base', 'Previous Owner') ?></strong></div>
<div class="panel-body" style="padding-bottom:8px;">
<div class="row">
<div class="col-sm-6"><?= $renderCustomField('previous_owner_user_id', $model, $customDefinitions) ?></div>
<div class="col-sm-6"><?= $renderCustomField('previous_owner_name', $model, $customDefinitions) ?></div>
<div class="col-sm-12"><?= $renderCustomField('previous_owner_business_name', $model, $customDefinitions) ?></div>
<div class="col-sm-12"><?= $renderCustomField('previous_owner_street_address', $model, $customDefinitions) ?></div>
<div class="col-sm-4"><?= $renderCustomField('previous_owner_city', $model, $customDefinitions) ?></div>
<div class="col-sm-4"><?= $renderCustomField('previous_owner_state', $model, $customDefinitions) ?></div>
<div class="col-sm-4"><?= $renderCustomField('previous_owner_zip', $model, $customDefinitions) ?></div>
<div class="col-sm-6"><?= $renderCustomField('previous_owner_cell_phone', $model, $customDefinitions) ?></div>
<div class="col-sm-6"><?= $renderCustomField('previous_owner_business_phone', $model, $customDefinitions) ?></div>
<div class="col-sm-12"><?= $renderCustomField('previous_owner_email', $model, $customDefinitions) ?></div>
</div>
</div>
</div>
<div class="panel panel-default" style="margin-bottom:14px;">
<div class="panel-heading"><strong><?= Yii::t('AnimalManagementModule.base', 'Display Field Overrides') ?></strong></div>
<div class="panel-body" style="padding-bottom:8px;">
<div class="row">
<div class="col-sm-6">
<label class="control-label" style="display:block;"><?= Yii::t('AnimalManagementModule.base', 'Tile Fields') ?></label>
<?= Html::checkboxList('AnimalForm[tileDisplayFields]', $model->tileDisplayFields, DisplaySettingsForm::fieldOptions(), ['separator' => '<br>']) ?>
</div>
<div class="col-sm-6">
<label class="control-label" style="display:block;"><?= Yii::t('AnimalManagementModule.base', 'Hero Fields') ?></label>
<?= Html::checkboxList('AnimalForm[heroDisplayFields]', $model->heroDisplayFields, DisplaySettingsForm::fieldOptions(), ['separator' => '<br>']) ?>
</div>
</div>
</div>
</div>
<?php
$remainingCustomFields = [];
foreach ($customDefinitions as $fieldKey => $definition) {
if (in_array($fieldKey, $knownProfileFieldKeys, true)) {
continue;
}
$remainingCustomFields[$fieldKey] = $definition;
}
?>
<?php if (!empty($remainingCustomFields)): ?>
<div class="panel panel-default" style="margin-bottom:14px;">
<div class="panel-heading"><strong><?= Yii::t('AnimalManagementModule.base', 'Custom Fields') ?></strong></div>
<div class="panel-body" style="padding-bottom:8px;">
<?php foreach ($remainingCustomFields as $fieldKey => $definition): ?>
<?= $renderCustomField($fieldKey, $model, $remainingCustomFields) ?>
<?php endforeach; ?>
</div>
</div>
<?php endif; ?>
</div>
</div>
<?= Button::save(!empty($isEdit)
? Yii::t('AnimalManagementModule.base', 'Save Changes')
: Yii::t('AnimalManagementModule.base', 'Create Animal'))->submit() ?>
<?= Button::asLink(Yii::t('AnimalManagementModule.base', 'Cancel'))->link(
!empty($isEdit) && $animal instanceof Animal
? $space->createUrl('/animal_management/animals/view', ['id' => $animal->id])
: $space->createUrl('/animal_management/animals/index')
) ?>
<?php if (!empty($isEdit) && $animal instanceof Animal): ?>
<?= Html::a(
Yii::t('AnimalManagementModule.base', 'Delete Animal'),
$space->createUrl('/animal_management/animals/delete', ['id' => $animal->id]),
[
'class' => 'btn btn-danger pull-right',
'style' => 'margin-left:8px;',
'data-method' => 'post',
'data-confirm' => Yii::t('AnimalManagementModule.base', 'Delete {name}? This will remove the animal record and local gallery uploads. This cannot be undone.', ['name' => $animal->getDisplayName()]),
]
) ?>
<?php endif; ?>
<div class="modal fade" id="animal-cover-image-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', 'Manage Cover Image') ?></h4>
</div>
<div class="modal-body">
<div class="help-block"><?= Yii::t('AnimalManagementModule.base', 'Select an existing gallery image or upload a new cover image.') ?></div>
<div class="alert alert-info" style="padding:8px 10px;margin-bottom:10px;">
<?= Yii::t('AnimalManagementModule.base', 'Your image preview updates immediately. Final save happens when you click {action} at the bottom of this intake form.', [
'action' => !empty($isEdit)
? Yii::t('AnimalManagementModule.base', 'Save Changes')
: Yii::t('AnimalManagementModule.base', 'Create Animal'),
]) ?>
</div>
<?php if (!empty($galleryImageUrls)): ?>
<div class="row" style="max-height:260px;overflow:auto;margin-bottom:8px;">
<?php foreach ($galleryImageUrls as $galleryUrl): ?>
<div class="col-xs-6 col-sm-4" style="margin-bottom:8px;">
<button type="button" class="btn btn-default animal-image-select-thumb" data-select-kind="cover" data-select-target="#animalform-coverimagegallerypath" data-select-value="<?= Html::encode($galleryUrl) ?>" style="padding:3px;width:100%;">
<img src="<?= Html::encode($galleryUrl) ?>" alt="<?= Yii::t('AnimalManagementModule.base', 'Cover option') ?>" style="width:100%;height:120px;object-fit:cover;border-radius:4px;">
</button>
</div>
<?php endforeach; ?>
</div>
<?php else: ?>
<div class="text-muted" style="margin-bottom:8px;"><?= Yii::t('AnimalManagementModule.base', 'No gallery images available yet.') ?></div>
<?php endif; ?>
<div class="form-group">
<button type="button" class="btn btn-default" id="animal-cover-upload-trigger">
<i class="fa fa-upload"></i> <?= Yii::t('AnimalManagementModule.base', 'Upload New Cover Image') ?>
</button>
<div id="animal-cover-upload-file-name" class="help-block" style="margin-bottom:0;"></div>
</div>
<?= $form->field($model, 'removeCoverImage')->checkbox() ?>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-default" data-dismiss="modal"><?= Yii::t('AnimalManagementModule.base', 'Done') ?></button>
</div>
</div>
</div>
</div>
<div class="modal fade" id="animal-profile-image-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', 'Manage Profile Image') ?></h4>
</div>
<div class="modal-body">
<div class="help-block"><?= Yii::t('AnimalManagementModule.base', 'Select an existing gallery image or upload a new profile image.') ?></div>
<div class="alert alert-info" style="padding:8px 10px;margin-bottom:10px;">
<?= Yii::t('AnimalManagementModule.base', 'Your image preview updates immediately. Final save happens when you click {action} at the bottom of this intake form.', [
'action' => !empty($isEdit)
? Yii::t('AnimalManagementModule.base', 'Save Changes')
: Yii::t('AnimalManagementModule.base', 'Create Animal'),
]) ?>
</div>
<?php if (!empty($galleryImageUrls)): ?>
<div class="row" style="max-height:260px;overflow:auto;margin-bottom:8px;">
<?php foreach ($galleryImageUrls as $galleryUrl): ?>
<div class="col-xs-6 col-sm-4" style="margin-bottom:8px;">
<button type="button" class="btn btn-default animal-image-select-thumb" data-select-kind="profile" data-select-target="#animalform-profileimagegallerypath" data-select-value="<?= Html::encode($galleryUrl) ?>" style="padding:3px;width:100%;">
<img src="<?= Html::encode($galleryUrl) ?>" alt="<?= Yii::t('AnimalManagementModule.base', 'Profile option') ?>" style="width:100%;height:120px;object-fit:cover;border-radius:4px;">
</button>
</div>
<?php endforeach; ?>
</div>
<?php else: ?>
<div class="text-muted" style="margin-bottom:8px;"><?= Yii::t('AnimalManagementModule.base', 'No gallery images available yet.') ?></div>
<?php endif; ?>
<div class="form-group">
<button type="button" class="btn btn-default" id="animal-profile-upload-trigger">
<i class="fa fa-upload"></i> <?= Yii::t('AnimalManagementModule.base', 'Upload New Profile Image') ?>
</button>
<div id="animal-profile-upload-file-name" class="help-block" style="margin-bottom:0;"></div>
</div>
<?= $form->field($model, 'removeProfileImage')->checkbox() ?>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-default" data-dismiss="modal"><?= Yii::t('AnimalManagementModule.base', 'Done') ?></button>
</div>
</div>
</div>
</div>
<?php ActiveForm::end(); ?>
</div>
</div>
<?php
$this->registerCss(<<<CSS
.animal-image-select-thumb.is-selected {
border-color: #2f7df4 !important;
box-shadow: 0 0 0 2px rgba(47, 125, 244, 0.22);
}
CSS
);
$this->registerJs(<<<JS
(function() {
function renderPlaceholder(kind) {
if (kind === 'profile') {
return '<div style="height:220px;display:flex;align-items:center;justify-content:center;color:#9ba5af;"><i class="fa fa-user-circle fa-3x"></i></div>';
}
return '<div style="height:220px;display:flex;align-items:center;justify-content:center;color:#9ba5af;"><i class="fa fa-image fa-3x"></i></div>';
}
function updatePreview(kind, value) {
var previewId = kind === 'profile' ? '#animal-profile-preview' : '#animal-cover-preview';
if (value && (value.indexOf('/') === 0 || /^https?:\/\//i.test(value) || /^data:image\//i.test(value))) {
$(previewId).html('<img src="' + value + '" alt="' + kind + ' image" style="width:100%;height:220px;object-fit:cover;display:block;">' +
'<button type="button" class="btn btn-default" data-toggle="modal" data-target="' + (kind === 'profile' ? '#animal-profile-image-manage-modal' : '#animal-cover-image-manage-modal') + '" style="position:absolute;top:10px;right:10px;border-radius:999px;"><i class="fa fa-pencil"></i></button>');
return;
}
$(previewId).html(renderPlaceholder(kind) +
'<button type="button" class="btn btn-default" data-toggle="modal" data-target="' + (kind === 'profile' ? '#animal-profile-image-manage-modal' : '#animal-cover-image-manage-modal') + '" style="position:absolute;top:10px;right:10px;border-radius:999px;"><i class="fa fa-pencil"></i></button>');
}
function markSelectedThumb(kind, value) {
var modalId = kind === 'profile' ? '#animal-profile-image-manage-modal' : '#animal-cover-image-manage-modal';
$(modalId + ' .animal-image-select-thumb').removeClass('is-selected');
if (!value) {
return;
}
$(modalId + ' .animal-image-select-thumb').each(function() {
if ($(this).data('select-value') === value) {
$(this).addClass('is-selected');
}
});
}
$(document).on('click', '.animal-image-select-thumb', function() {
var kind = $(this).data('select-kind') || 'cover';
var target = $(this).data('select-target');
var value = $(this).data('select-value');
if (target && value !== undefined) {
$(target).val(value);
if (kind === 'profile') {
$('#animalform-profileimagefile').val('');
$('#animalform-removeprofileimage').prop('checked', false);
} else {
$('#animalform-coverimagefile').val('');
$('#animalform-removecoverimage').prop('checked', false);
}
updatePreview(kind, value);
markSelectedThumb(kind, value);
if (kind === 'profile') {
$('#animal-profile-upload-file-name').text('');
} else {
$('#animal-cover-upload-file-name').text('');
}
}
$(this).closest('.modal').modal('hide');
});
$('#animal-cover-upload-trigger').on('click', function() {
$('#animalform-coverimagefile').trigger('click');
});
$('#animal-profile-upload-trigger').on('click', function() {
$('#animalform-profileimagefile').trigger('click');
});
$('#animalform-coverimagefile').on('change', function() {
var file = this.files && this.files[0] ? this.files[0] : null;
$('#animalform-coverimagegallerypath').val('');
$('#animalform-removecoverimage').prop('checked', false);
if (!file) {
return;
}
var reader = new FileReader();
reader.onload = function(e) {
updatePreview('cover', e.target.result);
markSelectedThumb('cover', '');
};
reader.readAsDataURL(file);
$('#animal-cover-upload-file-name').text(file.name || '');
$('#animal-cover-image-manage-modal').modal('hide');
});
$('#animalform-profileimagefile').on('change', function() {
var file = this.files && this.files[0] ? this.files[0] : null;
$('#animalform-profileimagegallerypath').val('');
$('#animalform-removeprofileimage').prop('checked', false);
if (!file) {
return;
}
var reader = new FileReader();
reader.onload = function(e) {
updatePreview('profile', e.target.result);
markSelectedThumb('profile', '');
};
reader.readAsDataURL(file);
$('#animal-profile-upload-file-name').text(file.name || '');
$('#animal-profile-image-manage-modal').modal('hide');
});
$('#animalform-removecoverimage').on('change', function() {
if ($(this).is(':checked')) {
$('#animalform-coverimagegallerypath').val('');
$('#animalform-coverimagefile').val('');
$('#animal-cover-upload-file-name').text('');
markSelectedThumb('cover', '');
updatePreview('cover', '');
}
});
$('#animalform-removeprofileimage').on('change', function() {
if ($(this).is(':checked')) {
$('#animalform-profileimagegallerypath').val('');
$('#animalform-profileimagefile').val('');
$('#animal-profile-upload-file-name').text('');
markSelectedThumb('profile', '');
updatePreview('profile', '');
}
});
$('#animal-cover-image-manage-modal').on('shown.bs.modal', function() {
markSelectedThumb('cover', $('#animalform-coverimagegallerypath').val());
});
$('#animal-profile-image-manage-modal').on('shown.bs.modal', function() {
markSelectedThumb('profile', $('#animalform-profileimagegallerypath').val());
});
})();
JS
);
?>

View File

@@ -0,0 +1,497 @@
<?php
use humhub\modules\animal_management\models\Animal;
use humhub\modules\animal_management\models\AnimalGalleryItem;
use humhub\modules\animal_management\models\AnimalMedicalVisit;
use humhub\modules\animal_management\models\forms\AnimalMedicalVisitForm;
use humhub\modules\space\models\Space;
use humhub\widgets\Button;
use yii\bootstrap\ActiveForm;
use yii\helpers\Html;
use yii\helpers\Json;
/* @var Space $space */
/* @var Animal $animal */
/* @var AnimalMedicalVisitForm $model */
/* @var AnimalMedicalVisit $medicalVisit */
/* @var string $returnTo */
/* @var AnimalGalleryItem[] $galleryItems */
/* @var bool $isInline */
$isInline = isset($isInline) ? (bool)$isInline : false;
$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',
];
$renderCustomField = static function (string $fieldKey, AnimalMedicalVisitForm $formModel, array $definitions): string {
if (!isset($definitions[$fieldKey])) {
return '';
}
$definition = $definitions[$fieldKey];
$inputType = (string)$definition['input_type'];
$vitalLabelOverrides = [
'blood_pressure' => 'BP',
'oxygen' => 'O₂',
];
$label = (string)($vitalLabelOverrides[$fieldKey] ?? $definition['label']);
if ((int)$definition['required'] === 1) {
$label .= ' *';
}
$fieldName = "AnimalMedicalVisitForm[customFields][$fieldKey]";
$fieldValue = $formModel->customFields[$fieldKey] ?? '';
ob_start();
?>
<?php if ($inputType === 'textarea'): ?>
<div class="form-group">
<label class="control-label" for="animalmedicalvisitform-customfields-<?= Html::encode($fieldKey) ?>"><?= Html::encode($label) ?></label>
<?= Html::textarea($fieldName, (string)$fieldValue, ['class' => 'form-control', 'rows' => 3, 'id' => "animalmedicalvisitform-customfields-$fieldKey"]) ?>
</div>
<?php elseif ($inputType === 'boolean'): ?>
<div class="checkbox" style="margin-bottom:10px;">
<label>
<?= Html::hiddenInput($fieldName, '0') ?>
<?= Html::checkbox($fieldName, !empty($fieldValue), ['value' => '1']) ?>
<?= Html::encode($label) ?>
</label>
</div>
<?php elseif ($inputType === 'select'): ?>
<div class="form-group">
<label class="control-label" for="animalmedicalvisitform-customfields-<?= Html::encode($fieldKey) ?>"><?= Html::encode($label) ?></label>
<?= Html::dropDownList(
$fieldName,
(string)$fieldValue,
$formModel->getCustomFieldSelectOptions($fieldKey),
['class' => 'form-control', 'prompt' => Yii::t('AnimalManagementModule.base', 'Select...'), 'id' => "animalmedicalvisitform-customfields-$fieldKey"]
) ?>
</div>
<?php elseif ($inputType === 'number'): ?>
<div class="form-group">
<label class="control-label" for="animalmedicalvisitform-customfields-<?= Html::encode($fieldKey) ?>"><?= Html::encode($label) ?></label>
<?= Html::input('number', $fieldName, (string)$fieldValue, ['class' => 'form-control', 'step' => 'any', 'id' => "animalmedicalvisitform-customfields-$fieldKey"]) ?>
</div>
<?php elseif ($inputType === 'date'): ?>
<div class="form-group">
<label class="control-label" for="animalmedicalvisitform-customfields-<?= Html::encode($fieldKey) ?>"><?= Html::encode($label) ?></label>
<?= Html::input('date', $fieldName, (string)$fieldValue, ['class' => 'form-control', 'id' => "animalmedicalvisitform-customfields-$fieldKey"]) ?>
</div>
<?php elseif ($inputType === 'datetime'): ?>
<div class="form-group">
<label class="control-label" for="animalmedicalvisitform-customfields-<?= Html::encode($fieldKey) ?>"><?= Html::encode($label) ?></label>
<?= Html::input('datetime-local', $fieldName, (string)$fieldValue, ['class' => 'form-control', 'id' => "animalmedicalvisitform-customfields-$fieldKey"]) ?>
</div>
<?php else: ?>
<div class="form-group">
<label class="control-label" for="animalmedicalvisitform-customfields-<?= Html::encode($fieldKey) ?>"><?= Html::encode($label) ?></label>
<?= Html::textInput($fieldName, (string)$fieldValue, ['class' => 'form-control', 'id' => "animalmedicalvisitform-customfields-$fieldKey"]) ?>
</div>
<?php endif; ?>
<?php
return (string)ob_get_clean();
};
$customDefinitions = $model->getCustomFieldDefinitions();
$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',
];
$remainingDefinitions = [];
foreach ($customDefinitions as $fieldKey => $definition) {
if (in_array($fieldKey, $knownMedicalKeys, true) || in_array($fieldKey, $hiddenMedicalKeys, true)) {
continue;
}
$remainingDefinitions[$fieldKey] = $definition;
}
$medicalMediaPath = trim((string)($model->customFields['medical_media_reference'] ?? $model->customFields['media_reference'] ?? ''));
$hasMedicalMedia = $medicalMediaPath !== '' && (preg_match('/^https?:\/\//i', $medicalMediaPath) || substr($medicalMediaPath, 0, 1) === '/');
$medicalGalleryModalId = 'edit-medical-media-gallery-modal';
$medicalFormId = 'edit-medical-visit-form';
$this->registerCss(<<<CSS
.inline-editor-shell.panel {
border: 1px solid rgba(255, 255, 255, 0.22);
border-radius: 12px;
background: rgba(10, 18, 28, 0.36);
}
.inline-editor-shell > .panel-heading {
color: #eef5fb;
background: rgba(10, 18, 28, 0.42);
border-color: rgba(255, 255, 255, 0.2);
}
.inline-editor-shell > .panel-body {
background: rgba(10, 18, 28, 0.2);
}
.inline-editor-shell .panel.panel-default {
border-color: rgba(255, 255, 255, 0.2);
background: rgba(10, 18, 28, 0.34);
}
.inline-editor-shell .panel.panel-default > .panel-heading {
color: #eef5fb;
background: rgba(10, 18, 28, 0.42);
border-color: rgba(255, 255, 255, 0.2);
}
.inline-editor-shell,
.inline-editor-shell .panel-body,
.inline-editor-shell .control-label,
.inline-editor-shell .checkbox label,
.inline-editor-shell .radio label,
.inline-editor-shell .help-block {
color: #eef5fb;
}
.inline-editor-shell .text-muted {
color: rgba(233, 242, 250, 0.78) !important;
}
.inline-editor-shell .form-control {
background: rgba(10, 18, 28, 0.56);
border-color: rgba(255, 255, 255, 0.44);
color: #f3f8ff;
}
.inline-editor-shell .form-control::placeholder {
color: rgba(243, 248, 255, 0.72);
}
.inline-editor-shell .form-control[readonly],
.inline-editor-shell .form-control[disabled] {
background: rgba(10, 18, 28, 0.42);
color: rgba(243, 248, 255, 0.72);
}
.inline-editor-shell select.form-control option {
color: #0f1b2a;
}
CSS
);
if ($isInline) {
$this->registerCss(<<<CSS
html, body {
margin: 0 !important;
padding: 0 !important;
background: transparent !important;
}
body > .panel:first-child {
margin-top: 0 !important;
}
CSS
);
}
?>
<style>
.medical-media-select-thumb.is-selected {
border-color: #1f8dd6;
box-shadow: 0 0 0 2px rgba(31, 141, 214, 0.2);
}
</style>
<div class="panel panel-default inline-editor-shell">
<div class="panel-heading">
<div style="display:flex;align-items:center;justify-content:space-between;gap:12px;">
<span><?= Yii::t('AnimalManagementModule.base', '<strong>Edit</strong> Medical Visit') ?></span>
<?php if ($isInline): ?>
<span style="display:inline-flex;gap:8px;">
<?= Html::submitButton('<i class="fa fa-check"></i>', [
'class' => 'btn btn-default btn-sm',
'title' => Yii::t('AnimalManagementModule.base', 'Save Medical Visit'),
'form' => $medicalFormId,
]) ?>
<?= Html::button('<i class="fa fa-times"></i>', [
'type' => 'button',
'class' => 'btn btn-default btn-sm',
'id' => 'medical-inline-cancel-icon',
'title' => Yii::t('AnimalManagementModule.base', 'Cancel'),
]) ?>
</span>
<?php endif; ?>
</div>
</div>
<div class="panel-body">
<?php
$formOptions = ['id' => $medicalFormId, 'enctype' => 'multipart/form-data'];
if (!$isInline) {
$formOptions['target'] = '_top';
}
$form = ActiveForm::begin(['options' => $formOptions]);
?>
<?= Html::hiddenInput('returnTo', (string)($returnTo ?? 'view')) ?>
<?= Html::hiddenInput('medicalMediaGalleryPath', $medicalMediaPath, ['id' => 'medical-media-gallery-path']) ?>
<?= $form->errorSummary($model, ['showAllErrors' => true]) ?>
<div class="panel panel-default" style="margin-bottom:12px;">
<div class="panel-heading"><strong><?= Yii::t('AnimalManagementModule.base', 'Visit') ?></strong></div>
<div class="panel-body" style="padding-bottom:8px;">
<div class="row">
<div class="col-sm-6"><?= $form->field($model, 'visit_at')->input('datetime-local') ?></div>
<div class="col-sm-6"><?= $form->field($model, 'provider_name') ?></div>
</div>
<?= $form->field($model, 'notes')->textarea(['rows' => 3]) ?>
<?= $form->field($model, 'recommendations')->textarea(['rows' => 3]) ?>
</div>
</div>
<div class="panel panel-default" style="margin-bottom:12px;">
<div class="panel-heading"><strong><?= Yii::t('AnimalManagementModule.base', 'Vitals') ?></strong></div>
<div class="panel-body" style="padding-bottom:8px;">
<div class="row">
<div class="col-sm-3"><?= $renderCustomField('weight', $model, $customDefinitions) ?></div>
<div class="col-sm-3"><?= $renderCustomField('pulse', $model, $customDefinitions) ?></div>
<div class="col-sm-3"><?= $renderCustomField('blood_pressure', $model, $customDefinitions) ?></div>
<div class="col-sm-3"><?= $renderCustomField('oxygen', $model, $customDefinitions) ?></div>
</div>
</div>
</div>
<div class="panel panel-default" style="margin-bottom:12px;">
<div class="panel-heading"><strong><?= Yii::t('AnimalManagementModule.base', 'Conditions') ?></strong></div>
<div class="panel-body" style="padding-bottom:8px;">
<?= $renderCustomField('chronic_conditions', $model, $customDefinitions) ?>
<?= $renderCustomField('acute_conditions', $model, $customDefinitions) ?>
<?= $renderCustomField('special_needs', $model, $customDefinitions) ?>
</div>
</div>
<div class="panel panel-default" style="margin-bottom:12px;">
<div class="panel-heading"><strong><?= Yii::t('AnimalManagementModule.base', 'Medical Visit Detail') ?></strong></div>
<div class="panel-body" style="padding-bottom:8px;">
<?= $renderCustomField('date_of_most_recent_medical_visit', $model, $customDefinitions) ?>
</div>
</div>
<div class="panel panel-default" style="margin-bottom:12px;">
<div class="panel-heading"><strong><?= Yii::t('AnimalManagementModule.base', 'Media') ?></strong></div>
<div class="panel-body" style="padding-bottom:8px;">
<div class="row">
<div class="col-sm-4" style="margin-bottom:8px;">
<div id="medical-media-preview" style="border-radius:8px;overflow:hidden;background:#f2f4f6;height:150px;display:flex;align-items:center;justify-content:center;">
<?php if ($hasMedicalMedia): ?>
<img src="<?= Html::encode($medicalMediaPath) ?>" alt="<?= Yii::t('AnimalManagementModule.base', 'Selected medical media') ?>" style="width:100%;height:100%;object-fit:cover;">
<?php else: ?>
<i class="fa fa-image fa-2x" style="color:#a7b0b8;"></i>
<?php endif; ?>
</div>
</div>
<div class="col-sm-8">
<button type="button" class="btn btn-default" data-toggle="modal" data-target="#<?= Html::encode($medicalGalleryModalId) ?>" style="margin-bottom:8px;">
<i class="fa fa-photo"></i> <?= Yii::t('AnimalManagementModule.base', 'Choose from Gallery or Upload') ?>
</button>
<div class="checkbox" style="margin-top:0;">
<label>
<input type="checkbox" name="removeMedicalMedia" value="1">
<?= Yii::t('AnimalManagementModule.base', 'Remove selected media') ?>
</label>
</div>
</div>
</div>
</div>
</div>
<div class="panel panel-default" style="margin-bottom:12px;">
<div class="panel-heading"><strong><?= Yii::t('AnimalManagementModule.base', 'Physician') ?></strong></div>
<div class="panel-body" style="padding-bottom:8px;">
<div class="row">
<div class="col-sm-6"><?= $renderCustomField('physician_name', $model, $customDefinitions) ?></div>
<div class="col-sm-6"><?= $renderCustomField('physician_business_name', $model, $customDefinitions) ?></div>
<div class="col-sm-12"><?= $renderCustomField('physician_street_address', $model, $customDefinitions) ?></div>
<div class="col-sm-4"><?= $renderCustomField('physician_city', $model, $customDefinitions) ?></div>
<div class="col-sm-4"><?= $renderCustomField('physician_state', $model, $customDefinitions) ?></div>
<div class="col-sm-4"><?= $renderCustomField('physician_zip', $model, $customDefinitions) ?></div>
<div class="col-sm-4"><?= $renderCustomField('physician_cell_phone', $model, $customDefinitions) ?></div>
<div class="col-sm-4"><?= $renderCustomField('physician_business_phone', $model, $customDefinitions) ?></div>
<div class="col-sm-4"><?= $renderCustomField('physician_license_number', $model, $customDefinitions) ?></div>
</div>
</div>
</div>
<div class="panel panel-default" style="margin-bottom:12px;">
<div class="panel-heading"><strong><?= Yii::t('AnimalManagementModule.base', 'Social Post') ?></strong></div>
<div class="panel-body" style="padding-bottom:8px;">
<?= $form->field($model, 'post_to_space_feed')->checkbox() ?>
<?= $form->field($model, 'post_to_animal_feed')->checkbox() ?>
</div>
</div>
<?php if (!empty($remainingDefinitions)): ?>
<div class="panel panel-default" style="margin-bottom:12px;">
<div class="panel-heading"><strong><?= Yii::t('AnimalManagementModule.base', 'Additional Details') ?></strong></div>
<div class="panel-body" style="padding-bottom:8px;">
<?php foreach ($remainingDefinitions as $fieldKey => $definition): ?>
<?= $renderCustomField($fieldKey, $model, $remainingDefinitions) ?>
<?php endforeach; ?>
</div>
</div>
<?php endif; ?>
<?= Button::save(Yii::t('AnimalManagementModule.base', 'Save Medical Visit'))->submit() ?>
<?php if ($isInline): ?>
<?= Html::button(Yii::t('AnimalManagementModule.base', 'Cancel'), [
'type' => 'button',
'class' => 'btn btn-default',
'id' => 'medical-inline-cancel',
]) ?>
<?php else: ?>
<?= Button::asLink(Yii::t('AnimalManagementModule.base', 'Cancel'))
->link(($returnTo ?? 'view') === 'medical-visits'
? $space->createUrl('/animal_management/animals/medical-visits', ['id' => $animal->id])
: $space->createUrl('/animal_management/animals/view', ['id' => $animal->id])) ?>
<?php endif; ?>
<?php ActiveForm::end(); ?>
</div>
</div>
<div class="modal fade" id="<?= Html::encode($medicalGalleryModalId) ?>" 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', 'Select Medical Media from Gallery') ?></h4>
</div>
<div class="modal-body">
<?php if (empty($galleryItems)): ?>
<div class="text-muted" style="margin-bottom:10px;"><?= Yii::t('AnimalManagementModule.base', 'No gallery images available yet.') ?></div>
<?php else: ?>
<div class="row" style="max-height:280px;overflow:auto;margin-bottom:10px;">
<?php foreach ($galleryItems as $galleryItem): ?>
<?php $galleryUrl = trim((string)$galleryItem->getImageUrl()); ?>
<?php if ($galleryUrl === '') { continue; } ?>
<div class="col-xs-6 col-sm-4" style="margin-bottom:8px;">
<button type="button" class="btn btn-default medical-media-select-thumb<?= $medicalMediaPath === $galleryUrl ? ' is-selected' : '' ?>" data-media-url="<?= Html::encode($galleryUrl) ?>" style="width:100%;padding:3px;">
<img src="<?= Html::encode($galleryUrl) ?>" alt="<?= Yii::t('AnimalManagementModule.base', 'Gallery image') ?>" style="width:100%;height:120px;object-fit:cover;display:block;">
</button>
</div>
<?php endforeach; ?>
</div>
<?php endif; ?>
<div class="form-group" style="margin-bottom:0;">
<label class="control-label" for="medicalMediaUpload"><?= Yii::t('AnimalManagementModule.base', 'Upload New Image') ?></label>
<input type="file" class="form-control" id="medicalMediaUpload" name="medicalMediaUpload" form="<?= Html::encode($medicalFormId) ?>" accept="image/*">
</div>
</div>
</div>
</div>
</div>
<?php
$this->registerJs(<<<JS
(function(){
function renderMedicalPreview(source) {
var preview = $('#medical-media-preview');
if (!preview.length) {
return;
}
if (source) {
preview.html('<img src="' + source + '" alt="Selected medical media" style="width:100%;height:100%;object-fit:cover;">');
} else {
preview.html('<i class="fa fa-image fa-2x" style="color:#a7b0b8;"></i>');
}
}
function markSelectedMedicalThumb(value) {
$('.medical-media-select-thumb').removeClass('is-selected');
if (!value) {
return;
}
$('.medical-media-select-thumb').each(function() {
if ($(this).data('media-url') === value) {
$(this).addClass('is-selected');
}
});
}
$(document).on('click', '.medical-media-select-thumb', function() {
var mediaUrl = $(this).data('media-url');
$('#medical-media-gallery-path').val(mediaUrl);
markSelectedMedicalThumb(mediaUrl);
$('#medicalMediaUpload').val('');
$('input[name="removeMedicalMedia"]').prop('checked', false);
if (mediaUrl) {
renderMedicalPreview(mediaUrl);
}
$('#{$medicalGalleryModalId}').modal('hide');
});
$('#medicalMediaUpload').on('change', function() {
var file = this.files && this.files[0] ? this.files[0] : null;
if (!file) {
return;
}
$('#medical-media-gallery-path').val('');
markSelectedMedicalThumb('');
$('input[name="removeMedicalMedia"]').prop('checked', false);
var reader = new FileReader();
reader.onload = function(e) {
renderMedicalPreview(e.target.result);
$('#{$medicalGalleryModalId}').modal('hide');
};
reader.readAsDataURL(file);
});
$('#{$medicalGalleryModalId}').on('shown.bs.modal', function() {
markSelectedMedicalThumb($('#medical-media-gallery-path').val());
});
})();
JS
, \yii\web\View::POS_END);
if ($isInline) {
$cancelPayload = Json::htmlEncode([
'source' => 'animal-inline-editor',
'type' => 'cancel',
'collapseId' => 'medical-edit-inline-' . (int)$medicalVisit->id,
]);
$this->registerJs(<<<JS
$(document).on('click', '#medical-inline-cancel, #medical-inline-cancel-icon', function() {
if (window.parent && window.parent !== window) {
window.parent.postMessage($cancelPayload, '*');
}
});
JS
, \yii\web\View::POS_END);
}
?>

View File

@@ -0,0 +1,424 @@
<?php
use humhub\modules\animal_management\models\Animal;
use humhub\modules\animal_management\models\AnimalGalleryItem;
use humhub\modules\animal_management\models\AnimalProgressUpdate;
use humhub\modules\animal_management\models\forms\AnimalProgressUpdateForm;
use humhub\modules\space\models\Space;
use humhub\widgets\Button;
use yii\bootstrap\ActiveForm;
use yii\helpers\Html;
use yii\helpers\Json;
/* @var Space $space */
/* @var Animal $animal */
/* @var AnimalProgressUpdateForm $model */
/* @var AnimalProgressUpdate $progressUpdate */
/* @var string $returnTo */
/* @var AnimalGalleryItem[] $galleryItems */
/* @var bool $isInline */
$isInline = isset($isInline) ? (bool)$isInline : false;
$renderCustomField = static function (string $fieldKey, AnimalProgressUpdateForm $formModel, array $definitions): string {
if (!isset($definitions[$fieldKey])) {
return '';
}
$definition = $definitions[$fieldKey];
$inputType = (string)$definition['input_type'];
$label = (string)$definition['label'];
if ((int)$definition['required'] === 1) {
$label .= ' *';
}
$fieldName = "AnimalProgressUpdateForm[customFields][$fieldKey]";
$fieldValue = $formModel->customFields[$fieldKey] ?? '';
ob_start();
?>
<?php if ($inputType === 'textarea'): ?>
<div class="form-group">
<label class="control-label" for="animalprogressupdateform-customfields-<?= Html::encode($fieldKey) ?>"><?= Html::encode($label) ?></label>
<?= Html::textarea($fieldName, (string)$fieldValue, ['class' => 'form-control', 'rows' => 3, 'id' => "animalprogressupdateform-customfields-$fieldKey"]) ?>
</div>
<?php elseif ($inputType === 'boolean'): ?>
<div class="checkbox" style="margin-bottom:10px;">
<label>
<?= Html::hiddenInput($fieldName, '0') ?>
<?= Html::checkbox($fieldName, !empty($fieldValue), ['value' => '1']) ?>
<?= Html::encode($label) ?>
</label>
</div>
<?php elseif ($inputType === 'select'): ?>
<div class="form-group">
<label class="control-label" for="animalprogressupdateform-customfields-<?= Html::encode($fieldKey) ?>"><?= Html::encode($label) ?></label>
<?= Html::dropDownList(
$fieldName,
(string)$fieldValue,
$formModel->getCustomFieldSelectOptions($fieldKey),
['class' => 'form-control', 'prompt' => Yii::t('AnimalManagementModule.base', 'Select...'), 'id' => "animalprogressupdateform-customfields-$fieldKey"]
) ?>
</div>
<?php elseif ($inputType === 'number'): ?>
<div class="form-group">
<label class="control-label" for="animalprogressupdateform-customfields-<?= Html::encode($fieldKey) ?>"><?= Html::encode($label) ?></label>
<?= Html::input('number', $fieldName, (string)$fieldValue, ['class' => 'form-control', 'step' => 'any', 'id' => "animalprogressupdateform-customfields-$fieldKey"]) ?>
</div>
<?php elseif ($inputType === 'date'): ?>
<div class="form-group">
<label class="control-label" for="animalprogressupdateform-customfields-<?= Html::encode($fieldKey) ?>"><?= Html::encode($label) ?></label>
<?= Html::input('date', $fieldName, (string)$fieldValue, ['class' => 'form-control', 'id' => "animalprogressupdateform-customfields-$fieldKey"]) ?>
</div>
<?php elseif ($inputType === 'datetime'): ?>
<div class="form-group">
<label class="control-label" for="animalprogressupdateform-customfields-<?= Html::encode($fieldKey) ?>"><?= Html::encode($label) ?></label>
<?= Html::input('datetime-local', $fieldName, (string)$fieldValue, ['class' => 'form-control', 'id' => "animalprogressupdateform-customfields-$fieldKey"]) ?>
</div>
<?php else: ?>
<div class="form-group">
<label class="control-label" for="animalprogressupdateform-customfields-<?= Html::encode($fieldKey) ?>"><?= Html::encode($label) ?></label>
<?= Html::textInput($fieldName, (string)$fieldValue, ['class' => 'form-control', 'id' => "animalprogressupdateform-customfields-$fieldKey"]) ?>
</div>
<?php endif; ?>
<?php
return (string)ob_get_clean();
};
$customDefinitions = $model->getCustomFieldDefinitions();
$knownProgressKeys = ['progress_notes', 'routine_updates', 'media_reference'];
$otherCustomDefinitions = [];
foreach ($customDefinitions as $fieldKey => $definition) {
if (in_array($fieldKey, $knownProgressKeys, true)) {
continue;
}
$otherCustomDefinitions[$fieldKey] = $definition;
}
$currentMediaReference = trim((string)($model->customFields['media_reference'] ?? ''));
$progressFormId = 'edit-progress-update-form';
$this->registerCss(<<<CSS
.inline-editor-shell.panel {
border: 1px solid rgba(255, 255, 255, 0.22);
border-radius: 12px;
background: rgba(10, 18, 28, 0.36);
}
.inline-editor-shell > .panel-heading {
color: #eef5fb;
background: rgba(10, 18, 28, 0.42);
border-color: rgba(255, 255, 255, 0.2);
}
.inline-editor-shell > .panel-body {
background: rgba(10, 18, 28, 0.2);
}
.inline-editor-shell .panel.panel-default {
border-color: rgba(255, 255, 255, 0.2);
background: rgba(10, 18, 28, 0.34);
}
.inline-editor-shell .panel.panel-default > .panel-heading {
color: #eef5fb;
background: rgba(10, 18, 28, 0.42);
border-color: rgba(255, 255, 255, 0.2);
}
.inline-editor-shell,
.inline-editor-shell .panel-body,
.inline-editor-shell .control-label,
.inline-editor-shell .checkbox label,
.inline-editor-shell .radio label,
.inline-editor-shell .help-block {
color: #eef5fb;
}
.inline-editor-shell .text-muted {
color: rgba(233, 242, 250, 0.78) !important;
}
.inline-editor-shell .form-control {
background: rgba(10, 18, 28, 0.56);
border-color: rgba(255, 255, 255, 0.44);
color: #f3f8ff;
}
.inline-editor-shell .form-control::placeholder {
color: rgba(243, 248, 255, 0.72);
}
.inline-editor-shell .form-control[readonly],
.inline-editor-shell .form-control[disabled] {
background: rgba(10, 18, 28, 0.42);
color: rgba(243, 248, 255, 0.72);
}
.inline-editor-shell select.form-control option {
color: #0f1b2a;
}
CSS
);
if ($isInline) {
$this->registerCss(<<<CSS
html, body {
margin: 0 !important;
padding: 0 !important;
background: transparent !important;
}
body > .panel:first-child {
margin-top: 0 !important;
}
CSS
);
}
?>
<div class="panel panel-default inline-editor-shell">
<div class="panel-heading">
<div style="display:flex;align-items:center;justify-content:space-between;gap:12px;">
<span><?= Yii::t('AnimalManagementModule.base', '<strong>Edit</strong> Progress Update') ?></span>
<?php if ($isInline): ?>
<span style="display:inline-flex;gap:8px;">
<?= Html::submitButton('<i class="fa fa-check"></i>', [
'class' => 'btn btn-default btn-sm',
'title' => Yii::t('AnimalManagementModule.base', 'Save Progress Update'),
'form' => $progressFormId,
]) ?>
<?= Html::button('<i class="fa fa-times"></i>', [
'type' => 'button',
'class' => 'btn btn-default btn-sm',
'id' => 'progress-inline-cancel-icon',
'title' => Yii::t('AnimalManagementModule.base', 'Cancel'),
]) ?>
</span>
<?php endif; ?>
</div>
</div>
<div class="panel-body">
<?php
$formOptions = ['id' => $progressFormId, 'enctype' => 'multipart/form-data'];
if (!$isInline) {
$formOptions['target'] = '_top';
}
$form = ActiveForm::begin(['options' => $formOptions]);
?>
<?= Html::hiddenInput('returnTo', (string)($returnTo ?? 'view')) ?>
<?= $form->errorSummary($model, ['showAllErrors' => true]) ?>
<div class="panel panel-default" style="margin-bottom:12px;">
<div class="panel-heading"><strong><?= Yii::t('AnimalManagementModule.base', 'Progress Update') ?></strong></div>
<div class="panel-body" style="padding-bottom:8px;">
<div class="row">
<div class="col-sm-4"><?= $form->field($model, 'weight') ?></div>
<div class="col-sm-8"><?= $form->field($model, 'vitals')->textInput(['maxlength' => 255]) ?></div>
<div class="col-sm-6"><?= $form->field($model, 'behavior_notes')->textarea(['rows' => 2]) ?></div>
<div class="col-sm-6"><?= $form->field($model, 'medical_concerns')->textarea(['rows' => 2]) ?></div>
<div class="col-sm-6"><?= $form->field($model, 'meal_plan_changes')->textarea(['rows' => 2]) ?></div>
<div class="col-sm-6"><?= $form->field($model, 'housing_changes')->textarea(['rows' => 2]) ?></div>
</div>
</div>
</div>
<div class="panel panel-default" style="margin-bottom:12px;">
<div class="panel-heading"><strong><?= Yii::t('AnimalManagementModule.base', 'Notes') ?></strong></div>
<div class="panel-body" style="padding-bottom:8px;">
<?= $renderCustomField('progress_notes', $model, $customDefinitions) ?>
<?= $renderCustomField('routine_updates', $model, $customDefinitions) ?>
</div>
</div>
<?php if (!empty($otherCustomDefinitions)): ?>
<div class="panel panel-default" style="margin-bottom:12px;">
<div class="panel-heading"><strong><?= Yii::t('AnimalManagementModule.base', 'Additional Details') ?></strong></div>
<div class="panel-body" style="padding-bottom:8px;">
<?php foreach ($otherCustomDefinitions as $fieldKey => $definition): ?>
<?= $renderCustomField($fieldKey, $model, $otherCustomDefinitions) ?>
<?php endforeach; ?>
</div>
</div>
<?php endif; ?>
<div class="panel panel-default" style="margin-bottom:12px;">
<div class="panel-heading"><strong><?= Yii::t('AnimalManagementModule.base', 'Media') ?></strong></div>
<div class="panel-body" style="padding-bottom:8px;">
<input type="hidden" id="progress-media-gallery-path" name="progressMediaGalleryPath" value="<?= Html::encode($currentMediaReference) ?>">
<div class="row">
<div class="col-sm-4" style="margin-bottom:8px;">
<div id="progress-media-preview" style="border-radius:8px;overflow:hidden;background:#f2f4f6;height:150px;display:flex;align-items:center;justify-content:center;">
<?php if ($currentMediaReference !== '' && (preg_match('/^https?:\/\//i', $currentMediaReference) || substr($currentMediaReference, 0, 1) === '/')): ?>
<img src="<?= Html::encode($currentMediaReference) ?>" alt="<?= Yii::t('AnimalManagementModule.base', 'Selected media') ?>" style="width:100%;height:100%;object-fit:cover;">
<?php else: ?>
<i class="fa fa-image fa-2x" style="color:#a7b0b8;"></i>
<?php endif; ?>
</div>
</div>
<div class="col-sm-8">
<button type="button" class="btn btn-default" data-toggle="modal" data-target="#progress-media-modal" style="margin-bottom:8px;">
<i class="fa fa-photo"></i> <?= Yii::t('AnimalManagementModule.base', 'Choose from Gallery or Upload') ?>
</button>
<div class="checkbox" style="margin-top:0;">
<label>
<input type="checkbox" name="removeProgressMedia" value="1">
<?= Yii::t('AnimalManagementModule.base', 'Remove selected media') ?>
</label>
</div>
</div>
</div>
</div>
</div>
<div class="panel panel-default" style="margin-bottom:12px;">
<div class="panel-heading"><strong><?= Yii::t('AnimalManagementModule.base', 'Social Post') ?></strong></div>
<div class="panel-body" style="padding-bottom:8px;">
<?= $form->field($model, 'post_to_space_feed')->checkbox() ?>
<?= $form->field($model, 'post_to_animal_feed')->checkbox() ?>
</div>
</div>
<div class="modal fade" id="progress-media-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', 'Select Progress Media') ?></h4>
</div>
<div class="modal-body">
<?php if (empty($galleryItems)): ?>
<div class="text-muted" style="margin-bottom:10px;"><?= Yii::t('AnimalManagementModule.base', 'No gallery images available yet.') ?></div>
<?php else: ?>
<div class="row" style="max-height:280px;overflow:auto;margin-bottom:10px;">
<?php foreach ($galleryItems as $galleryItem): ?>
<?php $galleryUrl = trim((string)$galleryItem->getImageUrl()); ?>
<?php if ($galleryUrl === '') { continue; } ?>
<div class="col-xs-6 col-sm-4" style="margin-bottom:8px;">
<button type="button" class="btn btn-default progress-media-select-thumb<?= $currentMediaReference === $galleryUrl ? ' is-selected' : '' ?>" data-media-url="<?= Html::encode($galleryUrl) ?>" style="width:100%;padding:3px;">
<img src="<?= Html::encode($galleryUrl) ?>" alt="<?= Yii::t('AnimalManagementModule.base', 'Gallery image') ?>" style="width:100%;height:120px;object-fit:cover;border-radius:4px;">
</button>
</div>
<?php endforeach; ?>
</div>
<?php endif; ?>
<div class="form-group" style="margin-bottom:0;">
<label class="control-label" for="progressMediaUpload"><?= Yii::t('AnimalManagementModule.base', 'Upload New Image') ?></label>
<input type="file" class="form-control" id="progressMediaUpload" name="progressMediaUpload" accept="image/*">
</div>
</div>
</div>
</div>
</div>
<?= Button::save(Yii::t('AnimalManagementModule.base', 'Save Progress Update'))->submit() ?>
<?php if ($isInline): ?>
<?= Html::button(Yii::t('AnimalManagementModule.base', 'Cancel'), [
'type' => 'button',
'class' => 'btn btn-default',
'id' => 'progress-inline-cancel',
]) ?>
<?php else: ?>
<?= Button::asLink(Yii::t('AnimalManagementModule.base', 'Cancel'))
->link(($returnTo ?? 'view') === 'progress-updates'
? $space->createUrl('/animal_management/animals/progress-updates', ['id' => $animal->id])
: $space->createUrl('/animal_management/animals/view', ['id' => $animal->id])) ?>
<?php endif; ?>
<?php ActiveForm::end(); ?>
</div>
</div>
<?php
$this->registerCss(<<<CSS
.progress-media-select-thumb.is-selected {
border-color: #2f7df4 !important;
box-shadow: 0 0 0 2px rgba(47, 125, 244, 0.22);
}
CSS
);
$this->registerJs(<<<JS
(function() {
function renderProgressPreview(source) {
var preview = $('#progress-media-preview');
if (!preview.length) {
return;
}
if (source) {
preview.html('<img src="' + source + '" alt="Selected media" style="width:100%;height:100%;object-fit:cover;">');
} else {
preview.html('<i class="fa fa-image fa-2x" style="color:#a7b0b8;"></i>');
}
}
function markSelectedMediaThumb(value) {
$('.progress-media-select-thumb').removeClass('is-selected');
if (!value) {
return;
}
$('.progress-media-select-thumb').each(function() {
if (($(this).attr('data-media-url') || '') === value) {
$(this).addClass('is-selected');
}
});
}
$(document).off('click.editProgressMediaSelect', '.progress-media-select-thumb').on('click.editProgressMediaSelect', '.progress-media-select-thumb', function() {
var mediaUrl = $(this).attr('data-media-url') || '';
$('#progress-media-gallery-path').val(mediaUrl);
markSelectedMediaThumb(mediaUrl);
$('#progressMediaUpload').val('');
$('input[name="removeProgressMedia"]').prop('checked', false);
if (mediaUrl) {
renderProgressPreview(mediaUrl);
}
$('#progress-media-modal').modal('hide');
});
$(document).off('change.editProgressMediaUpload', '#progressMediaUpload').on('change.editProgressMediaUpload', '#progressMediaUpload', function() {
var file = this.files && this.files[0] ? this.files[0] : null;
if (!file) {
return;
}
$('#progress-media-gallery-path').val('');
markSelectedMediaThumb('');
$('input[name="removeProgressMedia"]').prop('checked', false);
var reader = new FileReader();
reader.onload = function(e) {
renderProgressPreview(e.target.result);
$('#progress-media-modal').modal('hide');
};
reader.readAsDataURL(file);
});
$(document).off('shown.bs.modal.editProgressMediaModal', '#progress-media-modal').on('shown.bs.modal.editProgressMediaModal', '#progress-media-modal', function() {
markSelectedMediaThumb($('#progress-media-gallery-path').val());
});
})();
JS
, \yii\web\View::POS_END);
if ($isInline) {
$cancelPayload = Json::htmlEncode([
'source' => 'animal-inline-editor',
'type' => 'cancel',
'collapseId' => 'progress-edit-inline-' . (int)$progressUpdate->id,
]);
$this->registerJs(<<<JS
$(document).on('click', '#progress-inline-cancel, #progress-inline-cancel-icon', function() {
if (window.parent && window.parent !== window) {
window.parent.postMessage($cancelPayload, '*');
}
});
JS
, \yii\web\View::POS_END);
}
?>

283
views/animals/index.php Normal file
View File

@@ -0,0 +1,283 @@
<?php
use humhub\modules\animal_management\models\Animal;
use humhub\modules\animal_management\helpers\DateDisplayHelper;
use humhub\modules\animal_management\models\AnimalMedicalVisit;
use humhub\modules\animal_management\models\AnimalTransfer;
use humhub\modules\space\models\Space;
use humhub\widgets\Button;
use yii\helpers\Html;
/* @var Animal[] $animals */
/* @var AnimalTransfer[] $incomingTransfers */
/* @var AnimalTransfer[] $outgoingTransfers */
/* @var string $queryValue */
/* @var string $statusFilter */
/* @var string $speciesFilter */
/* @var string $viewMode */
/* @var string $sortKey */
/* @var string $sortDirection */
/* @var array $availableColumns */
/* @var array $selectedColumns */
/* @var array $speciesOptions */
/* @var array<int, AnimalMedicalVisit> $latestMedicalVisitByAnimal */
/* @var array<int, string> $animalImageUrls */
/* @var array<int, string> $transferAnimalImageUrls */
/* @var array $tileFields */
/* @var array<int, array> $tileFieldOverrides */
/* @var Space $space */
/* @var bool $canManage */
$currentParams = [
'q' => $queryValue,
'status' => $statusFilter,
'species' => $speciesFilter,
'view' => $viewMode,
'sort' => $sortKey,
'direction' => $sortDirection,
'cols' => $selectedColumns,
];
$buildUrl = static function (array $overrides) use ($space, $currentParams): string {
$params = array_merge($currentParams, $overrides);
return $space->createUrl('/animal_management/animals/index', $params);
};
$sortUrl = static function (string $column) use ($buildUrl, $sortKey, $sortDirection): string {
$nextDirection = ($sortKey === $column && $sortDirection === 'asc') ? 'desc' : 'asc';
return $buildUrl(['sort' => $column, 'direction' => $nextDirection, 'view' => 'table']);
};
?>
<div class="panel panel-default">
<div class="panel-heading" style="display:flex;justify-content:space-between;align-items:center;">
<span><?= Yii::t('AnimalManagementModule.base', '<strong>Animals</strong>') ?></span>
<span style="display:flex;gap:8px;flex-wrap:wrap;align-items:center;">
<?= Html::a('<i class="fa fa-th-large"></i>', $buildUrl(['view' => 'tiles']), [
'class' => 'btn btn-default btn-sm' . ($viewMode === 'tiles' ? ' active' : ''),
'title' => Yii::t('AnimalManagementModule.base', 'Tile View'),
'aria-label' => Yii::t('AnimalManagementModule.base', 'Tile View'),
]) ?>
<?= Html::a('<i class="fa fa-table"></i>', $buildUrl(['view' => 'table']), [
'class' => 'btn btn-default btn-sm' . ($viewMode === 'table' ? ' active' : ''),
'title' => Yii::t('AnimalManagementModule.base', 'Table View'),
'aria-label' => Yii::t('AnimalManagementModule.base', 'Table View'),
]) ?>
<?php if ($canManage): ?>
<?= Html::a('<i class="fa fa-plus"></i> ' . Yii::t('AnimalManagementModule.base', 'Intake'), $space->createUrl('/animal_management/animals/create'), [
'class' => 'btn btn-primary btn-sm',
'title' => Yii::t('AnimalManagementModule.base', 'Intake'),
'aria-label' => Yii::t('AnimalManagementModule.base', 'Intake'),
]) ?>
<?php endif; ?>
</span>
</div>
<div class="panel-body">
<form method="get" action="<?= Html::encode($space->createUrl('/animal_management/animals/index')) ?>" class="form-inline" style="margin-bottom:12px;display:flex;gap:8px;flex-wrap:wrap;">
<input type="hidden" name="view" value="<?= Html::encode($viewMode) ?>">
<input type="hidden" name="sort" value="<?= Html::encode($sortKey) ?>">
<input type="hidden" name="direction" value="<?= Html::encode($sortDirection) ?>">
<?php foreach ($selectedColumns as $col): ?>
<input type="hidden" name="cols[]" value="<?= Html::encode($col) ?>">
<?php endforeach; ?>
<div class="form-group" style="flex:1;min-width:260px;">
<input type="text" class="form-control" style="width:100%;" name="q" value="<?= Html::encode($queryValue) ?>" placeholder="<?= Yii::t('AnimalManagementModule.base', 'Search by name, species, ID, or breed') ?>">
</div>
<div class="form-group">
<?= Html::dropDownList('status', $statusFilter, ['' => Yii::t('AnimalManagementModule.base', 'All Statuses')] + Animal::statusOptions(), ['class' => 'form-control']) ?>
</div>
<div class="form-group">
<?php $speciesDropDown = ['' => Yii::t('AnimalManagementModule.base', 'All Species')]; ?>
<?php foreach ($speciesOptions as $speciesOption): ?>
<?php $speciesDropDown[$speciesOption] = $speciesOption; ?>
<?php endforeach; ?>
<?= Html::dropDownList('species', $speciesFilter, $speciesDropDown, ['class' => 'form-control']) ?>
</div>
<button type="submit" class="btn btn-default"><?= Yii::t('AnimalManagementModule.base', 'Apply') ?></button>
<?= Button::asLink(Yii::t('AnimalManagementModule.base', 'Reset'))->link($space->createUrl('/animal_management/animals/index', ['view' => $viewMode])) ?>
</form>
<?php if ($viewMode === 'table'): ?>
<details style="margin-bottom:12px;">
<summary style="cursor:pointer;"><?= Yii::t('AnimalManagementModule.base', 'Show/Hide Columns') ?></summary>
<form method="get" action="<?= Html::encode($space->createUrl('/animal_management/animals/index')) ?>" style="margin-top:8px;display:flex;gap:8px;flex-wrap:wrap;align-items:center;">
<input type="hidden" name="view" value="table">
<input type="hidden" name="q" value="<?= Html::encode($queryValue) ?>">
<input type="hidden" name="status" value="<?= Html::encode($statusFilter) ?>">
<input type="hidden" name="species" value="<?= Html::encode($speciesFilter) ?>">
<input type="hidden" name="sort" value="<?= Html::encode($sortKey) ?>">
<input type="hidden" name="direction" value="<?= Html::encode($sortDirection) ?>">
<?php foreach ($availableColumns as $columnKey => $columnLabel): ?>
<label style="margin:0 8px 0 0;">
<input type="checkbox" name="cols[]" value="<?= Html::encode($columnKey) ?>" <?= in_array($columnKey, $selectedColumns, true) ? 'checked' : '' ?> <?= $columnKey === 'name' ? 'disabled' : '' ?>>
<?= Html::encode($columnLabel) ?>
</label>
<?php endforeach; ?>
<button type="submit" class="btn btn-default btn-sm"><?= Yii::t('AnimalManagementModule.base', 'Update Columns') ?></button>
</form>
</details>
<?php endif; ?>
<?php if (empty($animals)): ?>
<div class="alert alert-info" style="margin-bottom:0;">
<?= Yii::t('AnimalManagementModule.base', 'No animal profiles yet. Create the first intake record to begin tracking.') ?>
</div>
<?php else: ?>
<?php if ($viewMode === 'tiles'): ?>
<div class="row">
<?php foreach ($animals as $animal): ?>
<?php $animalId = (int)$animal->id; ?>
<?php $lastMedical = $latestMedicalVisitByAnimal[$animalId] ?? null; ?>
<div class="col-sm-6 col-md-4" style="margin-bottom:16px;">
<?= $this->render('_tile', [
'animal' => $animal,
'contentContainer' => $space,
'lastMedical' => $lastMedical,
'imageUrl' => $animalImageUrls[$animalId] ?? '',
'tileFields' => $tileFieldOverrides[$animalId] ?? $tileFields,
'showMedicalIcon' => true,
]) ?>
</div>
<?php endforeach; ?>
</div>
<?php else: ?>
<div class="table-responsive">
<table class="table table-hover">
<thead>
<tr>
<?php if (in_array('animal_uid', $selectedColumns, true)): ?>
<th><a href="<?= Html::encode($sortUrl('animal_uid')) ?>"><?= Yii::t('AnimalManagementModule.base', 'ID') ?></a></th>
<?php endif; ?>
<th><a href="<?= Html::encode($sortUrl('name')) ?>"><?= Yii::t('AnimalManagementModule.base', 'Name') ?></a></th>
<?php if (in_array('species', $selectedColumns, true)): ?>
<th><a href="<?= Html::encode($sortUrl('species')) ?>"><?= Yii::t('AnimalManagementModule.base', 'Species') ?></a></th>
<?php endif; ?>
<?php if (in_array('status', $selectedColumns, true)): ?>
<th><a href="<?= Html::encode($sortUrl('status')) ?>"><?= Yii::t('AnimalManagementModule.base', 'Status') ?></a></th>
<?php endif; ?>
<?php if (in_array('last_medical', $selectedColumns, true)): ?>
<th><a href="<?= Html::encode($sortUrl('last_medical')) ?>"><?= Yii::t('AnimalManagementModule.base', 'Last Medical Visit') ?></a></th>
<?php endif; ?>
<?php if (in_array('updated_at', $selectedColumns, true)): ?>
<th><a href="<?= Html::encode($sortUrl('updated_at')) ?>"><?= Yii::t('AnimalManagementModule.base', 'Updated') ?></a></th>
<?php endif; ?>
<th></th>
</tr>
</thead>
<tbody>
<?php foreach ($animals as $animal): ?>
<?php $lastMedical = $latestMedicalVisitByAnimal[(int)$animal->id] ?? null; ?>
<tr>
<?php if (in_array('animal_uid', $selectedColumns, true)): ?>
<td><?= Html::encode($animal->animal_uid) ?></td>
<?php endif; ?>
<td>
<a href="<?= Html::encode($space->createUrl('/animal_management/animals/view', ['id' => $animal->id])) ?>">
<?= Html::encode($animal->getDisplayName()) ?>
</a>
</td>
<?php if (in_array('species', $selectedColumns, true)): ?>
<td><?= Html::encode((string)$animal->species) ?></td>
<?php endif; ?>
<?php if (in_array('status', $selectedColumns, true)): ?>
<td><?= Html::encode(Animal::statusOptions()[$animal->status] ?? $animal->status) ?></td>
<?php endif; ?>
<?php if (in_array('last_medical', $selectedColumns, true)): ?>
<td>
<?php if ($lastMedical instanceof AnimalMedicalVisit): ?>
<a href="<?= Html::encode($space->createUrl('/animal_management/animals/medical-visits', ['id' => $animal->id]) . '#medical-visit-' . (int)$lastMedical->id) ?>">
<?= Html::encode(DateDisplayHelper::format((string)$lastMedical->visit_at)) ?>
</a>
<?php else: ?>
<span class="text-muted">-</span>
<?php endif; ?>
</td>
<?php endif; ?>
<?php if (in_array('updated_at', $selectedColumns, true)): ?>
<td><?= Html::encode(DateDisplayHelper::format((string)$animal->updated_at)) ?></td>
<?php endif; ?>
<td class="text-right" style="white-space:nowrap;">
<?= Button::asLink(Yii::t('AnimalManagementModule.base', 'View'))
->link($space->createUrl('/animal_management/animals/view', ['id' => $animal->id])) ?>
<?= Button::asLink(Yii::t('AnimalManagementModule.base', 'Medical'))
->link($space->createUrl('/animal_management/animals/medical-visits', ['id' => $animal->id])) ?>
<?= Button::asLink(Yii::t('AnimalManagementModule.base', 'Progress'))
->link($space->createUrl('/animal_management/animals/progress-updates', ['id' => $animal->id])) ?>
<?php if ($canManage): ?>
<?= Button::asLink(Yii::t('AnimalManagementModule.base', 'Edit'))
->link($space->createUrl('/animal_management/animals/edit', ['id' => $animal->id])) ?>
<?= Button::asLink(Yii::t('AnimalManagementModule.base', 'Transfer'))
->link($space->createUrl('/animal_management/animals/transfer', ['id' => $animal->id])) ?>
<?php endif; ?>
</td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
</div>
<?php endif; ?>
<?php endif; ?>
<?php if ($canManage): ?>
<hr>
<h4 id="incoming-transfers" style="margin-top:0;"><?= Yii::t('AnimalManagementModule.base', 'Incoming Transfer Requests') ?></h4>
<?php if (empty($incomingTransfers)): ?>
<div class="text-muted" style="margin-bottom:12px;"><?= Yii::t('AnimalManagementModule.base', 'No incoming requests.') ?></div>
<?php else: ?>
<div class="row" style="margin-bottom:4px;">
<?php foreach ($incomingTransfers as $transfer): ?>
<?php
$fromSpace = $transfer->getFromSpace();
$toSpace = $transfer->getToSpace();
$animalLinkSpace = ($transfer->status === AnimalTransfer::STATUS_COMPLETED)
? ($toSpace ?: $fromSpace)
: ($fromSpace ?: $toSpace);
?>
<div class="col-sm-6" style="margin-bottom:14px;">
<?= $this->render('_transfer_tile', [
'transfer' => $transfer,
'space' => $space,
'otherRescueName' => $fromSpace ? $fromSpace->name : Yii::t('AnimalManagementModule.base', 'Unknown Rescue'),
'otherRescueUrl' => $fromSpace ? $fromSpace->createUrl('/space/space/home') : '',
'animalProfileUrl' => $animalLinkSpace ? $animalLinkSpace->createUrl('/animal_management/animals/view', ['id' => $transfer->animal_id]) : '',
'imageUrl' => trim((string)($transferAnimalImageUrls[(int)$transfer->animal_id] ?? '')),
'isIncoming' => true,
]) ?>
</div>
<?php endforeach; ?>
</div>
<?php endif; ?>
<h4 id="outgoing-transfers"><?= Yii::t('AnimalManagementModule.base', 'Outgoing Transfer History') ?></h4>
<?php if (empty($outgoingTransfers)): ?>
<div class="text-muted"><?= Yii::t('AnimalManagementModule.base', 'No outgoing transfers yet.') ?></div>
<?php else: ?>
<div class="row" style="margin-bottom:0;">
<?php foreach ($outgoingTransfers as $transfer): ?>
<?php
$fromSpace = $transfer->getFromSpace();
$toSpace = $transfer->getToSpace();
$animalLinkSpace = ($transfer->status === AnimalTransfer::STATUS_COMPLETED)
? ($toSpace ?: $fromSpace)
: ($fromSpace ?: $toSpace);
?>
<div class="col-sm-6 col-md-4" style="margin-bottom:14px;">
<?= $this->render('_transfer_tile', [
'transfer' => $transfer,
'space' => $space,
'otherRescueName' => $toSpace ? $toSpace->name : Yii::t('AnimalManagementModule.base', 'Unknown Rescue'),
'otherRescueUrl' => $toSpace ? $toSpace->createUrl('/space/space/home') : '',
'animalProfileUrl' => $animalLinkSpace ? $animalLinkSpace->createUrl('/animal_management/animals/view', ['id' => $transfer->animal_id]) : '',
'imageUrl' => trim((string)($transferAnimalImageUrls[(int)$transfer->animal_id] ?? '')),
'isIncoming' => false,
]) ?>
</div>
<?php endforeach; ?>
</div>
<?php endif; ?>
<?php endif; ?>
</div>
</div>

View File

@@ -0,0 +1,34 @@
<?php
use yii\helpers\Html;
/* @var string $collapseId */
/* @var array $refreshSelectors */
$payload = [
'source' => 'animal-inline-editor',
'type' => 'saved',
'collapseId' => (string)$collapseId,
'refreshSelectors' => array_values(array_map('strval', $refreshSelectors ?? [])),
];
$jsonPayload = json_encode($payload, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE);
?>
<div class="panel panel-success" style="margin:10px;">
<div class="panel-body" style="padding:12px;">
<?= Html::encode(Yii::t('AnimalManagementModule.base', 'Saved. Updating section...')) ?>
</div>
</div>
<?php
$this->registerJs(<<<JS
(function() {
var payload = {$jsonPayload};
if (window.parent && window.parent !== window) {
window.parent.postMessage(payload, '*');
}
})();
JS
, \yii\web\View::POS_END);
?>

View File

@@ -0,0 +1,901 @@
<?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\forms\AnimalMedicalVisitForm;
use humhub\modules\space\models\Space;
use humhub\widgets\Button;
use yii\bootstrap\ActiveForm;
use yii\helpers\Html;
/* @var Space $space */
/* @var Animal $animal */
/* @var bool $canManage */
/* @var AnimalMedicalVisitForm $medicalVisitForm */
/* @var AnimalMedicalVisit[] $medicalVisits */
/* @var AnimalGalleryItem[] $galleryItems */
$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',
];
$renderCustomField = static function (string $fieldKey, AnimalMedicalVisitForm $model, array $definitions): string {
if (!isset($definitions[$fieldKey])) {
return '';
}
$definition = $definitions[$fieldKey];
$inputType = (string)$definition['input_type'];
$vitalLabelOverrides = [
'blood_pressure' => 'BP',
'oxygen' => 'O₂',
];
$label = (string)($vitalLabelOverrides[$fieldKey] ?? $definition['label']);
if ((int)$definition['required'] === 1) {
$label .= ' *';
}
$fieldName = "AnimalMedicalVisitForm[customFields][$fieldKey]";
$fieldValue = $model->customFields[$fieldKey] ?? '';
ob_start();
?>
<?php if ($inputType === 'textarea'): ?>
<div class="form-group">
<label class="control-label" for="animalmedicalvisitform-customfields-<?= Html::encode($fieldKey) ?>"><?= Html::encode($label) ?></label>
<?= Html::textarea($fieldName, (string)$fieldValue, ['class' => 'form-control', 'rows' => 3, 'id' => "animalmedicalvisitform-customfields-$fieldKey"]) ?>
</div>
<?php elseif ($inputType === 'boolean'): ?>
<div class="checkbox" style="margin-bottom:10px;">
<label>
<?= Html::hiddenInput($fieldName, '0') ?>
<?= Html::checkbox($fieldName, !empty($fieldValue), ['value' => '1']) ?>
<?= Html::encode($label) ?>
</label>
</div>
<?php elseif ($inputType === 'select'): ?>
<div class="form-group">
<label class="control-label" for="animalmedicalvisitform-customfields-<?= Html::encode($fieldKey) ?>"><?= Html::encode($label) ?></label>
<?= Html::dropDownList(
$fieldName,
(string)$fieldValue,
$model->getCustomFieldSelectOptions($fieldKey),
['class' => 'form-control', 'prompt' => Yii::t('AnimalManagementModule.base', 'Select...'), 'id' => "animalmedicalvisitform-customfields-$fieldKey"]
) ?>
</div>
<?php elseif ($inputType === 'number'): ?>
<div class="form-group">
<label class="control-label" for="animalmedicalvisitform-customfields-<?= Html::encode($fieldKey) ?>"><?= Html::encode($label) ?></label>
<?= Html::input('number', $fieldName, (string)$fieldValue, ['class' => 'form-control', 'step' => 'any', 'id' => "animalmedicalvisitform-customfields-$fieldKey"]) ?>
</div>
<?php elseif ($inputType === 'date'): ?>
<div class="form-group">
<label class="control-label" for="animalmedicalvisitform-customfields-<?= Html::encode($fieldKey) ?>"><?= Html::encode($label) ?></label>
<?= Html::input('date', $fieldName, (string)$fieldValue, ['class' => 'form-control', 'id' => "animalmedicalvisitform-customfields-$fieldKey"]) ?>
</div>
<?php elseif ($inputType === 'datetime'): ?>
<div class="form-group">
<label class="control-label" for="animalmedicalvisitform-customfields-<?= Html::encode($fieldKey) ?>"><?= Html::encode($label) ?></label>
<?= Html::input('datetime-local', $fieldName, (string)$fieldValue, ['class' => 'form-control', 'id' => "animalmedicalvisitform-customfields-$fieldKey"]) ?>
</div>
<?php else: ?>
<div class="form-group">
<label class="control-label" for="animalmedicalvisitform-customfields-<?= Html::encode($fieldKey) ?>"><?= Html::encode($label) ?></label>
<?= Html::textInput($fieldName, (string)$fieldValue, ['class' => 'form-control', 'id' => "animalmedicalvisitform-customfields-$fieldKey"]) ?>
</div>
<?php endif; ?>
<?php
return (string)ob_get_clean();
};
$medicalCustomDefinitions = $medicalVisitForm->getCustomFieldDefinitions();
$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',
];
$remainingMedicalDefinitions = [];
foreach ($medicalCustomDefinitions as $fieldKey => $definition) {
if (in_array($fieldKey, $knownMedicalKeys, true) || in_array($fieldKey, $hiddenMedicalKeys, true)) {
continue;
}
$remainingMedicalDefinitions[$fieldKey] = $definition;
}
$newMedicalMediaPath = trim((string)($medicalVisitForm->customFields['medical_media_reference'] ?? $medicalVisitForm->customFields['media_reference'] ?? ''));
$hasNewMedicalMedia = $newMedicalMediaPath !== '' && (preg_match('/^https?:\/\//i', $newMedicalMediaPath) || substr($newMedicalMediaPath, 0, 1) === '/');
$medicalGalleryModalId = 'medical-media-gallery-modal';
$medicalAddModalId = 'add-medical-visit-modal';
$medicalFormId = 'add-medical-visit-form';
$openMedicalEditId = (int)Yii::$app->request->get('inlineMedicalEdit', 0);
$openMedicalAdd = (int)Yii::$app->request->get('inlineMedicalAdd', 0) === 1;
$medicalVitalLabelOverrides = [
'blood_pressure' => 'BP',
'oxygen' => 'O₂',
];
?>
<style>
.medical-feed-card {
position: relative;
overflow: hidden;
border: 1px solid #d5dfe8;
border-radius: 12px;
background: #223446;
margin-bottom: 12px;
min-height: 260px;
box-shadow: 0 8px 22px rgba(12, 24, 36, 0.16);
}
.medical-feed-cover {
position: absolute;
inset: 0;
width: 100%;
height: 100%;
object-fit: cover;
display: block;
}
.medical-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%);
}
.medical-feed-content {
position: relative;
z-index: 1;
min-height: 260px;
padding: 14px;
display: flex;
flex-direction: column;
align-items: flex-end;
gap: 10px;
}
.medical-feed-top-row {
width: 100%;
max-width: none;
margin-left: 0;
display: flex;
align-items: center;
justify-content: space-between;
gap: 8px;
}
.medical-feed-text-wrap {
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;
}
.medical-feed-header {
display: flex;
justify-content: space-between;
align-items: flex-start;
gap: 8px;
margin-bottom: 10px;
}
.medical-feed-date {
font-size: 15px;
font-weight: 700;
color: #ffffff;
margin-right: auto;
}
@media (max-width: 1200px) {
.medical-feed-text-wrap {
width: 55%;
max-width: 55%;
min-width: 0;
}
}
@media (max-width: 991px) {
.medical-feed-text-wrap {
width: 55%;
max-width: 55%;
min-width: 0;
}
.medical-chip-row--top {
justify-content: flex-start;
}
}
.medical-chip-row {
display: flex;
flex-wrap: wrap;
gap: 6px;
margin-bottom: 10px;
}
.medical-chip-row--top {
margin-bottom: 0;
justify-content: flex-end;
}
.medical-chip {
display: inline-block;
background: rgba(255, 255, 255, 0.16);
border: 1px solid rgba(255, 255, 255, 0.3);
border-radius: 999px;
padding: 4px 10px;
font-size: 12px;
color: #ffffff;
}
.medical-media-select-thumb.is-selected {
border-color: #1f8dd6;
box-shadow: 0 0 0 2px rgba(31, 141, 214, 0.2);
}
.medical-section-label {
font-size: 11px;
letter-spacing: .04em;
text-transform: uppercase;
color: rgba(231, 241, 249, 0.78);
margin-bottom: 4px;
}
.medical-section-body {
color: #eff5fb;
margin-bottom: 10px;
}
.medical-feed-inline-editor {
position: relative;
z-index: 2;
width: auto;
max-width: none;
min-width: 0;
margin: 0 14px 14px 14px;
padding-top: 0;
align-self: stretch;
box-sizing: border-box;
}
.medical-feed-inline-editor iframe {
display: block;
width: 100%;
}
@media (max-width: 1200px) {
.medical-feed-inline-editor {
width: auto;
max-width: none;
min-width: 0;
}
}
@media (max-width: 991px) {
.medical-feed-inline-editor {
width: auto;
max-width: none;
min-width: 0;
}
}
</style>
<div id="medical-visits-page" class="panel panel-default">
<div class="panel-heading" style="display:flex;align-items:center;">
<span style="font-size:20px;line-height:1.2;"><?= Yii::t('AnimalManagementModule.base', '<strong>Medical Visits</strong>') ?></span>
</div>
<div class="panel-body">
<div style="display:flex;gap:18px;flex-wrap:wrap;align-items:center;margin-bottom:12px;">
<?= Html::a(
Html::encode($animal->getDisplayName()),
$space->createUrl('/animal_management/animals/view', ['id' => $animal->id]),
['style' => 'font-size:20px;font-weight:700;line-height:1.2;']
) ?>
<?= Html::a(
Yii::t('AnimalManagementModule.base', 'All Animals'),
$space->createUrl('/animal_management/animals/index'),
['style' => 'font-size:16px;line-height:1.2;']
) ?>
<?php if ($canManage): ?>
<?= Html::a('<i class="fa fa-plus"></i> ' . Yii::t('AnimalManagementModule.base', 'Add Medical Visit'), '#medical-add-inline', [
'class' => 'btn btn-success btn-sm',
'title' => Yii::t('AnimalManagementModule.base', 'Add Medical Visit'),
'aria-label' => Yii::t('AnimalManagementModule.base', 'Add Medical Visit'),
'data-toggle' => 'collapse',
]) ?>
<?php endif; ?>
</div>
<?php if ($canManage): ?>
<div id="medical-add-inline" class="collapse medical-feed-inline-editor<?= $openMedicalAdd ? ' in' : '' ?>" style="margin:0 0 14px 0;">
<iframe
src="<?= Html::encode($space->createUrl('/animal_management/animals/add-medical-visit-inline', ['id' => $animal->id, 'inline' => 1, 'returnTo' => 'medical-visits', '_v' => time()])) ?>"
style="width:100%;min-height:860px;border:1px solid rgba(255,255,255,0.22);border-radius:10px;background:transparent;"
loading="lazy"
></iframe>
</div>
<?php endif; ?>
<?php if (empty($medicalVisits)): ?>
<div class="text-muted" style="margin-bottom:10px;"><?= Yii::t('AnimalManagementModule.base', 'No medical visits recorded.') ?></div>
<?php else: ?>
<?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);
$vitalKeys = ['weight', 'pulse', 'blood_pressure', 'oxygen'];
$hasVitals = false;
foreach ($vitalKeys as $vitalKey) {
if (!empty($visitFieldsByKey[$vitalKey]['value'])) {
$hasVitals = true;
break;
}
}
?>
<div id="medical-visit-<?= (int)$visit->id ?>" class="medical-feed-card">
<?php if ($hasMedicalMedia): ?>
<img class="medical-feed-cover" src="<?= Html::encode($medicalMedia) ?>" alt="<?= Yii::t('AnimalManagementModule.base', 'Medical media') ?>">
<?php endif; ?>
<div class="medical-feed-overlay"></div>
<div class="medical-feed-content">
<div class="medical-feed-top-row">
<span class="medical-feed-date"><?= Html::encode($visitDateDisplay !== '' ? $visitDateDisplay : (string)$visit->visit_at) ?></span>
<?php if ($hasVitals): ?>
<div class="medical-chip-row medical-chip-row--top">
<?php foreach ($vitalKeys as $vitalKey): ?>
<?php if (empty($visitFieldsByKey[$vitalKey]['value'])) { continue; } ?>
<span class="medical-chip">
<?= Html::encode($visitFieldsByKey[$vitalKey]['label']) ?>: <?= Html::encode($visitFieldsByKey[$vitalKey]['value']) ?>
</span>
<?php endforeach; ?>
</div>
<?php endif; ?>
</div>
<div class="medical-feed-text-wrap">
<div class="medical-feed-header">
<div>
<?php if (!empty($visit->provider_name)): ?>
<div style="font-size:12px;color:rgba(239,245,251,0.86);margin-top:2px;"><?= Yii::t('AnimalManagementModule.base', 'Provider') ?>: <?= Html::encode((string)$visit->provider_name) ?></div>
<?php endif; ?>
</div>
<?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->notes)): ?>
<div class="medical-section-label"><?= Yii::t('AnimalManagementModule.base', 'Clinical Notes') ?></div>
<div class="medical-section-body"><?= nl2br(Html::encode((string)$visit->notes)) ?></div>
<?php endif; ?>
<?php if (!empty($visit->recommendations)): ?>
<div class="medical-section-label"><?= Yii::t('AnimalManagementModule.base', 'Recommendations') ?></div>
<div class="medical-section-body"><?= 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="medical-section-label"><?= Html::encode($visitFieldsByKey[$conditionKey]['label']) ?></div>
<div class="medical-section-body"><?= nl2br(Html::encode($visitFieldsByKey[$conditionKey]['value'])) ?></div>
<?php endforeach; ?>
<?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="medical-section-label"><?= Yii::t('AnimalManagementModule.base', 'Care Contact') ?></div>
<div class="medical-section-body">
<?php foreach ($contactLines as $contactLine): ?>
<div><?= Html::encode($contactLine) ?></div>
<?php endforeach; ?>
</div>
<?php endif; ?>
<?php if (!empty($visitFieldsByKey['date_of_most_recent_medical_visit']['value'])): ?>
<div class="medical-section-label"><?= Html::encode($visitFieldsByKey['date_of_most_recent_medical_visit']['label']) ?></div>
<div class="medical-section-body"><?= Html::encode(DateDisplayHelper::format((string)$visitFieldsByKey['date_of_most_recent_medical_visit']['value'])) ?></div>
<?php endif; ?>
<?php if (!empty($additionalVisitFields)): ?>
<div class="medical-section-label"><?= Yii::t('AnimalManagementModule.base', 'Additional Fields') ?></div>
<div class="medical-section-body">
<?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>
</div>
<?php if ($canManage): ?>
<div id="medical-edit-inline-<?= (int)$visit->id ?>" class="collapse medical-feed-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, 'returnTo' => 'medical-visits', '_v' => time()])) ?>"
style="width:100%;min-height:760px;border:1px solid rgba(255,255,255,0.22);border-radius:10px;background:transparent;"
loading="lazy"
></iframe>
</div>
<?php endif; ?>
</div>
</div>
<?php endforeach; ?>
<?php endif; ?>
<?php if ($canManage): ?>
<div class="modal fade" id="<?= Html::encode($medicalAddModalId) ?>" 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 Medical Visit') ?></h4>
</div>
<div class="modal-body">
<?php $medicalForm = ActiveForm::begin([
'action' => $space->createUrl('/animal_management/animals/add-medical-visit', ['id' => $animal->id]),
'options' => ['id' => $medicalFormId, 'enctype' => 'multipart/form-data'],
]); ?>
<?= Html::hiddenInput('returnTo', 'medical-visits') ?>
<?= Html::hiddenInput('medicalMediaGalleryPath', $newMedicalMediaPath, ['id' => 'medical-media-gallery-path']) ?>
<?= $medicalForm->errorSummary($medicalVisitForm, ['showAllErrors' => true]) ?>
<div class="panel panel-default" style="margin-bottom:12px;">
<div class="panel-heading"><strong><?= Yii::t('AnimalManagementModule.base', 'Visit') ?></strong></div>
<div class="panel-body" style="padding-bottom:8px;">
<div class="row">
<div class="col-sm-6"><?= $medicalForm->field($medicalVisitForm, 'visit_at')->input('datetime-local') ?></div>
<div class="col-sm-6"><?= $medicalForm->field($medicalVisitForm, 'provider_name') ?></div>
</div>
<?= $medicalForm->field($medicalVisitForm, 'notes')->textarea(['rows' => 3]) ?>
<?= $medicalForm->field($medicalVisitForm, 'recommendations')->textarea(['rows' => 3]) ?>
</div>
</div>
<div class="panel panel-default" style="margin-bottom:12px;">
<div class="panel-heading"><strong><?= Yii::t('AnimalManagementModule.base', 'Vitals') ?></strong></div>
<div class="panel-body" style="padding-bottom:8px;">
<div class="row">
<div class="col-sm-3"><?= $renderCustomField('weight', $medicalVisitForm, $medicalCustomDefinitions) ?></div>
<div class="col-sm-3"><?= $renderCustomField('pulse', $medicalVisitForm, $medicalCustomDefinitions) ?></div>
<div class="col-sm-3"><?= $renderCustomField('blood_pressure', $medicalVisitForm, $medicalCustomDefinitions) ?></div>
<div class="col-sm-3"><?= $renderCustomField('oxygen', $medicalVisitForm, $medicalCustomDefinitions) ?></div>
</div>
</div>
</div>
<div class="panel panel-default" style="margin-bottom:12px;">
<div class="panel-heading"><strong><?= Yii::t('AnimalManagementModule.base', 'Conditions') ?></strong></div>
<div class="panel-body" style="padding-bottom:8px;">
<?= $renderCustomField('chronic_conditions', $medicalVisitForm, $medicalCustomDefinitions) ?>
<?= $renderCustomField('acute_conditions', $medicalVisitForm, $medicalCustomDefinitions) ?>
<?= $renderCustomField('special_needs', $medicalVisitForm, $medicalCustomDefinitions) ?>
</div>
</div>
<div class="panel panel-default" style="margin-bottom:12px;">
<div class="panel-heading"><strong><?= Yii::t('AnimalManagementModule.base', 'Medical Visit Detail') ?></strong></div>
<div class="panel-body" style="padding-bottom:8px;">
<?= $renderCustomField('date_of_most_recent_medical_visit', $medicalVisitForm, $medicalCustomDefinitions) ?>
</div>
</div>
<div class="panel panel-default" style="margin-bottom:12px;">
<div class="panel-heading"><strong><?= Yii::t('AnimalManagementModule.base', 'Media') ?></strong></div>
<div class="panel-body" style="padding-bottom:8px;">
<div class="row">
<div class="col-sm-4" style="margin-bottom:8px;">
<div id="medical-media-preview" style="border-radius:8px;overflow:hidden;background:#f2f4f6;height:150px;display:flex;align-items:center;justify-content:center;">
<?php if ($hasNewMedicalMedia): ?>
<img src="<?= Html::encode($newMedicalMediaPath) ?>" alt="<?= Yii::t('AnimalManagementModule.base', 'Selected medical media') ?>" style="width:100%;height:100%;object-fit:cover;">
<?php else: ?>
<i class="fa fa-image fa-2x" style="color:#a7b0b8;"></i>
<?php endif; ?>
</div>
</div>
<div class="col-sm-8">
<button type="button" class="btn btn-default" data-toggle="modal" data-target="#<?= Html::encode($medicalGalleryModalId) ?>" style="margin-bottom:8px;">
<i class="fa fa-photo"></i> <?= Yii::t('AnimalManagementModule.base', 'Choose from Gallery or Upload') ?>
</button>
<div class="checkbox" style="margin-top:0;">
<label>
<input type="checkbox" name="removeMedicalMedia" value="1">
<?= Yii::t('AnimalManagementModule.base', 'Remove selected media') ?>
</label>
</div>
</div>
</div>
</div>
</div>
<div class="panel panel-default" style="margin-bottom:12px;">
<div class="panel-heading"><strong><?= Yii::t('AnimalManagementModule.base', 'Physician') ?></strong></div>
<div class="panel-body" style="padding-bottom:8px;">
<div class="row">
<div class="col-sm-6"><?= $renderCustomField('physician_name', $medicalVisitForm, $medicalCustomDefinitions) ?></div>
<div class="col-sm-6"><?= $renderCustomField('physician_business_name', $medicalVisitForm, $medicalCustomDefinitions) ?></div>
<div class="col-sm-12"><?= $renderCustomField('physician_street_address', $medicalVisitForm, $medicalCustomDefinitions) ?></div>
<div class="col-sm-4"><?= $renderCustomField('physician_city', $medicalVisitForm, $medicalCustomDefinitions) ?></div>
<div class="col-sm-4"><?= $renderCustomField('physician_state', $medicalVisitForm, $medicalCustomDefinitions) ?></div>
<div class="col-sm-4"><?= $renderCustomField('physician_zip', $medicalVisitForm, $medicalCustomDefinitions) ?></div>
<div class="col-sm-4"><?= $renderCustomField('physician_cell_phone', $medicalVisitForm, $medicalCustomDefinitions) ?></div>
<div class="col-sm-4"><?= $renderCustomField('physician_business_phone', $medicalVisitForm, $medicalCustomDefinitions) ?></div>
<div class="col-sm-4"><?= $renderCustomField('physician_license_number', $medicalVisitForm, $medicalCustomDefinitions) ?></div>
</div>
</div>
</div>
<?php if (!empty($remainingMedicalDefinitions)): ?>
<div class="panel panel-default" style="margin-bottom:12px;">
<div class="panel-heading"><strong><?= Yii::t('AnimalManagementModule.base', 'Additional Details') ?></strong></div>
<div class="panel-body" style="padding-bottom:8px;">
<?php foreach ($remainingMedicalDefinitions as $fieldKey => $definition): ?>
<?= $renderCustomField($fieldKey, $medicalVisitForm, $remainingMedicalDefinitions) ?>
<?php endforeach; ?>
</div>
</div>
<?php endif; ?>
<?= Button::save(Yii::t('AnimalManagementModule.base', 'Add Medical Visit'))->submit() ?>
<?php ActiveForm::end(); ?>
</div>
</div>
</div>
</div>
<?php endif; ?>
</div>
</div>
<?php if ($canManage): ?>
<div class="modal fade" id="<?= Html::encode($medicalGalleryModalId) ?>" 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', 'Select Medical Media from Gallery') ?></h4>
</div>
<div class="modal-body">
<?php if (empty($galleryItems)): ?>
<div class="text-muted" style="margin-bottom:10px;"><?= Yii::t('AnimalManagementModule.base', 'No gallery images available yet.') ?></div>
<?php else: ?>
<div class="row" style="max-height:280px;overflow:auto;margin-bottom:10px;">
<?php foreach ($galleryItems as $galleryItem): ?>
<?php $galleryUrl = trim((string)$galleryItem->getImageUrl()); ?>
<?php if ($galleryUrl === '') { continue; } ?>
<div class="col-xs-6 col-sm-4" style="margin-bottom:8px;">
<button type="button" class="btn btn-default medical-media-select-thumb<?= $newMedicalMediaPath === $galleryUrl ? ' is-selected' : '' ?>" data-media-url="<?= Html::encode($galleryUrl) ?>" style="width:100%;padding:3px;">
<img src="<?= Html::encode($galleryUrl) ?>" alt="<?= Yii::t('AnimalManagementModule.base', 'Gallery image') ?>" style="width:100%;height:120px;object-fit:cover;display:block;">
</button>
</div>
<?php endforeach; ?>
</div>
<?php endif; ?>
<div class="form-group" style="margin-bottom:0;">
<label class="control-label" for="medicalMediaUpload"><?= Yii::t('AnimalManagementModule.base', 'Upload New Image') ?></label>
<input type="file" class="form-control" id="medicalMediaUpload" name="medicalMediaUpload" form="<?= Html::encode($medicalFormId) ?>" accept="image/*">
</div>
</div>
</div>
</div>
</div>
<?php
$this->registerJs(<<<JS
(function(){
var pageRootSelector = '#medical-visits-page';
var formSelector = '#{$medicalFormId}';
function scrollInlineEditorIntoView(editor) {
var editorNode = $(editor);
if (!editorNode.length) {
return;
}
var sideSpacing = parseFloat(editorNode.css('margin-left'));
if (!(sideSpacing > 0)) {
sideSpacing = parseFloat(editorNode.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, editorNode.offset().top - topReserve);
$('html, body').stop(true).animate({scrollTop: top}, 220);
}
function refreshMedicalVisitsPageRoot() {
return $.get(window.location.href).done(function(html) {
var doc = $('<div></div>').append($.parseHTML(html, document, true));
var nextRoot = doc.find(pageRootSelector).first();
if (!nextRoot.length) {
return;
}
$(pageRootSelector).replaceWith(nextRoot);
if (typeof window.initMedicalVisitsPage === 'function') {
window.initMedicalVisitsPage();
}
});
}
if (!window.__animalMedicalVisitsInlineListenerBound) {
window.__animalMedicalVisitsInlineListenerBound = true;
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');
}
refreshMedicalVisitsPageRoot();
}
});
}
window.initMedicalVisitsPage = function() {
$(document)
.off('shown.bs.collapse.medicalInlineScroll', pageRootSelector + ' .medical-feed-inline-editor')
.on('shown.bs.collapse.medicalInlineScroll', pageRootSelector + ' .medical-feed-inline-editor', function() {
scrollInlineEditorIntoView(this);
});
$(document)
.off('click.medicalInlineScroll', pageRootSelector + ' a[href^="#medical-edit-inline-"], ' + pageRootSelector + ' a[href="#medical-add-inline"]')
.on('click.medicalInlineScroll', pageRootSelector + ' a[href^="#medical-edit-inline-"], ' + pageRootSelector + ' a[href="#medical-add-inline"]', function() {
var target = $(this).attr('href');
if (!target || target.charAt(0) !== '#') {
return;
}
window.setTimeout(function() {
scrollInlineEditorIntoView($(target));
}, 260);
});
var preopenedEditor = $(pageRootSelector + ' .medical-feed-inline-editor.in').first();
if (preopenedEditor.length) {
window.setTimeout(function() {
scrollInlineEditorIntoView(preopenedEditor);
}, 260);
}
function renderMedicalPreview(source) {
var preview = $('#medical-media-preview');
if (!preview.length) {
return;
}
if (source) {
preview.html('<img src="' + source + '" alt="Selected medical media" style="width:100%;height:100%;object-fit:cover;">');
} else {
preview.html('<i class="fa fa-image fa-2x" style="color:#a7b0b8;"></i>');
}
}
function markSelectedMedicalThumb(value) {
$('.medical-media-select-thumb').removeClass('is-selected');
if (!value) {
return;
}
$('.medical-media-select-thumb').each(function() {
if (($(this).attr('data-media-url') || '') === value) {
$(this).addClass('is-selected');
}
});
}
$(document).off('click.medicalMediaSelect', pageRootSelector + ' .medical-media-select-thumb').on('click.medicalMediaSelect', pageRootSelector + ' .medical-media-select-thumb', function() {
var mediaUrl = $(this).attr('data-media-url') || '';
$('#medical-media-gallery-path').val(mediaUrl);
markSelectedMedicalThumb(mediaUrl);
$('#medicalMediaUpload').val('');
$('input[name="removeMedicalMedia"]').prop('checked', false);
if (mediaUrl) {
renderMedicalPreview(mediaUrl);
}
$('#{$medicalGalleryModalId}').modal('hide');
});
$(document).off('change.medicalMediaUpload', '#medicalMediaUpload').on('change.medicalMediaUpload', '#medicalMediaUpload', function() {
var file = this.files && this.files[0] ? this.files[0] : null;
if (!file) {
return;
}
$('#medical-media-gallery-path').val('');
markSelectedMedicalThumb('');
$('input[name="removeMedicalMedia"]').prop('checked', false);
var reader = new FileReader();
reader.onload = function(e) {
renderMedicalPreview(e.target.result);
$('#{$medicalGalleryModalId}').modal('hide');
};
reader.readAsDataURL(file);
});
$(document).off('shown.bs.modal.medicalMediaModal', '#{$medicalGalleryModalId}').on('shown.bs.modal.medicalMediaModal', '#{$medicalGalleryModalId}', function() {
markSelectedMedicalThumb($('#medical-media-gallery-path').val());
});
$(document).off('submit.medicalVisitAjax', formSelector).on('submit.medicalVisitAjax', formSelector, function(event) {
event.preventDefault();
var form = this;
var formData = new FormData(form);
var submitButtons = $(form).find('button[type="submit"], input[type="submit"]');
submitButtons.prop('disabled', true);
$.ajax({
url: form.action,
type: 'POST',
data: formData,
processData: false,
contentType: false
}).always(function() {
submitButtons.prop('disabled', false);
}).done(function() {
$('#{$medicalAddModalId}').modal('hide');
refreshMedicalVisitsPageRoot();
});
});
};
window.initMedicalVisitsPage();
})();
JS
, \yii\web\View::POS_END);
?>
<?php endif; ?>

View File

@@ -0,0 +1,694 @@
<?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\AnimalProgressUpdate;
use humhub\modules\animal_management\models\forms\AnimalProgressUpdateForm;
use humhub\modules\space\models\Space;
use humhub\widgets\Button;
use yii\bootstrap\ActiveForm;
use yii\helpers\Html;
/* @var Space $space */
/* @var Animal $animal */
/* @var bool $canManage */
/* @var AnimalProgressUpdateForm $progressUpdateForm */
/* @var AnimalProgressUpdate[] $progressUpdates */
/* @var AnimalGalleryItem[] $galleryItems */
$renderCustomField = static function (string $fieldKey, AnimalProgressUpdateForm $model, array $definitions): string {
if (!isset($definitions[$fieldKey])) {
return '';
}
$definition = $definitions[$fieldKey];
$inputType = (string)$definition['input_type'];
$label = (string)$definition['label'];
if ((int)$definition['required'] === 1) {
$label .= ' *';
}
$fieldName = "AnimalProgressUpdateForm[customFields][$fieldKey]";
$fieldValue = $model->customFields[$fieldKey] ?? '';
ob_start();
?>
<?php if ($inputType === 'textarea'): ?>
<div class="form-group">
<label class="control-label" for="animalprogressupdateform-customfields-<?= Html::encode($fieldKey) ?>"><?= Html::encode($label) ?></label>
<?= Html::textarea($fieldName, (string)$fieldValue, ['class' => 'form-control', 'rows' => 3, 'id' => "animalprogressupdateform-customfields-$fieldKey"]) ?>
</div>
<?php elseif ($inputType === 'boolean'): ?>
<div class="checkbox" style="margin-bottom:10px;">
<label>
<?= Html::hiddenInput($fieldName, '0') ?>
<?= Html::checkbox($fieldName, !empty($fieldValue), ['value' => '1']) ?>
<?= Html::encode($label) ?>
</label>
</div>
<?php elseif ($inputType === 'select'): ?>
<div class="form-group">
<label class="control-label" for="animalprogressupdateform-customfields-<?= Html::encode($fieldKey) ?>"><?= Html::encode($label) ?></label>
<?= Html::dropDownList(
$fieldName,
(string)$fieldValue,
$model->getCustomFieldSelectOptions($fieldKey),
['class' => 'form-control', 'prompt' => Yii::t('AnimalManagementModule.base', 'Select...'), 'id' => "animalprogressupdateform-customfields-$fieldKey"]
) ?>
</div>
<?php elseif ($inputType === 'number'): ?>
<div class="form-group">
<label class="control-label" for="animalprogressupdateform-customfields-<?= Html::encode($fieldKey) ?>"><?= Html::encode($label) ?></label>
<?= Html::input('number', $fieldName, (string)$fieldValue, ['class' => 'form-control', 'step' => 'any', 'id' => "animalprogressupdateform-customfields-$fieldKey"]) ?>
</div>
<?php elseif ($inputType === 'date'): ?>
<div class="form-group">
<label class="control-label" for="animalprogressupdateform-customfields-<?= Html::encode($fieldKey) ?>"><?= Html::encode($label) ?></label>
<?= Html::input('date', $fieldName, (string)$fieldValue, ['class' => 'form-control', 'id' => "animalprogressupdateform-customfields-$fieldKey"]) ?>
</div>
<?php elseif ($inputType === 'datetime'): ?>
<div class="form-group">
<label class="control-label" for="animalprogressupdateform-customfields-<?= Html::encode($fieldKey) ?>"><?= Html::encode($label) ?></label>
<?= Html::input('datetime-local', $fieldName, (string)$fieldValue, ['class' => 'form-control', 'id' => "animalprogressupdateform-customfields-$fieldKey"]) ?>
</div>
<?php else: ?>
<div class="form-group">
<label class="control-label" for="animalprogressupdateform-customfields-<?= Html::encode($fieldKey) ?>"><?= Html::encode($label) ?></label>
<?= Html::textInput($fieldName, (string)$fieldValue, ['class' => 'form-control', 'id' => "animalprogressupdateform-customfields-$fieldKey"]) ?>
</div>
<?php endif; ?>
<?php
return (string)ob_get_clean();
};
$progressCustomDefinitions = $progressUpdateForm->getCustomFieldDefinitions();
$knownProgressKeys = ['progress_notes', 'routine_updates', 'media_reference'];
$otherProgressCustomDefinitions = [];
foreach ($progressCustomDefinitions as $fieldKey => $definition) {
if (in_array($fieldKey, $knownProgressKeys, true)) {
continue;
}
$otherProgressCustomDefinitions[$fieldKey] = $definition;
}
$currentMediaReference = trim((string)($progressUpdateForm->customFields['media_reference'] ?? ''));
$progressAddModalId = 'add-progress-update-modal';
$progressFormId = 'add-progress-update-main-form';
$openProgressEditId = (int)Yii::$app->request->get('inlineProgressEdit', 0);
$openProgressAdd = (int)Yii::$app->request->get('inlineProgressAdd', 0) === 1;
?>
<style>
.progress-feed-card {
position: relative;
overflow: hidden;
border: 1px solid #d5dfe8;
border-radius: 12px;
background: #223446;
margin-bottom: 12px;
min-height: 240px;
box-shadow: 0 8px 22px rgba(12, 24, 36, 0.16);
}
.progress-feed-cover {
position: absolute;
inset: 0;
width: 100%;
height: 100%;
object-fit: cover;
display: block;
}
.progress-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%);
}
.progress-feed-content {
position: relative;
z-index: 1;
min-height: 240px;
padding: 14px;
display: flex;
flex-direction: column;
align-items: flex-end;
gap: 10px;
}
.progress-feed-top-row {
width: 100%;
max-width: none;
margin-left: 0;
display: flex;
align-items: center;
justify-content: space-between;
gap: 8px;
}
.progress-feed-date {
font-size: 15px;
font-weight: 700;
color: #ffffff;
margin-right: auto;
}
.progress-chip-row {
display: flex;
flex-wrap: wrap;
gap: 6px;
margin-bottom: 10px;
}
.progress-chip-row--top {
margin-bottom: 0;
justify-content: flex-end;
}
.progress-chip {
display: inline-block;
background: rgba(255, 255, 255, 0.16);
border: 1px solid rgba(255, 255, 255, 0.3);
border-radius: 999px;
padding: 4px 10px;
font-size: 12px;
color: #ffffff;
}
.progress-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;
}
.progress-feed-header {
display: flex;
justify-content: flex-end;
align-items: flex-start;
gap: 8px;
margin-bottom: 8px;
}
.progress-feed-label {
font-size: 11px;
text-transform: uppercase;
letter-spacing: .04em;
color: rgba(231, 241, 249, 0.78);
margin-bottom: 4px;
}
.progress-feed-copy {
color: #eff5fb;
margin-bottom: 10px;
}
.progress-feed-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;
}
.progress-feed-inline-editor iframe {
display: block;
width: 100%;
}
@media (max-width: 1200px) {
.progress-feed-details {
width: 55%;
max-width: 55%;
min-width: 0;
}
}
@media (max-width: 991px) {
.progress-feed-details {
width: 55%;
max-width: 55%;
min-width: 0;
}
.progress-chip-row--top {
justify-content: flex-start;
}
}
</style>
<div id="progress-updates-page" class="panel panel-default">
<div class="panel-heading" style="display:flex;align-items:center;">
<span style="font-size:20px;line-height:1.2;"><?= Yii::t('AnimalManagementModule.base', '<strong>Progress Feed</strong>') ?></span>
</div>
<div class="panel-body">
<div style="display:flex;gap:18px;flex-wrap:wrap;align-items:center;margin-bottom:12px;">
<?= Html::a(
Html::encode($animal->getDisplayName()),
$space->createUrl('/animal_management/animals/view', ['id' => $animal->id]),
['style' => 'font-size:20px;font-weight:700;line-height:1.2;']
) ?>
<?= Html::a(
Yii::t('AnimalManagementModule.base', 'All Animals'),
$space->createUrl('/animal_management/animals/index'),
['style' => 'font-size:16px;line-height:1.2;']
) ?>
<?php if ($canManage): ?>
<?= Html::a('<i class="fa fa-plus"></i> ' . Yii::t('AnimalManagementModule.base', 'Add Progress Update'), '#progress-add-inline', [
'class' => 'btn btn-success btn-sm',
'title' => Yii::t('AnimalManagementModule.base', 'Add Progress Update'),
'aria-label' => Yii::t('AnimalManagementModule.base', 'Add Progress Update'),
'data-toggle' => 'collapse',
]) ?>
<?php endif; ?>
</div>
<?php if ($canManage): ?>
<div id="progress-add-inline" class="collapse progress-feed-inline-editor<?= $openProgressAdd ? ' in' : '' ?>" style="margin:0 0 14px 0;">
<iframe
src="<?= Html::encode($space->createUrl('/animal_management/animals/add-progress-update-inline', ['id' => $animal->id, 'inline' => 1, 'returnTo' => 'progress-updates', '_v' => time()])) ?>"
style="width:100%;min-height:860px;border:1px solid rgba(255,255,255,0.22);border-radius:10px;background:transparent;"
loading="lazy"
></iframe>
</div>
<?php endif; ?>
<?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 id="progress-update-<?= (int)$update->id ?>" class="progress-feed-card">
<?php if ($hasMediaImage): ?>
<img class="progress-feed-cover" src="<?= Html::encode($mediaReference) ?>" alt="<?= Yii::t('AnimalManagementModule.base', 'Progress media') ?>">
<?php endif; ?>
<div class="progress-feed-overlay"></div>
<div class="progress-feed-content">
<div class="progress-feed-top-row">
<span class="progress-feed-date"><?= Html::encode(DateDisplayHelper::format((string)$update->update_at)) ?></span>
<?php if (!empty($update->weight) || !empty($update->vitals)): ?>
<div class="progress-chip-row progress-chip-row--top">
<?php if (!empty($update->weight)): ?><span class="progress-chip"><?= Yii::t('AnimalManagementModule.base', 'Weight') ?>: <?= Html::encode((string)$update->weight) ?></span><?php endif; ?>
<?php if (!empty($update->vitals)): ?><span class="progress-chip"><?= Yii::t('AnimalManagementModule.base', 'Vitals') ?></span><?php endif; ?>
</div>
<?php endif; ?>
</div>
<div class="progress-feed-details">
<div class="progress-feed-header">
<?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="progress-feed-label"><?= Yii::t('AnimalManagementModule.base', 'Vitals') ?></div><div class="progress-feed-copy"><?= nl2br(Html::encode($update->vitals)) ?></div><?php endif; ?>
<?php if (!empty($update->behavior_notes)): ?><div class="progress-feed-label"><?= Yii::t('AnimalManagementModule.base', 'Behavior') ?></div><div class="progress-feed-copy"><?= nl2br(Html::encode($update->behavior_notes)) ?></div><?php endif; ?>
<?php if (!empty($update->meal_plan_changes)): ?><div class="progress-feed-label"><?= Yii::t('AnimalManagementModule.base', 'Meal Plan') ?></div><div class="progress-feed-copy"><?= nl2br(Html::encode($update->meal_plan_changes)) ?></div><?php endif; ?>
<?php if (!empty($update->housing_changes)): ?><div class="progress-feed-label"><?= Yii::t('AnimalManagementModule.base', 'Housing') ?></div><div class="progress-feed-copy"><?= nl2br(Html::encode($update->housing_changes)) ?></div><?php endif; ?>
<?php if (!empty($update->medical_concerns)): ?><div class="progress-feed-label"><?= Yii::t('AnimalManagementModule.base', 'Medical Concerns') ?></div><div class="progress-feed-copy"><?= nl2br(Html::encode($update->medical_concerns)) ?></div><?php endif; ?>
<?php if (!empty($progressCustomDisplayValues)): ?>
<div class="progress-feed-label"><?= Yii::t('AnimalManagementModule.base', 'Additional Fields') ?></div>
<div class="progress-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 progress-feed-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, 'returnTo' => 'progress-updates', '_v' => time()])) ?>"
style="width:100%;min-height:760px;border:1px solid rgba(255,255,255,0.22);border-radius:10px;background:transparent;"
loading="lazy"
></iframe>
</div>
<?php endif; ?>
</div>
</div>
<?php endforeach; ?>
<?php endif; ?>
<?php if ($canManage): ?>
<div class="modal fade" id="<?= Html::encode($progressAddModalId) ?>" 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 Progress Update') ?></h4>
</div>
<div class="modal-body">
<?php $progressForm = ActiveForm::begin([
'action' => $space->createUrl('/animal_management/animals/add-progress-update', ['id' => $animal->id]),
'options' => ['id' => $progressFormId, 'enctype' => 'multipart/form-data'],
]); ?>
<?= Html::hiddenInput('returnTo', 'progress-updates') ?>
<?= $progressForm->errorSummary($progressUpdateForm, ['showAllErrors' => true]) ?>
<div class="panel panel-default" style="margin-bottom:12px;">
<div class="panel-heading"><strong><?= Yii::t('AnimalManagementModule.base', 'Progress Update') ?></strong></div>
<div class="panel-body" style="padding-bottom:8px;">
<div class="row">
<div class="col-sm-4"><?= $progressForm->field($progressUpdateForm, 'weight') ?></div>
<div class="col-sm-8"><?= $progressForm->field($progressUpdateForm, 'vitals')->textInput(['maxlength' => 255]) ?></div>
<div class="col-sm-6"><?= $progressForm->field($progressUpdateForm, 'behavior_notes')->textarea(['rows' => 2]) ?></div>
<div class="col-sm-6"><?= $progressForm->field($progressUpdateForm, 'medical_concerns')->textarea(['rows' => 2]) ?></div>
<div class="col-sm-6"><?= $progressForm->field($progressUpdateForm, 'meal_plan_changes')->textarea(['rows' => 2]) ?></div>
<div class="col-sm-6"><?= $progressForm->field($progressUpdateForm, 'housing_changes')->textarea(['rows' => 2]) ?></div>
</div>
</div>
</div>
<div class="panel panel-default" style="margin-bottom:12px;">
<div class="panel-heading"><strong><?= Yii::t('AnimalManagementModule.base', 'Notes') ?></strong></div>
<div class="panel-body" style="padding-bottom:8px;">
<?= $renderCustomField('progress_notes', $progressUpdateForm, $progressCustomDefinitions) ?>
<?= $renderCustomField('routine_updates', $progressUpdateForm, $progressCustomDefinitions) ?>
</div>
</div>
<?php if (!empty($otherProgressCustomDefinitions)): ?>
<div class="panel panel-default" style="margin-bottom:12px;">
<div class="panel-heading"><strong><?= Yii::t('AnimalManagementModule.base', 'Additional Details') ?></strong></div>
<div class="panel-body" style="padding-bottom:8px;">
<?php foreach ($otherProgressCustomDefinitions as $fieldKey => $definition): ?>
<?= $renderCustomField($fieldKey, $progressUpdateForm, $otherProgressCustomDefinitions) ?>
<?php endforeach; ?>
</div>
</div>
<?php endif; ?>
<div class="panel panel-default" style="margin-bottom:12px;">
<div class="panel-heading"><strong><?= Yii::t('AnimalManagementModule.base', 'Media') ?></strong></div>
<div class="panel-body" style="padding-bottom:8px;">
<input type="hidden" id="progress-media-gallery-path" name="progressMediaGalleryPath" value="<?= Html::encode($currentMediaReference) ?>">
<div class="row">
<div class="col-sm-4" style="margin-bottom:8px;">
<div id="progress-media-preview" style="border-radius:8px;overflow:hidden;background:#f2f4f6;height:150px;display:flex;align-items:center;justify-content:center;">
<?php if ($currentMediaReference !== '' && (preg_match('/^https?:\/\//i', $currentMediaReference) || substr($currentMediaReference, 0, 1) === '/')): ?>
<img src="<?= Html::encode($currentMediaReference) ?>" alt="<?= Yii::t('AnimalManagementModule.base', 'Selected media') ?>" style="width:100%;height:100%;object-fit:cover;">
<?php else: ?>
<i class="fa fa-image fa-2x" style="color:#a7b0b8;"></i>
<?php endif; ?>
</div>
</div>
<div class="col-sm-8">
<button type="button" class="btn btn-default" data-toggle="modal" data-target="#progress-media-modal" style="margin-bottom:8px;">
<i class="fa fa-photo"></i> <?= Yii::t('AnimalManagementModule.base', 'Choose from Gallery or Upload') ?>
</button>
<div class="checkbox" style="margin-top:0;">
<label>
<input type="checkbox" name="removeProgressMedia" value="1">
<?= Yii::t('AnimalManagementModule.base', 'Remove selected media') ?>
</label>
</div>
</div>
</div>
</div>
</div>
<div class="panel panel-default" style="margin-bottom:12px;">
<div class="panel-heading"><strong><?= Yii::t('AnimalManagementModule.base', 'Social Post') ?></strong></div>
<div class="panel-body" style="padding-bottom:8px;">
<?= $progressForm->field($progressUpdateForm, 'post_to_space_feed')->checkbox() ?>
<?= $progressForm->field($progressUpdateForm, 'post_to_animal_feed')->checkbox() ?>
</div>
</div>
<?= Button::save(Yii::t('AnimalManagementModule.base', 'Add Progress Update'))->submit() ?>
<?php ActiveForm::end(); ?>
</div>
</div>
</div>
</div>
<div class="modal fade" id="progress-media-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', 'Select Progress Media') ?></h4>
</div>
<div class="modal-body">
<?php if (empty($galleryItems)): ?>
<div class="text-muted" style="margin-bottom:10px;"><?= Yii::t('AnimalManagementModule.base', 'No gallery images available yet.') ?></div>
<?php else: ?>
<div class="row" style="max-height:280px;overflow:auto;margin-bottom:10px;">
<?php foreach ($galleryItems as $galleryItem): ?>
<?php $galleryUrl = trim((string)$galleryItem->getImageUrl()); ?>
<?php if ($galleryUrl === '') { continue; } ?>
<div class="col-xs-6 col-sm-4" style="margin-bottom:8px;">
<button type="button" class="btn btn-default progress-media-select-thumb" data-media-url="<?= Html::encode($galleryUrl) ?>" style="width:100%;padding:3px;">
<img src="<?= Html::encode($galleryUrl) ?>" alt="<?= Yii::t('AnimalManagementModule.base', 'Gallery image') ?>" style="width:100%;height:120px;object-fit:cover;border-radius:4px;">
</button>
</div>
<?php endforeach; ?>
</div>
<?php endif; ?>
<div class="form-group" style="margin-bottom:0;">
<label class="control-label" for="progressMediaUpload"><?= Yii::t('AnimalManagementModule.base', 'Upload New Image') ?></label>
<input type="file" class="form-control" id="progressMediaUpload" name="progressMediaUpload" form="<?= Html::encode($progressFormId) ?>" accept="image/*">
</div>
</div>
</div>
</div>
</div>
<?php endif; ?>
</div>
</div>
<?php
$this->registerCss(<<<CSS
.progress-media-select-thumb.is-selected {
border-color: #2f7df4 !important;
box-shadow: 0 0 0 2px rgba(47, 125, 244, 0.22);
}
CSS
);
$this->registerJs(<<<JS
(function() {
var pageRootSelector = '#progress-updates-page';
var formSelector = '#{$progressFormId}';
function scrollInlineEditorIntoView(editor) {
var editorNode = $(editor);
if (!editorNode.length) {
return;
}
var sideSpacing = parseFloat(editorNode.css('margin-left'));
if (!(sideSpacing > 0)) {
sideSpacing = parseFloat(editorNode.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, editorNode.offset().top - topReserve);
$('html, body').stop(true).animate({scrollTop: top}, 220);
}
function refreshProgressUpdatesPageRoot() {
return $.get(window.location.href).done(function(html) {
var doc = $('<div></div>').append($.parseHTML(html, document, true));
var nextRoot = doc.find(pageRootSelector).first();
if (!nextRoot.length) {
return;
}
$(pageRootSelector).replaceWith(nextRoot);
if (typeof window.initProgressUpdatesPage === 'function') {
window.initProgressUpdatesPage();
}
});
}
if (!window.__animalProgressUpdatesInlineListenerBound) {
window.__animalProgressUpdatesInlineListenerBound = true;
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');
}
refreshProgressUpdatesPageRoot();
}
});
}
window.initProgressUpdatesPage = function() {
$(document)
.off('shown.bs.collapse.progressInlineScroll', pageRootSelector + ' .progress-feed-inline-editor')
.on('shown.bs.collapse.progressInlineScroll', pageRootSelector + ' .progress-feed-inline-editor', function() {
scrollInlineEditorIntoView(this);
});
$(document)
.off('click.progressInlineScroll', pageRootSelector + ' a[href^="#progress-edit-inline-"], ' + pageRootSelector + ' a[href="#progress-add-inline"]')
.on('click.progressInlineScroll', pageRootSelector + ' a[href^="#progress-edit-inline-"], ' + pageRootSelector + ' a[href="#progress-add-inline"]', function() {
var target = $(this).attr('href');
if (!target || target.charAt(0) !== '#') {
return;
}
window.setTimeout(function() {
scrollInlineEditorIntoView($(target));
}, 260);
});
var preopenedEditor = $(pageRootSelector + ' .progress-feed-inline-editor.in').first();
if (preopenedEditor.length) {
window.setTimeout(function() {
scrollInlineEditorIntoView(preopenedEditor);
}, 260);
}
function markSelectedMediaThumb(value) {
$('.progress-media-select-thumb').removeClass('is-selected');
if (!value) {
return;
}
$('.progress-media-select-thumb').each(function() {
if (($(this).attr('data-media-url') || '') === value) {
$(this).addClass('is-selected');
}
});
}
$(document).off('click.progressMediaSelect', '.progress-media-select-thumb').on('click.progressMediaSelect', '.progress-media-select-thumb', function(event) {
event.preventDefault();
var mediaUrl = $(this).attr('data-media-url') || '';
$('#progress-media-gallery-path').val(mediaUrl);
markSelectedMediaThumb(mediaUrl);
$('#progressMediaUpload').val('');
$('input[name="removeProgressMedia"]').prop('checked', false);
if (mediaUrl) {
$('#progress-media-preview').html('<img src="' + mediaUrl + '" alt="Selected media" style="width:100%;height:100%;object-fit:cover;">');
}
$('#progress-media-modal').modal('hide');
});
$(document).off('change.progressMediaUpload', '#progressMediaUpload').on('change.progressMediaUpload', '#progressMediaUpload', function() {
var file = this.files && this.files[0] ? this.files[0] : null;
if (!file) {
return;
}
$('#progress-media-gallery-path').val('');
markSelectedMediaThumb('');
$('input[name="removeProgressMedia"]').prop('checked', false);
var reader = new FileReader();
reader.onload = function(e) {
$('#progress-media-preview').html('<img src="' + e.target.result + '" alt="Selected media" style="width:100%;height:100%;object-fit:cover;">');
$('#progress-media-modal').modal('hide');
};
reader.readAsDataURL(file);
});
$(document).off('shown.bs.modal.progressMediaModal', '#progress-media-modal').on('shown.bs.modal.progressMediaModal', '#progress-media-modal', function() {
markSelectedMediaThumb($('#progress-media-gallery-path').val());
});
$(document).off('show.bs.modal.progressMediaStack', '#progress-media-modal').on('show.bs.modal.progressMediaStack', '#progress-media-modal', function() {
var zIndex = 1060 + (10 * $('.modal.in:visible').length);
$(this).css('z-index', zIndex);
window.setTimeout(function() {
$('.modal-backdrop').not('.progress-media-stack').last().css('z-index', zIndex - 1).addClass('progress-media-stack');
}, 0);
});
$(document).off('hidden.bs.modal.progressMediaStack', '#progress-media-modal').on('hidden.bs.modal.progressMediaStack', '#progress-media-modal', function() {
$(this).css('z-index', '');
if ($('#{$progressAddModalId}').is(':visible')) {
$('body').addClass('modal-open');
}
});
$(document).off('submit.progressAjax', formSelector).on('submit.progressAjax', formSelector, function(event) {
event.preventDefault();
var form = this;
var formData = new FormData(form);
var submitButtons = $(form).find('button[type="submit"], input[type="submit"]');
submitButtons.prop('disabled', true);
$.ajax({
url: form.action,
type: 'POST',
data: formData,
processData: false,
contentType: false
}).always(function() {
submitButtons.prop('disabled', false);
}).done(function() {
$('#{$progressAddModalId}').modal('hide');
refreshProgressUpdatesPageRoot();
});
});
};
window.initProgressUpdatesPage();
})();
JS
);
?>

View File

@@ -0,0 +1,31 @@
<?php
use humhub\modules\animal_management\models\Animal;
use humhub\modules\animal_management\models\forms\TransferRequestForm;
use humhub\modules\space\models\Space;
use humhub\widgets\Button;
use yii\bootstrap\ActiveForm;
/* @var Space $space */
/* @var Animal $animal */
/* @var TransferRequestForm $model */
?>
<div class="panel panel-default">
<div class="panel-heading">
<?= Yii::t('AnimalManagementModule.base', '<strong>Transfer Request</strong> for {animal}', ['animal' => $animal->getDisplayName()]) ?>
</div>
<div class="panel-body">
<?php $form = ActiveForm::begin(); ?>
<?= $form->field($model, 'to_space_id')->dropDownList($model->getTargetOptions(), ['prompt' => Yii::t('AnimalManagementModule.base', 'Select destination rescue')]) ?>
<?= $form->field($model, 'request_message')->textarea(['rows' => 4]) ?>
<?= $form->field($model, 'conditions_text')->textarea(['rows' => 3]) ?>
<?= Button::save(Yii::t('AnimalManagementModule.base', 'Send Request'))->submit() ?>
<?= Button::asLink(Yii::t('AnimalManagementModule.base', 'Cancel'))
->link($space->createUrl('/animal_management/animals/index')) ?>
<?php ActiveForm::end(); ?>
</div>
</div>

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>

View File

@@ -0,0 +1,104 @@
<?php
use humhub\libs\Html;
use humhub\modules\animal_management\models\Animal;
use humhub\modules\animal_management\models\AnimalGalleryItem;
use humhub\modules\gallery\assets\Assets as GalleryAssets;
use humhub\modules\gallery\helpers\Url;
use humhub\modules\gallery\models\CustomGallery;
use humhub\modules\space\models\Space;
use humhub\widgets\Button;
/* @var CustomGallery $gallery */
/* @var Animal|null $animal */
/* @var AnimalGalleryItem[] $items */
/* @var Space $container */
/* @var bool|string $showMore */
GalleryAssets::register($this);
$animalName = $animal instanceof Animal ? $animal->getDisplayName() : Yii::t('AnimalManagementModule.base', 'Animal');
$backUrl = Url::toGalleryOverview($container);
$animalUrl = $animal instanceof Animal
? $container->createUrl('/animal_management/animals/view', ['id' => (int)$animal->id])
: null;
$uiGalleryId = 'animal-gallery-native-' . (int)$gallery->id;
$descriptionText = preg_replace('/\s*\[animal-gallery:\d+\]\s*/', ' ', (string)$gallery->description);
$descriptionText = trim((string)$descriptionText);
$headerTitle = trim((string)$gallery->title);
$headerTitle = preg_replace('/\s+Gallery\s*$/i', '', $headerTitle);
if ($headerTitle === '') {
$headerTitle = $animalName;
}
?>
<div id="gallery-container" class="panel panel-default">
<div class="panel-heading clearfix" style="background-color: <?= $this->theme->variable('background-color-secondary') ?>;">
<div style="margin-right:40px;" class="pull-left">
<?= Yii::t('GalleryModule.base', '<strong>Gallery</strong> ') . Html::encode($headerTitle) ?>
</div>
<?= Button::back($backUrl, Yii::t('GalleryModule.base', 'Back to overview'))->right()->sm() ?>
</div>
<div class="panel-body">
<?php if ($animalUrl !== null): ?>
<div style="margin-bottom:10px;">
<?= Html::a(Html::encode($animalName), $animalUrl, ['style' => 'font-size:20px;font-weight:700;line-height:1.2;']) ?>
</div>
<?php endif; ?>
<?php if ($descriptionText !== ''): ?>
<div class="row clearfix" style="padding-bottom:8px;">
<div class="col-sm-12 gallery-description">
<i class="fa fa-arrow-circle-right"></i>
<?= Html::encode($descriptionText) ?>
</div>
</div>
<?php endif; ?>
<div id="gallery-list" class="col">
<div id="gallery-media-container" class="row">
<?php if (empty($items)): ?>
<div class="col-sm-12 text-muted" style="margin-bottom:10px;">
<?= Yii::t('AnimalManagementModule.base', 'No gallery images yet.') ?>
</div>
<?php else: ?>
<?php foreach ($items as $item): ?>
<?php
if (!$item instanceof AnimalGalleryItem) {
continue;
}
$imageUrl = trim((string)$item->getImageUrl());
if ($imageUrl === '') {
continue;
}
$title = trim((string)$item->caption);
$altText = $title !== '' ? $title : $animalName;
?>
<div class="col-sm-6 col-md-4 gallery-list-entry" style="margin-bottom:12px;">
<a href="<?= Html::encode($imageUrl) ?>#.jpeg"
data-type="image"
data-toggle="lightbox"
data-parent="#gallery-content"
data-ui-gallery="<?= Html::encode($uiGalleryId) ?>"
data-description="<?= Html::encode($title) ?>"
title="<?= Html::encode($title) ?>"
style="display:block;position:relative;aspect-ratio:4 / 3;background:#d8dee8;border-radius:12px;overflow:hidden;box-shadow:0 8px 24px rgba(15,23,42,0.14);">
<img class="gallery-img" src="<?= Html::encode($imageUrl) ?>" alt="<?= Html::encode($altText) ?>" style="display:none;position:absolute;inset:0;width:100%;height:100%;object-fit:cover;"/>
</a>
</div>
<?php endforeach; ?>
<?php endif; ?>
</div>
</div>
<?php if ($showMore): ?>
<div style="text-align:center;">
<?= Button::primary(Yii::t('GalleryModule.base', 'Show more'))->action('gallery.showMore', (string)$showMore) ?>
</div>
<?php endif; ?>
</div>
</div>

168
views/settings/index.php Normal file
View File

@@ -0,0 +1,168 @@
<?php
use humhub\modules\animal_management\models\forms\DisplaySettingsForm;
use humhub\modules\animal_management\models\forms\FieldDefinitionSettingsForm;
use humhub\widgets\Button;
use yii\bootstrap\ActiveForm;
use yii\helpers\Html;
/* @var string|null $subNav */
/* @var int $animalCount */
/* @var FieldDefinitionSettingsForm $fieldSettingsForm */
/* @var DisplaySettingsForm $displaySettingsForm */
?>
<div class="panel panel-default">
<div class="panel-heading"><?= Yii::t('AnimalManagementModule.base', '<strong>Animal Management</strong> Settings') ?></div>
<?php if (!empty($subNav)): ?>
<?= $subNav ?>
<?php endif; ?>
<div class="panel-body">
<p><?= Yii::t('AnimalManagementModule.base', 'Configure intake/profile field definitions used by Animal Management.') ?></p>
<div class="well well-sm" style="margin-bottom:12px;">
<strong><?= Yii::t('AnimalManagementModule.base', 'Current animals in this space') ?>:</strong>
<?= (int)$animalCount ?>
</div>
<h4 style="margin-top:0;"><?= Yii::t('AnimalManagementModule.base', 'Display Settings') ?></h4>
<?php $displayForm = ActiveForm::begin(); ?>
<?= $displayForm->errorSummary($displaySettingsForm, ['showAllErrors' => true]) ?>
<div class="row">
<div class="col-md-8">
<?= $displayForm->field($displaySettingsForm, 'search_block_heading')->textInput(['maxlength' => 190]) ?>
</div>
</div>
<div class="row">
<div class="col-md-6">
<label class="control-label" style="display:block;"><?= Yii::t('AnimalManagementModule.base', 'Animal Tile Fields') ?></label>
<?= Html::checkboxList(
'DisplaySettingsForm[tile_fields]',
$displaySettingsForm->tile_fields,
DisplaySettingsForm::fieldOptions(),
['separator' => '<br>']
) ?>
<p class="help-block" style="margin-top:6px;"><?= Yii::t('AnimalManagementModule.base', 'ID is hidden by default unless selected here.') ?></p>
</div>
<div class="col-md-6">
<label class="control-label" style="display:block;"><?= Yii::t('AnimalManagementModule.base', 'Animal Detail Hero Fields') ?></label>
<?= Html::checkboxList(
'DisplaySettingsForm[detail_fields]',
$displaySettingsForm->detail_fields,
DisplaySettingsForm::fieldOptions(),
['separator' => '<br>']
) ?>
</div>
</div>
<div style="margin-bottom:16px;">
<?= Button::save(Yii::t('AnimalManagementModule.base', 'Save Display Settings'))->submit() ?>
</div>
<?php ActiveForm::end(); ?>
<?php if (!$fieldSettingsForm->canUseFieldDefinition()): ?>
<div class="alert alert-warning" style="margin-bottom:0;">
<?= Yii::t('AnimalManagementModule.base', 'Field definition storage is unavailable. Enable rescue foundation migrations first.') ?>
</div>
<?php else: ?>
<?php $form = ActiveForm::begin(); ?>
<?php if ($fieldSettingsForm->hasErrors('rows')): ?>
<div class="alert alert-danger">
<?= Html::errorSummary($fieldSettingsForm, ['header' => '', 'footer' => '', 'showAllErrors' => true]) ?>
</div>
<?php endif; ?>
<h4 style="margin-top:0;"><?= Yii::t('AnimalManagementModule.base', 'Configured Fields') ?></h4>
<?php if (empty($fieldSettingsForm->rows)): ?>
<div class="alert alert-info"><?= Yii::t('AnimalManagementModule.base', 'No field definitions found for this module.') ?></div>
<?php else: ?>
<div class="table-responsive" style="margin-bottom:12px;">
<table class="table table-condensed table-hover">
<thead>
<tr>
<th><?= Yii::t('AnimalManagementModule.base', 'Field Key') ?></th>
<th><?= Yii::t('AnimalManagementModule.base', 'Label') ?></th>
<th><?= Yii::t('AnimalManagementModule.base', 'Group') ?></th>
<th><?= Yii::t('AnimalManagementModule.base', 'Input Type') ?></th>
<th><?= Yii::t('AnimalManagementModule.base', 'Required') ?></th>
<th><?= Yii::t('AnimalManagementModule.base', 'Active') ?></th>
<th><?= Yii::t('AnimalManagementModule.base', 'Visibility') ?></th>
<th><?= Yii::t('AnimalManagementModule.base', 'Sort') ?></th>
<th><?= Yii::t('AnimalManagementModule.base', 'Remove') ?></th>
</tr>
</thead>
<tbody>
<?php foreach ($fieldSettingsForm->rows as $index => $row): ?>
<?php $isCore = ((int)($row['is_core'] ?? 0)) === 1; ?>
<tr>
<td>
<?= Html::hiddenInput("FieldDefinitionSettingsForm[rows][$index][id]", (int)$row['id']) ?>
<?= Html::hiddenInput("FieldDefinitionSettingsForm[rows][$index][field_key]", (string)$row['field_key']) ?>
<?= Html::encode((string)$row['field_key']) ?>
<?php if ($isCore): ?>
<span class="label label-default" style="margin-left:4px;"><?= Yii::t('AnimalManagementModule.base', 'Core') ?></span>
<?php endif; ?>
</td>
<td><?= Html::textInput("FieldDefinitionSettingsForm[rows][$index][label]", (string)$row['label'], ['class' => 'form-control input-sm']) ?></td>
<td><?= Html::textInput("FieldDefinitionSettingsForm[rows][$index][group_key]", (string)$row['group_key'], ['class' => 'form-control input-sm']) ?></td>
<td>
<?= Html::encode((string)$row['input_type']) ?>
<?= Html::hiddenInput("FieldDefinitionSettingsForm[rows][$index][input_type]", (string)$row['input_type']) ?>
</td>
<td class="text-center" style="vertical-align:middle;">
<?= Html::checkbox("FieldDefinitionSettingsForm[rows][$index][required]", !empty($row['required']), ['disabled' => $isCore]) ?>
<?php if ($isCore): ?>
<?= Html::hiddenInput("FieldDefinitionSettingsForm[rows][$index][required]", !empty($row['required']) ? 1 : 0) ?>
<?php endif; ?>
</td>
<td class="text-center" style="vertical-align:middle;">
<?= Html::checkbox("FieldDefinitionSettingsForm[rows][$index][is_active]", !empty($row['is_active']), ['disabled' => $isCore]) ?>
<?php if ($isCore): ?>
<?= Html::hiddenInput("FieldDefinitionSettingsForm[rows][$index][is_active]", !empty($row['is_active']) ? 1 : 0) ?>
<?php endif; ?>
</td>
<td>
<?= Html::dropDownList(
"FieldDefinitionSettingsForm[rows][$index][visibility]",
(string)$row['visibility'],
FieldDefinitionSettingsForm::visibilityOptions(),
['class' => 'form-control input-sm']
) ?>
</td>
<td><?= Html::input('number', "FieldDefinitionSettingsForm[rows][$index][sort_order]", (int)$row['sort_order'], ['class' => 'form-control input-sm', 'style' => 'max-width:90px;']) ?></td>
<td class="text-center" style="vertical-align:middle;">
<?php if (!$isCore): ?>
<?= Html::checkbox("FieldDefinitionSettingsForm[rows][$index][remove]", false) ?>
<?php else: ?>
<span class="text-muted">-</span>
<?php endif; ?>
</td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
</div>
<?php endif; ?>
<h4><?= Yii::t('AnimalManagementModule.base', 'Add Custom Field') ?></h4>
<div class="row">
<div class="col-md-3"><?= $form->field($fieldSettingsForm, 'new_field_key')->textInput(['placeholder' => 'e.g. coat_color']) ?></div>
<div class="col-md-3"><?= $form->field($fieldSettingsForm, 'new_label') ?></div>
<div class="col-md-2"><?= $form->field($fieldSettingsForm, 'new_input_type')->dropDownList(FieldDefinitionSettingsForm::inputTypeOptions()) ?></div>
<div class="col-md-2"><?= $form->field($fieldSettingsForm, 'new_group_key') ?></div>
<div class="col-md-2"><?= $form->field($fieldSettingsForm, 'new_sort_order')->input('number') ?></div>
</div>
<div class="row">
<div class="col-md-3"><?= $form->field($fieldSettingsForm, 'new_visibility')->dropDownList(FieldDefinitionSettingsForm::visibilityOptions()) ?></div>
<div class="col-md-3"><?= $form->field($fieldSettingsForm, 'new_required')->checkbox() ?></div>
<div class="col-md-6"><?= $form->field($fieldSettingsForm, 'new_options')->textInput(['placeholder' => 'Optional JSON options']) ?></div>
</div>
<?= Button::save(Yii::t('AnimalManagementModule.base', 'Save Field Settings'))->submit() ?>
<?php ActiveForm::end(); ?>
<?php endif; ?>
</div>
</div>

View File

@@ -0,0 +1,42 @@
<?php
use humhub\modules\animal_management\models\Animal;
use yii\helpers\Html;
/* @var Animal[] $animals */
/* @var \humhub\modules\content\components\ContentContainerActiveRecord $contentContainer */
/* @var string $queryValue */
?>
<form method="get" action="<?= Html::encode($contentContainer->createUrl('/animal_management/animals/index')) ?>" style="margin-bottom:10px;">
<div class="input-group">
<input type="text" class="form-control" name="q" value="<?= Html::encode($queryValue) ?>" placeholder="<?= Yii::t('AnimalManagementModule.base', 'Search by name, species, or ID') ?>">
<span class="input-group-btn">
<button class="btn btn-default" type="submit"><?= Yii::t('AnimalManagementModule.base', 'Search') ?></button>
</span>
</div>
</form>
<?php if (empty($animals)): ?>
<div class="text-muted" style="margin-bottom:8px;">
<?= Yii::t('AnimalManagementModule.base', 'No animals found yet.') ?>
</div>
<?php else: ?>
<ul class="list-unstyled" style="margin-bottom:10px;">
<?php foreach ($animals as $animal): ?>
<li style="padding:6px 0;border-bottom:1px solid #f0f0f0;">
<strong><?= Html::encode($animal->getDisplayName()) ?></strong>
<div class="text-muted" style="font-size:12px;">
<?= Html::encode($animal->animal_uid) ?>
<?php if (!empty($animal->species)): ?>
· <?= Html::encode($animal->species) ?>
<?php endif; ?>
</div>
</li>
<?php endforeach; ?>
</ul>
<?php endif; ?>
<a class="btn btn-primary btn-sm" href="<?= Html::encode($contentContainer->createUrl('/animal_management/animals/index')) ?>">
<?= Yii::t('AnimalManagementModule.base', 'Open Animals') ?>
</a>