226 lines
12 KiB
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>
|