|
|
@@ -0,0 +1,344 @@
|
|
|
+# Analyse - 09/2022
|
|
|
+
|
|
|
+## Workflow du traitement de données
|
|
|
+
|
|
|
+Etude du workflow des pages `pages/organization/index.vue` et `pages/organization/address/*`
|
|
|
+
|
|
|
+### Entités
|
|
|
+
|
|
|
+#### Get
|
|
|
+
|
|
|
+##### Via UiCollection
|
|
|
+
|
|
|
+> Exemple : l'affichage des adresses sur la fiche de la structure
|
|
|
+
|
|
|
+La page intègre le component `UiCollection`, qui prend en paramètres :
|
|
|
+
|
|
|
+* la query (récupérée depuis le repo)
|
|
|
+* le modèle de l'entity concernée
|
|
|
+* le modèle d'origine (ici Organization)
|
|
|
+* l'id d'origine (celui de l'orga)
|
|
|
+
|
|
|
+L'`UiCollection` récupère la méthode `getCollection` de `useDataUtils` (à qui elle a passé le `dataProvider`)
|
|
|
+
|
|
|
+Elle appelle ensuite cette méthode et récupère le `fetchState` et les items sous forme de ref
|
|
|
+
|
|
|
+Enfin, elle passe les items à son slot, qui les utilise pour afficher les données
|
|
|
+
|
|
|
+
|
|
|
+##### Via UiItemFromUri
|
|
|
+
|
|
|
+> Exemple : l'affichage du pays dans le détail des adresses de la fiche de la structure
|
|
|
+
|
|
|
+Extrait l'id de l'Uri
|
|
|
+
|
|
|
+utilise le `useFetch` natif de nuxt en lui passant en callback un appel à `$dataProvider.invoke`
|
|
|
+
|
|
|
+L'item est ensuite retourné sous forme d'une ComputedRef qui retourne elle-même `queryHelper.getItem`
|
|
|
+
|
|
|
+
|
|
|
+##### Via UiDataTable
|
|
|
+
|
|
|
+Prend en paramètres :
|
|
|
+
|
|
|
+* la query (récupérée depuis le repo)
|
|
|
+* le modèle de l'entity concernée
|
|
|
+* le modèle depuis lequel il est modifié
|
|
|
+* l'id du modèle depuis lequel il est modifié
|
|
|
+
|
|
|
+Utilise `useFetch` en lui passant en callback un appel à `$dataProvider.invoke(type: QUERY_TYPE.MODEL, ...)`
|
|
|
+
|
|
|
+
|
|
|
+##### UiLayoutAlertBarCotisation
|
|
|
+
|
|
|
+Récupère l'état des cotisations avec un `useFetch` auquel on passe en callback un appel à `$dataProvider.invoke({type: QUERY_TYPE.DEFAULT, ...})`
|
|
|
+
|
|
|
+##### Composable useCountryProvider
|
|
|
+
|
|
|
+Utilise `useFetch` en lui passant en callback un appel à `$dataProvider.invoke(type: QUERY_TYPE.MODEL, ...)`
|
|
|
+
|
|
|
+##### Composable useTypeOfPracticeProvider
|
|
|
+
|
|
|
+Utilise `useFetch` en lui passant en callback un appel à `$dataProvider.invoke(type: QUERY_TYPE.MODEL, ...)`
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+#### Create
|
|
|
+
|
|
|
+##### Page `entity/new.vue`
|
|
|
+
|
|
|
+Passe le `$dataProvider` à `useDataUtils` qui retourne la méthode `createItem`
|
|
|
+
|
|
|
+Passe le store et le type d'entité à la méthode `createItem` qui retourne une fonction `create` et deux refs : `loading` (bool) et `item` (Model).
|
|
|
+La méthode `createItem` commit aussi le type du formulaire (`FORM_STATUS.CREATE`), et met le loading à true
|
|
|
+
|
|
|
+Côté client seulement: instancie une nouvelle Entité, et la passe à la méthode `create` qui la persist dans le repository
|
|
|
+
|
|
|
+La page inclut ensuite un component FormXxxx correspondant au type d'entité.
|
|
|
+
|
|
|
+##### Component FormXxxx
|
|
|
+
|
|
|
+Peut utiliser des composables pour retourner des données complémentaires (ex: listes déroulantes). Il récupère alors le dataProvider depuis
|
|
|
+le context pour le passer aux méthodes du composable.
|
|
|
+
|
|
|
+Récupère le repo propre à l'entité via `repositoryHelper.getRepository(Xxxxx)`
|
|
|
+
|
|
|
+Génère une query depuis le repo, déclare éventuellement les relations à charger au passage
|
|
|
+
|
|
|
+Génère une méthode `submitActions` qui retourne une liste d'actions caractérisées par : type d'action (cf. `SUBMIT_TYPE`) et adresse à laquelle se rendre après l'action
|
|
|
+
|
|
|
+Inclut le composant générique UiForm et lui passe l'`id`, le `model`, la `query`, les `submitActions`
|
|
|
+
|
|
|
+
|
|
|
+##### Component UiForm
|
|
|
+
|
|
|
+Importe via des methodes de composables :
|
|
|
+
|
|
|
+* `$dataPersister` et `store`
|
|
|
+* `router`
|
|
|
+* `markAsDirty`, `markAsNotDirty`, `readonly`, `nextStepFactory`
|
|
|
+
|
|
|
+Créé un store de type `Page`
|
|
|
+Créé une ref null nommée `form`
|
|
|
+
|
|
|
+Construit une computed ref `entry` qui sera passée au slot (le FormXxxx). Cette ref doit récupérer l'entrée du repo via `queryHelper.getFlattenEntry(query.value, id.value)`
|
|
|
+
|
|
|
+Construit la méthode `updateRepository` qui sera aussi passée au slot. Cette méthode invoque la méthode `markAsDirty()` qui met à jour le store Form pour le déclarer en dirty, maj la valeur du champs de l'entité correspondante et persist le tout dans le repo.
|
|
|
+
|
|
|
+Créé une méthode `validate` qui utilise la méthode du même nom du v-form (natif de vuetify)
|
|
|
+
|
|
|
+
|
|
|
+Créé une méthode `submit` qui reçoit un argument `next` (type de l'action exécutée):
|
|
|
+
|
|
|
+* Appelle la méthode `validate`
|
|
|
+* Si le formulaire est valide :
|
|
|
+ * Appelles la méthode `$dataPersister.invoke` et récupère la réponse
|
|
|
+ * ajoutes une alerte de type Success au store de la page
|
|
|
+ * appelles la méthode nextStep en lui passant l'argument `next`, qui est donc le type de l'action exécutée
|
|
|
+* Sinon :
|
|
|
+ * Ajoutes des alertes au store de la page et à un nouveau store de type Form (pqoi?)
|
|
|
+
|
|
|
+Créé la méthode `nextStep`, qui appelles la méthode `nextStepFactory` de `useForm`. Cette méthode associe les types d'actions à des callbacks appellant des méthodes de `useForm` (`save`, `sageAndGoto`). Exécute ensuite le callback retourné. Ces deux callbacks ne font qu'appeller le router pour rediriger vers la page suivante.
|
|
|
+
|
|
|
+Créé des méthodes `showDialog` et `closeDialog` qui mettent à jour le store générique
|
|
|
+
|
|
|
+Créé enfin une méthode `saveAndQuit` qui :
|
|
|
+
|
|
|
+* appelle la méthode `submit`
|
|
|
+* devrait appeller la méthode `quitForm` (voir plus bas), mais sauf erreur, le router a déjà été appellé par la méthode `submit`...
|
|
|
+
|
|
|
+Lorsqu'on quitte le formulaire, la boite de dialogue s'affiche et propose trois options : `back_to_form`, `save_and_quit`, `quit_form`
|
|
|
+
|
|
|
+Un clic sur le bouton `back_to_form` du formulaire appelle la méthode `closeDialog` qui ferme la boite de dialogue.
|
|
|
+
|
|
|
+Un clic sur le bouton `save_and_quit` du formulaire appelle la méthode `saveAndQuit` (voir au dessus)
|
|
|
+
|
|
|
+Un clic sur le bouton `quit_form` du formulaire appelle la méthode `quitForm`, qui à son tour:
|
|
|
+* appelles la méthode markAsNotDirty()
|
|
|
+* confirme dans le store la volonté de quitter
|
|
|
+* remet le store dans son état original
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+##### Component FormXxxx (à nouveau)
|
|
|
+
|
|
|
+Le formulaire reçoit de UiForm la ref `entry` et la méthode `updateRepository`
|
|
|
+
|
|
|
+L'entry correspond à l'entité dans le store telle que récupérée via le repo
|
|
|
+
|
|
|
+`updateRepository` sera appellée par chaque input qui sont mis à jour
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+#### Edit
|
|
|
+
|
|
|
+
|
|
|
+##### Page `entity/_id.vue`
|
|
|
+
|
|
|
+Idem *create*, excepté que le formulaire est inclus dans une page `entity/_id.vue`
|
|
|
+
|
|
|
+Passe le `$dataProvider` à `useDataUtils` qui retourne la méthode `getItemToEdit`
|
|
|
+
|
|
|
+Parse l'id de l'entité à éditer directement depuis l'url
|
|
|
+
|
|
|
+Passe l'id et le type d'entité à la méthode `getItemToEdit` qui appelle la méthode `$dataProvider.invoke` et retourne une ref `fetchState`.
|
|
|
+
|
|
|
+
|
|
|
+##### Composable useMyProfile
|
|
|
+
|
|
|
+Propose une méthode pour maj le profile via `$dataPersister.invoke({type: QUERY_TYPE.MODEL, ...})`
|
|
|
+
|
|
|
+
|
|
|
+#### Delete
|
|
|
+
|
|
|
+##### Via UiButtonDelete
|
|
|
+
|
|
|
+Appelle directement `$dataDeleter.invoke`
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+#### Cas particulier : UiLayoutHeaderNotification
|
|
|
+
|
|
|
+Récupère les notifs depuis l'api via un useFetch avec un callback à `await $dataProvider.invoke({type: QUERY_TYPE.ENUM, enumType})`
|
|
|
+
|
|
|
+Récupère ensuite les notifs depuis le store sous forme de computed ref
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+### Enums
|
|
|
+
|
|
|
+#### Get
|
|
|
+
|
|
|
+##### Via UiInputEnum
|
|
|
+
|
|
|
+Récupère l'enum via un useFetch avec un callback à `await $dataProvider.invoke({type: QUERY_TYPE.ENUM, enumType})`
|
|
|
+
|
|
|
+Lorsque les notifications ont été lues, créé un nouvel objet NotificationUser en :
|
|
|
+
|
|
|
+* mettant à jour le store avec `repositoryHelper.persist`
|
|
|
+* l'api avec `$dataPersister.invoke(type: QUERY_TYPE.MODEL, ...)`
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+### Images
|
|
|
+
|
|
|
+#### Get
|
|
|
+
|
|
|
+##### Via UiImage
|
|
|
+
|
|
|
+Récupère l'image grâce à la méthode `getOne` du composable `useImageProvider` (à qui on a passé le data provider et la config du context)
|
|
|
+
|
|
|
+`getOne` appelle un dataProvider avec le type `QUERY_TYPE.IMAGE` et lui passe les arguments de config de l'image (id, hauteur, largeur)
|
|
|
+En cas d'erreur, retourne une image par défaut.
|
|
|
+
|
|
|
+
|
|
|
+##### Via UiInputImage
|
|
|
+
|
|
|
+Si un id a été passé aux props, récupère l'objet File via un useFetch avec un
|
|
|
+callback à `await $dataProvider.invoke({type: QUERY_TYPE.DEFAULT, url: 'api/files', id: props.existingImageId })`
|
|
|
+
|
|
|
+On récupère ensuite l'image grâce à la méthode `getOne` du composable `useImageProvider` (à qui on a passé le data provider et la config du context)
|
|
|
+
|
|
|
+`getOne` appelle un dataProvider avec le type `QUERY_TYPE.IMAGE` et lui passe les arguments de config de l'image (id, hauteur, largeur)
|
|
|
+En cas d'erreur, retourne une image par défaut.
|
|
|
+
|
|
|
+#### Put
|
|
|
+
|
|
|
+##### Via UiInputImage
|
|
|
+
|
|
|
+Met à jour le store avec `repositoryHelper.persist`
|
|
|
+
|
|
|
+Envoie la requête de maj du File avec `$dataPersister.invoke(type: QUERY_TYPE.MODEL, ...)`
|
|
|
+
|
|
|
+#### Post
|
|
|
+
|
|
|
+##### Via UiInputImage
|
|
|
+
|
|
|
+Créé un nouvel objet File à sauvegarder
|
|
|
+
|
|
|
+L'envoie à l'API avec `$dataPersister.invoke(type: QUERY_TYPE.FILE, ...)`
|
|
|
+
|
|
|
+
|
|
|
+### Api externes ou routes custom
|
|
|
+
|
|
|
+#### UiInputAutocompleteWithApi
|
|
|
+
|
|
|
+Appelle `useFetch` en lui passant le callback `$dataProvider.invoke({type: QUERY_TYPE.DEFAULT, ...})`
|
|
|
+
|
|
|
+#### UiTemplateMobytStatus
|
|
|
+
|
|
|
+Appelle `useFetch` en lui passant le callback `$dataProvider.invoke({type: QUERY_TYPE.DEFAULT, ...})`
|
|
|
+
|
|
|
+#### UiMap
|
|
|
+
|
|
|
+Appelle la route `/api/gps-coordinate-searching` via un appel à `$dataProvider.invoke({type: QUERY_TYPE.DEFAULT, ...})`
|
|
|
+
|
|
|
+#### Composable useAccessesProvider
|
|
|
+
|
|
|
+Appelle la route `/api/access_people` via un appel à `$dataProvider.invoke({type: QUERY_TYPE.DEFAULT, ...})`
|
|
|
+
|
|
|
+#### Composable useAddressPostalUtils
|
|
|
+
|
|
|
+Appelle la route `https://api-adresse.data.gouv.fr/search/...` via un appel à `$dataProvider.invoke({type: QUERY_TYPE.DEFAULT, ...})`
|
|
|
+
|
|
|
+#### Composable useValidator
|
|
|
+
|
|
|
+Appelle la route `/api/siret-checking` via un appel à `$dataProvider.invoke({type: QUERY_TYPE.DEFAULT, ...})`
|
|
|
+
|
|
|
+ou
|
|
|
+
|
|
|
+Appelle la route `/api/subdomains` via un appel à `$dataProvider.invoke({type: QUERY_TYPE.DEFAULT, ...})`
|
|
|
+
|
|
|
+#### Page subscription
|
|
|
+
|
|
|
+Appelle la route `/api/dolibarr/account/` via un appel à `$dataProvider.invoke({type: QUERY_TYPE.DEFAULT, ...})`
|
|
|
+
|
|
|
+
|
|
|
+## Propositions
|
|
|
+
|
|
|
+### Notes diverses
|
|
|
+
|
|
|
+##### Stores
|
|
|
+
|
|
|
+* faire un store 'appState', renommer form en formState? renommer page en pageState?
|
|
|
+* utiliser des interfaces pour le state des stores : https://pinia.vuejs.org/core-concepts/state.html#typescript (déja fait a priori)
|
|
|
+* j'ai l'impression que les dialogs ne sont pas gérés depuis le store "page", ce serait ptêt pas mal?
|
|
|
+
|
|
|
+##### Pinia
|
|
|
+
|
|
|
+* utiliser les hooks pinia pour répercuter les maj sur l'api? >> https://pinia-orm.codedredd.de/guide/model/lifecycle-hooks
|
|
|
+
|
|
|
+
|
|
|
+##### Data managers
|
|
|
+
|
|
|
+* je pense qu'on devrait sortir le `$nuxt.$loading.start()` du service baseDataManager, c'est pas trop à sa place ici
|
|
|
+* le data deleter ne passe pas par un data processor pour mettre à jour le store. mais est-ce nécessaire au final?
|
|
|
+* est-ce qu'on devrait pas choisir soit de récupérer les données dans des composables (ex: useTypeOfPracticeProvider, useCountryProvider), soit dans les components? si on maintient les composables, on pourrait
|
|
|
+ les mettre dans un sous-dossier? ou même factoriser la méthode?
|
|
|
+
|
|
|
+
|
|
|
+##### Forms
|
|
|
+
|
|
|
+* passer le create et le loader de la page Address/new dans le formulaire? faire la même chose partout?
|
|
|
+* `const entryCopy = query.value.first()` dabs `useForm` (L185): on pourrait peut-être factoriser ça dans un composable? au passage, v-form a une méthode `reset`, ça pourrait ptêt faire le job?
|
|
|
+* UiForm, methode `saveAndQuit`: est-ce qu'on passe dans la méthode `quitForm` au final? puisque la méthode nextStep appellée depuis submit a déjà dû faire la redirection?
|
|
|
+* page address/_id, ligne 18 : on ne pourrait pas faire mieux pour le `const id = parseInt(route.value.params.id)`? faire ça en amont? dans un service? ou carrément parser les paramètres directement depuis un middleware et les stocker dans un store dédié en lecture seule?
|
|
|
+* plutôt que d'instancier une entité dans la page et de la passer au form, est-ce qu'on ne passerait pas un param 'create' au form? il faudrait lever des erreurs si `create == true & id != null` ou `create == false & id == null`
|
|
|
+* comment imposer des propriétés à tous les FormXxxx? Serait-il possible de mettre en place une interface pour l'array props, et de la tester depuis UiForm?
|
|
|
+* useError, ligne 7 : je suppose que la docstring est pas la bonne?
|
|
|
+
|
|
|
+##### Actions des forms
|
|
|
+
|
|
|
+* `actions[SUBMIT_TYPE.SAVE] = { path: `/organization/address/` }` : un moyen de rendre plus explicite que l'url dans path est la page où l'on se rend après l'action?
|
|
|
+* est-ce que les chemins où se rendre après les actions submit ne devraient pas être paramétrables dans les props du formulaire lui-même?
|
|
|
+* plutôt que de passer un param 'accordion' (`{ path: `/organization`, query: { accordion: 'address_postal' } }`), est-ce qu'on ne pourrait pas utiliser des anchors?
|
|
|
+
|
|
|
+##### Divers
|
|
|
+
|
|
|
+* voir à sortir les services profiles/* et rights/* et à les passer dans les composables (il faudra maj les plugins ability et castl)
|
|
|
+* UiItemFromUri, ligne 52 : pqoi ne pas appeller directement `ModelsUtils.extractIdFromUri(uri)`
|
|
|
+* UiInputImage, ligne 120 : est-ce que ce ne serait pas à sa place dans un processor attaché au data provider ça?
|
|
|
+
|
|
|
+### Structure
|
|
|
+
|
|
|
+Mettre en place un "guichet unique" pour les entités, nommé par ex entityManager
|
|
|
+
|
|
|
+Décliner les data-managers en :
|
|
|
+
|
|
|
+* EntityManager (`em`)
|
|
|
+* EnumManager (`enumManager`)
|
|
|
+* FileManager (`fileManager`)
|
|
|
+* ImageManager (`imageManager`), utile? ou fusionner avec le FileManager?
|
|
|
+
|
|
|
+Ces managers auraient des méthodes comme : `getRepository` (voir si nécessaire de garder l'étape des repos), `fetch`, `persist`, `delete`, `new` ou `init`, `flush` (?)
|
|
|
+
|
|
|
+Ils seraient responsables de la validation, de l'état de loading, de l'état dirty
|
|
|
+
|
|
|
+Ils seraient importables depuis un composable du type `useDataManager` ou `useEntityManager`
|
|
|
+
|
|
|
+> Incorporer les méthodes de useDataUtils dans ce manager
|
|
|
+> Voir si utile de récupérer des méthodes de useForm? Pas sûr, mais l'objectif serait que useForm soit seul à interagir avec le store formState, et aussi le seul à interagir avec.
|
|
|
+
|