Journal des modifications
Les évolutions de Cohez.io, de la plus récente à la plus ancienne.
173 versions publiées.
Versions 161 à 170 sur 173
0.4.1.0 - 2026-04-14
Ajouts
- Chiffrement des secrets tenant — les clés API HelloAsso (
hello_asso_client_secret,hello_asso_webhook_secret) sont maintenant chiffrées au repos dans la colonnesettingsJSON du tenant via un cast EloquentEncryptedSettingsCast. Toute clé dont le nom se termine par_secretest automatiquement chiffrée en écriture et déchiffrée en lecture (AES-256-CBC via APP_KEY). L'accès depuis le code applicatif est transparent. - Rotation d'APP_KEY — support de
APP_PREVIOUS_KEYSpour déchiffrer les secrets chiffrés avec une ancienne clé sans interruption de service. La commandephp artisan settings:reencrypt-secretsre-chiffre tous les secrets sous la clé courante pour permettre de viderAPP_PREVIOUS_KEYSaprès rotation. - Migration de données idempotente — la migration
encrypt_tenant_settings_secretschiffre les secrets existants en clair dans la base de production. L'opération est idempotente (les blobs déjà chiffrés sont ignorés) et peut être réexécutée sans risque. - Commande de re-chiffrement en deux phases —
settings:reencrypt-secretsopère en phase 1 (lecture et validation de tous les secrets) puis phase 2 (écriture), de sorte qu'une erreur de déchiffrement sur un tenant annule l'opération avant qu'une seule écriture ait lieu. - UX intégrations — masquage des secrets existants — le formulaire de configuration HelloAsso affiche un placeholder "Configuré — laisser vide pour conserver" lorsqu'un secret est déjà enregistré, évitant d'exposer la valeur et signalant clairement que le champ peut être laissé vide.
- Confirmation du mot de passe sur la page intégrations — la sauvegarde des identifiants HelloAsso requiert maintenant la confirmation du mot de passe courant de l'utilisateur.
Sécurité
- Les secrets API HelloAsso ne sont plus stockés en clair dans la base de données. La colonne
settingscontient désormais des blobs chiffrés AES-256-CBC pour toutes les valeurs dont la clé se termine par_secret.
0.4.0.0 - 2026-04-14
Ajouts
- Formulaire d'inscription public — consentements RGPD — les inscriptions publiques collectent désormais les consentements newsletter, partage de données et droit à l'image. Si la personne existe déjà en base, ses consentements sont mis à jour en mode opt-in uniquement (jamais de rétrogradation). La date de consentement est enregistrée lors de toute acceptation.
- Formulaire d'inscription public — acceptation des documents — le formulaire recueille l'acceptation des statuts et du règlement intérieur. Ces acceptations sont obligatoires pour valider la soumission et sont stockées sur l'adhésion créée.
- Formulaire d'inscription public — responsables légaux (tuteurs) — les membres mineurs peuvent désormais avoir un ou plusieurs responsables légaux renseignés directement depuis le formulaire public. Le premier tuteur est automatiquement marqué comme contact principal (
is_primary_contact). L'interface permet d'ajouter le premier responsable en un seul clic. - Formulaire d'inscription public — adresse ligne 2 — le champ adresse ligne 2 est maintenant disponible dans le formulaire public, mappé vers la Person créée ou mise à jour.
Corrections
- Normalisation des données — emails des tuteurs — les emails des responsables légaux sont normalisés (minuscules + trim) lors de la préparation de la requête, cohérent avec le traitement des emails du membre et du payeur. Prévient les doublons liés à la casse.
- is_primary_contact — premier tuteur — le premier responsable légal est automatiquement désigné contact principal (index 0), les suivants ne le sont pas.
- DRY — calcul de la minorité — la logique de détection d'un membre mineur (age < 18 ou présence de tuteurs) était dupliquée dans
PersonDeduplicationService. Extraite dans une méthode privéeresolveIsMinor(). - Design — polices et composants design system — activation de
font-displaypour Fraunces sur lePublicLayout, attributcrossoriginsur le lien stylesheet Bunny Fonts, suppression des astérisques redondants sur les champs, alignement des composantsSelectetButtonsur le design system. - UX — ouverture automatique du premier tuteur — lorsqu'on clique sur le bouton d'ajout de tuteur, le formulaire s'ouvre directement au lieu d'afficher un accordéon vide.
Modifications
- Couverture de tests : ajout de la validation du chemin opt-in RGPD pour les personnes existantes (33 tests, 106 assertions).
0.3.2.3 - 2026-04-13
Corrections
- Sécurité — validation serveur du montant unitaire — le contrôleur d'inscription publique acceptait n'importe quel
unit_amountsans vérification, permettant à un utilisateur malveillant de soumettre 0 € pour une proposition à prix fixe. LaFormRequestimpose maintenant que leunit_amountcorresponde exactement auamountde la proposition pour les typesfixed, et soit nul pour les typesfree. Les propositionscustom(montant libre) acceptent tout montant ≥ 0. - Bug — propositions gratuites bloquaient le formulaire — dans
CampaignPropositionSelector.vue, les propositions de typefreen'initialisaient pasunit_amountlors de la sélection (radio ou checkbox). Le JSON envoyé ne contenait pas la cléunit_amount, déclenchant l'erreur de validationrequiredcôté serveur et laissant l'utilisateur bloqué à l'étape 2 avec une erreur impossible à corriger. Le champunit_amountest maintenant systématiquement initialisé à0pour tous les types non-fixed. - UX — navigation automatique vers l'étape en erreur — après une soumission rejetée par le serveur, l'utilisateur restait bloqué à l'étape 4 (récapitulatif) sans voir les erreurs de validation sur les étapes précédentes. Des watchers Vue naviguent maintenant automatiquement vers l'étape 1, 2 ou 3 dès qu'une erreur serveur est détectée sur les champs correspondants. Le watcher de l'étape 1 inclut désormais un guard
currentStep.value > 1pour éviter une navigation intempestive.
0.3.2.2 - 2026-04-13
Corrections
- Redirection HelloAsso — TypeError corrigé en production — le formulaire d'inscription publique retournait
RedirectResponsemaisInertia::location()retourneSymfonyResponselors d'une requête XHR Inertia. Sans ce correctif, la redirection vers HelloAsso déclenchait unTypeErrorPHP 8 en production dès qu'un tenant avait ses identifiants HelloAsso configurés, empêchant tout paiement via HelloAsso. Le type de retour destore()est maintenantSymfonyResponse(compatible avec tous les chemins de retour existants). - Champ montant libre — affichage en euros et stockage en centimes — dans le sélecteur de propositions, les champs de montant libre (type
custom) affichaient les centimes bruts (ex :5000au lieu de50,00 €) et sauvegardaient la valeur en euros au lieu des centimes. Le correctif divise par 100 à l'affichage et multiplie par 100 à la saisie, alignant le comportement avec le reste du système. Un attributmax="999999.99"est ajouté pour bloquer les valeurs aberrantes côté client. - Tests HelloAsso — compatibilité environnement sandbox — les fakes
Http::fake()dans les tests de retour et d'erreur HelloAsso utilisaient le domaine de production (api.helloasso.com) en dur. AvecHELLOASSO_SANDBOX=true, ces tests tentaient une vraie requête réseau. Les patterns sont alignés sur*.helloasso*pour couvrir production et sandbox de manière identique aux helpers existants.
0.3.2.1 - 2026-04-10
Corrections
- Page de succès HelloAsso — message affiché selon l'état du paiement — après un paiement HelloAsso, les utilisateurs dont le paiement est encore en cours de traitement voyaient à tort le message "Un email de confirmation vous a été envoyé". La page affiche maintenant une icône horloge et un message "Votre paiement est en cours de traitement" quand le paiement n'est pas encore confirmé, et ne bascule vers le message de confirmation qu'une fois le paiement validé. Le titre de la page s'adapte également (
Paiement en cours de traitementouInscription confirmée).
0.3.2.0 - 2026-04-10
Corrections
- Crash 500 sur la liste des membres — les numéros de téléphone invalides en base (hérités de données importées ou saisies avant validation) provoquaient une
InvalidPhoneNumberExceptionlors de la reconstruction du domaine. Un mécanisme de quarantaine (hydratePhone()) isole maintenant ces valeurs sans bloquer l'affichage. - Normalisation des formats de téléphone à la saisie — les utilisateurs peuvent saisir
06 12 34 56 78,06.12.34.56.78,+33612345678,0033612345678ou un numéro mobile à 9 chiffres sans le 0 initial ; le numéro est automatiquement converti au format canonique0XXXXXXXXXavant validation. Concerne les formulaires membre, tuteur légal, inscription admin et inscription publique. - Regex de validation plus stricte — l'ancre
\zremplace$dans le pattern/^0\d{9}\z/pour rejeter les valeurs avec caractère de fin de ligne masqué.
Ajouts
- Migration de nettoyage des téléphones — normalise ou nullifie les numéros existants en base qui ne respectent pas le format
0XXXXXXXXX, avec logwarningpour chaque valeur supprimée.
0.3.1.0 - 2026-04-09
Ajouts
- Paiement HelloAsso en ligne — depuis le formulaire public d'inscription, les visiteurs peuvent désormais payer directement via HelloAsso Checkout. L'association configure ses identifiants OAuth2 et son slug dans les settings, et le bouton "Payer en ligne" apparaît automatiquement sur le wizard.
- Page Intégrations (
/settings/integrations) — les administrateurs saisissent leur Client ID, Client Secret, slug HelloAsso et secret webhook depuis une interface dédiée. Les secrets ne sont jamais réexposés en frontend après enregistrement. - Webhook HelloAsso (
POST /webhook/helloasso/{tenantId}) — endpoint vérifié par signature HMAC-SHA256. Marque le paiement comme reçu, fait passer l'inscription enPaid, et envoie le mail de confirmation exactement une fois (idempotence viaconfirmation_email_sent_at). - URL de retour HelloAsso (
/register/{token}/helloasso/return) — après paiement, vérifie l'état du checkout intent via l'API HelloAsso. Si payé, confirme l'inscription immédiatement. Si l'API est indisponible, affiche un message "en cours de traitement" et laisse le webhook finaliser. - Page d'erreur HelloAsso (
/register/{token}/helloasso/error) — affichée si le paiement échoue ou si l'API HelloAsso retourne une erreur. L'inscription orpheline est supprimée automatiquement pour permettre une nouvelle tentative. - Service
HelloAssoClient— client HTTP OAuth2 avec cache du token (25 min), support sandbox viaHELLOASSO_SANDBOX=true, deux méthodes :createCheckoutIntent()etgetCheckoutIntent(). - Index
payments_reference_index— index standalone surpayments.referencepour les requêtes de fallback du webhook (la contrainte uniqueUNIQUE(reference, method)existante ne suffit pas pour les lookups parreferenceseul).
Sécurité
- La valeur
redirectUrlretournée par HelloAsso est validée contre une liste d'origines autorisées (helloasso.com/helloasso-sandbox.com) avantredirect()->away()pour se prémunir d'une compromission de l'API côté HelloAsso. - Le webhook est exclu du CSRF Laravel et vérifié par HMAC-SHA256 à la place (
X-Helloasso-Signature). - Rate limiter du webhook keyé sur
tenantId(120 req/h) plutôt que sur l'IP (HelloAsso partage un pool IP entre toutes les organisations).
0.3.0.0 - 2026-04-08
Ajouts
- Formulaire public d'inscription — les associations peuvent générer un lien public (token signé, expirable) depuis les paramètres d'une campagne. Ce lien donne accès à un wizard en 4 étapes (informations membre, chargement des propositions, sélection + payeur, récapitulatif) accessible sans compte. Aucun compte HelloAsso requis.
- Token public de campagne (
CampaignPublicToken) — modèle avec expiration configurable, génération viaStr::uuid()->toString()(UUID v4, colchar(36)), révocation instantanée depuis les settings. Un token révoqué retourne 410. - Déduplication silencieuse — si l'email soumis correspond à une
Personexistante dans le tenant, le service réutilise le membre ou crée unMemberlié sans jamais révéler au visiteur si son compte existait. - Détection des doublons d'inscription — si le même membre tente de s'inscrire deux fois à la même campagne, une erreur de validation claire est retournée sur le champ
member_email. Contrainte unique(member_id, campaign_id)ajoutée en base. - Mail de confirmation (
PublicRegistrationConfirmationMail) — envoyé en queue après inscription réussie si un email est fourni, avec récapitulatif des propositions sélectionnées et du mode de paiement. - Page de confirmation (
/register/{token}/success) — page dédiée après soumission réussie, protégée par le même middleware token. - Branding dans le layout public — le nom de l'association est affiché au-dessus du titre de la page pour que le visiteur sache à quelle organisation il s'inscrit.
- Throttling — les routes publiques d'inscription sont limitées à 10 req/min par IP pour limiter l'abus.
Modifications
- Middleware
ResolveCampaignPublicToken— vérifie désormais que la campagne est en statutOpen(en plus du contrôle d'expiration du token). Une campagneDraftouClosedretourne 410. PersonDeduplicationService—is_minorcalculé dynamiquement depuisdateNaissance(Carbon::age < 18) au lieu d'être codé àfalse.
Corrections
- Migration
add_unique_member_campaign_to_registrations: guard contre les doublons existants avant d'appliquer la contrainte unique (évite un crash silencieux en production si des données corrompues sont présentes).
0.2.1.0 - 2026-04-07
Ajouts
- Système de design Cohez.io — identité visuelle définie et codée : polices Fraunces (titres) + Geist (interface) + Geist Mono (données financières), chargées via Bunny Fonts (GDPR-compliant). Palette de couleurs basée sur le vert sauge (
#2D6A4F) comme couleur brand, fond crème#FAF9F7, neutrals chauds — différenciant dans la catégorie face au bleu corporate omniprésent (AssoConnect, HelloAsso). - Composant EmptyState (
@/components/ui/empty-state) — état vide réutilisable : icône Lucide + titre + description + slot pour CTA optionnel. Appliqué sur les listes de membres, groupes, réunions. - Composant ConfirmDialog (
@/components/ui/confirm-dialog) — dialogue de confirmation avant toute action destructive. Remplace entièrement lesconfirm()natifs (qui bloquent les events browser). Utilisé pour la désactivation/suppression de membres et groupes. - FlashToast (
@/components/layout/FlashToast) — notification toast bottom-right consommant automatiquementflash.successetflash.errordepuis les props Inertia partagées. Intégré dansAppSidebarLayout. - Bannière alerte impayés sur le tableau de bord — si des inscriptions non payées existent, une bannière rouge s'affiche en haut de page avec un lien direct vers la liste filtrée.
- DESIGN.md — documentation complète du design system : couleurs avec rationale, typographie, espacement, border radius, motion, composants, patterns UI, responsive, accessibilité et journal de décisions.
Modifications
- Dashboard — agenda de la semaine en empty state explicite quand aucune réunion n'est planifiée ; bannière impayés avec filtre URL correct.
- Pages membres et groupes — états vides cohérents avec le composant EmptyState au lieu de textes inline.
- Wizard d'inscription — garde contre 0 campagnes disponibles avec empty state explicite ; correction de l'affichage du récapitulatif sur mobile.
Corrections
- Suppression du
</div>en trop dans le wizard d'inscription (registrations/Create.vue). - Le lien "impayés" du dashboard utilisait un format de query params incorrect pour Wayfinder (
{ status: 'unpaid' }→{ query: { status: 'unpaid' } }). FlashToast: ajout duonUnmountedpour nettoyer le timer, ajout debreak-wordssur le message pour éviter l'overflow sur les messages longs.
0.2.0.0 - 2026-04-03
Ajouts
- Responsables légaux pour membres mineurs — les membres peuvent être marqués comme mineurs (toggle sur la fiche membre). Le secrétaire peut ensuite associer un ou plusieurs responsables légaux (parent, tuteur légal, autre…) via une interface dédiée : recherche de personne existante, création d'une nouvelle personne, ou sélection depuis le wizard d'inscription. Chaque responsable légal porte un type de relation, un indicateur "contact principal" et des notes libres.
- Gestion complète des responsables légaux depuis la fiche membre : ajout, modification (type de relation, contact principal, notes) et retrait — sans supprimer la personne sous-jacente.
- Support des mineurs dans le wizard d'inscription — lors de la création d'un nouveau membre mineur via le wizard, il est désormais possible d'ajouter directement ses responsables légaux à l'étape 2, en sélectionnant des personnes existantes.
- Création de membre inline dans le wizard d'inscription — il n'est plus nécessaire de créer un membre avant de l'inscrire. Le wizard propose un toggle "Membre existant / Nouveau membre" à l'étape 2. En mode "Nouveau membre", le formulaire complet s'affiche directement, et la création du membre + l'inscription sont atomiques (une seule transaction).
- Trait MemberValidationRules — les règles de validation des membres sont centralisées dans un trait partagé entre
StoreMemberRequestetStoreRegistrationRequest(avec système de préfixe), éliminant la duplication.
Corrections
- Correction du DTO CreateMemberRequest — la création d'un membre sans email (avec seulement un téléphone) provoquait une
InvalidArgumentException. Le champemailest désormais nullable dans le DTO et l'use case. - Protection contre les doublons de responsables légaux — ajouter deux fois la même personne comme responsable légal depuis le wizard d'inscription retourne désormais une erreur de validation (422) au lieu d'une erreur serveur 500.
- Recherche de personnes — les requêtes de moins de 2 caractères retournent une liste vide (évite l'exposition inutile de données personnelles).
- Recherche insensible à la casse — la recherche de membres et de responsables légaux fonctionne désormais quelle que soit la casse (ex. "bec" trouve "BECQUE"). Corrige un comportement PostgreSQL où
LIKEest case-sensitive. - Syntaxe Vue invalide dans MemberForm —
:errors?=provoquait une erreur Vite bloquant l'affichage du formulaire. Corrigé en:errors=. - Wayfinder dans LegalGuardianSection — le toggle "mineur" utilisait
route()(Ziggy, non disponible) au lieu de Wayfinder. Corrigé. - Input.vue accepte null — les champs optionnels typés
string | null(nom, téléphone, date de naissance…) ne provoquent plus d'erreurs TypeScript.