@cityway/bo-ui 1.1.0-beta004 → 1.1.0-beta006
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/cityway-bo-ui-1.1.0-beta005.tgz +0 -0
- package/cityway-bo-ui-1.1.0-beta006.tgz +0 -0
- package/docs/components/export-button.md +198 -0
- package/docs/components/import-button.md +134 -0
- package/docs/components/import-wizard.md +342 -0
- package/docs/components/page-header.md +238 -160
- package/docs/components/table.md +8 -1
- package/docs/i18n.md +65 -0
- package/fesm2022/cityway-bo-ui.mjs +1126 -169
- package/fesm2022/cityway-bo-ui.mjs.map +1 -1
- package/index.d.ts +840 -30
- package/lib/assets/i18n/common.en.json +142 -0
- package/lib/assets/i18n/common.fr.json +150 -0
- package/package.json +2 -2
- package/styles/bo-ui.styles.min.css +1 -1
- package/styles/bo-ui.styles.min.css.map +1 -1
- package/cityway-bo-ui-1.1.0-beta004.tgz +0 -0
|
Binary file
|
|
Binary file
|
|
@@ -0,0 +1,198 @@
|
|
|
1
|
+
# `cw-export-button` — Bouton d'export
|
|
2
|
+
|
|
3
|
+
> Lib : `@cityway/bo-ui` · Sélecteur : `cw-export-button` · Standalone : oui
|
|
4
|
+
> Import : `import { ExportButtonComponent } from '@cityway/bo-ui';`
|
|
5
|
+
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
> ⚠️ **Prérequis d'intégration : `<cw-toast>` doit être monté dans le layout racine.**
|
|
9
|
+
> Le toast d'échec passe par `ToastService`, dont la pile n'est rendue que par le conteneur
|
|
10
|
+
> `<cw-toast>` de `basic-ui`. **Il n'est monté nulle part dans le template BO à ce jour** :
|
|
11
|
+
> sans lui, un export qui échoue ne produit **aucun message visible**. À placer une seule
|
|
12
|
+
> fois, typiquement dans le composant racine (cf. `basic-ui/docs/components/toast.md`).
|
|
13
|
+
|
|
14
|
+
---
|
|
15
|
+
|
|
16
|
+
## 1. Rôle & quand l'utiliser
|
|
17
|
+
|
|
18
|
+
Bouton *Exporter* qui déclenche la production d'un fichier et le remet au navigateur.
|
|
19
|
+
**Une seule option** → bouton simple ; **deux ou plus** → menu déroulant listant les options.
|
|
20
|
+
|
|
21
|
+
Le composant **ne porte aucun métier** : il ne connaît ni le format exporté, ni la façon de
|
|
22
|
+
nommer le fichier, ni ce qui est exporté. L'hôte sérialise et nomme (`ExportPayload`) ; la
|
|
23
|
+
lib déclenche, restitue l'état et transporte. Conséquence : aucune dépendance à `xlsx`,
|
|
24
|
+
`file-saver` ou quoi que ce soit d'autre — JSON, CSV, XLSX, PDF sont indifférents.
|
|
25
|
+
|
|
26
|
+
À ne PAS utiliser pour : un import (`cw-import-button`), un simple lien de téléchargement
|
|
27
|
+
vers une URL connue (`cw-file-to-download` de `basic-ui`).
|
|
28
|
+
|
|
29
|
+
---
|
|
30
|
+
|
|
31
|
+
## 2. API
|
|
32
|
+
|
|
33
|
+
### Inputs
|
|
34
|
+
|
|
35
|
+
| Input | Type | Défaut | Description |
|
|
36
|
+
|-------|------|--------|-------------|
|
|
37
|
+
| `options` | `ExportOptions` | — (**requis**) | Options proposées, dans l'ordre d'affichage. **Tuple non vide.** |
|
|
38
|
+
| `label` | `I18nKey` | `_COMMON._ACTION.EXPORT` | Clé i18n du libellé du bouton (ou du menu). |
|
|
39
|
+
| `errorKey` | `I18nKey` | `_COMMON._EXPORT.ERROR` | Clé i18n du message du toast d'échec. |
|
|
40
|
+
| `successKey` | `I18nKey \| null` | `null` | Clé i18n d'un toast de succès. `null` → **aucun toast**. |
|
|
41
|
+
|
|
42
|
+
Aucun output : ni le succès ni l'échec ne demandent d'action de l'hôte — l'export ne
|
|
43
|
+
modifie rien, il n'y a donc rien à recharger.
|
|
44
|
+
|
|
45
|
+
---
|
|
46
|
+
|
|
47
|
+
## 3. Types
|
|
48
|
+
|
|
49
|
+
```ts
|
|
50
|
+
interface ExportPayload {
|
|
51
|
+
blob: Blob; // contenu déjà sérialisé par l'hôte
|
|
52
|
+
filename: string; // nom complet, extension comprise
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
interface ExportOption {
|
|
56
|
+
label: I18nKey; // clé i18n, traduite par le composant
|
|
57
|
+
fn: () => Observable<ExportPayload>; // produit le fichier
|
|
58
|
+
disabled?: boolean; // [Option] désactive CETTE option seule
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/** Tuple non vide : `[]` ne compile pas. */
|
|
62
|
+
type ExportOptions = readonly [ExportOption, ...ExportOption[]];
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
### Ce que le type rend inexprimable
|
|
66
|
+
|
|
67
|
+
| Configuration invalide | Empêchée par |
|
|
68
|
+
|---|---|
|
|
69
|
+
| Aucune option | tuple non vide `[ExportOption, ...ExportOption[]]` — le rendu « bouton simple » ne peut plus lire un `options[0]` absent |
|
|
70
|
+
| Libellé d'option en texte déjà traduit | `label: I18nKey` — la conversion doit être écrite noir sur blanc |
|
|
71
|
+
|
|
72
|
+
**Le type nominal `I18nKey` n'est pas une garantie de contenu** : il dit d'où vient la
|
|
73
|
+
chaîne, pas ce qu'elle contient. `i18nKey('Tout exporter')` compile. Ce qu'il apporte : la
|
|
74
|
+
conversion est explicite donc visible en revue, et on ne peut plus passer une **variable**
|
|
75
|
+
de texte déjà localisé dans un champ de clé sans l'écrire.
|
|
76
|
+
|
|
77
|
+
---
|
|
78
|
+
|
|
79
|
+
## 4. Exemples
|
|
80
|
+
|
|
81
|
+
### Deux options, dans l'en-tête de page
|
|
82
|
+
|
|
83
|
+
```html
|
|
84
|
+
<cw-page-header [title]="'DOMAIN.USERS.TITLE'" [actions]="[actionEnum.add]" (action)="onAction($event)">
|
|
85
|
+
<cw-export-button cwPageHeaderExport [options]="exportOptions" />
|
|
86
|
+
</cw-page-header>
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
```ts
|
|
90
|
+
import { buildFilename, ExportOptions, i18nKey, jsonBlob, todayIsoDate } from '@cityway/bo-ui';
|
|
91
|
+
|
|
92
|
+
readonly exportOptions: ExportOptions = [
|
|
93
|
+
{
|
|
94
|
+
label: i18nKey('DOMAIN.USERS.EXPORT_ALL'),
|
|
95
|
+
fn: () => this.api.exportAll().pipe(
|
|
96
|
+
map(rows => ({
|
|
97
|
+
blob: jsonBlob(rows),
|
|
98
|
+
// Tout le métier de nommage est ICI : la lib n'en compose aucune partie.
|
|
99
|
+
filename: buildFilename(['utilisateurs', this.instance()?.code, todayIsoDate()], 'json'),
|
|
100
|
+
}))
|
|
101
|
+
),
|
|
102
|
+
},
|
|
103
|
+
{
|
|
104
|
+
label: i18nKey('DOMAIN.USERS.EXPORT_FILTERED'),
|
|
105
|
+
fn: () => this.api.export(this.filters()).pipe(map(rows => ({ … }))),
|
|
106
|
+
// Un droit qui porte sur un périmètre, pas sur l'export entier.
|
|
107
|
+
disabled: !this.canExportFiltered(),
|
|
108
|
+
},
|
|
109
|
+
];
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
### Export CSV / XLSX via `ExportService` de `bo-core`
|
|
113
|
+
|
|
114
|
+
Le composant ne sérialise pas ; il consomme le blob que vous produisez. `ExportService`
|
|
115
|
+
(`@cityway/bo-core`) télécharge lui-même via `file-saver` — pour l'utiliser ici, produisez
|
|
116
|
+
le blob plutôt que de laisser le service l'enregistrer, ou construisez-le directement :
|
|
117
|
+
|
|
118
|
+
```ts
|
|
119
|
+
fn: () => this.api.exportAll().pipe(
|
|
120
|
+
map(rows => ({
|
|
121
|
+
blob: new Blob([toCsv(rows)], { type: 'text/csv;charset=utf-8' }),
|
|
122
|
+
filename: buildFilename(['tarifs', todayIsoDate()], 'csv'),
|
|
123
|
+
}))
|
|
124
|
+
),
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
### Utilitaires fournis
|
|
128
|
+
|
|
129
|
+
```ts
|
|
130
|
+
downloadBlob(payload) // utilisé par le composant ; disponible pour un usage direct
|
|
131
|
+
jsonBlob(data) // Blob JSON indenté
|
|
132
|
+
todayIsoDate() // 'YYYY-MM-DD', en heure LOCALE (pas toISOString, qui décale en UTC)
|
|
133
|
+
buildFilename(parts, extension) // ignore les fragments absents, sans tiret orphelin
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
---
|
|
137
|
+
|
|
138
|
+
## 5. Notes d'intégration
|
|
139
|
+
|
|
140
|
+
- **`<cw-toast>` doit être monté** — voir l'avertissement en tête de page.
|
|
141
|
+
- **Pas d'interruption, pas d'annulation, délibérément.** Contrairement à l'import,
|
|
142
|
+
l'export **n'écrit rien** : un export abandonné n'a aucune conséquence sur l'intégrité
|
|
143
|
+
des données. Le traitement de l'interruption de `cw-import-wizard` ne s'applique pas
|
|
144
|
+
ici, et ce n'est **pas un oubli**. Ne l'« harmonisez » pas.
|
|
145
|
+
- **Un seul export à la fois.** Un second clic pendant qu'un export est en vol est ignoré.
|
|
146
|
+
Ce garde-fou est dans le code et **pas** dans le rendu : `cw-button [isLoading]` ne pose
|
|
147
|
+
pas `[disabled]`, le bouton reste cliquable pendant tout l'export.
|
|
148
|
+
- **Aucun spinner sur le bouton.** `[isLoading]` n'est pas utilisé : il injecte sa propre
|
|
149
|
+
région live `role="status"`, ce qui ferait deux régions concurrentes pour un seul
|
|
150
|
+
événement (et il n'existe de toute façon pas dans la branche `isDropdown`). L'état de
|
|
151
|
+
chargement est porté par la région du composant, cf. ci-dessous.
|
|
152
|
+
- **Restitution (RGAA)** : le composant possède **une** région `role="status"`
|
|
153
|
+
*visually-hidden*, permanente dans le DOM, qui annonce successivement *Export en cours* →
|
|
154
|
+
*Export terminé, le téléchargement de « … » a démarré* / *L'export a échoué*.
|
|
155
|
+
- C'est une région live et non un déplacement de focus — à la différence de
|
|
156
|
+
`cw-import-wizard` — parce que l'export ne fait apparaître **aucun contenu** vers
|
|
157
|
+
lequel déplacer le focus : le téléchargement est une action du navigateur, invisible
|
|
158
|
+
dans le document.
|
|
159
|
+
- La région est **vidée puis réécrite au tour de boucle suivant** à chaque annonce : une
|
|
160
|
+
région `role="status"` n'est réannoncée que si son contenu change, et deux exports
|
|
161
|
+
synchrones successifs y écriraient deux fois la même phrase.
|
|
162
|
+
- Elle double le toast d'échec, qui n'est aujourd'hui **jamais annoncé** : la chaîne
|
|
163
|
+
`cw-toast` → `cw-toast-item` → `cw-alert` ne porte aucun `role` ni `aria-live`.
|
|
164
|
+
- **Menu déroulant** : les items suivent le motif documenté de `basic-ui`
|
|
165
|
+
(`<button cwDropdownItem>` en direct — cf. `basic-ui/docs/components/button.md`). C'est
|
|
166
|
+
la classe `dropdown-item` posée par la directive sur la **cible du clic** qui permet à
|
|
167
|
+
`cw-button` de refermer son menu après la sélection. Un `<cw-button>` imbriqué ne la
|
|
168
|
+
porterait pas au bon endroit.
|
|
169
|
+
- **Le menu entier ne se désactive** que si **toutes** les options sont `disabled`.
|
|
170
|
+
- **Échec** : une erreur de l'observable, **et** une source qui complète sans rien émettre,
|
|
171
|
+
valent échec. Sans ce second cas, l'export se terminerait en silence — pas de fichier,
|
|
172
|
+
pas de message, bouton redevenu actif comme si tout allait bien.
|
|
173
|
+
- **Succès sans toast par défaut** : le fichier qui se télécharge *est* le retour. N'ajoutez
|
|
174
|
+
`successKey` que si l'hôte veut un accusé explicite.
|
|
175
|
+
- **Nommage du fichier** : entièrement à l'hôte. C'est ce qui garde le composant sans
|
|
176
|
+
métier — le code client, l'horodatage ou le périmètre n'ont pas à traverser la frontière
|
|
177
|
+
de la lib. `buildFilename` / `todayIsoDate` sont des aides, jamais appelées à votre place.
|
|
178
|
+
- **i18n** : libellés sous `_COMMON._EXPORT.*` dans le bundle **bo-ui**
|
|
179
|
+
(`@cityway/bo-ui/lib/assets/i18n/common.{fr,en}.json`) — voir `bo-ui/docs/i18n.md`.
|
|
180
|
+
`_COMMON._ACTION.EXPORT` et les titres de toast (`_COMMON._ACTION_ERROR.LABEL`,
|
|
181
|
+
`_COMMON._ACTION_SUCCESS.LABEL`) viennent de **basic-ui**.
|
|
182
|
+
- **Limite connue de `ToastService`** : deux messages identiques émis à moins d'une seconde
|
|
183
|
+
d'intervalle sont dédupliqués — le second est avalé. Le garde-fou anti-double-clic couvre
|
|
184
|
+
le cas courant.
|
|
185
|
+
|
|
186
|
+
---
|
|
187
|
+
|
|
188
|
+
## 6. Prompts-types (génération assistée)
|
|
189
|
+
|
|
190
|
+
- « Ajoute un bouton d'export CSV sur la page tarifs, avec deux options : tout, et la vue filtrée. »
|
|
191
|
+
- « Branche l'export de la liste utilisateurs sur `cw-export-button`, fichier JSON nommé avec le code client et la date. »
|
|
192
|
+
- « Désactive l'option "tout exporter" quand l'utilisateur n'a pas le droit correspondant. »
|
|
193
|
+
|
|
194
|
+
**Rappels pour Claude :**
|
|
195
|
+
- `options` est un **tuple non vide**. `label` est une **clé** (`i18nKey(...)`), pas du texte.
|
|
196
|
+
- `fn()` doit renvoyer un `ExportPayload` — **l'hôte sérialise et nomme**. Ne jamais ajouter de dépendance de format dans la lib.
|
|
197
|
+
- Ne **pas** poser `[isLoading]` sur le bouton, ni ajouter un bouton d'annulation d'export.
|
|
198
|
+
- Vérifier que `<cw-toast>` est monté dans le layout de l'application.
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
# `cw-import-button` — Bouton d'import
|
|
2
|
+
|
|
3
|
+
> Lib : `@cityway/bo-ui` · Sélecteur : `cw-import-button` · Standalone : oui
|
|
4
|
+
> Import : `import { ImportButtonComponent } from '@cityway/bo-ui';`
|
|
5
|
+
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
## 1. Rôle & quand l'utiliser
|
|
9
|
+
|
|
10
|
+
Bouton *Importer* qui ouvre `cw-import-wizard` dans une modale **sidepanel**. C'est la façon
|
|
11
|
+
standard de déclencher un import — inutile de recâbler `ModalsService` à la main.
|
|
12
|
+
|
|
13
|
+
Le composant **n'ajoute aucun contrat** : il transporte l'`ImportWizardConfig` de l'hôte
|
|
14
|
+
**tel quel** jusqu'à l'assistant. Pas de type miroir, pas de champ recopié — toute évolution
|
|
15
|
+
de `cw-import-wizard` est disponible ici sans modification symétrique.
|
|
16
|
+
|
|
17
|
+
Ce qu'il apporte, et qui serait sinon réécrit dans chaque page :
|
|
18
|
+
- le `ng-template` du corps de modale et l'ouverture au bon type ;
|
|
19
|
+
- l'absence de `labelBtnAction1`/`2` (un bouton d'action au pied **fermerait** la modale) ;
|
|
20
|
+
- la **séparation des deux signaux de sortie** que la SFD de l'assistant impose de ne pas
|
|
21
|
+
confondre (§4).
|
|
22
|
+
|
|
23
|
+
Pour tout ce qui concerne le parcours lui-même — étapes, modes, analyse, interruption,
|
|
24
|
+
rapport —, la référence est **`import-wizard.md`**.
|
|
25
|
+
|
|
26
|
+
---
|
|
27
|
+
|
|
28
|
+
## 2. API
|
|
29
|
+
|
|
30
|
+
### Inputs
|
|
31
|
+
|
|
32
|
+
| Input | Type | Défaut | Description |
|
|
33
|
+
|-------|------|--------|-------------|
|
|
34
|
+
| `config` | `ImportWizardConfig` | — (**requis**) | Paramétrage de l'assistant, transmis tel quel. Voir `import-wizard.md` §3. |
|
|
35
|
+
| `modalTitle` | `I18nKey` | — (**requis**) | Clé i18n du titre de la modale. |
|
|
36
|
+
| `label` | `I18nKey` | `_COMMON._ACTION.IMPORT` | Clé i18n du libellé du bouton. |
|
|
37
|
+
| `disabled` | `boolean` | `false` | Désactive le bouton (droit utilisateur). |
|
|
38
|
+
|
|
39
|
+
### Outputs
|
|
40
|
+
|
|
41
|
+
| Output | Payload | Description |
|
|
42
|
+
|--------|---------|-------------|
|
|
43
|
+
| `closed` | `void` | Modale fermée, **par n'importe quel chemin**. → l'hôte **recharge** ici. |
|
|
44
|
+
| `completed` | `ImportOutcome` | État terminal de l'assistant. → message, journalisation, action métier. |
|
|
45
|
+
|
|
46
|
+
---
|
|
47
|
+
|
|
48
|
+
## 3. Exemple
|
|
49
|
+
|
|
50
|
+
```html
|
|
51
|
+
<cw-page-header [title]="'DOMAIN.USERS.TITLE'" [actions]="[actionEnum.add]" (action)="onAction($event)">
|
|
52
|
+
<cw-import-button
|
|
53
|
+
cwPageHeaderImport
|
|
54
|
+
[config]="importConfig"
|
|
55
|
+
[modalTitle]="importTitle"
|
|
56
|
+
(closed)="reload()"
|
|
57
|
+
(completed)="onImported($event)" />
|
|
58
|
+
</cw-page-header>
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
```ts
|
|
62
|
+
import {
|
|
63
|
+
i18nKey, ImportEventKindEnum, ImportInterruptionEnum, ImportModeEnum, ImportOutcome,
|
|
64
|
+
ImportButtonComponent, ImportWizardConfig
|
|
65
|
+
} from '@cityway/bo-ui';
|
|
66
|
+
|
|
67
|
+
readonly importTitle = i18nKey('DOMAIN.USERS.IMPORT_TITLE');
|
|
68
|
+
|
|
69
|
+
readonly importConfig: ImportWizardConfig = {
|
|
70
|
+
accept: '.csv,.xlsx',
|
|
71
|
+
acceptLabel: 'CSV, XLSX',
|
|
72
|
+
maxSizeBytes: 10 * 1024 * 1024,
|
|
73
|
+
modes: [
|
|
74
|
+
{ mode: ImportModeEnum.addOnly, keyFieldHint: i18nKey('DOMAIN.USERS.IMPORT_DUPLICATE_HINT') },
|
|
75
|
+
{ mode: ImportModeEnum.upsert, keyFieldHint: i18nKey('DOMAIN.USERS.IMPORT_KEY_HINT') },
|
|
76
|
+
],
|
|
77
|
+
showErrorDetails: true,
|
|
78
|
+
interruption: { kind: ImportInterruptionEnum.serverCancel, cancel: ctx => this.api.cancel(ctx) },
|
|
79
|
+
source: {
|
|
80
|
+
analyze: (file, mode) => this.api.dryRun(file, mode),
|
|
81
|
+
execute: (file, mode, ctx) => this.api.run(file, mode, ctx),
|
|
82
|
+
},
|
|
83
|
+
};
|
|
84
|
+
|
|
85
|
+
// Signal de RÉSULTAT : uniquement ce qui dépend de l'issue. On NE FERME PAS la modale ici,
|
|
86
|
+
// l'utilisateur doit pouvoir lire son rapport.
|
|
87
|
+
onImported(outcome: ImportOutcome): void {
|
|
88
|
+
if (outcome.kind === ImportEventKindEnum.report && outcome.success) {
|
|
89
|
+
this.toaster.success('DOMAIN.USERS.IMPORT_DONE');
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
---
|
|
95
|
+
|
|
96
|
+
## 4. Notes d'intégration
|
|
97
|
+
|
|
98
|
+
- **Rechargez sur `(closed)`, pas sur `(completed)`.** C'est la règle centrale, reprise de
|
|
99
|
+
`import-wizard.md` §5. `closed` est émis à **tous** les chemins de fermeture — croix,
|
|
100
|
+
Échap, clic sur le fond, bouton *Fermer* du pied — y compris la fermeture **pendant** un
|
|
101
|
+
import, cas où `completed` n'a jamais été émis alors que des données ont pu être écrites.
|
|
102
|
+
Le composant traite les **deux** issues de la promesse de `ModalsService` (résolution
|
|
103
|
+
comme rejet) : le rejet est ainsi marqué comme géré, et si un chemin de fermeture venait
|
|
104
|
+
à la résoudre, le rechargement partirait quand même.
|
|
105
|
+
- **`completed` est le signal de RÉSULTAT** : message de confirmation, journalisation,
|
|
106
|
+
action métier. Émis à tout état terminal — succès, échec, interruption — mais il ne voit
|
|
107
|
+
pas la fermeture, donc il ne peut pas porter le rechargement.
|
|
108
|
+
- **Ne fermez pas la modale** dans le gestionnaire de `completed` : l'utilisateur doit
|
|
109
|
+
pouvoir lire son rapport, en particulier un rapport d'interruption en état incertain. Le
|
|
110
|
+
composant n'expose d'ailleurs aucune sortie de fermeture.
|
|
111
|
+
- **Aucun bouton d'action au pied.** Le composant ne passe pas `labelBtnAction1`/`2` : le
|
|
112
|
+
pied par défaut rend exactement le *Fermer* attendu, et tout bouton d'action configuré
|
|
113
|
+
**fermerait** la modale.
|
|
114
|
+
- **Placement** : dans le slot `cwPageHeaderImport` de `cw-page-header`, qui le rend à la
|
|
115
|
+
position habituelle du bouton *Importer*. Il fonctionne aussi seul, hors en-tête.
|
|
116
|
+
L'import **n'est pas** une valeur de `PageHeaderActionEnum` : le rendre depuis
|
|
117
|
+
`cw-page-header` obligerait celui-ci à référencer l'assistant, donc à l'embarquer dans
|
|
118
|
+
toute page affichant un titre — ng-packagr aplatit la lib en un FESM unique, un `@defer`
|
|
119
|
+
n'y change rien.
|
|
120
|
+
- **i18n** : `_COMMON._ACTION.IMPORT` vient de **basic-ui** ; les libellés de l'assistant
|
|
121
|
+
sont sous `_COMMON._IMPORT.*` dans le bundle **bo-ui**.
|
|
122
|
+
|
|
123
|
+
---
|
|
124
|
+
|
|
125
|
+
## 5. Prompts-types (génération assistée)
|
|
126
|
+
|
|
127
|
+
- « Branche le bouton *Importer* de la page utilisateurs sur `cw-import-button`, formats CSV/XLSX, 10 Mo max, modes ajout et mise à jour. »
|
|
128
|
+
- « Ajoute l'import de configuration en écrasement complet dans l'en-tête de la page paramètres. »
|
|
129
|
+
|
|
130
|
+
**Rappels pour Claude :**
|
|
131
|
+
- Recharger sur `(closed)`, **jamais** sur `(completed)`. Ne jamais fermer la modale depuis `completed`.
|
|
132
|
+
- `config` est un `ImportWizardConfig` **complet** : voir `import-wizard.md` §3 pour ses règles (tuple `modes`, `interruption` requis, `keyFieldHint`).
|
|
133
|
+
- `modalTitle` et `label` sont des **clés** (`i18nKey(...)`), pas du texte.
|
|
134
|
+
- Ne pas rouvrir `ModalsService` à la main pour un import : c'est ce composant qui le fait.
|