# Rapport — Phase 2 : Site public (Blade) — pages & design system

> Migration HR CONSULTING & CO (Next.js → Laravel). Branche : `phase-2` (basée sur `phase-1`).
> Date : 2026-06-07.

## 1. Résumé

| | |
|---|---|
| **Objectif** | Recréer le site public navigable en Blade : layout (navbar + footer responsive), toutes les pages de [STRUCTURE.md](../../STRUCTURE.md), et la page d'accueil complète, en appliquant la direction artistique [DESIGN.md](../../DESIGN.md). |
| **Livrable attendu** | « Site public navigable affichant les données de la base » (PLAN.md §Phase 2). |
| **Livrable atteint** | ✅ **Oui** — 14 routes publiques rendues en `200` sur serveur réel, alimentées par les données seedées en Phase 1. |
| **Décision tranchée** | Menu **Services en méga-menu** + pages détail `/services/{slug}` (remplace le lien cassé `/conseils`), choisi par l'utilisateur parmi les variantes de STRUCTURE.md §9. Route `/services/{slug}` ajoutée au périmètre. |
| **Écarts** | Aucun écart fonctionnel. Le **carrousel de logos clients** est construit (composant marquee) mais masqué tant qu'aucun client n'est saisi (CRUD + normalisation = Phase 4). Les **formulaires** (Contact, Dépôt de CV, Formation sur-mesure) sont des **pages d'attente** (coordonnées réelles affichées) ; leur traitement arrive en Phases 5/6 conformément à PLAN.md. |

## 2. Environnement & stack

- **PHP** 8.3.30 (Laragon) · **MySQL** 8.4.3 · **Node** 22 · **Vite** 7.
- **Laravel 12** · **Blade** full-stack · **Tailwind CSS v4** (CSS-first, tokens `@theme`) · **Alpine.js 3** (+ plugins `intersect` et `collapse`) · **blade-heroicons** (icônes) · **mews/purifier** (anti-XSS).
- Tests : **PHPUnit 11** sur **SQLite `:memory:`**.

## 3. Travaux réalisés

### 3.1 Routage & contrôleurs
- `routes/web.php` : 14 routes nommées (`home`, `about`, `services.index/show`, `formations.index/show`, `instructors.index`, `news.index/show`, `contact`, `send-resume`, `custom-formation`). Liaison de route par **slug** (pas d'ID séquentiel → pas d'IDOR).
- 6 contrôleurs documentés (PHPDoc) : `HomeController`, `ServiceController`, `FormationController`, `InstructorController`, `NewsController`, `PageController`.
  - Contenu non publié **inaccessible** : `abort_unless($model->is_active, 404)` (services/formations) ; News show exige `status=published` **ET** `published_at` passée (brouillon **et** planifié → 404).
  - `NewsController@index` : pagination 9/page + **filtre catégorie sécurisé** (whitelist issue de la base, conserve la query string) + eager-loading de l'auteur.
  - `HomeController@index` : eager-loading (`with('author')`, `with('media')`) pour éviter les requêtes N+1.

### 3.2 Design system Blade (charte DESIGN.md)
- **Layout** `layouts/app.blade.php` : SEO (title/meta), **Open Graph**, favicons, polices Poppins/Inter, **skip-link** d'accessibilité, intégration `@vite`.
- **Navbar** : sticky + `backdrop-blur` au scroll, **méga-menu Services** (pôles depuis la base via *view composer*), **menu mobile** Alpine (hamburger + sous-menu repliable), lien actif souligné vert.
- **Footer** : 4 colonnes sur dégradé navy (présentation + réseaux sociaux, liens pratiques, coordonnées issues des `settings`, newsletter — aperçu UI, traitement Phase 5).
- **11 composants réutilisables** : `btn` (variantes primary/secondary/white/outline-white), `section-heading` (surtitre + titre), `page-header` (bannière + fil d'Ariane), `reveal` (apparition au scroll), `service-card`, `news-card`, `testimonial-card`, `instructor-card`, `stat` (compteur animé), `navbar`, `footer`.
- **Helpers** : `media_url()` (résolution d'URL d'image + **durcissement anti-XSS**), réutilise `setting()`.
- **Front** : `app.js` (Alpine + plugins + `countUp` pour les statistiques), `app.css` (animations `marquee`, `reveal`, styles `prose-news` pour le HTML des actualités, règle `[x-cloak]`).

### 3.3 Pages
- **Accueil** (9 sections, DESIGN.md §5) : Hero (titre corrigé « **Habilitation FDFP** »), Logos (marquee), Cibles/Welcome, Pourquoi nous choisir, Services, **Statistiques** (bandeau navy, compteurs animés), **Témoignages** (carrousel scroll-snap), Actualités, **CTA dépôt de CV**.
- **Pages internes** : `about`, `services/index`, `services/show`, `formations/index` (+ carte « Formation sur-mesure »), `formations/show`, `instructors/index`, `news/index` (filtres + pagination), `news/show` (contenu purifié + SEO/OG + articles liés).
- **Pages d'attente** : `forms/contact` (coordonnées réelles), `forms/send-resume`, `forms/custom-formation`.

## 4. Tests fonctionnels

Vérification sur **serveur réel** (`php artisan serve`) — 14 routes en `200` :
`/`, `/about`, `/services`, `/services/{slug}` (formation, recrutement, conseil…), `/formations`, `/formations/{slug}`, `/instructors`, `/news`, `/news?category=…`, `/news/{slug}`, `/contact`, `/send-resume`, `/custom-formation`.

Parcours validés : navigation navbar/méga-menu/mobile, filtre catégorie des actualités, états vides (collections vides → pas d'erreur), 404 du contenu non publié, rendu du contenu d'actualité purifié.

## 5. Couverture de tests

- **Suite : 41 tests verts / 121 assertions** (`php artisan test`).
- **17 nouveaux tests Phase 2** :
  - `PublicSiteTest` (12) : rendu accueil, méga-menu, footer/settings, pages d'index, 404 services/formations inactifs, article publié, **404 brouillon/planifié**, filtre catégorie, **purification XSS**, 404 route inconnue, témoignages.
  - `MediaUrlHelperTest` (5) : repli, URL absolues, chemin racine, **rejet des schémas dangereux** (`javascript:`/`data:`/`//host`), résolution storage.
- **Couverture en % non mesurée** : aucun driver Xdebug/PCOV installé dans l'environnement Laragon. Couverture **fonctionnelle** assurée (toutes les routes + cas limites + sécurité).

## 6. Tests de sécurité (OWASP)

Revue OWASP Top 10 appliquée (agent sécurité dédié) + audit des dépendances.

| Réf | Catégorie | Sévérité | Statut |
|---|---|---|---|
| — | **A03 XSS WYSIWYG** | — | ✅ Contrôle OK : seul `{!! clean($article->content) !!}` (mews/purifier, config par défaut → `javascript:`/`data:` interdits). Test dédié vert. |
| — | **A01 Contrôle d'accès** | — | ✅ Contenu non publié → 404 ; liaison par slug (pas d'IDOR) ; filtre catégorie en whitelist. |
| SEC-002 | A03/A04 `media_url()` | Faible | ✅ **Corrigé** : rejet `javascript:`/`data:`/`//host` + test. |
| SEC-003 | A04 URLs sociales | Faible | ✅ **Corrigé** : filtrage des schémas `http(s)` au rendu. |
| SEC-004 | A04 `mailto:`/`tel:` | Faible | ⏳ `tel:` déjà assaini ; validation des settings → Phase 3 (formulaire backoffice). |
| SEC-005 | A05 `APP_DEBUG` prod | Moyen* | ⏳ Local OK ; à verrouiller (`APP_DEBUG=false`, `APP_ENV=production`) au déploiement (Phase 9). |
| SEC-006 | A05 En-têtes/CSP | Faible | ⏳ Défense en profondeur → Phase 9 (prod). |

\* *Moyen uniquement si le `.env` de debug part en production.*

- **A06 Dépendances** : `composer audit` → **aucune** vulnérabilité ; `npm audit` → **0** vulnérabilité.
- **A09 Logs** : aucun log sensible introduit.
- **Note outillage** : le serveur MCP **aikido n'est pas connecté** dans cette session (scan SAST automatisé non exécuté) ; la revue OWASP manuelle approfondie l'a remplacée. Findings critiques/élevés : **0**.

## 7. Performance / scalabilité

- **Eager-loading** systématique (auteur des actualités, médias des logos) → pas de N+1.
- **Cache** : `setting()` mis en cache (Phase 1) ; le *view composer* du méga-menu fait 1 requête indexée/page — mise en cache prévue avec le CRUD Services (Phase 3).
- **Front** : assets compilés (CSS 76 kB / 13 kB gz, JS 92 kB / 34 kB gz), images en `loading` adapté, animations CSS respectant `prefers-reduced-motion`.
- **Suggestion** : index DB sur `news.category` lorsque le volume d'articles augmentera.

## 8. Dette technique / TODO reportés

- **Carrousel logos** : composant prêt, en attente du CRUD + normalisation (Phase 4).
- **Config `mews/purifier`** : la whitelist par défaut n'autorise pas `h2/h3/table/blockquote` que produira l'éditeur TipTap — publier un `config/purifier.php` (whitelist étendue **sûre**) avec le News WYSIWYG (Phase 3). Les styles `prose-news` sont déjà prêts.
- **Validation des settings** (e-mail, URLs sociales) au niveau du formulaire backoffice (Phase 3).
- **404 personnalisée**, en-têtes de sécurité/CSP, config prod (Phase 9).

## 9. Branche & Pull Request

- **Branche** : `phase-2` (basée sur `phase-1`).
- **PR** vers `master` : <!-- URL renseignée après push --> `https://github.com/HR-CONSULTING-CO/hrconsulting_website/compare/master...phase-2?expand=1`
  > ℹ️ PR **empilée** sur `phase-1` : tant que `phase-1` n'est pas mergée, son diff apparaît dans la PR ; il se résorbe automatiquement après le merge de `phase-1`. Pour une revue isolée du seul travail Phase 2 : `compare/phase-1...phase-2`.

## 10. Prochaine phase recommandée

**Phase 3 — Backoffice Filament (contenu)** : resources CRUD (Testimonials, Clients/Logos, Services, Formations, Instructors, **News WYSIWYG**), page Settings, rôles admin. Permettra de remplacer les contenus placeholder par du contenu réel éditable, et de traiter la dette purifier/validation identifiée ci-dessus.
