# Reprise du projet — passation

Ce document existe pour qu'une **nouvelle session de travail** (autre poste,
autre compte, autre personne) puisse reprendre le projet sans repartir de zero.

Il ne remplace pas le [README](../README.md) (qui decrit *ce qu'est* le projet)
ni [CLAUDE.md](../CLAUDE.md) (qui fixe *les regles*). Il raconte **l'etat reel,
les decisions prises, et surtout les pieges qui ont deja coute du temps**.

> **Aucun secret ici.** Identifiants, jetons, mots de passe et cles vivent
> uniquement dans le `.env` du serveur et dans le gestionnaire de mots de passe
> du client. Ne jamais les recopier dans le depot, ni dans une conversation.

---

## 1. Etat du projet

**Le site est EN PRODUCTION** sur <https://www.biome.immo>.

Ce n'est plus une preproduction : c'est la **meme installation OVH** qui servait
`new.biome.immo`, passee sur le domaine principal (decision client du
2026-07-16 : « on ne reinstalle rien, seul le nom de domaine change »). Meme
dossier, meme base, memes secrets, meme miroir CRM.

> Consequence : **ne jamais regenerer `APP_KEY` ni les secrets**. Le WordPress
> d'origine a ete deplace sur `sst.biome.immo`.

Ce qui tourne reellement :

- Le site public et le back-office `/admin`.
- L'integration CRM **dans les deux sens** : le CRM pousse les biens (avec
  photos), le site transmet les leads acheteurs.
- Les e-mails, via l'API HTTP de Mailjet.
- Le planificateur, declenche par un **cron HTTP externe**.

---

## 2. Comment on travaille

Convention etablie avec le client, a respecter :

1. Une branche par demande.
2. Implementation.
3. **Suite complete verte** (`php artisan test`) — non negociable.
4. Commit, push, PR via `gh`.
5. **Fusion immediate** sans redemander (`gh pr merge --merge --delete-branch`).
6. Retour sur `main` + `git pull`.

Le client relit a posteriori sur GitHub. S'il faut lui redemander quelque chose
a chaque lot, on perd le benefice.

Points de methode qui ont fait leurs preuves :

- **Toujours donner la sequence de deploiement** en fin de reponse (`git pull`,
  migration ou non, `optimize:clear`).
- **Verifier avant d'affirmer.** Plusieurs diagnostics « evidents » se sont
  reveles faux ; les mesures ont tranche a chaque fois.
- **Ne jamais demander un secret.** Indiquer *ou* le mettre, jamais le recevoir.

---

## 3. Environnement de developpement

Poste Windows, projet dans `Projets/Biome Site`, depot GitHub
`BiomeOrganization/Nouveau_Site_Biome.immo` (renomme le 2026-09-16 ;
GitHub conserve une redirection depuis l'ancien nom `Biome_New_Site`, mais il
vaut mieux corriger le `remote` : `git remote set-url origin ...`).

| Outil | Chemin (hors PATH — toujours en absolu) |
|---|---|
| PHP | `C:\xampp\php\php.exe` |
| Composer | `C:\xampp\php\php.exe C:\xampp\php\composer.phar` |
| MySQL | `C:\xampp\mysql\bin\mysql.exe -u root` (base `biome_site`) |
| GitHub CLI | `C:\Program Files\GitHub CLI\gh.exe` |

Un compte admin Filament local existe pour tester le back-office ; ses
identifiants sont chez le client (jamais dans le depot). Au besoin, en creer un
avec `php artisan tinker`.

Serveur de dev : `php artisan serve` (port 8000).

---

## 4. Les pieges qui ont deja coute du temps

C'est la section la plus utile. Chacun de ces points a provoque un faux
diagnostic ou une perte de temps reelle.

### 4.1 Hebergement mutualise OVH

- **`php artisan config:cache` est INTERDIT** — provoque une erreur 500.
  Utiliser `optimize:clear`.
- **PHP CLI hors PATH** : `/usr/local/php8.3/bin/php`.
- **Le dossier personnel EST la racine Laravel.** `~` = la racine du projet, les
  logs sont dans `~/storage/logs/laravel.log`.
- **Pas de cron systeme utilisable** : le planificateur est declenche par un cron
  HTTP externe appelant `/planificateur/{jeton}`.
- **Pas de worker de file d'attente** : aucun traitement asynchrone en demon.

### 4.2 Les sorties reseau ne marchent PAS en SSH

Un `curl` depuis le shell SSH echoue (`Connection refused`) **meme vers des
sites parfaitement joignables**. En revanche, les appels sortants **fonctionnent
depuis PHP en contexte web**.

Consequence : un test en SSH donne un **faux negatif**. Pour verifier une
integration sortante, il faut passer par le planificateur HTTP, pas par
`php artisan` en SSH. Ce piege a fait croire pendant un moment que l'endpoint de
la plateforme RH etait injoignable.

### 4.3 E-mails : SMTP bloque

Le SMTP sortant est bloque, **y compris vers les serveurs mail d'OVH**. Les
e-mails passent donc par l'**API HTTP de Mailjet**, avec un transport maison
(`App\Mail\Transport\MailjetApiTransport`).

Diagnostic : `php artisan mail:test <adresse>`.

### 4.4 Livewire et le `.htaccess`

Le JS de Livewire doit etre servi **en statique** (`public/vendor/livewire`),
sinon l'admin ne parvient pas a se connecter (fichier tronque par OVH).

Un bloc `no-gzip` avait ete ajoute **a la main sur le serveur** sans etre
commite : au premier `git pull`, conflit. Il est desormais versionne.

Piege associe, corrige depuis : une regle destinee au *fichier JS* de Livewire
s'appliquait aussi a l'**endpoint AJAX `/livewire/update`**, lui retirant son
`Content-Length`. Symptome : l'action reussissait cote serveur mais le navigateur
affichait « Erreur lors du chargement de la page ».

### 4.5 Le responsive est verrouille par un test

Tout le CSS mobile vit dans `public/css/mobile.css`, et **uniquement** dans des
media queries `max-width:1024px`, `max-width:480px` ou `min-width:1025px`.
`MobileSocleTest` echoue si on ajoute un autre point de rupture. **Le desktop est
la reference pixel-perfect et ne doit jamais bouger.**

### 4.6 Faux positifs de « code mort »

Les 16 partiels `resources/views/formulaires/champs/*.blade.php` n'apparaissent
**nulle part** en recherche textuelle : ils sont inclus **dynamiquement par
concatenation**. Les supprimer casserait tous les formulaires personnalises.
Meme vigilance pour les conventions Filament (decouverte automatique), les
composants Blade `<x-...>`, et les noms passes en chaine.

---

## 4bis. Deux bugs recurrents a connaitre

- **Champ caste en `array` dans un Infolist Filament** : `formatStateUsing()` est
  invoque **par element**, pas sur le tableau. Un type-hint `?array` provoque une
  erreur 500 a l'affichage. Utiliser `->state(fn ($record) => ...)`.
- **Entites HTML dans un champ rendu echappe** : un libelle ecrit
  `D&eacute;placements` s'affiche tel quel si la vue utilise `{{ }}`. Ecrire
  l'accent en clair — ne pas passer en `{!! !!}` si des valeurs voisines viennent
  de la base.

---

## 5. Integrations et leur etat

### 5.1 CRM « Portail vendeur » — OPERATIONNEL

Modele **PUSH + miroir** : le CRM pousse les biens vers le site
(`POST /api/crm/biens...`, Bearer + HMAC), stockes dans `biens_miroir`. Le site
lit ce miroir (`CRM_SOURCE=miroir`) et transmet les leads acheteurs
(`CRM_LEADS_TRANSPORT=http`), par la commande planifiee.

Leads **transmis** : demandes de visite, demandes d'info sur un bien, messages de
contact « maison », et soumissions de formulaires personnalises marques comme
tels. **Non transmis** : telechargements du guide, leads terrain, contacts hors
« maison ».

> **Piege ops** : ne jamais combiner `CRM_SOURCE=fixtures` avec
> `CRM_LEADS_TRANSPORT=http` — les leads porteraient des identifiants de biens
> inexistants cote CRM (erreur 422). La recette doit tourner en `miroir`.

Le titre des vignettes est recompose cote site :
`Localite - Commune - Lot N`.

### 5.2 Plateforme de recrutement — CODE PRET, PAS ENCORE BRANCHE

Les candidatures recues sur le site doivent alimenter une **plateforme de
recrutement** developpee par une autre equipe (Next.js + PostgreSQL, projet
« Recrutement »).

Cote site, **tout est livre** : `App\Services\Rh\`, commande planifiee, suivi
dans l'admin (colonne « Recrutement »), reessais plafonnes, respect du debit.
Le transport est en mode **bouchon** (`RH_TRANSPORT=journal`) tant que les
identifiants ne sont pas en place.

Pour brancher : renseigner `RH_TRANSPORT=http`, `RH_INGEST_URL`,
`RH_INGEST_TOKEN`, `RH_INGEST_HMAC_SECRET` dans le `.env`, puis
`optimize:clear`.

**Points ouverts a connaitre :**

- La plateforme **n'a aucun environnement de recette** : un seul environnement,
  la production. Les tests ecrivent donc dans leurs donnees reelles. Protocole
  convenu : prefixer les essais via `RH_EXTERNAL_ID_PREFIX=test-`, utiliser des
  adresses e-mail dediees, et viser une offre inconnue.
  ⚠️ Ce prefixe s'applique a **tout** ce qui part : une vraie candidature
  envoyee pendant la fenetre de test serait effacee avec les essais.
- **Aucune purge RGPD n'existe de leur cote** alors que le site purge a 24 mois.
  A arbitrer avec le responsable de traitement avant mise en service.
- **Question de l'historique non tranchee** : a la bascule, toutes les
  candidatures jamais transmises partiront. A decider si on veut l'historique
  ou seulement les nouvelles.
- Le **nom/prenom** est envoye en un seul champ (`fullName`). La separation a la
  source reste a faire, cote formulaire public.
- **Chaque nouvelle offre publiee** doit voir son *slug* communique a l'equipe
  recrutement, sinon ses candidatures arrivent en « Candidature spontanee »,
  sans alerte.

---

## 6. Ce qui reste ouvert

| Sujet | Etat |
|---|---|
| Pages legales (mentions, confidentialite) | **Liens du footer encore vides.** Une politique de confidentialite est pourtant exigee : les formulaires collectent des donnees personnelles. Textes a fournir par le client. |
| Google Fonts + carte OpenStreetMap | Chargees depuis des tiers (transmission d'IP). Durcissement possible : auto-heberger les polices, charger la carte au clic. |
| Images televersees en admin | **Non optimisees** : un PNG de 4 Mo est servi tel quel. Correctif durable : brancher `App\Services\Images\OptimiseurImage` sur les `FileUpload`. |
| Style de code (Pint) | ~74 fichiers avec des ecarts **preexistants**. Volontairement non corriges : le diff serait massif et purement cosmetique. Decision a prendre separement (adopter Pint *et* l'automatiser, ou ne pas y toucher). |
| Dette technique identifiee | Un audit a recense ~81 points de duplication/simplification. Les plus rentables ont ete traites ; le reste est documente et volontairement laisse. |

---

## 7. Reperes utiles

- **Tailles d'images conseillees** dans l'admin : le `helperText` de chaque champ
  fait foi. Les bandeaux ne sont **pas uniformes** — selon la longueur du texte,
  la zone visible va de ~2:1 a ~2.7:1. Une photo **portrait** y sera toujours
  fortement rognee.
- **Bannière cookies** en place ; le site ne pose que des cookies techniques. Le
  mecanisme est pret a bloquer un futur traceur (script depose en
  `type="text/plain"` avec sa categorie).
- **Consultation des logs prod** :
  `grep -h "lead.notification.echec" ~/storage/logs/*.log | tail -5` (et
  `compte.lien-mot-de-passe.echec` pour les invitations).

---

## 8. Ou chercher ensuite

| Besoin | Document |
|---|---|
| Comprendre le projet | [README.md](../README.md) |
| Les regles a respecter | [CLAUDE.md](../CLAUDE.md) |
| Qui possede quelle donnee | [architecture-donnees.md](architecture-donnees.md) |
| Pieges de deploiement | [deploiement-preproduction.md](deploiement-preproduction.md) |
| Contraintes OVH | [deploiement-ovh.md](deploiement-ovh.md) |
| Strategie responsive | [mobile-strategie.md](mobile-strategie.md) |
| Donnees personnelles | [rgpd.md](rgpd.md) |
| E-mails et comptes admin | [mailjet-invitations.md](mailjet-invitations.md) |

L'historique complet des decisions est dans les **messages de commit** et les
**descriptions de pull request** : elles sont volontairement detaillees (cause,
preuve, correctif, verification). En cas de doute sur un choix, `git log` sur le
fichier concerne donne la reponse plus surement que n'importe quelle synthese.
