Aller au contenu

Frontend SvelteKit

Le dépôt web utilise SvelteKit 2, Svelte 5, TypeScript strict, Tailwind CSS v4, Paraglide, Vitest, Playwright et Storybook. Il rend le site public, le back-office et les endpoints BFF.

Flux des couches du frontend Le Clos des Eucalyptus

src/
├── lib/
│ ├── domain/<feature>/ # entités et ports TypeScript purs
│ ├── application/<feature>/ # cas d’usage
│ ├── infrastructure/<feature>/ # repositories HTTP et types contractuels
│ ├── presentation/<feature>/ # composants publics et view models
│ ├── presentation/backoffice/ # présentation administrative
│ ├── components/ui/ # primitives shadcn-svelte générées
│ ├── server/ # helpers impossibles à importer côté client
│ └── container.ts # assemblage des implémentations
├── routes/
│ ├── (app)/ # routes publiques
│ ├── back-office/ # pages administratives authentifiées
│ ├── login/ # mot de passe, TOTP et invitations
│ └── api/ # endpoints BFF
└── app.css # tokens Tailwind publics et back-office

src/lib/domain contient les modèles et interfaces stables. Il n’importe ni Svelte, ni SvelteKit, ni infrastructure. Une règle qui peut être testée sans navigateur ni HTTP appartient ici.

Un cas d’usage dépend de ports du domaine et expose une méthode execute(). Il orchestre une action mais ne connaît pas fetch, les cookies ou les composants.

Les repositories HTTP implémentent les ports, appellent HttpClient et convertissent le JSON en entités. Les types qui reproduisent le snake_case du backend restent dans cette couche.

Les view models formatent dates, devises, labels et états d’affichage. Un composant reçoit des props prêtes à rendre. Une route compose la page ; elle ne devient pas un service métier.

src/lib/container.ts est le seul point d’assemblage. Les lectures publiques peuvent utiliser des singletons sans identité. Les fonctions create<Feature>UseCases(token) créent un client authentifié par requête : ne transformez jamais ces usages en singleton.

Besoin Mécanisme
Charger des données avec secret ou token +page.server.ts
Mutation de formulaire form action dans +page.server.ts
Flux SSE, upload ou binaire route +server.ts sous /api/back-office
Données publiques sans secret load serveur par défaut
État visuel local runes dans le composant ou classe .svelte.ts

Utilisez use:enhance pour améliorer une form action, pas pour déplacer sa validation dans le navigateur. Une page fonctionnelle sans JavaScript doit rester possible lorsque le composant s’y prête.

Pour ajouter la lecture d’un objet Amenity :

  1. créez domain/amenity/Amenity.ts et IAmenityRepository.ts ;
  2. créez application/amenity/GetAmenitiesUseCase.ts ;
  3. implémentez infrastructure/amenity/AmenityHttpRepository.ts et les types JSON ;
  4. câblez le repository dans container.ts ;
  5. créez presentation/amenity/AmenityViewModel.ts ;
  6. chargez le cas d’usage dans +page.server.ts ;
  7. rendez le view model dans un composant testé et documenté par une story.

Les tests du domaine et du cas d’usage utilisent un port factice simple. Ils ne mockent pas HttpClient.

Les deux surfaces partagent le domaine, l’application et l’infrastructure, mais pas leur système de présentation.

Règle Site public Back-office
Routes routes/(app) routes/back-office
UI composants presentation/shared shadcn-svelte components/ui
Langues cinq catalogues Paraglide français inline
Thème tokens publics @theme tokens sous [data-back-office]
Indexation publique noindex,nofollow

N’importez pas les primitives shadcn dans le site public et n’utilisez pas les composants partagés publics dans le back-office. Les portails (Dialog, Popover, Sheet, menus et toasts) doivent porter data-back-office pour conserver le thème administratif.

hooks.server.ts lit le cookie harmony_backoffice_session, valide le token auprès du backend et remplit event.locals. Chaque load, action et endpoint vérifie ensuite lui-même la session. Les actions sensibles contrôlent le rôle côté serveur, même si le menu est masqué.

Les cookies de session et de challenge sont httpOnly, sameSite=lax et secure hors mode dev. Le challenge TOTP expire après cinq minutes ; une session mémorisée reçoit au maximum sept jours.

Consultez le guide Sécurité et authentification avant de modifier ce flux.

Les erreurs HTTP sont traduites dans un helper serveur par domaine. Une indisponibilité critique utilise error(). Une page qui peut rester utile en mode dégradé renvoie un view model vide et un indicateur loadError.

Une mutation d’un enregistrement existant transmet expectedUpdatedAt. Un 409 demande à l’utilisateur de recharger ; il ne faut pas écraser silencieusement la version concurrente.

Les actions multiples renvoient un payload discriminé avec action, success et un message français prêt à afficher dans une zone live.

  • Déclarez les props avec $props() et les valeurs calculées avec $derived.
  • Réservez $effect aux effets de bord.
  • Utilisez SvelteSet et SvelteMap pour les collections réactives.
  • Lisez la route avec $app/state, pas les stores dépréciés.
  • Protégez localStorage derrière onMount pour préserver le SSR.
  • Extrayez un état client complexe dans une classe .svelte.ts testable.

Les chaînes publiques vivent dans messages/en.json, fr.json, it.json, de.json et es.json. Ajoutez la même clé partout, puis exécutez :

Fenêtre de terminal
npm run i18n:compile

Ne modifiez jamais src/lib/paraglide à la main. Le middleware Paraglide applique la stratégie URL, cookie puis locale de base et renseigne les attributs lang et dir du document. Une URL publique sans préfixe désigne le français : un cookie anglais ne la redirige pas vers /en/. Le serveur aligne le cookie de préférence sur la variante visitée. Le sélecteur conserve le chemin, la query et le fragment pour accéder à la page équivalente.

Les pages utilisent data.locale, fourni par le layout serveur, pour leurs textes et calendriers. getLocale() peut servir de repli ; évitez getLocaleForUrl() pendant le SSR, car il ne consulte pas le contexte de requête serveur et peut retourner la langue de base avant hydratation.

Le back-office est en français inline, sauf le système de notifications partagé qui utilise des clés backoffice_* dans tous les catalogues.

Le navigateur appelle uniquement les routes BFF authentifiées /api/back-office/ai/**. Les éditeurs ne connaissent ni le fournisseur ni ses identifiants et une proposition ne modifie jamais le brouillon avant une action explicite.

Pour enregistrer un nouveau type de contenu :

  1. ajoutez le type d’entité et ses champs autorisés au contrat partagé domain/ai/AITranslation.ts, en accord avec le registre du backend ;
  2. déclarez les libellés, formats, champs requis et longueurs maximales dans AITranslationEditorViewModel.ts ;
  3. écrivez deux fonctions pures : une pour convertir le brouillon en dictionnaire de champs, une pour réappliquer partiellement ce dictionnaire sans toucher au reste de l’état ;
  4. affichez AITranslationEditor.svelte uniquement lorsque la locale active diffère de la locale source activée, puis passez-lui les valeurs non enregistrées et la fonction d’application locale ;
  5. ajoutez un test de mapping, un test d’intégration de l’éditeur et un parcours Playwright avec une réponse BFF déterministe.

Le composant commun gère le chargement, l’annulation, les erreurs, la détection d’une source devenue obsolète, la protection des contenus cibles existants et le signalement des résultats applied ou rejected. L’enregistrement et la publication restent les actions de formulaire existantes.

  • Utilisez les tokens CSS, jamais une couleur hexadécimale dans un composant.
  • Respectez WCAG 2.1 AA : nom accessible, focus logique, contraste, état non transmis par la couleur.
  • Masquez les icônes décoratives avec aria-hidden="true".
  • Associez chaque champ à un label.
  • Utilisez les variantes des primitives avant d’ajouter des classes ad hoc.
  • Ajoutez une story CSF 3 à chaque composant présentatif réutilisable.
Fenêtre de terminal
npm run check
npm run test -- chemin/du/test.test.ts
npm run lint
npm run build
npm run storybook

Avant livraison, make verify dans web/ exécute la validation de l’application puis docs:check. Ajoutez Playwright lorsque le comportement dépend de la navigation, du SSR, des cookies ou de plusieurs écrans.