@amsom-habitat/cresol
v0.2.8
Published
Création de sollicitation (assistant 3 étapes) réutilisable par les outils AMSOM Habitat
Readme
Cresol
Assistant de création de sollicitation, extrait de Cally2 et réutilisable par les outils AMSOM Habitat.
L'assistant est autonome : il charge lui-même la nomenclature, l'arbre patrimonial et les collaborateurs, puis poste la création et les documents joints. Il ne connaît rien de l'outil qui l'héberge. Celui-ci lui donne :
- les réclamants possibles (le dossier consulté) ;
- éventuellement un brouillon pré-alimenté et des champs figés ;
- le contenu des volets latéraux (sollicitations similaires, évènements), qui restent les siens.
En retour, il reçoit des événements à chaque moment clé : avant et après la création, et aux changements de réclamant, de motif et d'étape.
┌──────────────── outil hôte ────────────────┐
│ props : reclamants, modelValue, readonly… │
│ slots : volets similaires / évènements │
└───────────────┬────────────────▲───────────┘
│ │ événements : beforeCreate, created,
▼ │ createError, motifChange…
┌──────────────── Cresol ────────────────┐
│ [Réclamant] › Catégorisation › Cible › │
│ Détails → POST création + documents │
└────────────────────────────────────────┘Sommaire
- Installation
- Configuration
- Démarrage rapide
- Props
- Événements
- Slots
- Le choix du réclamant
- Champs figés (
readonly) - Restreindre la nomenclature
- Le brouillon (
v-model) - La création
- Exports
- Endpoints consommés
- Développement
Installation
npm i @amsom-habitat/cresolPas de feuille de style à importer : le CSS est injecté par le JS du paquet. L'outil fournit, lui,
Bootstrap (@amsom-habitat/bootstrap-5) et le composant global font-awesome-icon. Les icônes
sont l'affaire du paquet : celles de son interface sont enregistrées au chargement (src/icons.js),
celles portées par les données (motifs, catégories…) à la volée.
Configuration
À poser une fois au démarrage de l'outil :
import { configureCresol } from '@amsom-habitat/cresol'
configureCresol({ baseUrl: import.meta.env.VITE_API_URL })| Option | Défaut (@amsom-habitat/user-manager) | Rôle |
| --------------- | -------------------------------------- | ----------------------------------- |
| baseUrl | — (obligatoire) | Racine de l'API métier |
| getToken | cookie partagé du domaine | Jeton envoyé à chaque appel |
| setToken | écrit le cookie | Jeton renouvelé (refresh 449) |
| logout | déconnexion du domaine | Refresh impossible |
| goToLoginPage | page de login du domaine | 401 / 498 sur un appel qui redirige |
Un outil du domaine ne pose que baseUrl : le jeton suit le SSO. Les autres options servent à un
outil qui gère son authentification lui-même.
La prop baseUrl surcharge l'API pour une instance (prop › configureCresol › défaut) :
storybook, démo, seconde API. Le jeton, lui, ne passe que par configureCresol : un
getToken / setToken couplés est ce qui permet au refresh 449 de rejouer la requête, et le
logout est celui de l'outil (pas de déconnexion SSO décidée par le paquet). Sans baseUrl nulle part, l'appel échoue avec une erreur
explicite plutôt que de partir en relatif sur l'origine de la page.
Démarrage rapide
<template>
<creer-sollicitation-modal
v-if="open"
:reclamants="[{ type: 'L', id: 2200523, libelle: 'Contrat' }]"
:model-value="{ provenance: 'T' }"
:readonly="['reclamant']"
@created="onCreated"
@error="$toast.error($event)"
@close="open = false"
>
<template #aside-similaires="{ idMotif, codeProgramme, idCible }">
<mes-similaires :id-motif="idMotif" :code-programme="codeProgramme" :id-cible="idCible" />
</template>
<template #aside-evenements>
<mes-evenements-de-patrimoine />
</template>
</creer-sollicitation-modal>
</template>
<script>
import { CreerSollicitationModal } from '@amsom-habitat/cresol'
export default {
components: { CreerSollicitationModal },
data: () => ({ open: true }),
methods: {
onCreated(sollicitations) {
this.$toast.success(`${sollicitations.length} sollicitation(s) créée(s)`)
// … recharger le listing de l'outil
}
}
}
</script>Deux composants, même contrat (props, événements, slots, définis une seule fois dans
src/props.js) :
CreerSollicitationForm: l'assistant seul, à placer dans une page ou dans sa propre modale ;CreerSollicitationModal: le même, dans uneAmsomModal. Il ajoute les propstitre(défaut'Nouvelle sollicitation') etcloseOnCreated(défauttrue: émetclosejuste aprèscreated), et l'événementclose(croix, ou création réussie). Ses attributs non déclarés (reducible,title…) descendent surAmsomModal.
Props
| Prop | Type | Défaut | Rôle |
| ---------------------- | ---------------- | ---------- | ---------------------------------------------------------------------------------------------------------- |
| reclamants | Array | [] | Réclamants proposables : [{ type, id, libelle }] (détail) |
| reclamantMode | String | 'select' | Présentation du choix : select, switch, autocomplete, step |
| typesReclamant | Array | null | Codes KRCLENT.RCLT cherchables en mode step (null = tous) (détail) |
| typesReclamantExclus | Array | [] | Codes KRCLENT.RCLT retirés de la recherche en mode step |
| provenances | Array | 6 canaux | Codes KRCLENT.CTRANS proposés, dans cet ordre (null = toutes les actives) (détail) |
| provenancesExclues | Array | [] | Codes KRCLENT.CTRANS retirés des provenances proposées |
| modelValue | Object | null | Brouillon pré-alimenté, partiel ; suivi en v-model (détail) |
| readonly | Boolean | Array | false | Champs figés : true, false ou une liste (détail) |
| modules | Array | null | Modules du réclamant ; laissés null, chargés pour un réclamant contrat |
| idModule | String | Number | null | Module à préférer comme cible par défaut parmi ceux du réclamant |
| nomenclatureFilter | Function | null | Prédicat : quels motifs sont proposables (détail) |
| aside | Boolean | true | Affiche le volet latéral (ses onglets viennent des slots) |
| nbSimilaires | Number | 0 | Badge de l'onglet « Similarité » |
| baseUrl | String | null | API de cette instance (sinon configureCresol) |
Chaque prop porte une seule responsabilité. Les valeurs de la saisie passent par
modelValue, les choix possibles par reclamants, ce qui est modifiable par readonly, et la
présentation du réclamant par reclamantMode. Il n'y a donc pas de prop provenance : on la
passe dans modelValue ({ provenance: 'M' }) ; provenances ne dit que lesquelles proposer.
Les provenances
Libellés et activité viennent de la table krclorig (vue AHVWS_SOLLICITATION_PROVENANCE,
GET /v2/sollicitation/provenances?estDesactive=false) : une provenance inactive n'est jamais
proposée, sans rien changer dans l'outil. Le paquet ne porte que les icônes
(PROVENANCE_CONTACT_ICONS, repli comment-dots).
Comme pour typesReclamant, l'outil choisit lesquelles proposer : provenances (liste blanche,
qui fixe aussi l'ordre) puis provenancesExclues (liste noire). Par défaut, les six canaux saisis
par un agent (PROVENANCES_CONTACT_DEFAUT : T, M, G, V, IP, C) ; null propose toute
la table, canaux automatiques compris (Volly, Link…). Une provenance pré-remplie (modelValue)
qui n'est pas proposée — exclue ou devenue inactive — bloque l'étape, comme une provenance vide.
<!-- accueil physique -->
<creer-sollicitation-form :provenances="['V', 'C', 'T']" />
<!-- toute la table sauf Volly et Link -->
<creer-sollicitation-form :provenances="null" :provenances-exclues="['VO', 'LI']" />modelValue n'est lu qu'à l'ouverture : le modifier ensuite ne réinitialise pas l'assistant.
Pour repartir d'un autre brouillon, on remonte le composant (v-if ou :key).
Événements
| Événement | Quand | Charge utile |
| ------------------- | ----------------------------------------- | ------------------------------------------------------------ |
| update:modelValue | à chaque modification du brouillon | le brouillon |
| reclamantChange | l'utilisateur change de réclamant | l'entrée de reclamants choisie |
| motifChange | le motif ou les motifs associés changent | { idCategorie, idType, idNature, idMotif, motifsAssocies } |
| stepChange | changement d'étape | { step, key, previous: { step, key } } |
| beforeCreate | clic sur « Créer », avant l'appel API | { payload, cancel, waitUntil } |
| created | création réussie (documents compris) | (sollicitations, { payload, fichiers }) |
| createError | l'API a refusé la création | { error, message, payload } |
| error | toute erreur affichable | message (chaîne) |
| close | (modale) fermeture demandée | — |
reclamantChange, motifChange et stepChange ne sont pas émis pour l'état initial : l'outil
le connaît déjà, puisque c'est lui qui l'a fourni. Ils rendent compte de ce que fait l'utilisateur.
Le cycle de la création
clic « Créer »
│
├─ beforeCreate ── cancel() / waitUntil(false | rejet) ──▶ rien n'est envoyé, l'assistant reste ouvert
│
├─ POST creer_sollicitation ── échec ──▶ createError + error, l'assistant reste ouvert
│
├─ POST upload_sollicitation_ged (un par document, en parallèle)
│ échec d'un document ──▶ error (la création, elle, est acquise)
│
└─ created(sollicitations, { payload, fichiers }) (modale : puis close)Pendant tout le cycle, l'assistant est sous overlay et un second clic est ignoré.
Succès et échec sont séparés (created / createError) : l'outil n'y fait pas la même
chose (fermer et recharger d'un côté, garder la saisie et expliquer de l'autre), et chacun
porte une charge utile différente. error reste le canal unique des messages pour qui ne
veut que toaster : il reçoit aussi les échecs de création, ceux des documents et ceux des
chargements. Règle simple :
- pour afficher une erreur, écouter
error; - pour réagir à l'échec de la création (journaliser, proposer autre chose…), écouter
createError.
beforeCreate : intervenir avant l'envoi
L'événement porte un objet :
| Clé | Rôle |
| -------------------- | ------------------------------------------------------------------------------ |
| payload | le corps qui va partir (forme) ; modifiable en place |
| cancel() | annule la création |
| waitUntil(promise) | fait attendre l'envoi ; résolue à false, ou rejetée, elle annule la création |
// Confirmation
onBeforeCreate({ payload, waitUntil }) {
waitUntil(this.$confirm(`Créer ${payload.lignes.length} sollicitation(s) ?`))
}
// Veto synchrone
onBeforeCreate({ payload, cancel }) {
if (!this.peutCreer(payload)) cancel()
}
// Enrichir le payload
onBeforeCreate({ payload }) {
payload.lignes.forEach((ligne) => (ligne.description += `\n[${this.origine}]`))
}Une annulation est silencieuse : rien n'est émis ensuite. C'est l'outil qui l'a décidée, il sait déjà pourquoi.
created : le compte rendu
onCreated(sollicitations, { payload, fichiers }) {
// sollicitations : une par ligne, sérialisées par l'API ([{ id, idMotif, … }])
// fichiers : un compte rendu par document joint
// [{ idMotif, nom, idSollicitation, ok: true | false, message }]
const enEchec = fichiers.filter((f) => !f.ok)
}Un document en échec n'annule pas la création : il est signalé par error et marqué
ok: false, et l'utilisateur pourra le joindre depuis le détail de la sollicitation.
Les autres événements
// Recharger ce qui dépend du réclamant (sollicitations similaires, évènements…)
onReclamantChange(reclamant) {}
// Suggestions, alertes métier propres à l'outil sur un motif
onMotifChange({ idMotif, motifsAssocies }) {}
// Suivi d'usage, aide contextuelle par étape
onStepChange({ key, previous }) {} // key : 'reclamant' | 'categorie' | 'cible' | 'details'Slots
| Slot | Contenu | Absent ⇒ |
| ------------------ | ----------------------------------------------- | --------------- |
| aside-similaires | onglet « Similarité » ; scopé (voir ci-dessous) | pas d'onglet |
| aside-evenements | onglet « Événements » | pas d'onglet |
| aside | remplace tout le volet latéral | volet à onglets |
| reclamant | (mode step) remplace l'étape réclamant | recherche |
Les deux volets appartiennent à l'outil : c'est lui qui interroge son API et rend ses cartes. Le paquet ne lui fournit que le scope de la saisie en cours :
| Clé du scope | Valeur |
| --------------- | ----------------------------------------------------------------------- |
| idMotif | motif de la ligne principale |
| codeProgramme | programme de la localisation ciblée (nul quand la cible est la demande) |
| idCible | id de la demande ciblée (nul sinon) |
| reclamant | le réclamant sélectionné |
Le badge de l'onglet revient par la prop nbSimilaires.
Le choix du réclamant
reclamants est une liste ordonnée d'entrées { type, id, libelle } :
typeest le codeKRCLENT.RCLT:L/P= contrat (TYPES_RECLAMANT_CONTRAT),D= demande,U= individu « Autre »,T= tiers,F= fournisseur,E= personnel ; un code inconnu n'est pas traité comme un contrat ;libelleest ce qui est affiché (côté Cally : « Contrat », « Demande », « Partie externe ») ;- il n'y a pas de clé technique : un réclamant s'identifie par son couple
type+id.
Le premier de la liste est sélectionné, sauf si modelValue.reclamant ({ type, id }) en
désigne un autre. Le type choisi pilote la suite :
| Réclamant | Cible proposée |
| ------------------------- | ------------------------------------------------------------------------------ |
| contrat | le contrat lui-même, sur l'un de ses modules (chargés si modules est null) |
| demande (D) | toujours la demande, sans localisation |
| partie externe (U, T) | une localisation du patrimoine (pas de contrat propre) |
Présentation, selon reclamantMode :
| reclamantMode | Rendu |
| --------------- | --------------------------------------------------------------------- |
| select | liste déroulante (défaut) |
| switch | boutons côte à côte |
| autocomplete | AmsomAutocomplete, pour une liste longue (recherche sur le libellé) |
| step | une étape dédiée de recherche, avant la catégorisation |
Avec un seul réclamant, ou 'reclamant' dans readonly, il est seulement affiché (modes select,
switch, autocomplete). Le mode step est à part : le réclamant y est cherché, reclamants
et 'reclamant' dans readonly y sont ignorés ; seul modelValue.reclamant le présélectionne
(cf. ci-dessous). Le stepper compte une étape de plus et
stepChange émet key: 'reclamant'.
L'étape réclamant (step)
Par défaut, l'étape fait chercher le réclamant : on choisit d'abord Locataire, Demandeur, Tiers, Autre, Fournisseur, Personnel ou Tout (toutes les catégories proposées à la fois), puis on tape dans une barre de recherche (nom, prénom, n° de contrat ou de demande, adresse, téléphone, mail), comme la recherche de contrat de l'étape cible. Rien d'autre : pas de liste de l'outil à côté.
| Catégorie | Réclamant (type) | Ce qui est proposé |
| ------------- | ---------------------- | ------------------------------------------------------------------------------------------------------------------------- |
| locataire | le contrat (L, P…) | signataires et occupants de contrats non archivés, sauf les occupants non signataires d'un contrat de personne morale |
| demandeur | la demande (D) | signataires de demandes non archivées, actives ou modifiées depuis moins d'un an |
| tiers | le tiers (T) | tiers valides |
| autre | l'individu (U) | individus « Autre » |
| fournisseur | le fournisseur (F) | fournisseurs valides de la société courante |
| personnel | le collaborateur (E) | collaborateurs non invalidés |
| tout | selon la ligne | les catégories proposées, mélangées ; chaque résultat porte sa categorie |
L'outil règle les réclamants proposés par code KRCLENT.RCLT (L, P, D, T, U, F,
E — tous par défaut) ; une catégorie n'apparaît que si l'un de ses codes est autorisé, et les
résultats sont filtrés par code (['L'] écarte les contrats P). Tout n'apparaît qu'à partir de
deux catégories :
<!-- pas de fournisseurs -->
<creer-sollicitation-form reclamant-mode="step" :types-reclamant-exclus="['F']" />
<!-- fournisseurs et tiers uniquement -->
<creer-sollicitation-form reclamant-mode="step" :types-reclamant="['F', 'T']" />Le réclamant est celui de Cally (KRCLENT.RCLT + RCLC) : le contrat, la demande, le
tiers ou l'« Autre », jamais l'individu. La recherche porte sur les individus (noms, téléphones,
mails des signataires, occupants, demandeurs), puis les résultats sont regroupés par
réclamant : un contrat à deux signataires trouvés n'apparaît qu'une fois, avec les deux noms.
Individus archivés exclus ; les contrats sortis et demandes inactives restent proposés,
signalés et classés après les autres. Le réclamant trouvé est une entrée
{ type, id, libelle, categorie, libelleReclamant, adresse, estActif, individus } : c'est elle
que reçoivent reclamantChange et le scope des volets. Ses modules sont chargés à la sélection
(cible par défaut).
Un réclamant pré-alimenté par modelValue.reclamant ({ type, id }, ex. une duplication après
création) est retrouvé par la recherche à l'ouverture, puis affiché comme s'il venait d'être
choisi ; il reste modifiable. Introuvable, il est gardé tel quel (libellé type/id).
Le slot reclamant remplace l'étape par la recherche de l'outil. Seule la provenance du
contact reste au-dessus quand l'étape est la première : elle est exigée pour avancer.
<creer-sollicitation-form reclamant-mode="step">
<template #reclamant="{ reclamant, select }">
<ma-recherche :valeur="reclamant" @choisi="select({ type: $event.type, id: $event.id, libelle: $event.nom })" />
</template>
</creer-sollicitation-form>| Clé du scope | Valeur |
| ------------ | ------------------------------------------------------ |
| reclamant | le réclamant sélectionné (ou null) |
| select | select({ type, id, libelle }) : choisit le réclamant |
Champs figés (readonly)
Une seule prop, trois formes :
<!-- tout se saisit (défaut) -->
<creer-sollicitation-form :readonly="false" />
<!-- partiel : l'outil impose le réclamant et le canal -->
<creer-sollicitation-form :readonly="['reclamant', 'provenance']" />
<!-- tout est figé : l'utilisateur relit et valide -->
<creer-sollicitation-form readonly />| Champ | Figé, il affiche… | … et l'assistant ne fait plus |
| ------------- | -------------------------------------------------------------------- | ----------------------------------------------- |
| reclamant | son libellé | — |
| provenance | le canal (icône + libellé) | — |
| motif | la chaîne catégorie › type › nature › motif ; motifs associés grisés | — |
| cible | un résumé par ligne | déduire la cible du réclamant ou de ses modules |
| description | le texte | — |
| dateFaits | la date | — |
| responsable | son nom, ou « Déterminé automatiquement » | suggérer un responsable d'après le paramétrage |
| fichier | le document, sans ajout ni suppression | — |
La liste est exportée (READONLY_FIELDS) et une valeur inconnue est signalée par Vue.
Un champ figé affiche ce qu'on lui fournit, par reclamants et modelValue. Les champs
obligatoires restent exigés pour avancer : figer provenance, motif, cible ou description
engage l'outil à les fournir, sans quoi l'utilisateur reste bloqué à l'étape. Un responsable figé
et vide est déterminé côté serveur.
Restreindre la nomenclature
nomenclatureFilter est un prédicat qui reçoit le motif entier : on filtre sur n'importe
quel champ, pas seulement sur les identifiants.
// Sur une demande, seuls certains motifs sont proposables (règle d'office de Cally).
(motif, { reclamant }) => reclamant?.type !== 'D' || MOTIFS_DEMANDE.includes(motif.id)
// N'importe quel champ du référentiel fait l'affaire.
(motif) => !motif.libelle.startsWith('[INTERNE]')Il s'applique à la recherche et à la sélection guidée, et il est rejoué quand le réclamant change. Absent, toute la nomenclature active est proposée.
Le brouillon (v-model)
{
reclamant: { type, id },
provenance: 'T', // code KRCLENT.CTRANS (cf. prop `provenances`)
lignes: [ lignePrincipale, ...motifsAssociés ],
}Un brouillon partiel suffit : il est fusionné avec le squelette (getDefaultDraft,
getDefaultLigne, getDefaultPatrimoine, exportés), l'appelant n'a pas à connaître toutes les
clés.
Une ligne :
| Clé | Rôle |
| ---------------------------------------------- | ------------------------------------------------------------------- |
| idCategorie, idType, idNature, idMotif | nomenclature (hiérarchique : changer un niveau efface les suivants) |
| cibleMode | contrat, patrimoine ou demande (voir ci-dessous) |
| patrimoine | localisation : commune, adresse, étage, module… |
| contratReclamant, contratCible | contrat ciblé en mode contrat |
| reprendreLocataireActuel | viser l'occupant actuel du module plutôt que le contrat ciblé |
| modulePrecis | mode patrimoine : un module (true) ou les parties communes |
| description, dateFaits | détail (dateFaits en timestamp Unix) |
| responsable | collaborateur, ou son id ; null = déterminé côté serveur |
| fichier | document joint (AmsomUploadFile), envoyé après la création |
- Cible (
cibleMode) :contrat: un contrat et l'un de ses modules. C'est celui du réclamant sicontratReclamant, sinon lecontratCibletrouvé par la recherche. Quand ce contrat n'occupe plus le module,reprendreLocataireActuelbascule sur son occupant actuel ;patrimoine:modulePrecischoisit entre un module précis (obligatoire) et les parties communes (une adresse, un étage au plus, jamais de module) ;demande: la demande elle-même, pour un réclamant demande.
- Héritage des motifs associés :
lignes[0]porte toujours sa cible et son détail. Sur une ligne associée :description: null= hérite de la principale, une valeur la surcharge ;- la cible se surcharge par
cibleOverride; - la date se surcharge par
dateFaitsOverride, parce quenullest une date valide (« pas de date ») et ne peut pas marquer l'héritage ; - le responsable n'hérite jamais.
La création
beforeCreate expose le corps envoyé à POST /v2/cresol/creer_sollicitation. L'héritage y est
déjà résolu : chaque ligne est autonome et porte les mêmes clés.
{
typeReclamant: 'L',
reclamant: 2200523,
provenance: 'T',
lignes: [
{
categorie, type, nature, motif,
description, dateFaits, responsable, // responsable : id ou null
patrimoine, // absent quand la cible est la demande
codeTypeCible, idCible, // cible contractuelle explicite…
deduireCible // …ou à déduire du module par le serveur
}
]
}L'API renvoie les sollicitations créées (une par ligne). Chaque document joint part ensuite par
POST /v2/cresol/upload_sollicitation_ged, apparié à la sollicitation du même motif. Le fichier
ne figure jamais dans le corps de création.
Exports
import {
CreerSollicitationForm,
CreerSollicitationModal,
configureCresol, // configuration du transport
resetCresolCache, // vide le cache des référentiels
getDefaultDraft, // squelettes du brouillon
getDefaultLigne,
getDefaultPatrimoine,
READONLY_FIELDS, // champs acceptés par `readonly`
PROVENANCES_CONTACT_DEFAUT, // codes proposés par défaut (prop `provenances`)
PROVENANCE_CONTACT_ICONS, // { code: icône }
DEFAULT_PROVENANCE_CONTACT
} from '@amsom-habitat/cresol'Les référentiels et l'arbre patrimonial sont mis en cache au niveau du module, par API : deux
ouvertures de l'assistant ne rechargent rien. resetCresolCache() le vide (changement
d'utilisateur, tests).
Endpoints consommés
| Méthode | Chemin | Usage |
| ------- | ----------------------------------------------------------- | ---------------------------------------------------------------------------------------- |
| GET | /v2/sollicitation/{motifs,categories,types,natures,etats} | Nomenclature (sans jeton) |
| GET | /v2/sollicitation/responsables | Paramétrage des responsables |
| GET | /collaborateurs?actif=true | Responsables proposables |
| GET | /communes · /v2/patrimoine/{entrees,modules} | Arbre patrimonial (cible) |
| GET | /v2/patrimoine/contrat_modules | Modules d'un contrat |
| GET | /v2/patrimoine/contrat_module_rechercher?recherche= | Recherche libre de contrat |
| GET | /v2/cresol/reclamant_rechercher?categorie=&recherche= | Recherche de réclamant (step) ; categorie = une catégorie, une liste a,b ou tout |
| POST | /v2/cresol/creer_sollicitation | Création (1 à n lignes) |
| POST | /v2/cresol/upload_sollicitation_ged | Document joint à une ligne |
Développement
npm i
cp .env.example .env # VITE_API_URL=http://…
npm run storybook # http://localhost:6006
npm run lint
npm run buildLes stories parlent à une vraie API : la nomenclature, le patrimoine et les collaborateurs
viennent du serveur désigné par .env. Les contrôles baseUrl et token permettent
de surcharger ponctuellement ; ils sont conservés d'une story à l'autre et au rechargement
(localStorage), et le jeton est renouvelé sur place par un refresh 449. Le journal sous chaque story liste les
événements émis, avec leur charge utile.
Les stories n'embarquent aucun identifiant de réclamant : une story soumise crée une vraie
sollicitation, et un identifiant fictif y laisserait un réclamant inexistant. Les contrôles
idContrat et idDemande désignent un dossier réel de l'API visée et sont conservés comme
baseUrl / token. Tant que rien n'est saisi, ils valent les dossiers de recette
idContrat: 2251519 et idDemande: 2256564 (EXEMPLES dans .storybook/preview.js). Vidés, le
réclamant n'est pas proposé, et une story qui en attend un affiche un avertissement au lieu du
formulaire.
| Story | Montre |
| ----------------------------------------------------------------- | ----------------------------------------------------------------------------- |
| Contrat | cas courant, volet à onglets + nbSimilaires |
| Demande | réclamant demande + nomenclatureFilter |
| PlusieursReclamants | plusieurs réclamants, reclamantMode select/switch/autoc. |
| ReclamantRecherche / ReclamantRechercheRestreinte | mode step, toutes catégories / typesReclamant |
| ReclamantRechercheSurchargee | slot reclamant |
| BrouillonPrealimente | modelValue partiel |
| ReclamantEtProvenanceFiges / Relecture | readonly partiel / total |
| ProvenancesRestreintes / ProvenancesToutesSaufExclues | provenances / provenances: null + provenancesExclues |
| SansVolet / VoletRemplace | aside: false / slot aside |
| InterceptionAvantCreation | beforeCreate : payload enrichi + waitUntil |
| Defaut (modale) | ouverture, close, closeOnCreated |
| DuplicationApresCreation (…Etape) / EnchainementAutomatique | (modale) enchaîner les créations avec un brouillon dupliqué (étape réclamant) |
Organisation du code
src/
CreerSollicitationForm.vue racine : fournit brouillon, données et options ; porte la création
CreerSollicitationModal.vue coquille modale (props et événements relayés)
props.js contrat public unique (props + événements)
components/ étapes et blocs de saisie (lisent le brouillon injecté)
composables/
useSollicitationDraft.js le brouillon : état, validation, champs figés, payload
useCresolData.js référentiels et patrimoine (cache module, par API)
useCresolContext.js options de l'outil (injectées, sans prop-drilling)
api/ un fichier par domaine ; transport/ = axios-overlay + jeton
utils/ fonctions pures (texte, dates, erreurs, icônes)Toute la logique de saisie (héritage, validation, champs figés, payload) vit dans
useSollicitationDraft : les composants ne font qu'afficher et appeler ses setters.
Publier
Le numéro de package.json doit avoir son entrée ## VX.Y.… dans changelog.md
(check-version.sh).
make publish # main : lint, build, tag, npm publish
make alpha_publish # dev : version prerelease beta