diff --git a/CHANGELOG.md b/CHANGELOG.md index 47ed982..cb39f70 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,16 @@ Toutes les modifications importantes du projet sont consignées ici. Le format s’inspire de [Keep a Changelog](https://keepachangelog.com/fr/1.1.0/) et le projet suit le [versionnage sémantique](https://semver.org/lang/fr/). +## [1.0.0-rc.31] — 2026-10-01 + +### Navigation et synchronisation des dossiers animaux + +- correction du lien ouvrant la fiche d’un adoptant depuis la fiche animale ; +- ajout d’une colonne « Actions » et d’un bouton « Modifier » explicite dans la liste des vaccins ; +- transformation du rafraîchissement i-CAD en formulaire utilisable même sans JavaScript ; +- contrôle des erreurs HTTP et maintien de l’actualisation dynamique de la fiche i-CAD ; +- validation réelle du connecteur avec une fiche i-CAD, complétée par des tests de non-régression. + ## [1.0.0-rc.30] — 2026-10-01 ### Fiabilisation des dossiers et du suivi médical diff --git a/HISTORIQUE.md b/HISTORIQUE.md index 6cd62df..2b6f8d3 100644 --- a/HISTORIQUE.md +++ b/HISTORIQUE.md @@ -10,6 +10,10 @@ L’import depuis Animal Shelter Manager a été conçu comme un parcours pruden Avant la version 1.0, le projet a fait l’objet d’un nettoyage structurel et d’un découpage de ses fichiers les plus volumineux. La recherche universelle, les salles configurables, les traductions française et anglaise et une suite de contrôles isolés ont complété cette phase de stabilisation. +## `1.0.0-rc.31` — Parcours de correction plus fiables + +Les liens entre animaux et adoptants conduisent désormais directement à la bonne fiche d’annuaire. La modification des vaccins devient explicite et la synchronisation i-CAD ne dépend plus entièrement du JavaScript : un formulaire serveur assure le rafraîchissement même lorsque l’interface dynamique rencontre un problème. + ## `1.0.0-rc.30` — Corrections issues de la validation terrain Une passe croisée avec les sauvegardes ASM3 a corrigé une collision historique entre Gribouille et Lucci. Le suivi médical gagne une date de test FIV/FeLV, des catalogues administrables et une meilleure association entre tarifs et cliniques. La synchronisation I-CAD, la navigation mobile et l’identité Android ont également été adaptées aux conditions réelles d’utilisation. diff --git a/README.md b/README.md index c73a1ce..fffd27a 100644 --- a/README.md +++ b/README.md @@ -200,7 +200,7 @@ L’option `--apply` ne doit être utilisée qu’après lecture des rapports. L ## État du projet -La candidate actuelle est `1.0.0-rc.30`. La branche 1.0 est gelée fonctionnellement : seuls les défauts bloquants découverts pendant les validations terrain sont corrigés avant `1.0.0`. +La candidate actuelle est `1.0.0-rc.31`. La branche 1.0 est gelée fonctionnellement : seuls les défauts bloquants découverts pendant les validations terrain sont corrigés avant `1.0.0`. Le contrôle de stabilisation est disponible dans [docs/AUDIT-1.0.md](docs/AUDIT-1.0.md). diff --git a/VERSION b/VERSION index 370478c..99cb14c 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -1.0.0-rc.30 +1.0.0-rc.31 diff --git a/app/Controllers/AnimalsController.php b/app/Controllers/AnimalsController.php index 209c6dd..adaec7c 100644 --- a/app/Controllers/AnimalsController.php +++ b/app/Controllers/AnimalsController.php @@ -448,6 +448,7 @@ final class AnimalsController } $db = DB::pdo(); $id = (int) ($_POST['id'] ?? 0); + $wantsJson = str_contains(strtolower((string) ($_SERVER['HTTP_ACCEPT'] ?? '')), 'application/json'); if ($id <= 0) { http_response_code(400); echo h(t('error.bad_request')); @@ -468,6 +469,10 @@ final class AnimalsController $chipId = preg_replace('/\D+/', '', (string) ($animal['chip_id'] ?? '')); if ($chipId === '') { + if (!$wantsJson) { + header('Location: /animal?id=' . $id . '#tab-ident'); + return; + } header('Content-Type: application/json; charset=utf-8'); echo json_encode(['cached' => null, 'stale' => true, 'error' => 'chip_id vide'], JSON_UNESCAPED_UNICODE); return; @@ -489,6 +494,10 @@ final class AnimalsController $stale = $svc->isStale($cached, 86400); } + if (!$wantsJson) { + header('Location: /animal?id=' . $id . '#tab-ident'); + return; + } header('Content-Type: application/json; charset=utf-8'); echo json_encode(['cached' => $cached, 'stale' => $stale], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES); } diff --git a/app/Views/_animal_show_identity.php b/app/Views/_animal_show_identity.php index d1d6b9a..6d9fda0 100644 --- a/app/Views/_animal_show_identity.php +++ b/app/Views/_animal_show_identity.php @@ -225,7 +225,7 @@ $hasCurrent = ($currentLat !== null && $currentLng !== null);
:
- +
- +
+ + + + +
diff --git a/app/Views/_animal_show_medical.php b/app/Views/_animal_show_medical.php index 7835891..064d39f 100644 --- a/app/Views/_animal_show_medical.php +++ b/app/Views/_animal_show_medical.php @@ -443,6 +443,7 @@ ORDER BY created_at ASC + @@ -457,7 +458,7 @@ ORDER BY created_at ASC - + data-notes=""> diff --git a/app/Views/_animal_show_modals.php b/app/Views/_animal_show_modals.php index 7c58636..a01e550 100644 --- a/app/Views/_animal_show_modals.php +++ b/app/Views/_animal_show_modals.php @@ -1160,6 +1160,7 @@ document.addEventListener('DOMContentLoaded', () => { diff --git a/app/Views/evolutions.php b/app/Views/evolutions.php index e4cef25..5aaa120 100644 --- a/app/Views/evolutions.php +++ b/app/Views/evolutions.php @@ -31,10 +31,10 @@
- 1.0.0-rc.30 -

Données fiabilisées sur le terrain

-

Décès historiques corrigibles, synchronisation I-CAD réparée, catalogues - médicaux administrables et tarifs filtrés par clinique.

+ 1.0.0-rc.31 +

Parcours de correction plus fiables

+

Liens d’adoptants corrigés, modification des vaccins plus explicite et + synchronisation i-CAD utilisable même sans JavaScript.

diff --git a/tests/run.php b/tests/run.php index 0ef1c1e..7c8eda6 100644 --- a/tests/run.php +++ b/tests/run.php @@ -215,6 +215,18 @@ try { $assert(strpos($vaccines, 'if ($entryId > 0)') < strpos($vaccines, 'InventoryService::consumeBatch')); $assert(strpos($dewormings, 'if ($entryId > 0)') < strpos($dewormings, 'InventoryService::consumeBatch')); }); + $test('La liste des vaccins affiche une action de modification explicite', function () use ($assert): void { + $medical = file_get_contents(__DIR__ . '/../app/Views/_animal_show_medical.php'); + $assert(str_contains($medical, "h(t('common.actions'))"), 'L’en-tête Actions manque aux vaccins'); + $assert( + str_contains($medical, 'btn btn-sm btn-outline-primary edit-vaccine'), + 'Le bouton Modifier des vaccins est absent', + ); + $assert( + !preg_match('/class="[^"]*edit-vaccine[^"]*"[\s\S]*?>✏️<\/button>/', $medical), + 'Le crayon est encore utilisé pour modifier un vaccin', + ); + }); $test('Les paramètres sont enregistrés et relus', function () use ($assert): void { AppSettings::save(['association_name' => 'Refuge Test', 'app_language' => 'en'], null); $assert(AppSettings::get('association_name') === 'Refuge Test'); @@ -491,6 +503,26 @@ try { $assert($structure !== null && $structure['label'] === 'Structure test'); $assert((int) $structure['total_cents'] === 1234); }); + $test('Le nom de l’adoptant ouvre la bonne route annuaire', function () use ($assert): void { + $view = file_get_contents(__DIR__ . '/../app/Views/_animal_show_identity.php'); + $routes = file_get_contents(__DIR__ . '/../app/Routes/web.php'); + $assert( + str_contains($view, 'href="/directory/view?id="'), + 'Le lien de l’adoptant ne cible pas /directory/view', + ); + $assert(!str_contains($view, '/directory/show?id='), 'L’ancienne route annuaire est encore utilisée'); + $assert(str_contains($routes, "case '/directory/view':"), 'La route /directory/view n’est pas déclarée'); + }); + $test('Le rafraîchissement i-CAD fonctionne avec et sans JavaScript', function () use ($assert): void { + $identity = file_get_contents(__DIR__ . '/../app/Views/_animal_show_identity.php'); + $scripts = file_get_contents(__DIR__ . '/../app/Views/_animal_show_modals.php'); + $controller = file_get_contents(__DIR__ . '/../app/Controllers/AnimalsController.php'); + $assert(str_contains($identity, 'action="/animal/icad"'), 'Le bouton i-CAD n’a pas de formulaire de secours'); + $assert(str_contains($identity, 'name="csrf"'), 'Le formulaire i-CAD ne contient pas le jeton CSRF'); + $assert(str_contains($scripts, "form.addEventListener('submit', refresh)"), 'L’actualisation dynamique i-CAD est absente'); + $assert(str_contains($scripts, 'if (!r.ok)'), 'Les erreurs HTTP i-CAD ne sont pas contrôlées'); + $assert(str_contains($controller, "header('Location: /animal?id=' . \$id . '#tab-ident')"), 'Le repli sans JavaScript est absent'); + }); $test('La recherche globale retrouve les dossiers accessibles', function () use ($assert): void { $animals = GlobalSearchService::search('Animal Test'); $assert(($animals['animals'][0]['title'] ?? '') === 'Animal Test', 'Animal absent de la recherche');