Globinours/app/Views/litter_form.php

226 lines
12 KiB
PHP

<?php
$coatColors = [
'Bicolore Noir / Blanc' => t('coat.black_white'), 'Bicolore Gris / Blanc' => t('coat.grey_white'), 'Bicolore Roux / Blanc' => t('coat.ginger_white'), 'Bicolore Tigré et Blanc' => t('coat.tabby_white'),
'Gris' => t('coat.grey'), 'Roux' => t('coat.ginger'), 'Noir' => t('coat.black'), 'Chocolat' => t('coat.chocolate'), 'Blanc' => t('coat.white'), 'Tricolore / Calico' => t('coat.calico'),
'Écaille de tortue' => t('coat.tortoiseshell'), 'Colourpoint / Siamois' => t('coat.colourpoint'), 'Tigré / Tabby' => t('coat.tabby'),
];
$prefillCount = max(0, min(20, (int)($_GET['count'] ?? 4)));
?>
<form method="post" action="/litter/save" class="card">
<input type="hidden" name="csrf" value="<?= h(Auth::csrf()) ?>">
<div class="card-header d-flex align-items-center justify-content-between">
<h3 class="card-title"><?= h(t('litter.create')) ?></h3>
<a href="<?= $sourceAnimalId ? '/animal?id=' . (int)$sourceAnimalId . '#tab-litters' : '/animals' ?>"
class="btn btn-ghost-secondary"><?= h(t('common.cancel')) ?></a>
</div>
<div class="card-body">
<input type="hidden" name="source_animal_id" value="<?= (int)$sourceAnimalId ?>">
<div class="row g-3 mb-4">
<div class="col-md-4">
<label class="form-label"><?= h(t('litter.mother')) ?> <span
class="text-muted"><?= h(t('common.optional_parentheses')) ?></span></label>
<select class="form-select" name="mother_id" id="motherId">
<option value="">— <?= h(t('litter.unknown_parent')) ?> —</option>
<?php foreach ($mothers as $parent): ?>
<option value="<?= (int)$parent['id'] ?>"
<?= (int)$motherId === (int)$parent['id'] ? 'selected' : '' ?>><?= h($parent['name']) ?>
</option>
<?php endforeach; ?>
</select>
</div>
<div class="col-md-4">
<label class="form-label"><?= h(t('litter.father')) ?> <span
class="text-muted"><?= h(t('common.optional_parentheses')) ?></span></label>
<select class="form-select" name="father_id" id="fatherId">
<option value="">— <?= h(t('litter.unknown_parent')) ?> —</option>
<?php foreach ($fathers as $parent): ?>
<option value="<?= (int)$parent['id'] ?>"
<?= (int)$fatherId === (int)$parent['id'] ? 'selected' : '' ?>><?= h($parent['name']) ?>
</option>
<?php endforeach; ?>
</select>
</div>
<div class="col-md-4">
<label class="form-label"><?= h(t('litter.birth_date')) ?> <span
class="text-muted"><?= h(t('common.optional_parentheses')) ?></span></label>
<input class="form-control" type="date" name="birth_date" placeholder="jj/mm/aaaa">
</div>
</div>
<input type="hidden" name="kitten_mode" id="kittenMode" value="new">
<div class="nav nav-tabs mb-4" role="tablist" aria-label="<?= h(t('litter.kitten_type')) ?>">
<button class="nav-link active" type="button" data-kitten-mode="new" aria-selected="true">✨
<?= h(t('litter.new_kittens')) ?></button>
<button class="nav-link" type="button" data-kitten-mode="existing" aria-selected="false">🔎
<?= h(t('litter.existing_cats')) ?></button>
</div>
<div id="newKittensPane">
<div class="row g-3 mb-4">
<div class="col-md-4">
<label class="form-label" for="kittenCount"><?= h(t('litter.kitten_count')) ?></label>
<input class="form-control" id="kittenCount" type="number" min="1" max="20" step="1"
value="<?= max(1, $prefillCount) ?>" required>
<div class="form-hint"><?= h(t('litter.create_records')) ?></div>
</div>
</div>
<div class="mb-2">
<h4 class="mb-0"><?= h(t('litter.new_records')) ?></h4>
<div class="text-muted small"><?= h(t('litter.auto_name')) ?></div>
</div>
<div id="kittens" class="vstack gap-2"></div>
</div>
<div id="existingKittensPane" class="d-none">
<div class="mb-3">
<h4 class="mb-1"><?= h(t('litter.select_siblings')) ?></h4>
<div class="text-muted small mb-3"><?= h(t('litter.no_new_record')) ?></div>
<input class="form-control" id="existingKittenSearch" type="search"
placeholder="<?= h(t('litter.search_name_code')) ?>" autocomplete="off">
</div>
<div class="row g-2" id="existingKittenChoices">
<?php foreach (($existingKittens ?? []) as $candidate): ?>
<div class="col-md-4 existing-kitten-choice"
data-search="<?= h(mb_strtolower($candidate['name'] . ' ' . $candidate['internal_code'])) ?>"
data-animal-id="<?= (int)$candidate['id'] ?>">
<label class="form-select d-flex align-items-center gap-2 h-100" style="cursor:pointer">
<input class="form-check-input mt-0 existing-kitten-checkbox" type="checkbox"
name="existing_kitten_ids[]" value="<?= (int)$candidate['id'] ?>" disabled>
<span class="coat-swatch" style="<?= h(coat_swatch_style($candidate['color'] ?? '')) ?>"></span>
<span><strong><?= h($candidate['name']) ?></strong><br><small
class="text-muted"><?= h($candidate['internal_code'] ?: t('animal.no_code')) ?></small></span>
<?php if (!empty($candidate['archived_at'])): ?><span
class="badge bg-secondary-lt ms-auto"><?= h(t('animal.archived_badge')) ?></span><?php endif; ?>
</label>
</div>
<?php endforeach; ?>
</div>
</div>
</div>
<div class="card-footer text-end">
<button class="btn btn-primary" type="submit"><?= h(t('litter.create_litter')) ?></button>
</div>
</form>
<template id="kittenTemplate">
<div class="card card-sm kitten-row">
<div class="card-body row g-2 align-items-end">
<div class="col-md-1 fw-bold kitten-number"></div>
<div class="col-md-4"><label class="form-label"><?= h(t('litter.name_optional')) ?></label><input
class="form-control kitten-name" placeholder="<?= h(t('litter.auto_name_placeholder')) ?>"></div>
<div class="col-md-3"><label class="form-label"><?= h(t('animal.coat')) ?></label><select
class="form-select kitten-color">
<option value=""><?= h(t('common.to_define')) ?></option>
<?php foreach ($coatColors as $value => $label): ?><option value="<?= h($value) ?>"><?= h($label) ?>
</option><?php endforeach; ?>
</select></div>
<div class="col-md-2"><label class="form-label"><?= h(t('animal.sex_label')) ?></label><select
class="form-select kitten-sex">
<option value="U"><?= h(t('common.to_define')) ?></option>
<option value="F"><?= h(t('sex.female')) ?></option>
<option value="M"><?= h(t('sex.male')) ?></option>
</select></div>
<div class="col-md-2"><button class="btn btn-outline-danger w-100 remove-kitten"
type="button"><?= h(t('common.remove')) ?></button></div>
</div>
</div>
</template>
<script>
(() => {
const list = document.getElementById('kittens');
const template = document.getElementById('kittenTemplate');
const countInput = document.getElementById('kittenCount');
const modeInput = document.getElementById('kittenMode');
const newPane = document.getElementById('newKittensPane');
const existingPane = document.getElementById('existingKittensPane');
function renumber() {
[...list.children].forEach((row, index) => {
row.querySelector('.kitten-number').textContent = 'N° ' + (index + 1);
row.querySelector('.kitten-name').name = `kittens[${index}][name]`;
row.querySelector('.kitten-color').name = `kittens[${index}][color]`;
row.querySelector('.kitten-sex').name = `kittens[${index}][sex]`;
});
}
function add() {
const row = template.content.firstElementChild.cloneNode(true);
row.querySelector('.remove-kitten').addEventListener('click', () => {
row.remove();
countInput.value = list.children.length;
renumber();
});
list.appendChild(row);
renumber();
}
function requestedCount() {
const value = parseInt(countInput.value, 10);
return Math.max(1, Math.min(20, Number.isNaN(value) ? 1 : value));
}
function hasValues(row) {
return row.querySelector('.kitten-name').value !== '' ||
row.querySelector('.kitten-color').value !== '' ||
row.querySelector('.kitten-sex').value !== 'U';
}
function syncCount(confirmRemoval = false) {
const target = requestedCount();
if (target < list.children.length) {
const removedRows = [...list.children].slice(target);
if (confirmRemoval && removedRows.some(hasValues) &&
!window.confirm( < ? = json_encode(t('litter.reduce_confirm'), JSON_UNESCAPED_UNICODE |
JSON_UNESCAPED_SLASHES) ? > )) {
countInput.value = list.children.length;
return;
}
while (list.children.length > target) list.lastElementChild.remove();
}
while (list.children.length < target) add();
countInput.value = target;
renumber();
}
countInput.addEventListener('input', () => {
if (requestedCount() > list.children.length) syncCount();
});
countInput.addEventListener('change', () => syncCount(true));
syncCount();
const existingSearch = document.getElementById('existingKittenSearch');
const mother = document.getElementById('motherId');
const father = document.getElementById('fatherId');
const syncExistingChoices = () => {
const query = existingSearch?.value.trim().toLocaleLowerCase( < ? = json_encode(I18n::locale() ===
'fr' ? 'fr' : 'en') ? > ) || '';
document.querySelectorAll('.existing-kitten-choice').forEach(choice => {
const isParent = choice.dataset.animalId === mother?.value || choice.dataset
.animalId === father?.value;
choice.classList.toggle('d-none', isParent || (query !== '' && !choice.dataset.search
.includes(query)));
if (isParent) choice.querySelector('.existing-kitten-checkbox').checked = false;
});
};
existingSearch?.addEventListener('input', syncExistingChoices);
mother?.addEventListener('change', syncExistingChoices);
father?.addEventListener('change', syncExistingChoices);
syncExistingChoices();
const setMode = mode => {
const existing = mode === 'existing';
modeInput.value = existing ? 'existing' : 'new';
newPane.classList.toggle('d-none', existing);
existingPane.classList.toggle('d-none', !existing);
newPane.querySelectorAll('input,select').forEach(input => input.disabled = existing);
existingPane.querySelectorAll('input[type="checkbox"]').forEach(input => input.disabled = !
existing);
document.querySelectorAll('[data-kitten-mode]').forEach(button => {
const active = button.dataset.kittenMode === mode;
button.classList.toggle('active', active);
button.setAttribute('aria-selected', active ? 'true' : 'false');
});
};
document.querySelectorAll('[data-kitten-mode]').forEach(button => button.addEventListener('click', () =>
setMode(button.dataset.kittenMode)));
setMode('new');
})();
</script>