Feature/dp 1721 spike integrer maquettes espace fd listing formulaires cas - #1595
Conversation
…itationType Étape 1.1 — migration `form_templates` (slug unique, FK habilitation_type, service_provider_id string, jsonb steps/static_blocks/scopes_config/initialize_with), modèle avec friendly_id, paper_trail, invariants (slug pas en collision avec les UID YAML, au moins un default par HT, cascade-safe via destroyed_by_association). Étape 1.2 — callback `HabilitationType#after_create :ensure_default_form_template!` qui reprend `ordered_steps` + `form_introduction` (parité avec l'ancien `build_form_from_habilitation_type`). Rake idempotente `form_templates:backfill_defaults` pour les HTs existants en DB. Façade `AuthorizationRequestForm.db_records` non touchée à ce stade (Étape 1.3 suivra) : on prépare la donnée sans changer le surface API.
…emplate Étape 1.3 — la façade `AuthorizationRequestForm` lit désormais ses db_records depuis `FormTemplate.includes(habilitation_type: :data_provider)`. Un HT avec N FormTemplate produit N ARFs (1 default + N-1 non-default), au lieu d'1 ARF unique forcé `default: true` comme dans `build_form_from_habilitation_type` (supprimée). Surface API publique inchangée — le `uid` côté façade reste le slug, `authorization_request_class` est résolu via `template.habilitation_type`. Les nouveaux champs portés en DB depuis YAML (`use_case`, `initialize_with`, `static_blocks`, `service_provider_id`, `name`, `description`, `public`, `startable_by_applicant`, `single_page_view`, `scopes_config`) sont deep_symbolize_keys côté façade, conforme à ce que consomment les vues. Specs `authorization_request_form_spec.rb` réécrites pour passer par un vrai `FormTemplate` (au lieu de stub `db_records`), couvrant : 1 HT → N ARFs, default flag respecté, service_provider résolu via le YAML backend, jsonb symbolisé. Full RSpec 2631/0, full cucumber : flakes connus uniquement. Fix i18n: l'ajout du namespace `form_template` à `activerecord.fr.yml` (commit parent) était mal indenté et avait sectionné le bloc `habilitation_type` au milieu de `errors.models`, faisant disparaître `scopes.scope_value_duplicate` de la résolution i18n et cassant la validation visuelle des scopes en admin. Docs `docs/technique/habilitation_type_dynamique.md` mises à jour pour refléter le pont HabilitationType → FormTemplate → AuthorizationRequestForm, retrait des limitations levées (`use_case`, `initialize_with`, `static_blocks`, `service_provider` côté DB).
Étape 1.4 — 2 specs garantissent que `AuthorizationRequestForm.all` reflète une création / suppression de `FormTemplate` sans appel manuel à `reset!`. La synchronisation passe par `FormTemplate#after_save/destroy :reset_arf_cache` qui bump le compteur Redis via `StaticApplicationRecord#reset!` (invalide cross-process / cross-worktree, Redis partagé en dev). Étape 1.5 — `rubocop -A` final : `Rails/WhereExists` corrigé dans la rake de backfill (`where(default: true).exists?` → `exists?(default: true)`).
…eck, French wording
|
Je recopie ici ton message pour avoir les liens pratiques dans la PR Et hop, vous pouvez jouer avec l'explo Eva Spaeter [Beta] et Valentin Shamsnejad [Beta] : Puis par exemple : |
6535092 to
d39b056
Compare
…on maquettes Spike de catalogage en lecture du référentiel Formulaires + Cas d'usage, servi sous `/instruction/fournisseurs-donnees/...` à l'audience reporter+ (admin / reporter / instructor / manager / developer via RoleHierarchy). Stack au-dessus de PR #1564 (DP-1718 : cascade HT→FT + FormTemplate) dont il consomme les nouveautés (`form_template`, `inherited?`, `from_database?`). == Routes & Pundit == Nouvelles routes dans `namespace :instruction` : - `get '/fournisseurs-donnees' → data_providers#index` (catalogue racine) - `scope '/fournisseurs-donnees/:provider_slug'` avec : - `resources :formulaires, only: %i[index show]` (AuthorizationDefinition) - `resources :cas_usages, only: :show, path: 'cas-usage', param: :uid` (AuthorizationRequestForm sous le formulaire) Hiérarchie controllers : - `Instruction::DataProvidersController` < `InstructionController` (gate `reporter?` héritée — index seul, pas de @data_provider) - `Instruction::AbstractCatalogueController` < `InstructionController` (porte le `set_data_provider` partagé par les vues FD-scopées) - `Instruction::{Formulaires,CasUsages}Controller` < `Instruction::AbstractCatalogueController` Trois policies namespacées `Instruction::*` : - DataProviderPolicy + Scope : accessible si admin OU si l'utilisateur a un rôle reporter+ sur au moins une définition du provider. TODO explicite : itération sur `AuthorizationDefinition.all` à revisiter une fois la migration BDD (DP-1719/1671) en place. - AuthorizationDefinitionPolicy + Scope : `user.reporter?(record.id)` - AuthorizationRequestFormPolicy : déléguée à la définition parente Wiring via `policy_scope([:instruction, …])` sur l'index et `authorize [:instruction, …]` dans Abstract + show controllers. Refus → redirect vers dashboard via `Pundit::NotAuthorizedError` (comportement existant d'`AccessAuthorization`). == Vue Formulaire (`instruction/formulaires/show.html.erb`) == - Section Identification : uid - Section Identité : table kind, link, access_link, cgu_link, support_email (URL → lien fr-link target=_blank, fallback « Non renseigné ») - Section Propriétés : table booléens public / startable_by_applicant / unique - Section Fonctionnalités : liste \`features\` si non vide - Section Périmètres de données : tags scopes (\`scope.name.presence || scope.value\`) - Section Structure du formulaire : blocs - Section Cas d'usage : cards par cas d'usage avec badges « par défaut » + compteurs demandes/habilitations (filtrés par form_uid sur les tables AuthorizationRequest et Authorization) Listage parent \`instruction/formulaires/index\` simplifié (cards par AuthorizationDefinition). == Vue Cas d'usage (`instruction/cas_usages/show.html.erb`) == - Header : nom + badge « Par défaut » + badge « Hérité du formulaire » si name est null sur le FormTemplate - Section Description (onglets Rendu / HTML) : Rendu via \`sanitize\` allowlist \`strong em b i u br a p ul li\` HTML via \`<pre class=\"dp-code-block\">\` source échappée - Section Introduction (onglets Rendu / HTML) : allowlist étendue à \`div\` (les introductions utilisent souvent \`<p>\`, \`<ul>\`, \`<li>\`, \`<div>\`, \`<a>\`) - Section Propriétés : table public / startable_by_applicant + ligne dédiée « Template single-page » (avec tooltip d'info, rendu nil → \"(template par défaut)\" en grisé, string → \`<code>nom_template</code>\`) - Section Parcours et blocs statiques (onglets Vue / Colonnes) : Vue : liste unifiée des blocs dans l'ordre catalogue parent (\`@formulaire.blocks\`), numérotés. Blocs présents dans \`static_blocks\` rendus avec fr-tag--brown-cafe-creme + cadenas (lock-line), même sémantique que les scopes désactivés. Blocs « hors catalogue » (steps/static non déclarés sur la définition parente) appendus avec badge warning. Single-page : badge inline « Page unique » + nom du template (\`single_page_view.presence || uid.underscore\`). Colonnes : table dressant \`formulaire.blocks\`, \`steps\`, \`static_blocks\`, \`single_page_view\` brutes. - Section Configuration des périmètres (onglets Rendu / Code) : Rendu : tags groupés par \`scope.group\` avec icône d'état + check vert si scope dans \`initialize_with[:scopes]\`. Légende des 4 états en tête. Code : YAML du \`scopes_config\` + \`preselected_scopes\` propres au cas d'usage, puis dump \`formulaire_scopes\` avec \`name\`, \`value\`, \`group\`, \`link\`, \`included\`, \`disabled\`, \`deprecated\` (accès via \`scope.deprecated?\` / \`scope.deprecated_date\`). - Section Données initiales (onglets Rendu / Données) : Rendu : aperçu form-style avec \`<input>\` / \`<textarea>\` DSFR en \`readonly\`. Heuristique de typage par valeur (URL → type=url, multi-ligne ou >80 chars → textarea auto-dimensionné, Boolean → Oui/Non, Numeric → type=number, autre → JSON fallback). Cas spécial \`:modalities\` → tags DSFR. Données : \`<pre class=\"dp-code-block\">\` YAML brut. - Section Service utilisateur : nom + badge éditeur si applicable Partial \`_rendered_html_tabs.html.erb\` : locale \`source\` (et non \`raw\` qui shadowait le helper Rails). == Extensions DSFR (\`dsfr-extensions.css\`) == - \`.fr-tag.fr-tag--brown-cafe-creme\` : étend l'accent DSFR aux \`<span>\`/\`<p>\` informationnels via vars \`--text-action-high-brown-cafe-creme\` / \`--background-action-low-brown-cafe-creme\` - \`.dp-code-block\` : reprend le style scopé existant \`.markdown-content pre\` en classe générique - \`.dp-flow-steps\` : ol flex pour la liste numérotée du parcours == Index providers == \`instruction/data_providers/index.html.erb\` : cards DSFR avec logo, nom, slug, nombre de formulaires accessibles (filtre \`current_user.reporter?(ad.id)\`). Header partial (\`_header.html.erb\`) mis à jour : le titre « Espace fournisseur de données » devient un lien retour vers l'index providers. == Sécurité == Trois LinkToHref \`weak\` ajoutés à \`config/brakeman.ignore\` : \`link_to(model.link, model.link, …)\` sur \`DataProvider.link\` et \`AuthorizationDefinition.{link,cgu_link}\`. Les modèles valident l'URL via \`URL_REGEX\` au write et l'espace est scopé Pundit (admin/FD only). == Tests == Specs Pundit pour les trois policies (\`spec/policies/instruction/\`). Request specs (\`spec/requests/instruction/\`) couvrent les trois routes — pour chaque endpoint : - admin : happy path + erreurs 404 (mismatch provider, id inconnu) - FD reporter scopé sur le provider : 200 - FD reporter scopé sur un autre provider : redirect (autorisé par InstructionController, refusé par la policy) - utilisateur sans rôle reporter : redirect dashboard (InstructionController gate) - non authentifié : redirect == Locales == Fichier \`config/locales/instruction_catalogue.fr.yml\` regroupe l'ensemble des wordings sous \`instruction.{data_providers,formulaires, cas_usages}.*\` + titres de pages sous \`page_titles.instruction.*\`.
d39b056 to
618c085
Compare
Pour info j'ai changé les urls pour les mettre sous /instruction donc ça donne maintenant: |
631503e to
c9486b8
Compare
No description provided.