Checkpoint: Implémentation de l'assistant de création guidée avec interface différenciée

 **Assistant de création guidée en 3 étapes**

L'interface de l'assistant est maintenant complètement différente du mode avancé, offrant une expérience simplifiée et guidée pour les utilisateurs non techniques.

**Étape 1 - Conditions simples**
- Nom de la règle
- Question guidée : "Quand appliquer cette règle ?"
- Interface simplifiée : "Le fournisseur [contient/est égal à/commence par] [valeur]"
- Validation avant passage à l'étape suivante

**Étape 2 - Sélection des actions**
- Question guidée : "Que voulez-vous remplir automatiquement ?"
- Sélection simple des 3 champs : Type d'achat, Service concerné, Ventilation comptable
- Option "Ne pas modifier" pour chaque champ

**Étape 3 - Révision et validation**
- Récapitulatif visuel dans une Card
- Affichage clair du nom, des conditions et des actions
- Bouton "Créer la règle" pour finaliser

**Navigation intuitive**
- Boutons "Annuler" / "Précédent" / "Suivant"
- Badge indiquant l'étape courante (Étape X/3)
- Validation à chaque étape avant progression
- Messages d'erreur clairs si champs obligatoires manquants

**Différences Assistant vs Mode avancé**
- **Assistant** : Interface simplifiée, 1 condition sur le fournisseur uniquement, navigation par étapes, questions en français naturel
- **Mode avancé** : Formulaire complet, conditions multiples avec opérateurs complexes, logique AND/OR, bouton "Tester la règle", tout sur une seule page

Architecture technique :
- client/src/pages/AutomationRules.tsx : Ajout de la structure conditionnelle wizardMode
- client/src/pages/AutomationRules.tsx : Implémentation des 3 étapes avec wizardStep
- client/src/pages/AutomationRules.tsx : Navigation avec validation entre les étapes
- client/src/pages/AutomationRules.tsx : Séparation complète de l'UI wizard vs avancé
- client/src/pages/AutomationRules.tsx : Utilisation de Card pour le récapitulatif

Bénéfices utilisateur :
- **Accessibilité** : Interface guidée pour les non-techniciens
- **Simplicité** : Une seule condition à la fois, focus sur l'essentiel
- **Clarté** : Questions en langage naturel, pas de jargon technique
- **Sécurité** : Validation à chaque étape, récapitulatif avant création
- **Flexibilité** : Choix entre assistant simple et mode avancé selon le besoin
This commit is contained in:
Manus
2026-02-12 03:44:28 -05:00
parent d0ef89e6ba
commit 8857ce01ef
2 changed files with 247 additions and 15 deletions

View File

@@ -374,23 +374,204 @@ export default function AutomationRules() {
<Dialog open={dialogOpen} onOpenChange={setDialogOpen}>
<DialogContent className="max-w-3xl max-h-[80vh] overflow-y-auto">
<DialogHeader>
<DialogTitle>{editingRule ? "Modifier la règle" : "Nouvelle règle"}</DialogTitle>
<DialogTitle>
{wizardMode && !editingRule ? (
<div className="flex items-center gap-2">
<span>Assistant de création</span>
<Badge variant="outline">Étape {wizardStep}/3</Badge>
</div>
) : (
editingRule ? "Modifier la règle" : "Nouvelle règle"
)}
</DialogTitle>
<DialogDescription>
Définissez les conditions (SI) et les actions (ALORS) pour le remplissage automatique
{wizardMode && !editingRule ? (
wizardStep === 1 ? "Définissez les conditions pour déclencher la règle" :
wizardStep === 2 ? "Choisissez les champs à remplir automatiquement" :
"Vérifiez et validez votre règle"
) : (
"Définissez les conditions (SI) et les actions (ALORS) pour le remplissage automatique"
)}
</DialogDescription>
</DialogHeader>
<div className="space-y-4">
{/* Rule Name */}
<div className="space-y-2">
<Label htmlFor="ruleName">Nom de la règle</Label>
<Input
id="ruleName"
value={ruleName}
onChange={(e) => setRuleName(e.target.value)}
placeholder="Ex: Factures Microsoft CAPEX"
/>
</div>
{wizardMode && !editingRule ? (
// WIZARD MODE - Étapes guidées
<div className="space-y-6">
{wizardStep === 1 && (
// ÉTAPE 1: Nom et conditions simples
<div className="space-y-4">
<div className="space-y-2">
<Label htmlFor="ruleName">Nom de la règle</Label>
<Input
id="ruleName"
value={ruleName}
onChange={(e) => setRuleName(e.target.value)}
placeholder="Ex: Factures Microsoft CAPEX"
/>
</div>
<div className="space-y-3">
<h3 className="font-semibold">Quand appliquer cette règle ?</h3>
<p className="text-sm text-gray-500">Définissez les conditions pour déclencher la règle</p>
<div className="space-y-4">
<div className="space-y-2">
<Label>Le fournisseur</Label>
<div className="flex gap-2">
<Select
value={conditions[0]?.operator || "contains"}
onValueChange={(value) => updateCondition(0, "operator", value)}
>
<SelectTrigger className="w-[160px]">
<SelectValue />
</SelectTrigger>
<SelectContent>
<SelectItem value="contains">contient</SelectItem>
<SelectItem value="equals">est égal à</SelectItem>
<SelectItem value="startsWith">commence par</SelectItem>
</SelectContent>
</Select>
<Input
value={conditions[0]?.value || ""}
onChange={(e) => {
if (conditions.length === 0) {
setConditions([{ field: "supplierName", operator: "contains", value: e.target.value }]);
} else {
updateCondition(0, "value", e.target.value);
}
}}
placeholder="Ex: Microsoft, Amazon, etc."
className="flex-1"
/>
</div>
</div>
</div>
</div>
</div>
)}
{wizardStep === 2 && (
// ÉTAPE 2: Actions
<div className="space-y-4">
<div>
<h3 className="font-semibold mb-1">Que voulez-vous remplir automatiquement ?</h3>
<p className="text-sm text-gray-500">Sélectionnez les champs à compléter</p>
</div>
<div className="space-y-4">
<div className="space-y-2">
<Label htmlFor="typeAchat">Type d'achat</Label>
<Select
value={actions.typeAchat || "__NONE__"}
onValueChange={(value) => setActions({ ...actions, typeAchat: value === "__NONE__" ? undefined : value })}
>
<SelectTrigger>
<SelectValue placeholder="Ne pas modifier" />
</SelectTrigger>
<SelectContent>
<SelectItem value="__NONE__">Ne pas modifier</SelectItem>
<SelectItem value="CAPEX">CAPEX</SelectItem>
<SelectItem value="OPEX">OPEX</SelectItem>
</SelectContent>
</Select>
</div>
<div className="space-y-2">
<Label htmlFor="serviceConcerne">Service concerné</Label>
<Select
value={actions.serviceConcerne || "__NONE__"}
onValueChange={(value) => setActions({ ...actions, serviceConcerne: value === "__NONE__" ? undefined : value })}
>
<SelectTrigger>
<SelectValue placeholder="Ne pas modifier" />
</SelectTrigger>
<SelectContent>
<SelectItem value="__NONE__">Ne pas modifier</SelectItem>
{departments?.map((dept) => (
<SelectItem key={dept.id} value={dept.name}>
{dept.name}
</SelectItem>
))}
</SelectContent>
</Select>
</div>
<div className="space-y-2">
<Label htmlFor="ventilationComptable">Ventilation comptable</Label>
<Select
value={actions.ventilationComptable || "__NONE__"}
onValueChange={(value) => setActions({ ...actions, ventilationComptable: value === "__NONE__" ? undefined : value })}
>
<SelectTrigger>
<SelectValue placeholder="Ne pas modifier" />
</SelectTrigger>
<SelectContent>
<SelectItem value="__NONE__">Ne pas modifier</SelectItem>
{allocations?.map((alloc) => (
<SelectItem key={alloc.id} value={alloc.name}>
{alloc.name}
</SelectItem>
))}
</SelectContent>
</Select>
</div>
</div>
</div>
)}
{wizardStep === 3 && (
// ÉTAPE 3: Révision
<div className="space-y-4">
<div>
<h3 className="font-semibold mb-1">Récapitulatif de la règle</h3>
<p className="text-sm text-gray-500">Vérifiez les informations avant de créer</p>
</div>
<Card>
<CardContent className="pt-6 space-y-4">
<div>
<div className="text-sm font-semibold text-gray-500">Nom</div>
<div className="text-lg">{ruleName || "(non défini)"}</div>
</div>
<div>
<div className="text-sm font-semibold text-gray-500">Condition</div>
<div className="text-lg">
Le fournisseur {conditions[0]?.operator === "contains" ? "contient" : conditions[0]?.operator === "equals" ? "est égal à" : "commence par"} "{conditions[0]?.value || "(vide)"}"
</div>
</div>
<div>
<div className="text-sm font-semibold text-gray-500">Actions</div>
<div className="space-y-1">
{actions.typeAchat && <div>• Type d'achat : {actions.typeAchat}</div>}
{actions.serviceConcerne && <div> Service : {actions.serviceConcerne}</div>}
{actions.ventilationComptable && <div> Ventilation : {actions.ventilationComptable}</div>}
{!actions.typeAchat && !actions.serviceConcerne && !actions.ventilationComptable && (
<div className="text-gray-500 italic">Aucune action définie</div>
)}
</div>
</div>
</CardContent>
</Card>
</div>
)}
</div>
) : (
// MODE AVANCÉ - Formulaire complet
<div className="space-y-4">
{/* Rule Name */}
<div className="space-y-2">
<Label htmlFor="ruleName">Nom de la règle</Label>
<Input
id="ruleName"
value={ruleName}
onChange={(e) => setRuleName(e.target.value)}
placeholder="Ex: Factures Microsoft CAPEX"
/>
</div>
{/* Conditions */}
<div className="space-y-2">
@@ -529,9 +710,50 @@ export default function AutomationRules() {
</div>
</div>
</div>
</div>
)}
</div>
<DialogFooter className="flex justify-between">
{wizardMode && !editingRule ? (
// Navigation wizard
<>
<Button
variant="outline"
onClick={() => {
if (wizardStep > 1) {
setWizardStep(wizardStep - 1);
} else {
closeDialog();
}
}}
>
{wizardStep === 1 ? "Annuler" : "Précédent"}
</Button>
<Button
onClick={() => {
if (wizardStep < 3) {
if (wizardStep === 1 && !ruleName.trim()) {
toast.error("Le nom de la règle est obligatoire");
return;
}
if (wizardStep === 1 && !conditions[0]?.value?.trim()) {
toast.error("Veuillez définir au moins une condition");
return;
}
setWizardStep(wizardStep + 1);
} else {
handleSave();
}
}}
disabled={createMutation.isPending}
>
{wizardStep === 3 ? "Créer la règle" : "Suivant"}
</Button>
</>
) : (
// Navigation mode avancé
<>
<Button
variant="secondary"
onClick={() => {
@@ -553,10 +775,12 @@ export default function AutomationRules() {
<Button variant="outline" onClick={closeDialog}>
Annuler
</Button>
<Button onClick={handleSave} disabled={createMutation.isPending || updateMutation.isPending}>
{editingRule ? "Mettre à jour" : "Créer"}
</Button>
<Button onClick={handleSave} disabled={createMutation.isPending || updateMutation.isPending}>
{editingRule ? "Mettre à jour" : "Créer"}
</Button>
</div>
</>
)}
</DialogFooter>
</DialogContent>
</Dialog>

View File

@@ -325,3 +325,11 @@
- [x] Afficher les résultats du test (nombre de factures affectées)
- [x] Ajouter le bouton "Dupliquer" dans la liste des règles
- [x] Tester toutes les nouvelles fonctionnalités
## Assistant de création guidée (interface différenciée)
- [x] Créer l'interface de l'assistant avec navigation par étapes (1/2/3)
- [x] Implémenter l'étape 1 : Questions simples pour les conditions
- [x] Implémenter l'étape 2 : Sélection des actions à appliquer
- [x] Implémenter l'étape 3 : Récapitulatif et validation
- [x] Ajouter la navigation Précédent/Suivant entre les étapes
- [x] Tester le parcours complet de l'assistant