docs: architecture, QA checklist and changelog for the guided path
All checks were successful
PR Check — Frontend / frontend (pull_request) Successful in 1m59s
All checks were successful
PR Check — Frontend / frontend (pull_request) Successful in 1m59s
- architecture.md: the Onboarding section describes the guided path as delivered (provider and useOnboarding(), the data-change channel of ADR 0022, the three onboarding preferences, rule D9, the pure models of onboardingPath.ts, the banner, the card latch and CARD_PAGES, the step cards, the highlight by props and CSS); the utils count and the user_preferences row follow the stack. - qa-onboarding.html: the phase 1 cases that still hold, without the compact bar ones, and four guided-path groups, including the manual checks the five links left for a release build (WebKitGTK and WebView2, Orca and NVDA, reduced motion, keyboard, Free edition, rule D9). - CHANGELOG (EN and FR): one entry for what every step card does, one per page, the path hidden on active profiles covers the banner too, the skipped state, and the guide's new sub-section. - STATE.md: manual checks due on the next release. Resolves #401 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
parent
7ce5816bb2
commit
d6f42dbe32
5 changed files with 295 additions and 124 deletions
|
|
@ -4,15 +4,17 @@
|
||||||
|
|
||||||
### Ajouté
|
### Ajouté
|
||||||
|
|
||||||
- Un bandeau de démarrage en haut de chaque page tant que le parcours de démarrage est affiché : « Démarrage 3/6 », l'étape suivante avec un lien vers la page où la faire, « Voir la liste », qui déplie les six étapes en place, et « Masquer », qui masque le bandeau et la liste pour le profil courant. Il n'apparaît pas sur le tableau de bord d'un profil sans transaction, dont la carte d'accueil contient déjà la liste. Une fois toutes les étapes faites, il l'annonce et propose « Ouvrir le guide », qui mène à la section « Premiers pas » du guide d'utilisation, et « Fermer », qui le retire jusqu'à un réaffichage depuis Paramètres → Utilisateurs (#397).
|
- Un bandeau de démarrage en haut de chaque page tant que le parcours de démarrage est affiché : « Démarrage 3/6 », l'étape suivante avec un lien vers la page où la faire, « Voir la liste », qui déplie les six étapes en place, et « Masquer », qui masque le bandeau et la liste pour le profil courant. Il n'apparaît pas sur le tableau de bord d'un profil sans transaction, dont la carte d'accueil contient déjà la liste. Une fois toutes les étapes faites ou passées, il l'annonce et propose « Ouvrir le guide », qui mène à la section « Premiers pas » du guide d'utilisation, et « Fermer », qui le retire jusqu'à un réaffichage depuis Paramètres → Utilisateurs (#397).
|
||||||
- Une carte de démarrage sur la page Import, à la place du bandeau, tant que l'étape suivante du parcours s'y fait (choisir un dossier d'import, configurer une source d'import, importer un premier fichier). Elle indique « Étape n sur N », dit quoi faire à chaque écran de l'assistant d'import et désigne le contrôle à utiliser par un contour qui pulse trois fois, et pas du tout quand le système demande de réduire les animations. Elle suit l'assistant pas à pas, annonce « Fait. » une fois le fichier importé et propose « Étape suivante », qui mène à la page de l'étape d'après ; elle n'annonce pas « Fait. » pour un import qui n'a ajouté aucune transaction. Arriver sur la page Import par un lien « Étape suivante » place le focus du clavier sur le titre de la carte. « Passer cette étape » marque l'étape comme passée dans la liste de démarrage, où « Reprendre » l'annule (#398).
|
- Des cartes de démarrage sur les pages Import, Transactions et Budget. Tant que l'étape suivante du parcours se fait sur l'une de ces pages, une carte y prend la place du bandeau : elle indique « Étape n sur N », dit quoi faire et désigne le contrôle à utiliser par un contour qui pulse trois fois, et pas du tout quand le système demande de réduire les animations. Quand l'étape est faite, la carte reste, annonce « Fait. » et propose « Étape suivante », qui mène à la page de l'étape d'après, ou la fin du parcours. Arriver sur une page par un lien « Étape suivante » place le focus du clavier sur le titre de la carte. « Passer cette étape » marque l'étape comme passée dans la liste de démarrage, où « Reprendre » l'annule (#398, #399, #400).
|
||||||
- Une carte de démarrage sur la page Transactions, à la place du bandeau, tant que l'étape suivante du parcours est de classer une transaction à la main. Le lien de cette étape ouvre la page sur les transactions non catégorisées, toutes dates confondues, ou sur toutes les transactions s'il n'en reste aucune à classer. La carte dit quoi faire et désigne la catégorie de la première transaction affichée. Choisir une catégorie coche l'étape : la carte annonce « Fait. » et propose « Saisir un premier budget », ou la fin du parcours en édition Gratuite (#399).
|
- Sur la page Import, la carte de démarrage suit l'assistant d'import écran par écran, à travers ses trois étapes (choisir un dossier d'import, configurer une source d'import, importer un premier fichier), et n'annonce pas « Fait. » pour un import qui n'a ajouté aucune transaction (#398).
|
||||||
- Une carte de démarrage sur la page Budget, à la place du bandeau, tant que l'étape suivante du parcours est de saisir un premier budget. La grille du budget s'ouvre catégories repliées : la carte désigne d'abord « Tout déplier », puis la cellule du mois courant de la première catégorie à remplir, et ne déplie jamais la grille à votre place. Elle rappelle qu'Entrée enregistre, et que Tab enregistre et passe au mois suivant. Enregistrer un montant coche l'étape : la carte annonce « Fait. » et termine le parcours, avec « Ouvrir le guide » et « Fermer » (#400).
|
- Sur la page Transactions, le lien de l'étape « Classer une transaction à la main » ouvre la page sur les transactions non catégorisées, toutes dates confondues, ou sur toutes les transactions s'il n'en reste aucune à classer, et la carte désigne la catégorie de la première transaction affichée. En édition Gratuite, qui n'a pas d'étape Budget, classer une transaction termine le parcours (#399).
|
||||||
|
- Sur la page Budget, dont la grille s'ouvre catégories repliées, la carte de démarrage désigne d'abord « Tout déplier », puis la cellule du mois courant de la première catégorie à remplir, et ne déplie jamais la grille à votre place. Elle rappelle qu'Entrée enregistre, et que Tab enregistre et passe au mois suivant. Enregistrer un montant termine le parcours (#400).
|
||||||
|
- Une sous-section « Le parcours de démarrage » dans la section « Premiers pas » du guide d'utilisation, celle qu'ouvre « Ouvrir le guide » à la fin du parcours : le bandeau et sa liste, les cartes d'étape et leur contour, « Passer cette étape » et « Reprendre », la fin du parcours, les profils qui le trouvent masqué d'office, et son réaffichage depuis Paramètres → Utilisateurs. Les sections Tableau de bord, Profils et Paramètres du guide décrivent le parcours tel qu'il fonctionne désormais (#401).
|
||||||
|
|
||||||
### Modifié
|
### Modifié
|
||||||
|
|
||||||
- La liste de démarrage ne s'affiche plus dans un profil qui avait déjà des transactions avant cette version : à la première ouverture d'un tel profil, la liste est masquée, comme après un clic sur « Masquer », et « Afficher la liste de démarrage », dans Paramètres → Utilisateurs, la fait revenir. Un profil sans transaction la garde (#396).
|
- Le parcours de démarrage ne s'affiche plus dans un profil qui avait déjà des transactions avant cette version : à la première ouverture d'un tel profil, le bandeau et sa liste sont masqués, comme après un clic sur « Masquer », et « Afficher la liste de démarrage », dans Paramètres → Utilisateurs, les fait revenir. Un profil sans transaction les garde (#396).
|
||||||
- Le tableau de bord n'affiche plus la barre compacte de démarrage une fois que le profil a des transactions : le bandeau la remplace sur chaque page. Dans la liste détaillée, les lecteurs d'écran annoncent désormais si chaque étape est faite, verrouillée ou en attente (#397).
|
- Le tableau de bord n'affiche plus la barre compacte de démarrage une fois que le profil a des transactions : le bandeau la remplace sur chaque page. Dans la liste détaillée, les lecteurs d'écran annoncent désormais si chaque étape est faite, passée, verrouillée ou en attente (#397).
|
||||||
- Paramètres → Utilisateurs propose « Afficher la liste de démarrage » dès que le parcours est masqué, y compris depuis le bandeau de cette même page, et confirme désormais que le parcours réapparaît en haut des pages (#397).
|
- Paramètres → Utilisateurs propose « Afficher la liste de démarrage » dès que le parcours est masqué, y compris depuis le bandeau de cette même page, et confirme désormais que le parcours réapparaît en haut des pages (#397).
|
||||||
|
|
||||||
### Corrigé
|
### Corrigé
|
||||||
|
|
|
||||||
14
CHANGELOG.md
14
CHANGELOG.md
|
|
@ -4,15 +4,17 @@
|
||||||
|
|
||||||
### Added
|
### Added
|
||||||
|
|
||||||
- A getting-started banner at the top of every page while the getting-started path is shown: "Getting started 3/6", the next step with a link to the page where it is done, "Show the list", which unfolds the six steps in place, and "Hide", which hides the banner and the list for the current profile. It does not show on the dashboard of a profile without transactions, whose welcome card already holds the list. Once every step is done, it says so and offers "Open the guide", which leads to the "Getting Started" section of the user guide, and "Close", which removes it until it is shown again from Settings → Users (#397).
|
- A getting-started banner at the top of every page while the getting-started path is shown: "Getting started 3/6", the next step with a link to the page where it is done, "Show the list", which unfolds the six steps in place, and "Hide", which hides the banner and the list for the current profile. It does not show on the dashboard of a profile without transactions, whose welcome card already holds the list. Once every step is done or skipped, it says so and offers "Open the guide", which leads to the "Getting Started" section of the user guide, and "Close", which removes it until it is shown again from Settings → Users (#397).
|
||||||
- A getting-started card on the Import page, in place of the banner, while the next step of the path is done there (choose an import folder, set up an import source, import a first file). It shows "Step n of N", says what to do on each screen of the import wizard and points at the control to use with an outline that pulses three times, not at all when the system asks for reduced motion. It follows the wizard as you go, says "Done." once the file is imported and offers "Next step", which leads to the page of the following step; it does not say "Done." for an import that added no transaction. Arriving on the Import page by a "Next step" link puts the keyboard focus on the card's title. "Skip this step" marks the step as skipped in the getting-started list, where "Resume" takes it back (#398).
|
- Getting-started cards on the Import, Transactions and Budget pages. While the next step of the path is done on one of these pages, a card takes the place of the banner there: it shows "Step n of N", says what to do and points at the control to use with an outline that pulses three times, not at all when the system asks for reduced motion. When the step gets done, the card stays, says "Done." and offers "Next step", which leads to the page of the following step, or the end of the path. Arriving on a page by a "Next step" link puts the keyboard focus on the card's title. "Skip this step" marks the step as skipped in the getting-started list, where "Resume" takes it back (#398, #399, #400).
|
||||||
- A getting-started card on the Transactions page, in place of the banner, while the next step of the path is to categorize a transaction by hand. The link of that step opens the page on the uncategorized transactions whatever their date, or on every transaction when none is left uncategorized. The card says what to do and points at the category of the first transaction shown. Choosing a category ticks the step: the card says "Done." and offers "Enter a first budget", or the end of the path in the Free edition (#399).
|
- On the Import page, the getting-started card follows the import wizard screen by screen through its three steps (choose an import folder, set up an import source, import a first file), and does not say "Done." for an import that added no transaction (#398).
|
||||||
- A getting-started card on the Budget page, in place of the banner, while the next step of the path is to enter a first budget. The budget grid opens with its categories folded: the card first points at "Expand all", then at the current month's cell of the first category you can fill in, and never unfolds the grid for you. It reminds you that Enter saves, and that Tab saves and moves to the next month. Saving an amount ticks the step: the card says "Done." and ends the path, with "Open the guide" and "Close" (#400).
|
- On the Transactions page, the link of the "Categorize a transaction by hand" step opens the page on the uncategorized transactions whatever their date, or on every transaction when none is left uncategorized, and the card points at the category of the first transaction shown. In the Free edition, which has no Budget step, categorizing a transaction ends the path (#399).
|
||||||
|
- On the Budget page, whose grid opens with its categories folded, the getting-started card first points at "Expand all", then at the current month's cell of the first category you can fill in, and never unfolds the grid for you. It reminds you that Enter saves, and that Tab saves and moves to the next month. Saving an amount ends the path (#400).
|
||||||
|
- A sub-section, "The getting-started path", in the "Getting Started" section of the user guide, the one "Open the guide" opens at the end of the path: the banner and its list, the step cards and their outline, "Skip this step" and "Resume", the end of the path, the profiles that find it hidden from the start, and how to show it again from Settings → Users. The Dashboard, Profiles and Settings sections of the guide describe the path as it now works (#401).
|
||||||
|
|
||||||
### Changed
|
### Changed
|
||||||
|
|
||||||
- The getting-started checklist no longer shows in a profile that already had transactions before this version: the first time such a profile is opened, the checklist is hidden, as if you had clicked "Hide", and "Show the getting-started checklist" in Settings → Users brings it back. A profile without transactions keeps it (#396).
|
- The getting-started path no longer shows in a profile that already had transactions before this version: the first time such a profile is opened, the banner and its checklist are hidden, as if you had clicked "Hide", and "Show the getting-started checklist" in Settings → Users brings them back. A profile without transactions keeps them (#396).
|
||||||
- The dashboard no longer shows the compact getting-started bar once the profile has transactions: the banner replaces it on every page. In the detailed list, screen readers now hear whether each step is done, locked or pending (#397).
|
- The dashboard no longer shows the compact getting-started bar once the profile has transactions: the banner replaces it on every page. In the detailed list, screen readers now hear whether each step is done, skipped, locked or pending (#397).
|
||||||
- Settings → Users offers "Show the getting-started checklist" as soon as the path is hidden, also when it was hidden from the banner of that same page, and now confirms that the path reappears at the top of the pages (#397).
|
- Settings → Users offers "Show the getting-started checklist" as soon as the path is hidden, also when it was hidden from the banner of that same page, and now confirms that the path reappears at the top of the pages (#397).
|
||||||
|
|
||||||
### Fixed
|
### Fixed
|
||||||
|
|
|
||||||
1
STATE.md
1
STATE.md
|
|
@ -65,6 +65,7 @@ Audit critique de la page Bilan livré (`docs/audit-bilan-2026-05.md`, revue CPA
|
||||||
- **Le paiement est live : « aucun tag avant le go-live » n'a plus de raison d'être, et la release desktop qu'attendait le go-live est publiée (v0.16.0, 2026-10-03).** Bascule live le 2026-09-29 (Product et Price live à 19 $ CAD, webhook, variables Stripe de production, redeploy `3673d09` de maximus-api, URL `cs_live_` vérifiée), puis achat réel de contrôle le 2026-09-30 : clé sur `/merci` et par courriel, activation dans l'app, remboursement complet et licence révoquée par `charge.refunded`. **Volet serveur clos le 2026-10-03** (derniers commentaires relus le 2026-10-04) : maximus-api#62 fermée, tous critères cochés (maximus-api 0.4.2 en prod, secrets en runtime seul), avec la milestone `spec-paiements` de maximus-api (12/12) ; la-compagnie-maximus#150 (Link dans les textes légaux) livrée et fermée le 2026-10-01. **Clôture du go-live = la-compagnie-maximus#141** (`status:blocked`), qui n'attend plus que ce dépôt : l'updater vérifié (Free refuse et invite à acheter, Base accepte), #350 étant fermée depuis le 2026-10-04. la-compagnie-maximus#184 (politique de confidentialité : données d'activation, contrôle au démarrage) est en `status:review` ; sa PR reste en brouillon jusqu'au déploiement de maximus-api#85 (commentaire du 2026-10-03). Les deux textes des suites de licence (contrôle à la saisie, retrait de la clé) y sont postés depuis le 2026-10-05 (commentaire 19724). **Vérifications manuelles sur la release publiée, toutes faites** : #365 le 2026-10-03, #346 et #350 le 2026-10-04 (voir Décisions). Reste l'étape 10 de `/release` sous Windows (Michel).
|
- **Le paiement est live : « aucun tag avant le go-live » n'a plus de raison d'être, et la release desktop qu'attendait le go-live est publiée (v0.16.0, 2026-10-03).** Bascule live le 2026-09-29 (Product et Price live à 19 $ CAD, webhook, variables Stripe de production, redeploy `3673d09` de maximus-api, URL `cs_live_` vérifiée), puis achat réel de contrôle le 2026-09-30 : clé sur `/merci` et par courriel, activation dans l'app, remboursement complet et licence révoquée par `charge.refunded`. **Volet serveur clos le 2026-10-03** (derniers commentaires relus le 2026-10-04) : maximus-api#62 fermée, tous critères cochés (maximus-api 0.4.2 en prod, secrets en runtime seul), avec la milestone `spec-paiements` de maximus-api (12/12) ; la-compagnie-maximus#150 (Link dans les textes légaux) livrée et fermée le 2026-10-01. **Clôture du go-live = la-compagnie-maximus#141** (`status:blocked`), qui n'attend plus que ce dépôt : l'updater vérifié (Free refuse et invite à acheter, Base accepte), #350 étant fermée depuis le 2026-10-04. la-compagnie-maximus#184 (politique de confidentialité : données d'activation, contrôle au démarrage) est en `status:review` ; sa PR reste en brouillon jusqu'au déploiement de maximus-api#85 (commentaire du 2026-10-03). Les deux textes des suites de licence (contrôle à la saisie, retrait de la clé) y sont postés depuis le 2026-10-05 (commentaire 19724). **Vérifications manuelles sur la release publiée, toutes faites** : #365 le 2026-10-03, #346 et #350 le 2026-10-04 (voir Décisions). Reste l'étape 10 de `/release` sous Windows (Michel).
|
||||||
- **Vérifications post-merge de #390** (PR #394, sur le prochain build) : la case 5 se coche après un classement manuel ; sous Linux, un vrai profil secondaire ancien affiche « database updated with versions … » au Journal, sa copie est dans `~/.config/com.simpl.resultat/migration-backups/` et l'import y fonctionne ; supprimer un profil retire son `.db` ; les rendements du Bilan s'affichent. #390 reste en `status:blocked` jusque-là.
|
- **Vérifications post-merge de #390** (PR #394, sur le prochain build) : la case 5 se coche après un classement manuel ; sous Linux, un vrai profil secondaire ancien affiche « database updated with versions … » au Journal, sa copie est dans `~/.config/com.simpl.resultat/migration-backups/` et l'import y fonctionne ; supprimer un profil retire son `.db` ; les rendements du Bilan s'affichent. #390 reste en `status:blocked` jusque-là.
|
||||||
- **Vérifications manuelles dues sur la v0.17.0 publiée** (onboarding + suites de licence ; Linux puis Windows), dans un ordre qui ne détruit le jeton d'activation qu'à la fin de ce qui en dépend : (1) #370, machine activée hors ligne → « Retirer la clé » : seconde confirmation qui donne la raison, Annuler ne change rien, sauts de ligne des fenêtres ; (2) #367, s'il existe un second poste avec la même clé : y désactiver ce poste, puis ouvrir ici la liste des machines → « Activer » revient sans recharger, ligne au Journal ; (3) #370 en ligne → place libérée, carte en Gratuite ; (4) #373 et #370 avec la clé révoquée du 2026-09-30 : Base une à deux secondes puis Gratuite, ligne « License check: the license server says the key was revoked » ; retirée → aucune désactivation envoyée, puis au redémarrage « no valid key stored, nothing sent » ; ressaisie → reste en Gratuite ; (5) clé testeur Premium ressaisie, « Activer », `verif-350.py` rend `64 brut: False dérivé: True` ; (6) #367, dossier v0.15.0 (clé A à côté du jeton d'une licence B révoquée) : couvert par un test de bout en bout, à faire seulement si un tel dossier existe encore. Plus la liste QA de l'onboarding (`docs/qa-onboarding.html`).
|
- **Vérifications manuelles dues sur la v0.17.0 publiée** (onboarding + suites de licence ; Linux puis Windows), dans un ordre qui ne détruit le jeton d'activation qu'à la fin de ce qui en dépend : (1) #370, machine activée hors ligne → « Retirer la clé » : seconde confirmation qui donne la raison, Annuler ne change rien, sauts de ligne des fenêtres ; (2) #367, s'il existe un second poste avec la même clé : y désactiver ce poste, puis ouvrir ici la liste des machines → « Activer » revient sans recharger, ligne au Journal ; (3) #370 en ligne → place libérée, carte en Gratuite ; (4) #373 et #370 avec la clé révoquée du 2026-09-30 : Base une à deux secondes puis Gratuite, ligne « License check: the license server says the key was revoked » ; retirée → aucune désactivation envoyée, puis au redémarrage « no valid key stored, nothing sent » ; ressaisie → reste en Gratuite ; (5) clé testeur Premium ressaisie, « Activer », `verif-350.py` rend `64 brut: False dérivé: True` ; (6) #367, dossier v0.15.0 (clé A à côté du jeton d'une licence B révoquée) : couvert par un test de bout en bout, à faire seulement si un tel dossier existe encore. Plus la liste QA de l'onboarding (`docs/qa-onboarding.html`).
|
||||||
|
- **Vérifications manuelles dues sur la prochaine release : parcours de démarrage guidé** (#396 à #401, milestone `planned-2026-10-06-onboarding-parcours-guide`), d'abord sous Linux avec Orca, puis sous Windows avec NVDA : les sections « Parcours guidé » de `docs/qa-onboarding.html` (profil neuf de bout en bout, bandeau et liste, « Passer » et « Reprendre », règle D9 sur un vrai profil ancien, édition Gratuite à 5/5, contour dans WebKitGTK et WebView2, mouvement réduit, clavier, annonces). #401 se ferme au merge ; la liste de la phase 1 pour la v0.17.0 reste lisible par `git show v0.17.0:docs/qa-onboarding.html`.
|
||||||
- Backlog ouvert : **vague `planned-2026-09-24-paiements` close 3/3** (milestone fermée le 2026-09-30) : #270 (PR #359) et #346 (PR #362) mergées le 2026-09-27, #347 (PR #364, ferme aussi #360) mergée le 2026-09-30 ; volet site **clos** (maximus/la-compagnie-maximus#153, redirection de `/simpl-resultat` en prod). **#365 fermée le 2026-10-03** après sa vérification manuelle. **Milestone `planned-2026-10-03-suites-licence` fermée 3/3 le 2026-10-05** (#373, #370, #367 mergées, voir Décisions ; suivi #389 en `status:triage`). Hors milestone, en `status:ready` : **#369** (réserver le fractionnement de transactions à l'édition Base) et **#372** (la liste complète des versions repousse le Journal en bas de Paramètres → Systèmes). **#350 fermée le 2026-10-04** (étape 3 vérifiée en production, voir Décisions) ; reste de #363 (plafond de `/issue-license` à 36525 jours et essai d'une vraie clé de 100 ans, débloqués par le déploiement de maximus-api#63) ; réglés côté serveur et site : maximus-api#63 (clés de 100 ans) et maximus-api#70 (réactivation d'une machine désactivée), fermées et contenues dans `3673d09`, le commit déployé à la bascule du 2026-09-29, et la-compagnie-maximus#157, fermée, dont les CGV sont publiées en tronc commun plus annexe Simpl-Résultat (annexe relue en production le 2026-09-30, plus aucune durée de 10 ans) ; côté serveur, `/v1/prices` ne vérifie pas qu'une machine est encore active (une machine désactivée garde les cours jusqu'à l'expiration de son jeton), issue maximus-api à ouvrir ; suites de #350 ouvertes le 2026-10-01 : maximus-api#82 (`status:triage` : identifiants machine bruts déjà stockés, lignes désactivées comprises ; à rehacher avant l'expiration des premiers jetons bruts, sinon une réactivation crée une seconde ligne) et la-compagnie-maximus#184 (`status:review`, voir ci-dessus) ; clôture du go-live = la-compagnie-maximus#141 seule, maximus-api#62 étant fermée ; `spec-paiements` reste le parapluie des trois dépôts (ici 4/5, reste #363) ; onboarding phase 1 **livrée** : milestone `planned-2026-10-03-onboarding-phase1` fermée 7/7 le 2026-10-04 (#351 à #357, mergées en avance rapide, `667e058`) ; suivis ouverts : **#384** (`status:ready`, suivis non bloquants des revues) et **#371** (`status:blocked`, hors `/autopilot` : en-têtes CSV BMO à fournir par Max depuis un compte réel, un par type de compte, chèques/épargne et Mastercard, avec des valeurs fictives) ; analyse d'origine dans `docs/analyse-onboarding-2026-09.md`, plan détaillé dans les `spec-*-onboarding-phase1.md` locaux, ignorés par git ; #271 absorbé par #301, livré ; `spec-ci-build-optimization` **fermée 4/4** le 2026-10-05 (#232 livrée le 2026-07-27 ; #234 close le 2026-10-05 avec ses quatre preuves : dispatch run 464, PR #386 run 480, run planifié 488, `actcache` à 999 Mo) ; **#376** (`status:triage` : Node 20, en fin de vie depuis le 2026-04-30, encore installé par `check-frontend.yml` et `audit.yml`, et commentaire `concurrency` de `check-rust.yml` démenti par `forgejo-ops.md` pour les runs de PR). Le suivi « glob `src-tauri/**` non prouvé » ouvert avec #232 est **levé** : la PR #344 est le cas test isolé attendu (`Cargo.lock` + 2 `.md` seulement) — `check-rust` a bien tourné, `check-frontend` a bien été sauté par sa denylist.
|
- Backlog ouvert : **vague `planned-2026-09-24-paiements` close 3/3** (milestone fermée le 2026-09-30) : #270 (PR #359) et #346 (PR #362) mergées le 2026-09-27, #347 (PR #364, ferme aussi #360) mergée le 2026-09-30 ; volet site **clos** (maximus/la-compagnie-maximus#153, redirection de `/simpl-resultat` en prod). **#365 fermée le 2026-10-03** après sa vérification manuelle. **Milestone `planned-2026-10-03-suites-licence` fermée 3/3 le 2026-10-05** (#373, #370, #367 mergées, voir Décisions ; suivi #389 en `status:triage`). Hors milestone, en `status:ready` : **#369** (réserver le fractionnement de transactions à l'édition Base) et **#372** (la liste complète des versions repousse le Journal en bas de Paramètres → Systèmes). **#350 fermée le 2026-10-04** (étape 3 vérifiée en production, voir Décisions) ; reste de #363 (plafond de `/issue-license` à 36525 jours et essai d'une vraie clé de 100 ans, débloqués par le déploiement de maximus-api#63) ; réglés côté serveur et site : maximus-api#63 (clés de 100 ans) et maximus-api#70 (réactivation d'une machine désactivée), fermées et contenues dans `3673d09`, le commit déployé à la bascule du 2026-09-29, et la-compagnie-maximus#157, fermée, dont les CGV sont publiées en tronc commun plus annexe Simpl-Résultat (annexe relue en production le 2026-09-30, plus aucune durée de 10 ans) ; côté serveur, `/v1/prices` ne vérifie pas qu'une machine est encore active (une machine désactivée garde les cours jusqu'à l'expiration de son jeton), issue maximus-api à ouvrir ; suites de #350 ouvertes le 2026-10-01 : maximus-api#82 (`status:triage` : identifiants machine bruts déjà stockés, lignes désactivées comprises ; à rehacher avant l'expiration des premiers jetons bruts, sinon une réactivation crée une seconde ligne) et la-compagnie-maximus#184 (`status:review`, voir ci-dessus) ; clôture du go-live = la-compagnie-maximus#141 seule, maximus-api#62 étant fermée ; `spec-paiements` reste le parapluie des trois dépôts (ici 4/5, reste #363) ; onboarding phase 1 **livrée** : milestone `planned-2026-10-03-onboarding-phase1` fermée 7/7 le 2026-10-04 (#351 à #357, mergées en avance rapide, `667e058`) ; suivis ouverts : **#384** (`status:ready`, suivis non bloquants des revues) et **#371** (`status:blocked`, hors `/autopilot` : en-têtes CSV BMO à fournir par Max depuis un compte réel, un par type de compte, chèques/épargne et Mastercard, avec des valeurs fictives) ; analyse d'origine dans `docs/analyse-onboarding-2026-09.md`, plan détaillé dans les `spec-*-onboarding-phase1.md` locaux, ignorés par git ; #271 absorbé par #301, livré ; `spec-ci-build-optimization` **fermée 4/4** le 2026-10-05 (#232 livrée le 2026-07-27 ; #234 close le 2026-10-05 avec ses quatre preuves : dispatch run 464, PR #386 run 480, run planifié 488, `actcache` à 999 Mo) ; **#376** (`status:triage` : Node 20, en fin de vie depuis le 2026-04-30, encore installé par `check-frontend.yml` et `audit.yml`, et commentaire `concurrency` de `check-rust.yml` démenti par `forgejo-ops.md` pour les runs de PR). Le suivi « glob `src-tauri/**` non prouvé » ouvert avec #232 est **levé** : la PR #344 est le cas test isolé attendu (`Cargo.lock` + 2 `.md` seulement) — `check-rust` a bien tourné, `check-frontend` a bien été sauté par sa denylist.
|
||||||
- Suivis des advisories : **#312, #313 et #315 fermées le 2026-08-15** (PRs #321/#322 mergées). **#314 fermée le 2026-08-15, prémisse infirmée** — elle affirmait « zéro run `schedule` », or `audit.yml` tire tous les jours à 06:00 UTC (**19 runs `schedule` verts** du 07-28 au 08-15, sans trou) ; elle a été écrite le 07-27 vers 23:56Z, ~6 h avant le premier tir programmé possible, et décrivait donc une fenêtre trop courte, pas un scheduler en panne. Ces 19 runs confirment au passage que `.cargo/audit.toml` est bien lu en conteneur **sur `main`** — l'ADR 0018 ne l'avait vérifié que par un `workflow_dispatch` sur une branche. **#343 fermée le 2026-09-04** (PR #344) — RUSTSEC-2026-0258 sur `h2`, la seule vulnérabilité qui rougissait l'audit depuis le 08-19 ; l'audit est de nouveau vert sur `main`, prouvé par `workflow_dispatch` (run 393) et non déduit du run de PR. **#348 fermée le 2026-09-21** (PR #349) — RUSTSEC-2026-0285 sur `rustls`, seule vulnérabilité rougissant l'audit depuis le 09-15 (7 runs, 405→411) ; vert reprouvé par `workflow_dispatch` sur `main` (run 414 : 666 paquets, zéro vulnérabilité, 12 warnings autorisés). Deux advisories en cinq semaines sur le même chemin, celui de la mise à jour automatique. Restent **#317** (prémisse caduque depuis le 2026-09-26 : `react-router-dom` 7.18.2 et suivantes corrigent l'advisory par un bump de patch dans `^7.18.1`, sans migration v8 ; absorbée par #358, dont la PR la fermera), **#319** (déclencheur de retrait pour `rsa`, désormais **seule** entrée de `.cargo/audit.toml` — le garde-fou est passé à 2 vérifications) et **#345** (domicilier la contrainte `windows-sys 0.61.2` — analysée le 09-04 : le corps d'origine, écrit de mémoire en fin de revue, se trompait sur trois points. Le déclencheur n'est pas « n'importe quel `cargo update` » mais tout bump atteignant le sous-arbre réseau/TLS — mesuré : `-p anyhow` ne bouge rien, `-p hyper` fait descendre les 7 — soit précisément là où tombent les advisories. **Prémisse restreinte le 2026-09-21 par #348** : `-p rustls`, au cœur de ce même sous-arbre, n'en déplace **aucun** — le déclencheur tient à `hyper` et à ce qu'il tire, pas à la couche TLS en général, donc « sous-arbre réseau/TLS » prédit une dérive là où il n'y en a pas et ferait écrire une strophe à la main sans nécessité. D'où la suggestion portée en commentaire sur l'issue : formuler le garde-fou sur la **version résolue de `windows-sys`** plutôt que sur une liste de crates déclencheurs — la liste est ce qui vient d'être trouvé faux, la version est ce qui est protégé. #310 avait **bien** vu la dérive et l'avait jugée inoffensive *sur Linux*, où elle l'est ; son coût tombe sur le build Windows que personne ne mesurait : **3 versions de `windows-sys` compilées contre 5**. Aucun risque de correction — les 7 crates déclarent des bornes larges couvrant les deux états. Et cargo annonce « Locking 1 package » en écrivant 8 changements, donc le silence est le sien. Décision : garde-fou CI sur la version + ADR portant la condition de sortie. Le check croisé Windows depuis Linux a été **testé et écarté** — `cc-rs` s'arrête sur `lib.exe`, il faudrait `cargo-xwin`). Côté npm, **#358** ouverte le 2026-09-26 par `/analyse-vulnerabilite` : `npm audit` remonte 7 findings (4 high, 3 moderate) sur 4 dépendances racines, contre les 2 acceptés au 07-27, et chaque correctif tient dans la borne déjà déclarée ; le lock se régénère avec `npx npm@11` (npm 10 plante sur un bump de vitest 4.1.x).
|
- Suivis des advisories : **#312, #313 et #315 fermées le 2026-08-15** (PRs #321/#322 mergées). **#314 fermée le 2026-08-15, prémisse infirmée** — elle affirmait « zéro run `schedule` », or `audit.yml` tire tous les jours à 06:00 UTC (**19 runs `schedule` verts** du 07-28 au 08-15, sans trou) ; elle a été écrite le 07-27 vers 23:56Z, ~6 h avant le premier tir programmé possible, et décrivait donc une fenêtre trop courte, pas un scheduler en panne. Ces 19 runs confirment au passage que `.cargo/audit.toml` est bien lu en conteneur **sur `main`** — l'ADR 0018 ne l'avait vérifié que par un `workflow_dispatch` sur une branche. **#343 fermée le 2026-09-04** (PR #344) — RUSTSEC-2026-0258 sur `h2`, la seule vulnérabilité qui rougissait l'audit depuis le 08-19 ; l'audit est de nouveau vert sur `main`, prouvé par `workflow_dispatch` (run 393) et non déduit du run de PR. **#348 fermée le 2026-09-21** (PR #349) — RUSTSEC-2026-0285 sur `rustls`, seule vulnérabilité rougissant l'audit depuis le 09-15 (7 runs, 405→411) ; vert reprouvé par `workflow_dispatch` sur `main` (run 414 : 666 paquets, zéro vulnérabilité, 12 warnings autorisés). Deux advisories en cinq semaines sur le même chemin, celui de la mise à jour automatique. Restent **#317** (prémisse caduque depuis le 2026-09-26 : `react-router-dom` 7.18.2 et suivantes corrigent l'advisory par un bump de patch dans `^7.18.1`, sans migration v8 ; absorbée par #358, dont la PR la fermera), **#319** (déclencheur de retrait pour `rsa`, désormais **seule** entrée de `.cargo/audit.toml` — le garde-fou est passé à 2 vérifications) et **#345** (domicilier la contrainte `windows-sys 0.61.2` — analysée le 09-04 : le corps d'origine, écrit de mémoire en fin de revue, se trompait sur trois points. Le déclencheur n'est pas « n'importe quel `cargo update` » mais tout bump atteignant le sous-arbre réseau/TLS — mesuré : `-p anyhow` ne bouge rien, `-p hyper` fait descendre les 7 — soit précisément là où tombent les advisories. **Prémisse restreinte le 2026-09-21 par #348** : `-p rustls`, au cœur de ce même sous-arbre, n'en déplace **aucun** — le déclencheur tient à `hyper` et à ce qu'il tire, pas à la couche TLS en général, donc « sous-arbre réseau/TLS » prédit une dérive là où il n'y en a pas et ferait écrire une strophe à la main sans nécessité. D'où la suggestion portée en commentaire sur l'issue : formuler le garde-fou sur la **version résolue de `windows-sys`** plutôt que sur une liste de crates déclencheurs — la liste est ce qui vient d'être trouvé faux, la version est ce qui est protégé. #310 avait **bien** vu la dérive et l'avait jugée inoffensive *sur Linux*, où elle l'est ; son coût tombe sur le build Windows que personne ne mesurait : **3 versions de `windows-sys` compilées contre 5**. Aucun risque de correction — les 7 crates déclarent des bornes larges couvrant les deux états. Et cargo annonce « Locking 1 package » en écrivant 8 changements, donc le silence est le sien. Décision : garde-fou CI sur la version + ADR portant la condition de sortie. Le check croisé Windows depuis Linux a été **testé et écarté** — `cc-rs` s'arrête sur `lib.exe`, il faudrait `cargo-xwin`). Côté npm, **#358** ouverte le 2026-09-26 par `/analyse-vulnerabilite` : `npm audit` remonte 7 findings (4 high, 3 moderate) sur 4 dépendances racines, contre les 2 acceptés au 07-27, et chaque correctif tient dans la borne déjà déclarée ; le lock se régénère avec `npx npm@11` (npm 10 plante sur un bump de vitest 4.1.x).
|
||||||
- Nouveau bug ouvert : **#320** — les installations `.rpm` ne reçoivent aucune mise à jour (`latest.json` ne porte qu'une entrée `linux-x86_64` construite depuis le `.deb`, `release.yml:104-122`), alors que les `.rpm` continuent d'être publiés en assets. Documenté en §3 de `docs/qa-update-cycle.md` (« ne pas tester : cassé, pas seulement non vérifié »).
|
- Nouveau bug ouvert : **#320** — les installations `.rpm` ne reçoivent aucune mise à jour (`latest.json` ne porte qu'une entrée `linux-x86_64` construite depuis le `.deb`, `release.yml:104-122`), alors que les `.rpm` continuent d'être publiés en assets. Documenté en §3 de `docs/qa-update-cycle.md` (« ne pas tester : cassé, pas seulement non vérifié »).
|
||||||
|
|
|
||||||
|
|
@ -45,7 +45,7 @@ simpl-resultat/
|
||||||
│ ├── pages/ # 14 pages (dont 4 sous-pages rapports)
|
│ ├── pages/ # 14 pages (dont 4 sous-pages rapports)
|
||||||
│ ├── services/ # 26 services métier (20 détaillés dans la section « Services TypeScript »)
|
│ ├── services/ # 26 services métier (20 détaillés dans la section « Services TypeScript »)
|
||||||
│ ├── shared/ # Types, constantes, matrice d'entitlements (entitlements.ts), gate profils (profileGate.ts), CTA d'upsell (upsellCta.ts), URL d'achat (purchaseUrl.ts), échéance de licence (licenseExpiry.ts)
|
│ ├── shared/ # Types, constantes, matrice d'entitlements (entitlements.ts), gate profils (profileGate.ts), CTA d'upsell (upsellCta.ts), URL d'achat (purchaseUrl.ts), échéance de licence (licenseExpiry.ts)
|
||||||
│ ├── utils/ # 14 utilitaires purs (parsing montants/dates, détection CSV, codec de format d'import, dictionnaire lexical, signatures de banques, charts, drapeaux SQL 0/1)
|
│ ├── utils/ # 16 utilitaires purs (parsing montants/dates, détection CSV, codec de format d'import, dictionnaire lexical, signatures de banques, charts, drapeaux SQL 0/1, liste et parcours de démarrage)
|
||||||
│ ├── i18n/ # Config i18next + locales FR/EN
|
│ ├── i18n/ # Config i18next + locales FR/EN
|
||||||
│ ├── __fixtures__/ # csv/ : corpus de détection (15 cas figés) ; samples/ : gabarits du dossier d'exemple embarqués par Rust
|
│ ├── __fixtures__/ # csv/ : corpus de détection (15 cas figés) ; samples/ : gabarits du dossier d'exemple embarqués par Rust
|
||||||
│ ├── App.tsx # Router principal
|
│ ├── App.tsx # Router principal
|
||||||
|
|
@ -97,7 +97,7 @@ simpl-resultat/
|
||||||
| `budget_templates` | Modèles de budget réutilisables |
|
| `budget_templates` | Modèles de budget réutilisables |
|
||||||
| `budget_template_entries` | Catégories et montants dans les modèles |
|
| `budget_template_entries` | Catégories et montants dans les modèles |
|
||||||
| `import_config_templates` | Modèles prédéfinis de config d'import |
|
| `import_config_templates` | Modèles prédéfinis de config d'import |
|
||||||
| `user_preferences` | Préférences applicatives (clé-valeur) — `import_folder`, `balance_show_returns`, `balance_starter_proposed`, `onboarding_checklist_hidden` (« 1 » quand la liste de démarrage a été masquée ; la clé est supprimée par « Afficher la liste de démarrage », #351-#352)… **et l'état de repli des catégories** (clés `reports-compare-expanded`, `reports-bva-expanded`, `reports-trends-expanded`, `budget-grid-expanded`) : persisté ici **par profil** plutôt qu'en `localStorage`, pour être détruit avec le profil (pas de résidu machine-global survivant à la suppression) — voir [ADR 0016](adr/0016-persistance-etat-ui-par-profil.md) |
|
| `user_preferences` | Préférences applicatives (clé-valeur) — `import_folder`, `balance_show_returns`, `balance_starter_proposed`, `onboarding_checklist_hidden` (« 1 » quand le parcours de démarrage est masqué ; la clé est supprimée par « Afficher la liste de démarrage », #351-#352, #397), `onboarding_skipped_<clé>` (« 1 » par étape passée, #396), `onboarding_guided_initialized` (« 1 » une fois la règle D9 appliquée au profil, #396) — voir la section « Onboarding »… **et l'état de repli des catégories** (clés `reports-compare-expanded`, `reports-bva-expanded`, `reports-trends-expanded`, `budget-grid-expanded`) : persisté ici **par profil** plutôt qu'en `localStorage`, pour être détruit avec le profil (pas de résidu machine-global survivant à la suppression) — voir [ADR 0016](adr/0016-persistance-etat-ui-par-profil.md) |
|
||||||
| `balance_categories` | Taxonomie des **classes d'actif** (Liquidités, Fonds/FNB, Actions, Crypto, Autres) — `kind ∈ {simple, priced}` (défaut suggéré pour les nouveaux comptes), `custom_label` pour le renommage bilingue-safe (v12). Les ex-types véhicules (TFSA/RRSP) ont migré vers `balance_accounts.vehicle_type` (Étape 1, v12/v13, [ADR 0014](adr/0014-balance-vehicule-attribut.md)) |
|
| `balance_categories` | Taxonomie des **classes d'actif** (Liquidités, Fonds/FNB, Actions, Crypto, Autres) — `kind ∈ {simple, priced}` (défaut suggéré pour les nouveaux comptes), `custom_label` pour le renommage bilingue-safe (v12). Les ex-types véhicules (TFSA/RRSP) ont migré vers `balance_accounts.vehicle_type` (Étape 1, v12/v13, [ADR 0014](adr/0014-balance-vehicule-attribut.md)) |
|
||||||
| `balance_accounts` | Comptes de bilan (rattachés à une catégorie). `currency` hardcodée à `CAD` au MVP via CHECK. `archived_at` pour soft-delete. `vehicle_type` (enveloppe fiscale nullable, v12, [ADR 0014](adr/0014-balance-vehicule-attribut.md)). `kind ∈ {simple, detailed}` + `detailed_since` (pivot faisant autorité, v15, [ADR 0015](adr/0015-balance-detail-par-titre.md)) — porte désormais l'axe simple/détaillé (auparavant dérivé de `category.kind`). **Issue #179** : 4 comptes de départ seedés (`consolidated_schema.sql`) + proposés aux profils existants via `StarterAccountsModal` |
|
| `balance_accounts` | Comptes de bilan (rattachés à une catégorie). `currency` hardcodée à `CAD` au MVP via CHECK. `archived_at` pour soft-delete. `vehicle_type` (enveloppe fiscale nullable, v12, [ADR 0014](adr/0014-balance-vehicule-attribut.md)). `kind ∈ {simple, detailed}` + `detailed_since` (pivot faisant autorité, v15, [ADR 0015](adr/0015-balance-detail-par-titre.md)) — porte désormais l'axe simple/détaillé (auparavant dérivé de `category.kind`). **Issue #179** : 4 comptes de départ seedés (`consolidated_schema.sql`) + proposés aux profils existants via `StarterAccountsModal` |
|
||||||
| `balance_snapshots` | Snapshots datés (`snapshot_date` UNIQUE) — éditer = mettre à jour les lignes, pas dupliquer |
|
| `balance_snapshots` | Snapshots datés (`snapshot_date` UNIQUE) — éditer = mettre à jour les lignes, pas dupliquer |
|
||||||
|
|
@ -306,9 +306,9 @@ L'étape `file-preview`, déclarée depuis l'origine dans `ImportWizardStep` san
|
||||||
|
|
||||||
Deux relevés **inventés** (`exemple-desjardins.csv` : compte chèque Desjardins ; `exemple-tangerine.csv` : carte de crédit Tangerine ; 41 lignes chacun), avec les jetons `{{M1}}` / `{{M2}}` que la commande Rust `create_sample_import_folders` remplace par les deux mois complets précédant le clic (`MM/YYYY`). Les fichiers sont embarqués dans le binaire par `include_str!` : le dépôt est la seule source, il n'y a rien à copier au build. Le module est **séparé** de `csv/index.ts` à dessein — ce corpus est figé à quinze cas par `csvAutoDetect.test.ts` et six boucles y passent chaque fixture ; y ajouter les gabarits élargirait le contrat figé en silence. `src/__integration__/onboarding-samples.test.ts` lit les **mêmes** fichiers et fige ce que l'assistant en fera : chaque fichier reconnu par la signature de la banque qu'il imite, toutes les lignes lues, aucune ligne en erreur, totaux signés attendus, et la part catégorisée par les 180 mots-clés du seed — en laissant exprès des lignes à classer à la main (dont « GARDERIE LES PETITS PAS »), pour que la cinquième case de la liste de démarrage ait quelque chose à cocher (#354).
|
Deux relevés **inventés** (`exemple-desjardins.csv` : compte chèque Desjardins ; `exemple-tangerine.csv` : carte de crédit Tangerine ; 41 lignes chacun), avec les jetons `{{M1}}` / `{{M2}}` que la commande Rust `create_sample_import_folders` remplace par les deux mois complets précédant le clic (`MM/YYYY`). Les fichiers sont embarqués dans le binaire par `include_str!` : le dépôt est la seule source, il n'y a rien à copier au build. Le module est **séparé** de `csv/index.ts` à dessein — ce corpus est figé à quinze cas par `csvAutoDetect.test.ts` et six boucles y passent chaque fixture ; y ajouter les gabarits élargirait le contrat figé en silence. `src/__integration__/onboarding-samples.test.ts` lit les **mêmes** fichiers et fige ce que l'assistant en fera : chaque fichier reconnu par la signature de la banque qu'il imite, toutes les lignes lues, aucune ligne en erreur, totaux signés attendus, et la part catégorisée par les 180 mots-clés du seed — en laissant exprès des lignes à classer à la main (dont « GARDERIE LES PETITS PAS »), pour que la cinquième case de la liste de démarrage ait quelque chose à cocher (#354).
|
||||||
|
|
||||||
## Onboarding — états vides et liste de démarrage
|
## Onboarding — états vides et parcours de démarrage
|
||||||
|
|
||||||
Phase 1 de l'accueil des nouveaux profils (#351-#357 ; analyse d'origine dans `docs/analyse-onboarding-2026-09.md`). Aucune migration ni table : tout se lit dans les données existantes, et une seule préférence est écrite.
|
Phase 1 de l'accueil des nouveaux profils (#351-#357 ; analyse d'origine dans `docs/analyse-onboarding-2026-09.md`) : les états vides, la liste de démarrage et le dossier d'exemple. Phase 2 (#396-#401) : le parcours guidé, un bandeau en haut de chaque page et une carte d'étape sur la page de l'étape en cours (voir « Parcours guidé », plus bas). Aucune migration ni table : tout se lit dans les données existantes, et le parcours n'écrit que des préférences par profil.
|
||||||
|
|
||||||
**Les quatre états vides** rendent le composant partagé `EmptyState` (`src/components/shared/` : icône, titre, description, action primaire en `<Link>` ou `<button>`, action secondaire optionnelle, enfants libres). Leur signal est le **compte brut** de transactions du profil, jamais le résultat d'un filtre — un profil filtré jusqu'à zéro ligne garde sa page normale :
|
**Les quatre états vides** rendent le composant partagé `EmptyState` (`src/components/shared/` : icône, titre, description, action primaire en `<Link>` ou `<button>`, action secondaire optionnelle, enfants libres). Leur signal est le **compte brut** de transactions du profil, jamais le résultat d'un filtre — un profil filtré jusqu'à zéro ligne garde sa page normale :
|
||||||
|
|
||||||
|
|
@ -319,7 +319,7 @@ Phase 1 de l'accueil des nouveaux profils (#351-#357 ; analyse d'origine dans `d
|
||||||
| Transactions `/transactions` | `showsEmptyState(state)` = `totalInDb === 0 && !isLoading`, `totalInDb` via `getTransactionCount` | `EmptyState` dans la page | « Importer des transactions » → `/import` |
|
| Transactions `/transactions` | `showsEmptyState(state)` = `totalInDb === 0 && !isLoading`, `totalInDb` via `getTransactionCount` | `EmptyState` dans la page | « Importer des transactions » → `/import` |
|
||||||
| Budget `/budget` | `showsEmptyState(state)` = `totalInDb === 0`, même compteur | `EmptyState` dans la page | « Importer des transactions » → `/import` |
|
| Budget `/budget` | `showsEmptyState(state)` = `totalInDb === 0`, même compteur | `EmptyState` dans la page | « Importer des transactions » → `/import` |
|
||||||
|
|
||||||
**La liste de démarrage** (`OnboardingChecklist`, dans la carte d'accueil du tableau de bord vide, et dépliée en place par « Voir la liste » du bandeau de démarrage `OnboardingBanner`, que `AppShell` pose en haut de chaque page, #397) a six lignes dans un ordre fixe. Chaque case est cochée par une **donnée**, jamais par un clic ; la source de vérité est `computeChecklist(counters, { importFolderSet, budgetAllowed })` :
|
**La liste de démarrage** (`OnboardingChecklist`, dans la carte d'accueil du tableau de bord vide, et dépliée en place par « Voir la liste » du bandeau de démarrage `OnboardingBanner`, que `AppShell` pose en haut de chaque page, #397) a six lignes dans un ordre fixe. Chaque case est cochée par une **donnée**, jamais par un clic ; la source de vérité est `computeChecklist(counters, { importFolderSet, budgetAllowed, skipped })` (`utils/onboardingChecklist.ts`, pur) :
|
||||||
|
|
||||||
| Case | Source de vérité |
|
| Case | Source de vérité |
|
||||||
|---|---|
|
|---|---|
|
||||||
|
|
@ -330,9 +330,48 @@ Phase 1 de l'accueil des nouveaux profils (#351-#357 ; analyse d'origine dans `d
|
||||||
| Classer une transaction à la main | `COUNT(*) FROM transactions WHERE is_manually_categorized = 1` ≥ 1 — ni « règle créée » ni « transactions catégorisées » : le seed IPC pose 180 mots-clés dans tout profil neuf et le wizard catégorise pendant l'import, ces deux cases seraient donc cochées d'avance |
|
| Classer une transaction à la main | `COUNT(*) FROM transactions WHERE is_manually_categorized = 1` ≥ 1 — ni « règle créée » ni « transactions catégorisées » : le seed IPC pose 180 mots-clés dans tout profil neuf et le wizard catégorise pendant l'import, ces deux cases seraient donc cochées d'avance |
|
||||||
| Saisir un premier budget | `COUNT(*) FROM budget_entries` ≥ 1 ; verrouillée hors Base+ (`useEntitlement("budget")`), alors exclue du total (5 au lieu de 6) et renvoyée vers `/settings/users` |
|
| Saisir un premier budget | `COUNT(*) FROM budget_entries` ≥ 1 ; verrouillée hors Base+ (`useEntitlement("budget")`), alors exclue du total (5 au lieu de 6) et renvoyée vers `/settings/users` |
|
||||||
|
|
||||||
`computeChecklist` rend `{ items, done, total, complete }` ; quand `complete`, le bandeau passe au message de fin (« Ouvrir le guide », « Fermer »). « Masquer » écrit `onboarding_checklist_hidden = "1"` dans `user_preferences` — **par profil**, donc détruite avec lui ([ADR 0016](adr/0016-persistance-etat-ui-par-profil.md)) et sans effet sur les autres profils. Paramètres → Utilisateurs lit `hidden` dans `useOnboarding()` et montre « Afficher la liste de démarrage » tant que le parcours est masqué, y compris quand le bandeau de cette page vient de le masquer ; le clic passe par `show()` du fournisseur, seul écrivain de la préférence, et une confirmation remplace le bouton (#397).
|
`computeChecklist` rend `{ items, done, total, complete }`. Chaque ligne porte `done` (la donnée), `skipped` (l'utilisateur l'a passée) et `locked` ; au niveau de la liste, `done` compte les lignes déverrouillées faites ou passées, une fois chacune, et `complete` vaut vrai quand toutes le sont. Une ligne verrouillée reste hors du total, quel que soit son état. Dans la liste, une ligne faite n'a plus de lien, une ligne passée montre le badge « Passée » et « Reprendre », une ligne verrouillée mène à la carte Licence ; chaque état sauf « à faire » est lu par un texte masqué visuellement (`onboarding.state.*`), les icônes étant cachées aux technologies d'assistance.
|
||||||
|
|
||||||
**Le dossier d'exemple** : sur un dossier d'import sans sous-dossier, `ImportEmptyState` propose « Créer un dossier d'exemple ». `useImportWizard.createSampleFolders` invoque `create_sample_import_folders` avec le dossier et la date du jour, rescanne, puis affiche `import.empty.sampleCreated` avec les noms rendus par la commande ; le bandeau tombe au changement de dossier. La commande refuse d'écraser (voir son contrat dans « Commandes Tauri ») ; les gabarits et leur test d'intégration sont décrits dans « Gabarits du dossier d'exemple ».
|
**Le dossier d'exemple** : sur un dossier d'import sans sous-dossier, `ImportEmptyState` propose « Créer un dossier d'exemple ». `useImportWizard.createSampleFolders` invoque `create_sample_import_folders` avec le dossier et la date du jour, rescanne, puis affiche `import.empty.sampleCreated` avec les noms rendus par la commande ; ce bandeau de réussite tombe au changement de dossier. La commande refuse d'écraser (voir son contrat dans « Commandes Tauri ») ; les gabarits et leur test d'intégration sont décrits dans « Gabarits du dossier d'exemple ».
|
||||||
|
|
||||||
|
### Parcours guidé
|
||||||
|
|
||||||
|
**Le fournisseur.** `OnboardingProvider` (`src/contexts/OnboardingContext.tsx`), monté dans `AppShell` autour du bandeau et de `<Outlet/>`, appelle une seule fois `useOnboardingChecklist` : l'état garde son `useReducer` de domaine ([ADR 0002](adr/0002-useReducer-vs-redux.md)) et toute l'application le partage. Les pages et les composants le lisent par `useOnboarding()`, qui lève hors du fournisseur : `status`, `checklist`, `counters`, `hidden`, `budgetReady`, `nextStep`, `mode`, `cardPage`, et les actions `hide()`, `show()`, `skip(clé)`, `resume(clé)`, `refresh()`. Le fournisseur survit à la navigation ; un changement de profil remonte `AppShell` (`BrowserRouter key={refreshKey}`), donc un fournisseur neuf, avec son propre chargeur. Il relit son état au montage, à chaque changement de route et après les écritures annoncées sur le canal de données. Ses chargements sont numérotés et seule la réponse du dernier lancé s'applique ; une action locale (`HIDE`, `SHOW`, `SKIP`, `RESUME`) prime sur le snapshot d'un chargement parti avant elle ; une relecture qui échoue après un premier succès garde l'état affiché.
|
||||||
|
|
||||||
|
**Le canal de données.** `src/services/dataEvents.ts` ([ADR 0022](adr/0022-canal-evenements-donnees.md)) : chaque écriture qui peut changer un compteur du parcours appelle `notifyDataChanged()` une fois réussie (la liste des écritures et la règle d'ajout sont dans l'ADR), et le fournisseur, seul abonné, relit son état 150 ms après la dernière écriture d'une rafale (`followDataChanges`). C'est ce qui coche une étape pendant que sa page est ouverte.
|
||||||
|
|
||||||
|
**Les préférences** (`user_preferences`, par profil, donc détruites avec lui et sans effet sur les autres profils, [ADR 0016](adr/0016-persistance-etat-ui-par-profil.md)). Le fournisseur en est le seul écrivain, et aucune de ces écritures ne passe par le canal : il met son état à jour lui-même, une fois l'écriture faite.
|
||||||
|
|
||||||
|
| Clé | Valeur | Écrite par |
|
||||||
|
|---|---|---|
|
||||||
|
| `onboarding_checklist_hidden` | `"1"` quand le parcours est masqué ; un seul état pour le bandeau et la liste. Supprimée au réaffichage | `hideChecklist` (« Masquer », et « Fermer » à la fin du parcours) et la règle D9 ; `showChecklist` la supprime |
|
||||||
|
| `onboarding_skipped_<clé>` | `"1"` par étape passée, `<clé>` étant l'une des six clés de `CHECKLIST_ITEM_KEYS`. Lues en une requête, puis filtrées sur ces clés et sur la valeur `"1"` | `skipStep` (« Passer cette étape ») ; `resumeStep` (« Reprendre ») la supprime |
|
||||||
|
| `onboarding_guided_initialized` | `"1"` une fois la règle D9 appliquée au profil | `initializeGuidedPath` |
|
||||||
|
|
||||||
|
**La règle D9.** Au premier chargement, `createOnboardingLoader` attend `initializeGuidedPath`, une fois par fournisseur (promesse partagée contre le double montage de StrictMode). Dans une transaction, si le drapeau `onboarding_guided_initialized` manque, un profil qui a déjà des transactions reçoit `onboarding_checklist_hidden = "1"`, avec une ligne au Journal, puis le drapeau est posé. Un échec annule les deux écritures et le chargement continue sur l'état stocké ; le drapeau absent fait réessayer à l'ouverture suivante. Un profil vide ou neuf garde donc le parcours, et un profil déjà actif le trouve masqué, réaffichable depuis Paramètres → Utilisateurs.
|
||||||
|
|
||||||
|
**Les modèles purs** (`src/utils/onboardingPath.ts`, sans React, routeur ni base) :
|
||||||
|
|
||||||
|
- `nextStep` : la première ligne déverrouillée ni faite ni passée ;
|
||||||
|
- `CARD_PAGES` (`/import`, `/transactions`, `/budget`) et `stepsOfPage` : les pages qui portent une carte d'étape, et les étapes que chacune guide ;
|
||||||
|
- `latchCardPage` : le verrou de carte, décrit ci-dessous ;
|
||||||
|
- `bannerMode` : `none` avant le premier chargement réussi, quand le parcours est masqué et sur le tableau de bord d'un profil sans transaction (sa carte d'accueil porte déjà la liste) ; `card` sur la page verrouillée ; `complete` quand toutes les lignes déverrouillées sont faites ou passées, une fois l'entitlement Budget connu (`budgetReady`) ; `banner` partout ailleurs ;
|
||||||
|
- `stepNavState` et `readOnboardingNavState` : l'état de navigation de tout lien vers une étape (`{ onboarding: { focus: true } }`, plus `guided: true` pour « Classer une transaction à la main »), et la seule lecture admise de `location.state` (CWE-476) ;
|
||||||
|
- `showsStepCard`, `cardStep` (l'étape montrée et « Étape n sur N ») et `cardNext` (après « Fait. » : le lien vers l'étape suivante, la fin, ou rien tant que la relecture n'est pas arrivée) ;
|
||||||
|
- les modèles de carte des pages, qui rendent la phrase, l'état « fait » et la cible : `importCardModel` (suit l'assistant ; sur le rapport, « Fait. » seulement si l'import a ajouté des transactions), `transactionsCardModel` (phrase selon le filtre « Non catégorisées ») et `budgetCardModel` (« Tout déplier » ou la cellule du mois, selon la cible que la grille remonte, calculée par `budgetOnboardingTarget`, `src/components/budget/`) ;
|
||||||
|
- `onboardingTargetAttributes` : les attributs du contrôle mis en évidence.
|
||||||
|
|
||||||
|
`deriveOnboardingView` (`useOnboardingChecklist.ts`, pur) les compose à chaque rendu du fournisseur : étape suivante, verrou de carte et mode.
|
||||||
|
|
||||||
|
**Le bandeau.** `OnboardingBanner` (`src/components/onboarding/`, piloté par props) est rendu par `AppShell` au-dessus de chaque page, dans le flux de la page, avec `key={pathname}` : chaque page commence liste repliée. En mode `banner` : « Démarrage n/N », qui attend `budgetReady`, « Étape suivante » avec le lien de l'étape, « Voir la liste » / « Fermer la liste » (`aria-expanded`, `aria-controls`) et « Masquer ». En mode `complete` : le message de fin, « Ouvrir le guide » (`/settings/users#gettingStarted`) et « Fermer », qui masque le parcours. Dans les modes `none` et `card`, il ne rend rien.
|
||||||
|
|
||||||
|
**Le verrou de carte.** `latchCardPage(previous, { pathname, nextStep, hidden })` rend la page qui porte la carte, ou null. Il est posé quand la route courante est celle de l'étape suivante et figure dans `CARD_PAGES`. Il tient quand l'étape se coche : la carte reste pour annoncer « Fait. » et proposer la suite, même quand l'étape suivante vit sur une autre page. Il est relâché au changement de route, quand le parcours est masqué, et par « Passer » (`SKIP`) ; le rendu suivant le repose si la nouvelle étape suivante est sur la même page. La fonction est idempotente : le fournisseur la dérive pendant le rendu et range le résultat dans son reducer (`SET_CARD_PAGE`), ce qui évite l'image de bandeau qu'un effet laisserait passer sur une page à carte.
|
||||||
|
|
||||||
|
**Les cartes d'étape.** `StepCard` (props seules) et `OnboardingStepCard` (connectée, seule pièce de la carte qui lit `useOnboarding()`). Chaque page qui porte une carte rend `OnboardingStepCard` en premier enfant, avant son en-tête, et ne lui passe que son modèle. La carte a un titre `h2` « Étape n sur N · <étape> », qui prend le focus après une arrivée par un lien d'étape (une fois par arrivée, garde sur `location.key`, puis l'état de navigation est consommé) ; la phrase du modèle, où les libellés des boutons viennent de leurs propres clés ; une zone `role="status"` toujours rendue, qui reçoit « Fait. » ; « Passer cette étape » tant que l'étape n'est pas faite ; puis le lien vers l'étape suivante ou le message de fin. Sur la page Transactions, l'entrée guidée (`guided`) passe `GUIDED_ENTRY_FILTERS` à `useTransactions` : non catégorisées, sans borne de date, avec un repli unique sur toutes les transactions quand aucune ne reste à classer.
|
||||||
|
|
||||||
|
**La mise en évidence.** Elle passe par props, jamais par une requête DOM. La page tire la cible de son modèle et ne la transmet que pendant que la carte est affichée (`showsStepCard`) : `onboardingTarget` sur l'action de `EmptyState` (par `ImportEmptyState`), sur la première carte de `SourceList`, sur le bouton suivant de `WizardNavigation`, sur la catégorie de la première ligne de `TransactionTable`, et sur « Tout déplier » ou la cellule du mois courant de `BudgetTable`, une seule cible à la fois. Le contrôle rend `data-onboarding-target="<clé fixe>"` et `data-onboarding-highlight="true"` (`onboardingTargetAttributes`). `styles.css` dessine sur ce second attribut un contour `3px solid var(--accent)` décalé de 2 px et une pulsation `box-shadow` de 1,2 s jouée trois fois, retirée sous `prefers-reduced-motion: reduce`. `--accent` atteint 3:1 contre le fond de la page et contre les cartes, dans les deux thèmes (`styles.contrast.test.ts`). Une cible absente de l'écran laisse la carte sans anneau.
|
||||||
|
|
||||||
|
**Paramètres → Utilisateurs** lit `hidden` dans `useOnboarding()` et montre « Afficher la liste de démarrage » tant que le parcours est masqué, y compris quand le bandeau de cette même page vient de le masquer. Le clic passe par `show()` du fournisseur, et une confirmation remplace le bouton (#397).
|
||||||
|
|
||||||
## Gating par édition (licence)
|
## Gating par édition (licence)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -5,11 +5,14 @@
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
<title>QA — Parcours d'accueil</title>
|
<title>QA — Parcours d'accueil</title>
|
||||||
<!--
|
<!--
|
||||||
Checklist de parcours pour la phase 1 de l'accueil (issues #351 à #357).
|
Checklist de parcours de l'accueil : phase 1 (issues #351 à #357) et parcours
|
||||||
Page statique, français seulement, aucune dépendance réseau : tout est inline.
|
guidé de la phase 2 (issues #396 à #401). Page statique, français seulement,
|
||||||
L'état (cases + commentaires) vit dans le localStorage du navigateur qui
|
aucune dépendance réseau : tout est inline. L'état (cases + commentaires) vit
|
||||||
ouvre le fichier ; « Copier le rapport » produit le texte brut à coller dans
|
dans le localStorage du navigateur qui ouvre le fichier, sous une clé propre à
|
||||||
Paramètres → Systèmes → Rétroaction.
|
cette version de la liste ; « Copier le rapport » produit le texte brut à
|
||||||
|
coller dans Paramètres → Système → Envoyer un feedback.
|
||||||
|
La liste de la phase 1 telle que publiée avec la v0.17.0 reste lisible par
|
||||||
|
`git show v0.17.0:docs/qa-onboarding.html`.
|
||||||
-->
|
-->
|
||||||
<style>
|
<style>
|
||||||
:root {
|
:root {
|
||||||
|
|
@ -65,6 +68,7 @@
|
||||||
padding: 12px 16px; margin: 12px 0 20px;
|
padding: 12px 16px; margin: 12px 0 20px;
|
||||||
}
|
}
|
||||||
.prereq ul { margin: 6px 0 0; padding-left: 20px; }
|
.prereq ul { margin: 6px 0 0; padding-left: 20px; }
|
||||||
|
.intro { color: var(--muted); font-size: 0.92rem; }
|
||||||
ol.steps { list-style: none; margin: 0; padding: 0; counter-reset: step; }
|
ol.steps { list-style: none; margin: 0; padding: 0; counter-reset: step; }
|
||||||
ol.steps li {
|
ol.steps li {
|
||||||
background: var(--card); border: 1px solid var(--border); border-radius: 10px;
|
background: var(--card); border: 1px solid var(--border); border-radius: 10px;
|
||||||
|
|
@ -119,11 +123,11 @@
|
||||||
<body>
|
<body>
|
||||||
<main>
|
<main>
|
||||||
<h1>QA — Parcours d'accueil</h1>
|
<h1>QA — Parcours d'accueil</h1>
|
||||||
<p class="lead">Première ouverture de Simpl'Résultat jusqu'à la liste de démarrage complète. Coche chaque étape quand elle se passe comme décrit, et note où tu as hésité, même une seconde : c'est l'hésitation qu'on cherche, pas l'échec.</p>
|
<p class="lead">Première ouverture de Simpl'Résultat jusqu'à la fin du parcours de démarrage guidé. Coche chaque étape quand elle se passe comme décrit, et note où tu as hésité, même une seconde : c'est l'hésitation qu'on cherche, pas l'échec.</p>
|
||||||
|
|
||||||
<div class="meta">
|
<div class="meta">
|
||||||
<label>Testeur <input type="text" id="tester" placeholder="prénom" autocomplete="off"></label>
|
<label>Testeur <input type="text" id="tester" placeholder="prénom" autocomplete="off"></label>
|
||||||
<label>Version <input type="text" id="version" placeholder="ex. 0.17.0" autocomplete="off"></label>
|
<label>Version <input type="text" id="version" placeholder="ex. 0.18.0" autocomplete="off"></label>
|
||||||
<label>Système <input type="text" id="os" placeholder="Windows 11, Ubuntu 24.04…" autocomplete="off"></label>
|
<label>Système <input type="text" id="os" placeholder="Windows 11, Ubuntu 24.04…" autocomplete="off"></label>
|
||||||
<span class="progress" id="progress"></span>
|
<span class="progress" id="progress"></span>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -131,14 +135,18 @@
|
||||||
<section class="prereq">
|
<section class="prereq">
|
||||||
<strong>Avant de commencer</strong>
|
<strong>Avant de commencer</strong>
|
||||||
<ul>
|
<ul>
|
||||||
|
<li>Une version publiée ou un build de release : les cas du parcours guidé vérifient le rendu réel du webview, WebKitGTK sous Linux et WebView2 sous Windows.</li>
|
||||||
<li>Une installation neuve, ou un profil neuf : la liste de démarrage n'apparaît que dans un profil sans transaction.</li>
|
<li>Une installation neuve, ou un profil neuf : la liste de démarrage n'apparaît que dans un profil sans transaction.</li>
|
||||||
<li>Pour l'étape Budget, une clé Base ou Premium ; sans clé, l'étape se présente verrouillée « Base et plus » et le total passe à 5 — note-le dans le champ, c'est attendu.</li>
|
<li>Pour l'étape Budget, une clé Base ou Premium ; sans clé, l'étape se présente verrouillée « Base et plus » et le total passe à 5 — note-le dans le champ, c'est attendu.</li>
|
||||||
|
<li>Pour le cas de l'édition Gratuite, une session utilisateur ou une machine sans clé : ne retire pas la clé de ta machine habituelle, puisque retirer une clé efface aussi le jeton d'activation.</li>
|
||||||
|
<li>Pour la règle D9, un profil qui a des transactions, créé avec la v0.17.0 ou avant, et qu'aucune version du parcours guidé n'a encore ouvert (un build de développement de la pile compte).</li>
|
||||||
|
<li>Les cas d'accessibilité et de rendu se font sous Linux avec Orca, puis sous Windows avec NVDA.</li>
|
||||||
<li>La page garde tes réponses dans ce navigateur. Rien n'est envoyé : c'est toi qui colles le rapport dans l'application.</li>
|
<li>La page garde tes réponses dans ce navigateur. Rien n'est envoyé : c'est toi qui colles le rapport dans l'application.</li>
|
||||||
|
<li>Pour vérifier la v0.17.0 elle-même, prends la liste publiée avec elle : <code>git show v0.17.0:docs/qa-onboarding.html</code>.</li>
|
||||||
</ul>
|
</ul>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
<h2>Étapes</h2>
|
<div id="sections"></div>
|
||||||
<ol class="steps" id="steps"></ol>
|
|
||||||
|
|
||||||
<div class="actions">
|
<div class="actions">
|
||||||
<button type="button" class="primary" id="copy">Copier le rapport</button>
|
<button type="button" class="primary" id="copy">Copier le rapport</button>
|
||||||
|
|
@ -147,7 +155,7 @@
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="fallback" id="fallback" hidden>
|
<div class="fallback" id="fallback" hidden>
|
||||||
<p>Le presse-papiers n'est pas accessible depuis ce fichier : sélectionne le texte ci-dessous (il est déjà sélectionné) et copie-le avec Ctrl+C, puis colle-le dans Paramètres → Systèmes → Rétroaction.</p>
|
<p>Le presse-papiers n'est pas accessible depuis ce fichier : sélectionne le texte ci-dessous (il est déjà sélectionné) et copie-le avec Ctrl+C, puis colle-le dans Paramètres → Système → Envoyer un feedback.</p>
|
||||||
<textarea id="report" readonly aria-label="Rapport à copier"></textarea>
|
<textarea id="report" readonly aria-label="Rapport à copier"></textarea>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
@ -158,44 +166,137 @@
|
||||||
(function () {
|
(function () {
|
||||||
"use strict";
|
"use strict";
|
||||||
|
|
||||||
var STORAGE_KEY = "simpl-resultat.qa-onboarding.v1";
|
// v2: the guided path (#396 to #401) changed the list; the answers of the
|
||||||
|
// phase 1 list stay under their own key.
|
||||||
|
var STORAGE_KEY = "simpl-resultat.qa-onboarding.v2";
|
||||||
|
|
||||||
// One row per step of the issue. `expect` is what the tester should see.
|
// One group per part of the path, one row per case. `expect` is what the
|
||||||
var STEPS = [
|
// tester should see. Ids are unique across the groups.
|
||||||
{ id: "install", title: "Installer et lancer l'application",
|
var SECTIONS = [
|
||||||
expect: "L'installateur s'exécute sans avertissement bloquant et l'application s'ouvre sur la sélection de profil." },
|
{
|
||||||
{ id: "profile", title: "Créer un profil",
|
id: "phase-1",
|
||||||
expect: "Le profil s'ouvre directement après sa création (NIP facultatif)." },
|
title: "Phase 1 : premier lancement, états vides et dossier d'exemple",
|
||||||
{ id: "dashboard-empty", title: "Tableau de bord vide avec la liste de démarrage à 1 / 6",
|
intro: "Les cas de la phase 1 (#351 à #357) qui restent valides avec le parcours guidé. Ils peuvent se faire dans la même passe que la section suivante, sur le même profil neuf.",
|
||||||
expect: "Une carte d'accueil remplace les graphiques, la liste montre six lignes, seule « Créer un profil » est cochée, le compteur indique 1 / 6 (ou 1 / 5 sans clé)." },
|
steps: [
|
||||||
{ id: "cta-folder", title: "Cliquer « Choisir un dossier d'import »",
|
{ id: "install", title: "Installer et lancer l'application",
|
||||||
expect: "Le bouton de la carte d'accueil mène à la page Import." },
|
expect: "L'installateur s'exécute sans avertissement bloquant et l'application s'ouvre sur la sélection de profil." },
|
||||||
{ id: "import-no-folder", title: "Page Import sans dossier",
|
{ id: "profile", title: "Créer un profil",
|
||||||
expect: "Un état vide « Aucun dossier d'import » avec le bouton « Choisir un dossier » et le lien « Comment exporter un CSV depuis ma banque ? »." },
|
expect: "Le profil s'ouvre directement après sa création (NIP facultatif)." },
|
||||||
{ id: "pick-folder", title: "Cliquer « Choisir un dossier » et prendre un dossier vide",
|
{ id: "dashboard-empty", title: "Tableau de bord vide avec la liste de démarrage à 1 / 6",
|
||||||
expect: "La fenêtre du système s'ouvre ; une fois le dossier choisi, l'état vide devient « Aucune source trouvée » et nomme le dossier." },
|
expect: "Une carte d'accueil remplace les graphiques, la liste montre six lignes, seule « Créer un profil » est cochée, le compteur indique 1 / 6 (ou 1 / 5 sans clé)." },
|
||||||
{ id: "create-sample", title: "Cliquer « Créer un dossier d'exemple »",
|
{ id: "cta-folder", title: "Cliquer « Choisir un dossier d'import »",
|
||||||
expect: "Un message annonce les deux dossiers créés et invite à cliquer sur l'un d'eux. Dans l'explorateur de fichiers, les deux sous-dossiers contiennent un fichier releve-AAAA-MM.csv." },
|
expect: "Le bouton de la carte d'accueil mène à la page Import." },
|
||||||
{ id: "two-sources", title: "Deux sources apparaissent",
|
{ id: "import-no-folder", title: "Page Import sans dossier",
|
||||||
expect: "« Exemple Desjardins » et « Exemple Tangerine » sont listées comme sources, sans actualiser à la main." },
|
expect: "Un état vide « Aucun dossier d'import » avec le bouton « Choisir un dossier » et le lien « Comment exporter un CSV depuis ma banque ? »." },
|
||||||
{ id: "import-desjardins", title: "Importer « Exemple Desjardins » : aperçu, signes, doublons, rapport",
|
{ id: "pick-folder", title: "Cliquer « Choisir un dossier » et prendre un dossier vide",
|
||||||
expect: "Le format est détecté (bandeau Desjardins), l'aperçu montre des sorties et des entrées dans le bon sens (pas besoin d'« Inverser les signes »), aucun doublon, puis le rapport d'import indique le nombre de transactions importées." },
|
expect: "La fenêtre du système s'ouvre ; une fois le dossier choisi, l'état vide devient « Aucune source trouvée » et nomme le dossier." },
|
||||||
{ id: "dashboard-compact", title: "Retour au tableau de bord : liste compacte à 4 / 6",
|
{ id: "create-sample", title: "Cliquer « Créer un dossier d'exemple »",
|
||||||
expect: "Les graphiques sont là, la liste est devenue une barre compacte au-dessus des filtres et affiche 4 / 6 (ou 4 / 5 sans clé) : dossier, source et premier import sont cochés." },
|
expect: "Un message annonce les deux dossiers créés et invite à cliquer sur l'un d'eux. Dans l'explorateur de fichiers, les deux sous-dossiers contiennent un fichier releve-AAAA-MM.csv." },
|
||||||
{ id: "manual-category", title: "Transactions : classer « GARDERIE LES PETITS PAS » à la main",
|
{ id: "two-sources", title: "Deux sources apparaissent",
|
||||||
expect: "La transaction est sans catégorie après l'import ; lui en donner une à la main la garde classée." },
|
expect: "« Exemple Desjardins » et « Exemple Tangerine » sont listées comme sources, sans actualiser à la main." },
|
||||||
{ id: "five-of-six", title: "Liste à 5 / 6",
|
{ id: "import-desjardins", title: "Importer « Exemple Desjardins » : aperçu, signes, doublons, rapport",
|
||||||
expect: "De retour sur le tableau de bord, « Classer une transaction à la main » est cochée." },
|
expect: "Le format est détecté (bandeau Desjardins), l'aperçu montre des sorties et des entrées dans le bon sens (pas besoin d'« Inverser les signes »), aucun doublon, puis le rapport d'import indique le nombre de transactions importées." },
|
||||||
{ id: "budget", title: "Budget (clé Base ou Premium) : saisir un montant",
|
{ id: "manual-category", title: "Transactions : classer « GARDERIE LES PETITS PAS » à la main",
|
||||||
expect: "La page Budget montre la grille (pas l'état vide, puisqu'il y a des transactions) ; un montant saisi dans une cellule est conservé." },
|
expect: "La transaction est sans catégorie après l'import ; lui en donner une à la main la garde classée." },
|
||||||
{ id: "six-of-six", title: "Liste à 6 / 6, puis disparue",
|
{ id: "budget", title: "Budget (clé Base ou Premium) : saisir un montant",
|
||||||
expect: "Le tableau de bord affiche brièvement 6 / 6 ou plus aucune liste : une liste complète ne s'affiche plus." },
|
expect: "La page Budget montre la grille (pas l'état vide, puisqu'il y a des transactions) ; un montant saisi dans une cellule est conservé." },
|
||||||
{ id: "hide-reshow", title: "« Masquer » sur un second profil, puis réaffichage depuis Paramètres",
|
{ id: "hide-reshow", title: "« Masquer » sur un second profil, puis réaffichage depuis Paramètres",
|
||||||
expect: "Créer un second profil (clé Base ou plus) : sa liste est à 1 / 6 ; « Masquer » la retire de ce profil seulement, le premier profil n'est pas touché ; Paramètres → Utilisateurs montre « Afficher la liste de démarrage », qui la ramène." },
|
expect: "Créer un second profil (clé Base ou plus) : sa carte d'accueil montre la liste à 1 / 6. « Masquer » retire la liste de ce profil seulement, ainsi que le bandeau de ses autres pages ; le premier profil n'est pas touché. Paramètres → Utilisateurs montre « Afficher la liste de démarrage », qui ramène le parcours." },
|
||||||
{ id: "guide-link", title: "Ouvrir le guide « Exporter un CSV depuis votre banque » depuis l'état vide d'Import",
|
{ id: "guide-link", title: "Ouvrir le guide « Exporter un CSV depuis votre banque » depuis l'état vide d'Import",
|
||||||
expect: "Sur un profil sans dossier ou sans source, le lien de l'état vide ouvre le guide directement sur la sous-section, avec les procédures par banque." }
|
expect: "Sur un profil sans dossier ou sans source, le lien de l'état vide ouvre le guide directement sur la sous-section, avec les procédures par banque." }
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "parcours-guide",
|
||||||
|
title: "Parcours guidé : un profil neuf de bout en bout",
|
||||||
|
intro: "Phase 2 (#396 à #401), sur un profil neuf en édition Base ou Premium. De l'étape 2 à l'étape 6, avance seulement par les liens d'étape (ligne de la liste, « Étape suivante »), sans revenir au tableau de bord : chaque page doit dire quoi faire.",
|
||||||
|
steps: [
|
||||||
|
{ id: "guided-dashboard", title: "Tableau de bord vide sans bandeau, autres pages avec bandeau",
|
||||||
|
expect: "Sur le tableau de bord, aucun bandeau de démarrage au-dessus du titre : la carte d'accueil porte déjà la liste. Ouvre Catégories, puis Rapports : chacune commence par le bandeau « Démarrage 1/6 · Étape suivante : Choisir un dossier d'import → », avec « Voir la liste » et « Masquer »." },
|
||||||
|
{ id: "guided-import-card", title: "Étape 2 : la carte Import, son contour et le focus",
|
||||||
|
expect: "Suis le lien « Choisir un dossier d'import » du bandeau ou de la liste. La page Import s'ouvre sur la carte « Étape 2 sur 6 · Choisir un dossier d'import », avant le titre et à la place du bandeau, et le focus du clavier est sur le titre de la carte. La phrase nomme le bouton « Choisir un dossier », entouré d'un contour de couleur d'accent qui pulse trois fois, puis reste." },
|
||||||
|
{ id: "guided-import-folder", title: "Étape 3 : dossier choisi, puis dossier d'exemple",
|
||||||
|
expect: "Choisis un dossier vide : sans quitter la page, la carte passe à « Étape 3 sur 6 · Configurer une source d'import », nomme le dossier et entoure « Créer un dossier d'exemple ». Après la création, elle demande de cliquer sur la carte de ta banque et entoure la première source." },
|
||||||
|
{ id: "guided-import-wizard", title: "Étape 4 : l'assistant, écran par écran",
|
||||||
|
expect: "Clique la source entourée (« Exemple Desjardins »). À chaque écran, la carte dit quoi vérifier et entoure le bouton suivant : « Aperçu », « Vérifier les doublons », « Confirmer », puis « Importer ». Pendant l'import, elle affiche « Import en cours… ». Sur le rapport, elle annonce « Fait. » et propose « Étape suivante : Classer une transaction à la main → » ; après « Terminé », elle garde « Fait. »." },
|
||||||
|
{ id: "guided-transactions", title: "Étape 5 : Transactions, entrée guidée",
|
||||||
|
expect: "Clique « Étape suivante » sur la carte Import. La page Transactions s'ouvre filtrée sur « Non catégorisées », période « Tout », avec la carte « Étape 5 sur 6 · Classer une transaction à la main » et le focus sur son titre ; le contour entoure la catégorie de la première ligne. Choisis une catégorie pour l'une d'elles (« GARDERIE LES PETITS PAS », par exemple) : sans quitter la page, la carte annonce « Fait. » et propose « Étape suivante : Saisir un premier budget → », et le contour disparaît." },
|
||||||
|
{ id: "guided-budget", title: "Étape 6 : Budget, « Tout déplier » puis la cellule du mois",
|
||||||
|
expect: "Clique « Étape suivante » sur la carte Transactions. La page Budget montre la carte « Étape 6 sur 6 · Saisir un premier budget », la grille repliée et le contour sur « Tout déplier ». Clique-le : la phrase change et le contour passe sur la cellule du mois courant de la première catégorie à remplir. Saisis un montant puis Entrée : la carte annonce « Fait. », puis le message de fin, avec « Ouvrir le guide » et « Fermer »." },
|
||||||
|
{ id: "guided-budget-tab", title: "Budget : Tab enregistre et passe au mois suivant",
|
||||||
|
expect: "Dans une cellule, saisis un montant puis Tab : le montant est enregistré et la saisie passe au mois suivant, comme le dit la carte." },
|
||||||
|
{ id: "guided-live", title: "Les étapes se cochent page ouverte",
|
||||||
|
expect: "Pendant les étapes 2 à 6, chaque étape s'est cochée sur la page même, sans recharger ni changer de page, en moins d'une seconde : passage de l'étape 2 à l'étape 3 au choix du dossier, puis « Fait. » après l'import, le classement et le budget. Note tout délai visible, ou une étape qui ne se coche qu'au changement de page." },
|
||||||
|
{ id: "guided-end", title: "Fin du parcours : bandeau de fin, « Ouvrir le guide » et « Fermer »",
|
||||||
|
expect: "Quitte la page Budget : en haut de chaque page, tableau de bord compris, le bandeau dit « Démarrage terminé. Le guide d'utilisation vous accompagne pour la suite. ». « Ouvrir le guide » ouvre Paramètres → Utilisateurs sur la section « Premiers pas », qui décrit le parcours (« Le parcours de démarrage »). « Fermer » retire le bandeau de toutes les pages, et Paramètres → Utilisateurs propose alors « Afficher la liste de démarrage »." }
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "parcours-guide-liste",
|
||||||
|
title: "Parcours guidé : bandeau, liste, Passer et Reprendre",
|
||||||
|
intro: "Sur un profil où le parcours n'est pas terminé, par exemple un second profil neuf (clé Base ou plus).",
|
||||||
|
steps: [
|
||||||
|
{ id: "guided-list", title: "« Voir la liste » et « Fermer la liste »",
|
||||||
|
expect: "Sur une page sans carte (Catégories, par exemple), « Voir la liste » déplie les six étapes sous le bandeau et devient « Fermer la liste », qui les replie. Change de page : la liste est de nouveau repliée. Une étape faite est barrée et sans lien ; une étape à faire mène à sa page." },
|
||||||
|
{ id: "guided-skip-resume", title: "« Passer cette étape », puis « Reprendre »",
|
||||||
|
expect: "Sur la carte Transactions ou Budget, clique « Passer cette étape » : la carte laisse la place au bandeau (ou au message de fin si c'était la dernière étape) et le compteur avance d'une étape. Sur la page Import, passer une étape fait passer la carte à l'étape suivante de la page. Dans la liste dépliée, l'étape passée montre l'icône « passer », le badge « Passée » et « Reprendre ». Depuis une autre page, « Reprendre » rouvre la page de l'étape avec sa carte, le focus sur son titre, et l'étape redevient à faire." },
|
||||||
|
{ id: "guided-hide-users", title: "« Masquer » depuis Paramètres → Utilisateurs",
|
||||||
|
expect: "Sur Paramètres → Utilisateurs, clique « Masquer » dans le bandeau : le bandeau disparaît et, sans quitter la page, la ligne « Parcours de démarrage » apparaît avec « Afficher la liste de démarrage ». Un clic la remplace par « Le parcours de démarrage réapparaît en haut des pages », et le bandeau revient en haut de la page." }
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "parcours-guide-cas",
|
||||||
|
title: "Parcours guidé : cas particuliers et limites connues",
|
||||||
|
intro: "Chaque cas dit sur quel profil le faire. Les limites connues sont à constater et à juger : note si elles gênent.",
|
||||||
|
steps: [
|
||||||
|
{ id: "guided-d9", title: "Profil déjà actif masqué d'office, puis réaffiché (règle D9)",
|
||||||
|
expect: "Ouvre un profil qui avait des transactions avant cette version. Aucun bandeau ni carte, sur aucune page ; le Journal (Paramètres → Système) porte « Onboarding: the profile already had transactions, getting-started path hidden (Settings → Users shows it again) ». Paramètres → Utilisateurs propose « Afficher la liste de démarrage » : un clic ramène le bandeau, avec un compteur fidèle aux données du profil. Rouvrir le profil ne le masque plus." },
|
||||||
|
{ id: "guided-free", title: "Édition Gratuite : 5 étapes, fin à 5/5",
|
||||||
|
expect: "Sur une session ou une machine sans clé, avec un profil neuf : le compteur dit 1/5, et la ligne Budget est verrouillée « Base et plus » et mène à la carte Licence. Après le classement d'une transaction, la carte Transactions (« Étape 5 sur 5 ») annonce « Fait. » puis le message de fin : aucune carte Budget, fin à 5/5." },
|
||||||
|
{ id: "guided-noop-import", title: "Un import qui n'ajoute rien",
|
||||||
|
expect: "Sur un profil de test où « Importer un premier fichier » reste à faire, fais un import qui n'ajoute aucune transaction (par exemple un fichier qui n'a que sa ligne d'en-tête, ou un format de date choisi à tort, qui met toutes les lignes en erreur dans l'aperçu). Sur le rapport, la carte dit « L'import n'a ajouté aucune transaction ; le rapport ci-dessous dit pourquoi. » et n'annonce pas « Fait. ». À juger : la liste coche quand même « Importer un premier fichier », parce qu'elle compte les fichiers importés et non les transactions, et la carte annonce « Fait. » après « Terminé »." },
|
||||||
|
{ id: "guided-fallback", title: "Transactions : aucune transaction à classer",
|
||||||
|
expect: "Sur un profil de test où l'étape 5 reste à faire, rends toutes les transactions catégorisées sans les classer à la main : ajoute, depuis la page Catégories, un mot-clé pour chaque transaction restée sans catégorie, puis clique « Auto-catégoriser » sur la page Transactions. Depuis une autre page, suis « Étape suivante : Classer une transaction à la main → ». La page montre toutes les transactions, période « Tout », une seule fois, et la carte dit « Cliquez sur la catégorie d'une transaction… ». « Aucune transaction trouvée. » peut s'afficher un très bref instant entre les deux lectures (limite connue)." },
|
||||||
|
{ id: "guided-same-page", title: "Limite connue : un lien d'étape suivi depuis la page Transactions",
|
||||||
|
expect: "Passe l'étape 5 depuis sa carte, change les filtres de la page Transactions, puis, sur cette même page, clique « Reprendre » dans la liste du bandeau. La carte revient, mais la page garde tes filtres au lieu de l'entrée guidée ; la phrase de la carte suit le filtre réellement affiché. À juger." },
|
||||||
|
{ id: "guided-budget-expanded", title: "Budget : grille déjà dépliée par le profil",
|
||||||
|
expect: "Sur un profil où l'étape 6 reste à faire, déplie la grille (« Tout déplier ») sans saisir de montant, puis quitte la page Budget. Reviens par le lien de l'étape 6 : la grille est restée dépliée et la carte entoure directement la cellule du mois courant." },
|
||||||
|
{ id: "guided-budget-offscreen", title: "Limite connue : cellule du mois hors de la zone visible",
|
||||||
|
expect: "En fenêtre étroite, la grille du budget défile horizontalement et la cellule du mois courant peut être hors de vue (octobre est vers la droite) : rien ne défile tout seul. À juger : la phrase et le contour suffisent-ils pour la trouver ?" },
|
||||||
|
{ id: "guided-budget-zero", title: "Budget : une cellule quittée vide enregistre 0 $",
|
||||||
|
expect: "Sur un profil où l'étape 6 reste à faire, clique une cellule puis quitte-la sans rien saisir : 0 $ est enregistré et la carte annonce « Fait. ». C'est le comportement existant de la grille ; à juger." },
|
||||||
|
{ id: "guided-import-flash", title: "Limite connue : « Aucun dossier configuré » à l'ouverture d'Import",
|
||||||
|
expect: "À l'ouverture de la page Import, la carte du dossier peut afficher un très bref instant « Aucun dossier configuré » avant la lecture du dossier ; l'état vide et la carte de démarrage, eux, attendent cette lecture. À juger." }
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "parcours-guide-a11y",
|
||||||
|
title: "Parcours guidé : accessibilité et rendu",
|
||||||
|
intro: "Sous Linux (WebKitGTK, lecteur d'écran Orca), puis sous Windows (WebView2, lecteur d'écran NVDA). Indique le système dans le champ prévu.",
|
||||||
|
steps: [
|
||||||
|
{ id: "guided-render-linux", title: "Contour et pulsation sous Linux (WebKitGTK)",
|
||||||
|
expect: "Un contour de couleur d'accent, d'environ 3 px et décalé de 2 px, entoure le contrôle désigné sur les trois pages ; la pulsation joue trois fois en un peu moins de 4 secondes, puis le contour reste fixe. Note s'il est rogné par la grille ou par un conteneur." },
|
||||||
|
{ id: "guided-render-windows", title: "Contour et pulsation sous Windows (WebView2)",
|
||||||
|
expect: "Même attendu que sous Linux : contour de couleur d'accent d'environ 3 px, décalé de 2 px, trois pulsations, puis un contour fixe, jamais rogné." },
|
||||||
|
{ id: "guided-reduced-motion", title: "Mouvement réduit (prefers-reduced-motion)",
|
||||||
|
expect: "Active l'option du système qui réduit les animations (GNOME : réduire les animations, dans Accessibilité ; Windows : effets d'animation désactivés, dans Accessibilité → Effets visuels), puis relance l'application si rien ne change : le contour s'affiche, sans aucune pulsation." },
|
||||||
|
{ id: "guided-contrast", title: "Contraste du contour, thème clair et thème sombre",
|
||||||
|
expect: "Dans les deux thèmes (bouton de la barre latérale), le contour se distingue nettement du fond de la page, des cartes et du contour de focus du clavier, sur les pages Import, Transactions et Budget." },
|
||||||
|
{ id: "guided-keyboard", title: "Clavier seul",
|
||||||
|
expect: "Sans souris, de l'étape 2 à l'étape 6 : Tab atteint « Étape suivante », « Voir la liste », « Masquer », les lignes de la liste, « Passer cette étape » et le contrôle entouré, et Entrée ou Espace les active. À l'arrivée par un lien d'étape, le focus est sur le titre de la carte, et Tab repart de là vers le contrôle entouré. Le focus reste visible et n'est jamais pris au piège." },
|
||||||
|
{ id: "guided-orca", title: "Annonces d'Orca (Linux)",
|
||||||
|
expect: "Le bandeau et la carte s'annoncent comme la région « Parcours de démarrage » ; « Voir la liste » annonce son état replié ou déplié ; dans la liste, chaque étape est suivie de son état (« faite », « passée », « verrouillée », « en attente »), sauf une étape à faire ; à l'arrivée par « Étape suivante », le titre de la carte est lu ; « Fait. » est annoncé sans déplacer le focus ; la confirmation de Paramètres → Utilisateurs est annoncée." },
|
||||||
|
{ id: "guided-nvda", title: "Annonces de NVDA (Windows)",
|
||||||
|
expect: "Mêmes annonces que sous Orca : région « Parcours de démarrage », état replié ou déplié de « Voir la liste », état de chaque étape de la liste, titre de la carte à l'arrivée par « Étape suivante », « Fait. » sans déplacement du focus, confirmation de Paramètres → Utilisateurs." }
|
||||||
|
]
|
||||||
|
}
|
||||||
];
|
];
|
||||||
|
|
||||||
|
var ALL_STEPS = [];
|
||||||
|
SECTIONS.forEach(function (section) {
|
||||||
|
section.steps.forEach(function (step) { ALL_STEPS.push(step); });
|
||||||
|
});
|
||||||
|
|
||||||
var state = load();
|
var state = load();
|
||||||
|
|
||||||
function load() {
|
function load() {
|
||||||
|
|
@ -231,65 +332,90 @@
|
||||||
document.getElementById("status").textContent = text;
|
document.getElementById("status").textContent = text;
|
||||||
}
|
}
|
||||||
|
|
||||||
function updateProgress() {
|
function countDone() {
|
||||||
var done = 0;
|
var done = 0;
|
||||||
STEPS.forEach(function (s) { if (stepState(s.id).done) done += 1; });
|
ALL_STEPS.forEach(function (s) { if (stepState(s.id).done) done += 1; });
|
||||||
document.getElementById("progress").textContent = done + " / " + STEPS.length + " étapes cochées";
|
return done;
|
||||||
|
}
|
||||||
|
|
||||||
|
function updateProgress() {
|
||||||
|
document.getElementById("progress").textContent = countDone() + " / " + ALL_STEPS.length + " étapes cochées";
|
||||||
|
}
|
||||||
|
|
||||||
|
function renderStep(step, list) {
|
||||||
|
var st = stepState(step.id);
|
||||||
|
var li = document.createElement("li");
|
||||||
|
li.className = st.done ? "done" : "";
|
||||||
|
|
||||||
|
var check = document.createElement("div");
|
||||||
|
check.className = "check";
|
||||||
|
var box = document.createElement("input");
|
||||||
|
box.type = "checkbox";
|
||||||
|
box.id = "done-" + step.id;
|
||||||
|
box.checked = !!st.done;
|
||||||
|
box.addEventListener("change", function () {
|
||||||
|
st.done = box.checked;
|
||||||
|
li.className = st.done ? "done" : "";
|
||||||
|
save();
|
||||||
|
updateProgress();
|
||||||
|
});
|
||||||
|
check.appendChild(box);
|
||||||
|
|
||||||
|
var title = document.createElement("label");
|
||||||
|
title.className = "title";
|
||||||
|
title.htmlFor = box.id;
|
||||||
|
title.textContent = step.title;
|
||||||
|
|
||||||
|
var expect = document.createElement("div");
|
||||||
|
expect.className = "expect";
|
||||||
|
expect.textContent = step.expect;
|
||||||
|
|
||||||
|
var where = document.createElement("div");
|
||||||
|
where.className = "where";
|
||||||
|
var noteLabel = document.createElement("label");
|
||||||
|
noteLabel.htmlFor = "note-" + step.id;
|
||||||
|
noteLabel.textContent = "Où as-tu hésité ?";
|
||||||
|
var note = document.createElement("input");
|
||||||
|
note.type = "text";
|
||||||
|
note.id = noteLabel.htmlFor;
|
||||||
|
note.value = st.note || "";
|
||||||
|
note.autocomplete = "off";
|
||||||
|
note.addEventListener("input", function () {
|
||||||
|
st.note = note.value;
|
||||||
|
save();
|
||||||
|
});
|
||||||
|
where.appendChild(noteLabel);
|
||||||
|
where.appendChild(note);
|
||||||
|
|
||||||
|
li.appendChild(check);
|
||||||
|
li.appendChild(title);
|
||||||
|
li.appendChild(expect);
|
||||||
|
li.appendChild(where);
|
||||||
|
list.appendChild(li);
|
||||||
}
|
}
|
||||||
|
|
||||||
function render() {
|
function render() {
|
||||||
var list = document.getElementById("steps");
|
var root = document.getElementById("sections");
|
||||||
list.textContent = "";
|
root.textContent = "";
|
||||||
STEPS.forEach(function (step) {
|
SECTIONS.forEach(function (section) {
|
||||||
var st = stepState(step.id);
|
var block = document.createElement("section");
|
||||||
var li = document.createElement("li");
|
block.id = section.id;
|
||||||
li.className = st.done ? "done" : "";
|
|
||||||
|
|
||||||
var check = document.createElement("div");
|
var heading = document.createElement("h2");
|
||||||
check.className = "check";
|
heading.textContent = section.title;
|
||||||
var box = document.createElement("input");
|
block.appendChild(heading);
|
||||||
box.type = "checkbox";
|
|
||||||
box.id = "done-" + step.id;
|
|
||||||
box.checked = !!st.done;
|
|
||||||
box.addEventListener("change", function () {
|
|
||||||
st.done = box.checked;
|
|
||||||
li.className = st.done ? "done" : "";
|
|
||||||
save();
|
|
||||||
updateProgress();
|
|
||||||
});
|
|
||||||
check.appendChild(box);
|
|
||||||
|
|
||||||
var title = document.createElement("label");
|
var intro = document.createElement("p");
|
||||||
title.className = "title";
|
intro.className = "intro";
|
||||||
title.htmlFor = box.id;
|
intro.textContent = section.intro;
|
||||||
title.textContent = step.title;
|
block.appendChild(intro);
|
||||||
|
|
||||||
var expect = document.createElement("div");
|
var list = document.createElement("ol");
|
||||||
expect.className = "expect";
|
list.className = "steps";
|
||||||
expect.textContent = step.expect;
|
section.steps.forEach(function (step) { renderStep(step, list); });
|
||||||
|
block.appendChild(list);
|
||||||
|
|
||||||
var where = document.createElement("div");
|
root.appendChild(block);
|
||||||
where.className = "where";
|
|
||||||
var noteLabel = document.createElement("label");
|
|
||||||
noteLabel.htmlFor = "note-" + step.id;
|
|
||||||
noteLabel.textContent = "Où as-tu hésité ?";
|
|
||||||
var note = document.createElement("input");
|
|
||||||
note.type = "text";
|
|
||||||
note.id = noteLabel.htmlFor;
|
|
||||||
note.value = st.note || "";
|
|
||||||
note.autocomplete = "off";
|
|
||||||
note.addEventListener("input", function () {
|
|
||||||
st.note = note.value;
|
|
||||||
save();
|
|
||||||
});
|
|
||||||
where.appendChild(noteLabel);
|
|
||||||
where.appendChild(note);
|
|
||||||
|
|
||||||
li.appendChild(check);
|
|
||||||
li.appendChild(title);
|
|
||||||
li.appendChild(expect);
|
|
||||||
li.appendChild(where);
|
|
||||||
list.appendChild(li);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
["tester", "version", "os"].forEach(function (key) {
|
["tester", "version", "os"].forEach(function (key) {
|
||||||
|
|
@ -310,20 +436,21 @@
|
||||||
var now = new Date();
|
var now = new Date();
|
||||||
var date = now.getFullYear() + "-" + pad(now.getMonth() + 1) + "-" + pad(now.getDate()) +
|
var date = now.getFullYear() + "-" + pad(now.getMonth() + 1) + "-" + pad(now.getDate()) +
|
||||||
" " + pad(now.getHours()) + ":" + pad(now.getMinutes());
|
" " + pad(now.getHours()) + ":" + pad(now.getMinutes());
|
||||||
var done = 0;
|
|
||||||
var lines = [];
|
var lines = [];
|
||||||
lines.push("QA parcours d'accueil — " + date);
|
lines.push("QA parcours d'accueil — " + date);
|
||||||
if (state.meta.tester) lines.push("Testeur : " + state.meta.tester);
|
if (state.meta.tester) lines.push("Testeur : " + state.meta.tester);
|
||||||
if (state.meta.version) lines.push("Version : " + state.meta.version);
|
if (state.meta.version) lines.push("Version : " + state.meta.version);
|
||||||
if (state.meta.os) lines.push("Système : " + state.meta.os);
|
if (state.meta.os) lines.push("Système : " + state.meta.os);
|
||||||
lines.push("");
|
lines.push("Étapes cochées : " + countDone() + " / " + ALL_STEPS.length);
|
||||||
STEPS.forEach(function (step, i) {
|
SECTIONS.forEach(function (section) {
|
||||||
var st = stepState(step.id);
|
lines.push("");
|
||||||
if (st.done) done += 1;
|
lines.push("== " + section.title + " ==");
|
||||||
lines.push((st.done ? "[x] " : "[ ] ") + (i + 1) + ". " + step.title);
|
section.steps.forEach(function (step, i) {
|
||||||
if (st.note && st.note.trim()) lines.push(" Hésitation : " + st.note.trim());
|
var st = stepState(step.id);
|
||||||
|
lines.push((st.done ? "[x] " : "[ ] ") + (i + 1) + ". " + step.title);
|
||||||
|
if (st.note && st.note.trim()) lines.push(" Hésitation : " + st.note.trim());
|
||||||
|
});
|
||||||
});
|
});
|
||||||
lines.splice(lines.indexOf("") , 0, "Étapes cochées : " + done + " / " + STEPS.length);
|
|
||||||
return lines.join("\n");
|
return lines.join("\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -341,7 +468,7 @@
|
||||||
document.getElementById("fallback").hidden = true;
|
document.getElementById("fallback").hidden = true;
|
||||||
if (navigator.clipboard && typeof navigator.clipboard.writeText === "function") {
|
if (navigator.clipboard && typeof navigator.clipboard.writeText === "function") {
|
||||||
navigator.clipboard.writeText(text).then(function () {
|
navigator.clipboard.writeText(text).then(function () {
|
||||||
setStatus("Rapport copié. Colle-le dans Paramètres → Systèmes → Rétroaction.");
|
setStatus("Rapport copié. Colle-le dans Paramètres → Système → Envoyer un feedback.");
|
||||||
}, function () {
|
}, function () {
|
||||||
setStatus("Copie automatique refusée : utilise la zone ci-dessous.");
|
setStatus("Copie automatique refusée : utilise la zone ci-dessous.");
|
||||||
showFallback(text);
|
showFallback(text);
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue