@atlashub/smartstack-cli 1.4.0 → 1.5.0
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/.documentation/agents.html +8 -4
- package/.documentation/apex.html +8 -4
- package/.documentation/business-analyse.html +833 -406
- package/.documentation/commands.html +8 -4
- package/.documentation/css/styles.css +153 -15
- package/.documentation/efcore.html +8 -4
- package/.documentation/gitflow.html +795 -230
- package/.documentation/hooks.html +8 -4
- package/.documentation/index.html +13 -9
- package/.documentation/installation.html +23 -19
- package/.documentation/ralph-loop.html +530 -0
- package/.documentation/test-web.html +8 -4
- package/README.md +52 -10
- package/dist/index.js +813 -283
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/templates/agents/efcore/conflicts.md +44 -17
- package/templates/agents/efcore/db-status.md +27 -6
- package/templates/agents/efcore/scan.md +43 -13
- package/templates/commands/ai-prompt.md +315 -315
- package/templates/commands/application/create.md +362 -362
- package/templates/commands/controller/create.md +216 -216
- package/templates/commands/controller.md +59 -0
- package/templates/commands/create/agent.md +138 -0
- package/templates/commands/create/command.md +166 -0
- package/templates/commands/create/hook.md +234 -0
- package/templates/commands/create/plugin.md +329 -0
- package/templates/commands/create/project.md +507 -0
- package/templates/commands/create/skill.md +199 -0
- package/templates/commands/create.md +220 -0
- package/templates/commands/documentation/module.md +202 -202
- package/templates/commands/efcore/_env-check.md +153 -153
- package/templates/commands/efcore/conflicts.md +109 -192
- package/templates/commands/efcore/db-status.md +101 -89
- package/templates/commands/efcore/migration.md +23 -11
- package/templates/commands/efcore/scan.md +115 -119
- package/templates/commands/efcore.md +54 -6
- package/templates/commands/feature-full.md +267 -267
- package/templates/commands/gitflow/11-finish.md +145 -11
- package/templates/commands/gitflow/13-sync.md +216 -216
- package/templates/commands/gitflow/14-rebase.md +251 -251
- package/templates/commands/gitflow/2-status.md +120 -10
- package/templates/commands/gitflow/3-commit.md +150 -0
- package/templates/commands/gitflow/7-pull-request.md +134 -5
- package/templates/commands/gitflow/9-merge.md +142 -1
- package/templates/commands/implement.md +663 -663
- package/templates/commands/init.md +562 -0
- package/templates/commands/mcp-integration.md +330 -0
- package/templates/commands/notification.md +129 -129
- package/templates/commands/validate.md +233 -0
- package/templates/commands/workflow.md +193 -193
- package/templates/skills/ai-prompt/SKILL.md +778 -778
- package/templates/skills/application/SKILL.md +563 -563
- package/templates/skills/application/templates-backend.md +450 -450
- package/templates/skills/application/templates-frontend.md +531 -531
- package/templates/skills/application/templates-i18n.md +520 -520
- package/templates/skills/application/templates-seed.md +647 -647
- package/templates/skills/controller/SKILL.md +240 -240
- package/templates/skills/controller/postman-templates.md +614 -614
- package/templates/skills/controller/templates.md +1468 -1468
- package/templates/skills/documentation/SKILL.md +133 -133
- package/templates/skills/documentation/templates.md +476 -476
- package/templates/skills/feature-full/SKILL.md +838 -838
- package/templates/skills/notification/SKILL.md +555 -555
- package/templates/skills/ui-components/SKILL.md +870 -870
- package/templates/skills/workflow/SKILL.md +582 -582
|
@@ -1,582 +1,582 @@
|
|
|
1
|
-
---
|
|
2
|
-
name: workflow
|
|
3
|
-
description: |
|
|
4
|
-
Cree et configure des workflows automatises SmartStack.
|
|
5
|
-
Utiliser ce skill quand:
|
|
6
|
-
- L'utilisateur veut automatiser des actions (emails, webhooks)
|
|
7
|
-
- L'utilisateur mentionne "workflow", "automatisation", "trigger"
|
|
8
|
-
- Creation d'un processus metier avec etapes
|
|
9
|
-
- Integration d'emails transactionnels
|
|
10
|
-
Types: SendEmail, Wait, Condition, Webhook
|
|
11
|
-
---
|
|
12
|
-
|
|
13
|
-
# Skill Workflow SmartStack
|
|
14
|
-
|
|
15
|
-
> **Architecture:** Workflow = Trigger → Steps → Actions (Email/Wait/Condition/Webhook)
|
|
16
|
-
> Les workflows sont executes automatiquement lors d'evenements applicatifs.
|
|
17
|
-
|
|
18
|
-
## QUAND CE SKILL S'ACTIVE
|
|
19
|
-
|
|
20
|
-
Claude invoque automatiquement ce skill quand il detecte :
|
|
21
|
-
|
|
22
|
-
| Declencheur | Exemple |
|
|
23
|
-
|-------------|---------|
|
|
24
|
-
| Demande explicite | "Cree un workflow pour l'inscription" |
|
|
25
|
-
| Email automatique | "Envoie un email quand un ticket est cree" |
|
|
26
|
-
| Automatisation | "Automatise le processus d'onboarding" |
|
|
27
|
-
| Enchainement | "Apres 24h sans reponse, envoyer un rappel" |
|
|
28
|
-
| Mots-cles | "workflow", "trigger", "automatisation", "email template" |
|
|
29
|
-
|
|
30
|
-
---
|
|
31
|
-
|
|
32
|
-
## ARCHITECTURE WORKFLOW
|
|
33
|
-
|
|
34
|
-
```
|
|
35
|
-
┌─────────────────────────────────────────────────────────────────────────────┐
|
|
36
|
-
│ WORKFLOW ENGINE │
|
|
37
|
-
├─────────────────────────────────────────────────────────────────────────────┤
|
|
38
|
-
│ │
|
|
39
|
-
│ [EVENT] ─────────────────────────────────────────────────────────────────┐ │
|
|
40
|
-
│ │ │ │
|
|
41
|
-
│ ▼ │ │
|
|
42
|
-
│ ┌─────────────────────────────┐ │ │
|
|
43
|
-
│ │ WorkflowTrigger │ │ │
|
|
44
|
-
│ │ ex: "user.registered" │ │ │
|
|
45
|
-
│ │ "ticket.created" │ │ │
|
|
46
|
-
│ └─────────────┬───────────────┘ │ │
|
|
47
|
-
│ │ │ │
|
|
48
|
-
│ ▼ │ │
|
|
49
|
-
│ ┌─────────────────────────────┐ │ │
|
|
50
|
-
│ │ IWorkflowService │ │ │
|
|
51
|
-
│ │ TriggerAsync(code, vars) │ │ │
|
|
52
|
-
│ └─────────────┬───────────────┘ │ │
|
|
53
|
-
│ │ │ │
|
|
54
|
-
│ ┌────────┴────────┐ │ │
|
|
55
|
-
│ ▼ ▼ │ │
|
|
56
|
-
│ ┌─────────┐ ┌─────────────┐ │ │
|
|
57
|
-
│ │Workflow │ │ Workflow │ (plusieurs workflows par trigger) │ │
|
|
58
|
-
│ │ "A" │ │ "B" │ │ │
|
|
59
|
-
│ └────┬────┘ └──────┬──────┘ │ │
|
|
60
|
-
│ │ │ │ │
|
|
61
|
-
│ ▼ ▼ │ │
|
|
62
|
-
│ ┌─────────────────────────────────────────────────────────────────────┐ │ │
|
|
63
|
-
│ │ WORKFLOW STEPS │ │ │
|
|
64
|
-
│ │ │ │ │
|
|
65
|
-
│ │ Step 1: SendEmail Step 2: Wait Step 3: Condition │ │ │
|
|
66
|
-
│ │ ┌───────────────┐ ┌─────────────┐ ┌────────────────┐ │ │ │
|
|
67
|
-
│ │ │ EmailTemplate │ → │ Delay 24h │ → │ IF condition │ │ │ │
|
|
68
|
-
│ │ │ + Variables │ │ │ │ THEN Step 4 │ │ │ │
|
|
69
|
-
│ │ └───────────────┘ └─────────────┘ │ ELSE Step 5 │ │ │ │
|
|
70
|
-
│ │ └────────────────┘ │ │ │
|
|
71
|
-
│ │ │ │ │
|
|
72
|
-
│ │ Step 4: Webhook Step 5: SendEmail │ │ │
|
|
73
|
-
│ │ ┌───────────────┐ ┌───────────────┐ │ │ │
|
|
74
|
-
│ │ │ POST /api/... │ │ ReminderEmail │ │ │ │
|
|
75
|
-
│ │ │ + Payload │ │ + Variables │ │ │ │
|
|
76
|
-
│ │ └───────────────┘ └───────────────┘ │ │ │
|
|
77
|
-
│ │ │ │ │
|
|
78
|
-
│ └─────────────────────────────────────────────────────────────────────┘ │ │
|
|
79
|
-
│ │ │
|
|
80
|
-
└─────────────────────────────────────────────────────────────────────────────┘
|
|
81
|
-
```
|
|
82
|
-
|
|
83
|
-
---
|
|
84
|
-
|
|
85
|
-
## TRIGGERS DISPONIBLES
|
|
86
|
-
|
|
87
|
-
### User Events
|
|
88
|
-
|
|
89
|
-
| Trigger Code | Declencheur | Variables Disponibles |
|
|
90
|
-
|--------------|-------------|----------------------|
|
|
91
|
-
| `user.registered` | Inscription | userId, email, displayName, confirmUrl |
|
|
92
|
-
| `user.email-confirmed` | Email confirme | userId, email, displayName |
|
|
93
|
-
| `user.password-reset-requested` | Reset demande | userId, email, resetUrl |
|
|
94
|
-
| `user.password-changed` | Password change | userId, email |
|
|
95
|
-
| `user.account-locked` | Compte bloque | userId, email, lockReason |
|
|
96
|
-
| `user.account-unlocked` | Compte debloque | userId, email |
|
|
97
|
-
| `user.onboarding-completed` | Onboarding termine | userId, email, displayName |
|
|
98
|
-
| `user.admin-password-reset` | Admin reset via lien | userId, email, resetUrl |
|
|
99
|
-
| `user.admin-password-reset-direct` | Admin reset direct | userId, email, tempPassword |
|
|
100
|
-
|
|
101
|
-
### Ticket Events (Support)
|
|
102
|
-
|
|
103
|
-
| Trigger Code | Declencheur | Variables Disponibles |
|
|
104
|
-
|--------------|-------------|----------------------|
|
|
105
|
-
| `ticket.created` | Ticket cree | ticketId, ticketNumber, title, creatorEmail, creatorName |
|
|
106
|
-
| `ticket.resolved` | Ticket resolu | ticketId, ticketNumber, title, resolverName |
|
|
107
|
-
| `ticket.assigned` | Ticket assigne | ticketId, ticketNumber, assigneeName, assigneeEmail |
|
|
108
|
-
| `ticket.commented` | Commentaire ajoute | ticketId, ticketNumber, commenterName, commentPreview |
|
|
109
|
-
| `ticket.sla-warning` | SLA proche expiration | ticketId, ticketNumber, deadline, remainingMinutes |
|
|
110
|
-
| `ticket.sla-breached` | SLA depasse | ticketId, ticketNumber, breachedAt |
|
|
111
|
-
|
|
112
|
-
### Ajouter un Nouveau Trigger
|
|
113
|
-
|
|
114
|
-
```csharp
|
|
115
|
-
// 1. Infrastructure/Persistence/Configurations/Communications/WorkflowTriggerConfiguration.cs
|
|
116
|
-
// Dans la methode GetSeedData(), ajouter:
|
|
117
|
-
|
|
118
|
-
new
|
|
119
|
-
{
|
|
120
|
-
Id = Guid.Parse("NEW-GUID-HERE"),
|
|
121
|
-
Code = "entity.event",
|
|
122
|
-
Name = "Entity Event",
|
|
123
|
-
Description = "Triggered when entity event occurs",
|
|
124
|
-
TriggerType = "EntityEvent",
|
|
125
|
-
AvailableVariablesJson = JsonSerializer.Serialize(new[]
|
|
126
|
-
{
|
|
127
|
-
new { Name = "entityId", Type = "Guid", Description = "Entity ID" },
|
|
128
|
-
new { Name = "entityName", Type = "string", Description = "Entity name" },
|
|
129
|
-
new { Name = "userEmail", Type = "string", Description = "User email" },
|
|
130
|
-
}),
|
|
131
|
-
IsActive = true,
|
|
132
|
-
CreatedAt = seedDate
|
|
133
|
-
},
|
|
134
|
-
|
|
135
|
-
// 2. Declencher dans le service/controller
|
|
136
|
-
await _workflowService.TriggerAsync(
|
|
137
|
-
"entity.event",
|
|
138
|
-
new Dictionary<string, object>
|
|
139
|
-
{
|
|
140
|
-
["entityId"] = entity.Id,
|
|
141
|
-
["entityName"] = entity.Name,
|
|
142
|
-
["userEmail"] = user.Email,
|
|
143
|
-
},
|
|
144
|
-
language: "fr");
|
|
145
|
-
```
|
|
146
|
-
|
|
147
|
-
---
|
|
148
|
-
|
|
149
|
-
## TYPES DE STEPS
|
|
150
|
-
|
|
151
|
-
### 1. SendEmail
|
|
152
|
-
|
|
153
|
-
Envoie un email en utilisant un template.
|
|
154
|
-
|
|
155
|
-
```csharp
|
|
156
|
-
// Creation via Factory Method
|
|
157
|
-
var step = WorkflowStep.CreateEmailStep(
|
|
158
|
-
name: "Welcome Email",
|
|
159
|
-
emailTemplateId: welcomeTemplateId,
|
|
160
|
-
order: 1);
|
|
161
|
-
|
|
162
|
-
// Configuration JSON (si specifique)
|
|
163
|
-
{
|
|
164
|
-
"templateId": "guid-template",
|
|
165
|
-
"recipientVariable": "userEmail", // Variable contenant l'email
|
|
166
|
-
"ccEmails": ["admin@company.com"], // CC optionnels
|
|
167
|
-
"bccEmails": []
|
|
168
|
-
}
|
|
169
|
-
```
|
|
170
|
-
|
|
171
|
-
**Variables automatiques:**
|
|
172
|
-
- Toutes les variables du trigger sont injectees dans le template
|
|
173
|
-
- `{{userName}}`, `{{userEmail}}`, `{{ticketNumber}}`, etc.
|
|
174
|
-
|
|
175
|
-
### 2. Wait
|
|
176
|
-
|
|
177
|
-
Ajoute un delai avant le step suivant.
|
|
178
|
-
|
|
179
|
-
```csharp
|
|
180
|
-
// Creation
|
|
181
|
-
var step = WorkflowStep.CreateWaitStep(
|
|
182
|
-
name: "Wait 24 hours",
|
|
183
|
-
delayMinutes: 24 * 60, // 1440 minutes
|
|
184
|
-
order: 2);
|
|
185
|
-
```
|
|
186
|
-
|
|
187
|
-
**Cas d'usage:**
|
|
188
|
-
- Rappel apres X heures sans reponse
|
|
189
|
-
- Sequence d'onboarding espacee
|
|
190
|
-
- Delai avant escalade
|
|
191
|
-
|
|
192
|
-
### 3. Condition
|
|
193
|
-
|
|
194
|
-
Branche conditionnelle basee sur les variables.
|
|
195
|
-
|
|
196
|
-
```csharp
|
|
197
|
-
// Creation
|
|
198
|
-
var step = WorkflowStep.CreateConditionStep(
|
|
199
|
-
name: "Check if premium user",
|
|
200
|
-
configurationJson: JsonSerializer.Serialize(new
|
|
201
|
-
{
|
|
202
|
-
condition = "{{userType}} == 'Premium'",
|
|
203
|
-
trueStepOrder = 3, // Si vrai, aller au step 3
|
|
204
|
-
falseStepOrder = 4 // Si faux, aller au step 4
|
|
205
|
-
}),
|
|
206
|
-
order: 2);
|
|
207
|
-
```
|
|
208
|
-
|
|
209
|
-
**Operateurs supportes:**
|
|
210
|
-
- `==`, `!=` (egalite)
|
|
211
|
-
- `>`, `<`, `>=`, `<=` (comparaison)
|
|
212
|
-
- `contains`, `startsWith`, `endsWith` (strings)
|
|
213
|
-
- `&&`, `||` (logique)
|
|
214
|
-
|
|
215
|
-
### 4. Webhook
|
|
216
|
-
|
|
217
|
-
Appelle une API externe.
|
|
218
|
-
|
|
219
|
-
```csharp
|
|
220
|
-
// Creation
|
|
221
|
-
var step = WorkflowStep.CreateWebhookStep(
|
|
222
|
-
name: "Notify CRM",
|
|
223
|
-
configurationJson: JsonSerializer.Serialize(new
|
|
224
|
-
{
|
|
225
|
-
url = "https://crm.example.com/api/events",
|
|
226
|
-
method = "POST",
|
|
227
|
-
headers = new Dictionary<string, string>
|
|
228
|
-
{
|
|
229
|
-
["Authorization"] = "Bearer {{crmApiKey}}",
|
|
230
|
-
["Content-Type"] = "application/json"
|
|
231
|
-
},
|
|
232
|
-
body = new
|
|
233
|
-
{
|
|
234
|
-
eventType = "user.registered",
|
|
235
|
-
userId = "{{userId}}",
|
|
236
|
-
email = "{{userEmail}}"
|
|
237
|
-
},
|
|
238
|
-
retryCount = 3,
|
|
239
|
-
timeoutSeconds = 30
|
|
240
|
-
}),
|
|
241
|
-
order: 3);
|
|
242
|
-
```
|
|
243
|
-
|
|
244
|
-
---
|
|
245
|
-
|
|
246
|
-
## WORKFLOW CREATION
|
|
247
|
-
|
|
248
|
-
### ETAPE 1: Definir le Trigger
|
|
249
|
-
|
|
250
|
-
```csharp
|
|
251
|
-
// Choisir un trigger existant ou en creer un nouveau
|
|
252
|
-
var triggerId = existingTriggerId; // ex: user.registered trigger ID
|
|
253
|
-
```
|
|
254
|
-
|
|
255
|
-
### ETAPE 2: Creer le Workflow
|
|
256
|
-
|
|
257
|
-
```csharp
|
|
258
|
-
// Domain/Communications/Workflow.cs via Factory Method
|
|
259
|
-
var workflow = Workflow.Create(
|
|
260
|
-
code: "welcome-sequence",
|
|
261
|
-
name: "Welcome Email Sequence",
|
|
262
|
-
description: "Sends welcome emails after registration",
|
|
263
|
-
triggerId: triggerId,
|
|
264
|
-
applicationId: null, // null = global
|
|
265
|
-
isSystem: false,
|
|
266
|
-
priority: 10); // Plus haut = execute en premier
|
|
267
|
-
```
|
|
268
|
-
|
|
269
|
-
### ETAPE 3: Ajouter les Steps
|
|
270
|
-
|
|
271
|
-
```csharp
|
|
272
|
-
// Step 1: Email de bienvenue immediat
|
|
273
|
-
workflow.AddStep(WorkflowStep.CreateEmailStep(
|
|
274
|
-
name: "Welcome Email",
|
|
275
|
-
emailTemplateId: welcomeTemplateId,
|
|
276
|
-
order: 1));
|
|
277
|
-
|
|
278
|
-
// Step 2: Attendre 24h
|
|
279
|
-
workflow.AddStep(WorkflowStep.CreateWaitStep(
|
|
280
|
-
name: "Wait 24h",
|
|
281
|
-
delayMinutes: 1440,
|
|
282
|
-
order: 2));
|
|
283
|
-
|
|
284
|
-
// Step 3: Email de suivi
|
|
285
|
-
workflow.AddStep(WorkflowStep.CreateEmailStep(
|
|
286
|
-
name: "Follow-up Email",
|
|
287
|
-
emailTemplateId: followUpTemplateId,
|
|
288
|
-
order: 3));
|
|
289
|
-
```
|
|
290
|
-
|
|
291
|
-
### ETAPE 4: Seed Data (Infrastructure)
|
|
292
|
-
|
|
293
|
-
```csharp
|
|
294
|
-
// Infrastructure/Persistence/Configurations/Communications/WorkflowConfiguration.cs
|
|
295
|
-
|
|
296
|
-
public void Configure(EntityTypeBuilder<Workflow> builder)
|
|
297
|
-
{
|
|
298
|
-
// ... configuration ...
|
|
299
|
-
|
|
300
|
-
builder.HasData(GetSeedData());
|
|
301
|
-
}
|
|
302
|
-
|
|
303
|
-
private static object[] GetSeedData()
|
|
304
|
-
{
|
|
305
|
-
var seedDate = new DateTime(2024, 1, 1, 0, 0, 0, DateTimeKind.Utc);
|
|
306
|
-
|
|
307
|
-
return new object[]
|
|
308
|
-
{
|
|
309
|
-
new
|
|
310
|
-
{
|
|
311
|
-
Id = Guid.Parse("WORKFLOW-GUID"),
|
|
312
|
-
Code = "welcome-sequence",
|
|
313
|
-
Name = "Welcome Email Sequence",
|
|
314
|
-
Description = "Sends welcome emails after registration",
|
|
315
|
-
TriggerId = Guid.Parse("TRIGGER-GUID"),
|
|
316
|
-
ApplicationId = (Guid?)null,
|
|
317
|
-
IsActive = true,
|
|
318
|
-
IsSystem = true,
|
|
319
|
-
Priority = 10,
|
|
320
|
-
CreatedAt = seedDate
|
|
321
|
-
}
|
|
322
|
-
};
|
|
323
|
-
}
|
|
324
|
-
```
|
|
325
|
-
|
|
326
|
-
---
|
|
327
|
-
|
|
328
|
-
## DECLENCHEMENT WORKFLOW
|
|
329
|
-
|
|
330
|
-
### Dans un Service
|
|
331
|
-
|
|
332
|
-
```csharp
|
|
333
|
-
public class UserService : IUserService
|
|
334
|
-
{
|
|
335
|
-
private readonly IWorkflowService _workflowService;
|
|
336
|
-
|
|
337
|
-
public async Task<Result> RegisterAsync(RegisterCommand command, CancellationToken ct)
|
|
338
|
-
{
|
|
339
|
-
// ... creation utilisateur ...
|
|
340
|
-
|
|
341
|
-
// Declencher le workflow
|
|
342
|
-
await _workflowService.TriggerAsync(
|
|
343
|
-
"user.registered",
|
|
344
|
-
new Dictionary<string, object>
|
|
345
|
-
{
|
|
346
|
-
["userId"] = user.Id,
|
|
347
|
-
["email"] = user.Email,
|
|
348
|
-
["displayName"] = user.DisplayName,
|
|
349
|
-
["confirmUrl"] = GenerateConfirmUrl(user.Id)
|
|
350
|
-
},
|
|
351
|
-
language: "fr",
|
|
352
|
-
cancellationToken: ct);
|
|
353
|
-
|
|
354
|
-
return Result.Success();
|
|
355
|
-
}
|
|
356
|
-
}
|
|
357
|
-
```
|
|
358
|
-
|
|
359
|
-
### Dans un Controller
|
|
360
|
-
|
|
361
|
-
```csharp
|
|
362
|
-
[HttpPost("tickets")]
|
|
363
|
-
public async Task<ActionResult> CreateTicket(CreateTicketRequest request, CancellationToken ct)
|
|
364
|
-
{
|
|
365
|
-
var ticket = await _ticketService.CreateAsync(request, ct);
|
|
366
|
-
|
|
367
|
-
// Declencher workflow
|
|
368
|
-
await _workflowService.TriggerAsync(
|
|
369
|
-
"ticket.created",
|
|
370
|
-
new Dictionary<string, object>
|
|
371
|
-
{
|
|
372
|
-
["ticketId"] = ticket.Id,
|
|
373
|
-
["ticketNumber"] = ticket.Number,
|
|
374
|
-
["title"] = ticket.Title,
|
|
375
|
-
["creatorEmail"] = _currentUser.Email,
|
|
376
|
-
["creatorName"] = _currentUser.DisplayName
|
|
377
|
-
});
|
|
378
|
-
|
|
379
|
-
return CreatedAtAction(nameof(GetById), new { id = ticket.Id }, ticket);
|
|
380
|
-
}
|
|
381
|
-
```
|
|
382
|
-
|
|
383
|
-
---
|
|
384
|
-
|
|
385
|
-
## EMAIL TEMPLATES
|
|
386
|
-
|
|
387
|
-
### Structure Template
|
|
388
|
-
|
|
389
|
-
```csharp
|
|
390
|
-
// Domain/Communications/EmailTemplate.cs
|
|
391
|
-
public class EmailTemplate : BaseEntity
|
|
392
|
-
{
|
|
393
|
-
public string Code { get; private set; } // "welcome-email"
|
|
394
|
-
public string Name { get; private set; } // "Welcome Email"
|
|
395
|
-
public string Category { get; private set; } // "Transactional"
|
|
396
|
-
public bool IsActive { get; private set; }
|
|
397
|
-
public bool IsSystem { get; private set; }
|
|
398
|
-
|
|
399
|
-
// Translations (multi-langue)
|
|
400
|
-
public ICollection<EmailTemplateTranslation> Translations { get; }
|
|
401
|
-
}
|
|
402
|
-
|
|
403
|
-
public class EmailTemplateTranslation
|
|
404
|
-
{
|
|
405
|
-
public string LanguageCode { get; set; } // "fr", "en"
|
|
406
|
-
public string Subject { get; set; } // "Bienvenue {{userName}}"
|
|
407
|
-
public string HtmlBody { get; set; } // HTML avec variables
|
|
408
|
-
public string TextBody { get; set; } // Version texte
|
|
409
|
-
}
|
|
410
|
-
```
|
|
411
|
-
|
|
412
|
-
### Variables dans Templates
|
|
413
|
-
|
|
414
|
-
```html
|
|
415
|
-
<!-- HtmlBody avec Handlebars-like syntax -->
|
|
416
|
-
<h1>Bienvenue {{userName}}!</h1>
|
|
417
|
-
<p>Votre compte a ete cree avec succes.</p>
|
|
418
|
-
<p>Email: {{userEmail}}</p>
|
|
419
|
-
<a href="{{confirmUrl}}">Confirmer votre email</a>
|
|
420
|
-
|
|
421
|
-
<!-- Conditions -->
|
|
422
|
-
{{#if isPremium}}
|
|
423
|
-
<p>Merci pour votre abonnement Premium!</p>
|
|
424
|
-
{{else}}
|
|
425
|
-
<p>Decouvrez nos offres Premium.</p>
|
|
426
|
-
{{/if}}
|
|
427
|
-
|
|
428
|
-
<!-- Boucles -->
|
|
429
|
-
{{#each items}}
|
|
430
|
-
<li>{{this.name}} - {{this.price}}</li>
|
|
431
|
-
{{/each}}
|
|
432
|
-
```
|
|
433
|
-
|
|
434
|
-
---
|
|
435
|
-
|
|
436
|
-
## FRONTEND WORKFLOW EDITOR
|
|
437
|
-
|
|
438
|
-
### API Workflows
|
|
439
|
-
|
|
440
|
-
```typescript
|
|
441
|
-
// services/api/workflowsApi.ts
|
|
442
|
-
export const workflowsApi = {
|
|
443
|
-
// CRUD Workflows
|
|
444
|
-
getAll: () => apiClient.get<WorkflowDto[]>('/admin/workflows'),
|
|
445
|
-
getById: (id: string) => apiClient.get<WorkflowDto>(`/admin/workflows/${id}`),
|
|
446
|
-
create: (data: CreateWorkflowRequest) => apiClient.post('/admin/workflows', data),
|
|
447
|
-
update: (id: string, data: UpdateWorkflowRequest) => apiClient.put(`/admin/workflows/${id}`, data),
|
|
448
|
-
delete: (id: string) => apiClient.delete(`/admin/workflows/${id}`),
|
|
449
|
-
|
|
450
|
-
// Triggers
|
|
451
|
-
getTriggers: () => apiClient.get<WorkflowTriggerDto[]>('/admin/workflows/triggers'),
|
|
452
|
-
|
|
453
|
-
// Steps
|
|
454
|
-
addStep: (workflowId: string, step: CreateStepRequest) =>
|
|
455
|
-
apiClient.post(`/admin/workflows/${workflowId}/steps`, step),
|
|
456
|
-
updateStep: (workflowId: string, stepId: string, step: UpdateStepRequest) =>
|
|
457
|
-
apiClient.put(`/admin/workflows/${workflowId}/steps/${stepId}`, step),
|
|
458
|
-
deleteStep: (workflowId: string, stepId: string) =>
|
|
459
|
-
apiClient.delete(`/admin/workflows/${workflowId}/steps/${stepId}`),
|
|
460
|
-
reorderSteps: (workflowId: string, stepIds: string[]) =>
|
|
461
|
-
apiClient.post(`/admin/workflows/${workflowId}/steps/reorder`, { stepIds }),
|
|
462
|
-
|
|
463
|
-
// Execution
|
|
464
|
-
execute: (workflowId: string, variables: Record<string, unknown>) =>
|
|
465
|
-
apiClient.post(`/admin/workflows/${workflowId}/execute`, { variables }),
|
|
466
|
-
};
|
|
467
|
-
```
|
|
468
|
-
|
|
469
|
-
### Composant WorkflowStepsEditor
|
|
470
|
-
|
|
471
|
-
```tsx
|
|
472
|
-
// components/admin/workflows/WorkflowStepsEditor.tsx
|
|
473
|
-
import { Mail, Clock, GitBranch, Webhook, Plus, GripVertical, Trash2 } from 'lucide-react';
|
|
474
|
-
|
|
475
|
-
const stepTypeConfig = {
|
|
476
|
-
SendEmail: { icon: Mail, color: 'blue', label: 'Email' },
|
|
477
|
-
Wait: { icon: Clock, color: 'amber', label: 'Delai' },
|
|
478
|
-
Condition: { icon: GitBranch, color: 'purple', label: 'Condition' },
|
|
479
|
-
Webhook: { icon: Webhook, color: 'green', label: 'Webhook' },
|
|
480
|
-
};
|
|
481
|
-
|
|
482
|
-
export function WorkflowStepsEditor({ workflowId, steps, onUpdate }) {
|
|
483
|
-
// Drag and drop pour reordonner
|
|
484
|
-
// Modal pour editer chaque step
|
|
485
|
-
// Bouton pour ajouter un step
|
|
486
|
-
|
|
487
|
-
return (
|
|
488
|
-
<div className="space-y-4">
|
|
489
|
-
{steps.map((step, index) => (
|
|
490
|
-
<WorkflowStepCard
|
|
491
|
-
key={step.id}
|
|
492
|
-
step={step}
|
|
493
|
-
index={index}
|
|
494
|
-
onEdit={() => openEditModal(step)}
|
|
495
|
-
onDelete={() => handleDelete(step.id)}
|
|
496
|
-
/>
|
|
497
|
-
))}
|
|
498
|
-
|
|
499
|
-
<button
|
|
500
|
-
onClick={() => openAddModal()}
|
|
501
|
-
className="w-full p-4 border-2 border-dashed rounded-lg"
|
|
502
|
-
>
|
|
503
|
-
<Plus className="w-5 h-5 mr-2" />
|
|
504
|
-
Ajouter une etape
|
|
505
|
-
</button>
|
|
506
|
-
</div>
|
|
507
|
-
);
|
|
508
|
-
}
|
|
509
|
-
```
|
|
510
|
-
|
|
511
|
-
---
|
|
512
|
-
|
|
513
|
-
## WORKFLOWS SYSTEME (SEED)
|
|
514
|
-
|
|
515
|
-
| Code | Trigger | Description |
|
|
516
|
-
|------|---------|-------------|
|
|
517
|
-
| `email-confirmation` | `user.registered` | Email de confirmation apres inscription |
|
|
518
|
-
| `welcome-email` | `user.email-confirmed` | Email de bienvenue apres confirmation |
|
|
519
|
-
| `password-reset` | `user.password-reset-requested` | Email avec lien de reset |
|
|
520
|
-
| `account-unlocked-notification` | `user.account-unlocked` | Notification compte debloque |
|
|
521
|
-
| `user-onboarding` | `user.onboarding-completed` | Email fin d'onboarding |
|
|
522
|
-
| `admin-password-reset` | `user.admin-password-reset` | Reset admin via lien |
|
|
523
|
-
| `admin-password-reset-direct` | `user.admin-password-reset-direct` | Reset admin direct |
|
|
524
|
-
|
|
525
|
-
---
|
|
526
|
-
|
|
527
|
-
## CHECKLIST CREATION WORKFLOW
|
|
528
|
-
|
|
529
|
-
```
|
|
530
|
-
□ Trigger identifie (existant ou nouveau)
|
|
531
|
-
□ Si nouveau trigger:
|
|
532
|
-
□ Ajoute dans WorkflowTriggerConfiguration.cs seed
|
|
533
|
-
□ Variables definies (AvailableVariablesJson)
|
|
534
|
-
□ Migration EF Core creee
|
|
535
|
-
□ Workflow cree avec:
|
|
536
|
-
□ Code unique (kebab-case)
|
|
537
|
-
□ Nom descriptif
|
|
538
|
-
□ Trigger lie
|
|
539
|
-
□ Priority definie
|
|
540
|
-
□ Steps configures:
|
|
541
|
-
□ SendEmail avec templateId valide
|
|
542
|
-
□ Wait avec delayMinutes
|
|
543
|
-
□ Condition avec expression valide
|
|
544
|
-
□ Webhook avec URL et payload
|
|
545
|
-
□ Email templates crees (si SendEmail steps)
|
|
546
|
-
□ Declenchement ajoute dans le code source
|
|
547
|
-
□ Tests:
|
|
548
|
-
□ Trigger declenche le workflow
|
|
549
|
-
□ Emails envoyes correctement
|
|
550
|
-
□ Variables substituees
|
|
551
|
-
□ Migration EF Core si seed data modifie
|
|
552
|
-
```
|
|
553
|
-
|
|
554
|
-
---
|
|
555
|
-
|
|
556
|
-
## REGLES ABSOLUES
|
|
557
|
-
|
|
558
|
-
1. **TOUJOURS** utiliser IWorkflowService.TriggerAsync (jamais execution directe)
|
|
559
|
-
2. **TOUJOURS** fournir toutes les variables attendues par le trigger
|
|
560
|
-
3. **TOUJOURS** specifier la langue pour les emails
|
|
561
|
-
4. **TOUJOURS** utiliser des codes kebab-case uniques
|
|
562
|
-
5. **TOUJOURS** definir une priority (10 = standard, 20+ = prioritaire)
|
|
563
|
-
6. **TOUJOURS** logger les executions de workflow
|
|
564
|
-
7. **JAMAIS** hardcoder les URLs dans les templates
|
|
565
|
-
8. **JAMAIS** exposer des secrets dans les variables
|
|
566
|
-
9. **JAMAIS** creer des boucles infinies (workflow A → trigger B → workflow A)
|
|
567
|
-
10. **JAMAIS** oublier les email templates pour les steps SendEmail
|
|
568
|
-
|
|
569
|
-
---
|
|
570
|
-
|
|
571
|
-
## FICHIERS CLES
|
|
572
|
-
|
|
573
|
-
| Fichier | Role |
|
|
574
|
-
|---------|------|
|
|
575
|
-
| `Domain/Communications/Workflow.cs` | Entite workflow |
|
|
576
|
-
| `Domain/Communications/WorkflowStep.cs` | Entite step |
|
|
577
|
-
| `Domain/Communications/WorkflowTrigger.cs` | Entite trigger |
|
|
578
|
-
| `Domain/Communications/Enums/WorkflowStepType.cs` | Types de steps |
|
|
579
|
-
| `Application/Common/Interfaces/IWorkflowService.cs` | Interface service |
|
|
580
|
-
| `Infrastructure/Services/Workflow/WorkflowExecutionService.cs` | Implementation |
|
|
581
|
-
| `Infrastructure/Persistence/Configurations/Communications/WorkflowConfiguration.cs` | EF Config + Seed |
|
|
582
|
-
| `web/src/components/admin/workflows/WorkflowStepsEditor.tsx` | UI Editor |
|
|
1
|
+
---
|
|
2
|
+
name: workflow
|
|
3
|
+
description: |
|
|
4
|
+
Cree et configure des workflows automatises SmartStack.
|
|
5
|
+
Utiliser ce skill quand:
|
|
6
|
+
- L'utilisateur veut automatiser des actions (emails, webhooks)
|
|
7
|
+
- L'utilisateur mentionne "workflow", "automatisation", "trigger"
|
|
8
|
+
- Creation d'un processus metier avec etapes
|
|
9
|
+
- Integration d'emails transactionnels
|
|
10
|
+
Types: SendEmail, Wait, Condition, Webhook
|
|
11
|
+
---
|
|
12
|
+
|
|
13
|
+
# Skill Workflow SmartStack
|
|
14
|
+
|
|
15
|
+
> **Architecture:** Workflow = Trigger → Steps → Actions (Email/Wait/Condition/Webhook)
|
|
16
|
+
> Les workflows sont executes automatiquement lors d'evenements applicatifs.
|
|
17
|
+
|
|
18
|
+
## QUAND CE SKILL S'ACTIVE
|
|
19
|
+
|
|
20
|
+
Claude invoque automatiquement ce skill quand il detecte :
|
|
21
|
+
|
|
22
|
+
| Declencheur | Exemple |
|
|
23
|
+
|-------------|---------|
|
|
24
|
+
| Demande explicite | "Cree un workflow pour l'inscription" |
|
|
25
|
+
| Email automatique | "Envoie un email quand un ticket est cree" |
|
|
26
|
+
| Automatisation | "Automatise le processus d'onboarding" |
|
|
27
|
+
| Enchainement | "Apres 24h sans reponse, envoyer un rappel" |
|
|
28
|
+
| Mots-cles | "workflow", "trigger", "automatisation", "email template" |
|
|
29
|
+
|
|
30
|
+
---
|
|
31
|
+
|
|
32
|
+
## ARCHITECTURE WORKFLOW
|
|
33
|
+
|
|
34
|
+
```
|
|
35
|
+
┌─────────────────────────────────────────────────────────────────────────────┐
|
|
36
|
+
│ WORKFLOW ENGINE │
|
|
37
|
+
├─────────────────────────────────────────────────────────────────────────────┤
|
|
38
|
+
│ │
|
|
39
|
+
│ [EVENT] ─────────────────────────────────────────────────────────────────┐ │
|
|
40
|
+
│ │ │ │
|
|
41
|
+
│ ▼ │ │
|
|
42
|
+
│ ┌─────────────────────────────┐ │ │
|
|
43
|
+
│ │ WorkflowTrigger │ │ │
|
|
44
|
+
│ │ ex: "user.registered" │ │ │
|
|
45
|
+
│ │ "ticket.created" │ │ │
|
|
46
|
+
│ └─────────────┬───────────────┘ │ │
|
|
47
|
+
│ │ │ │
|
|
48
|
+
│ ▼ │ │
|
|
49
|
+
│ ┌─────────────────────────────┐ │ │
|
|
50
|
+
│ │ IWorkflowService │ │ │
|
|
51
|
+
│ │ TriggerAsync(code, vars) │ │ │
|
|
52
|
+
│ └─────────────┬───────────────┘ │ │
|
|
53
|
+
│ │ │ │
|
|
54
|
+
│ ┌────────┴────────┐ │ │
|
|
55
|
+
│ ▼ ▼ │ │
|
|
56
|
+
│ ┌─────────┐ ┌─────────────┐ │ │
|
|
57
|
+
│ │Workflow │ │ Workflow │ (plusieurs workflows par trigger) │ │
|
|
58
|
+
│ │ "A" │ │ "B" │ │ │
|
|
59
|
+
│ └────┬────┘ └──────┬──────┘ │ │
|
|
60
|
+
│ │ │ │ │
|
|
61
|
+
│ ▼ ▼ │ │
|
|
62
|
+
│ ┌─────────────────────────────────────────────────────────────────────┐ │ │
|
|
63
|
+
│ │ WORKFLOW STEPS │ │ │
|
|
64
|
+
│ │ │ │ │
|
|
65
|
+
│ │ Step 1: SendEmail Step 2: Wait Step 3: Condition │ │ │
|
|
66
|
+
│ │ ┌───────────────┐ ┌─────────────┐ ┌────────────────┐ │ │ │
|
|
67
|
+
│ │ │ EmailTemplate │ → │ Delay 24h │ → │ IF condition │ │ │ │
|
|
68
|
+
│ │ │ + Variables │ │ │ │ THEN Step 4 │ │ │ │
|
|
69
|
+
│ │ └───────────────┘ └─────────────┘ │ ELSE Step 5 │ │ │ │
|
|
70
|
+
│ │ └────────────────┘ │ │ │
|
|
71
|
+
│ │ │ │ │
|
|
72
|
+
│ │ Step 4: Webhook Step 5: SendEmail │ │ │
|
|
73
|
+
│ │ ┌───────────────┐ ┌───────────────┐ │ │ │
|
|
74
|
+
│ │ │ POST /api/... │ │ ReminderEmail │ │ │ │
|
|
75
|
+
│ │ │ + Payload │ │ + Variables │ │ │ │
|
|
76
|
+
│ │ └───────────────┘ └───────────────┘ │ │ │
|
|
77
|
+
│ │ │ │ │
|
|
78
|
+
│ └─────────────────────────────────────────────────────────────────────┘ │ │
|
|
79
|
+
│ │ │
|
|
80
|
+
└─────────────────────────────────────────────────────────────────────────────┘
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
---
|
|
84
|
+
|
|
85
|
+
## TRIGGERS DISPONIBLES
|
|
86
|
+
|
|
87
|
+
### User Events
|
|
88
|
+
|
|
89
|
+
| Trigger Code | Declencheur | Variables Disponibles |
|
|
90
|
+
|--------------|-------------|----------------------|
|
|
91
|
+
| `user.registered` | Inscription | userId, email, displayName, confirmUrl |
|
|
92
|
+
| `user.email-confirmed` | Email confirme | userId, email, displayName |
|
|
93
|
+
| `user.password-reset-requested` | Reset demande | userId, email, resetUrl |
|
|
94
|
+
| `user.password-changed` | Password change | userId, email |
|
|
95
|
+
| `user.account-locked` | Compte bloque | userId, email, lockReason |
|
|
96
|
+
| `user.account-unlocked` | Compte debloque | userId, email |
|
|
97
|
+
| `user.onboarding-completed` | Onboarding termine | userId, email, displayName |
|
|
98
|
+
| `user.admin-password-reset` | Admin reset via lien | userId, email, resetUrl |
|
|
99
|
+
| `user.admin-password-reset-direct` | Admin reset direct | userId, email, tempPassword |
|
|
100
|
+
|
|
101
|
+
### Ticket Events (Support)
|
|
102
|
+
|
|
103
|
+
| Trigger Code | Declencheur | Variables Disponibles |
|
|
104
|
+
|--------------|-------------|----------------------|
|
|
105
|
+
| `ticket.created` | Ticket cree | ticketId, ticketNumber, title, creatorEmail, creatorName |
|
|
106
|
+
| `ticket.resolved` | Ticket resolu | ticketId, ticketNumber, title, resolverName |
|
|
107
|
+
| `ticket.assigned` | Ticket assigne | ticketId, ticketNumber, assigneeName, assigneeEmail |
|
|
108
|
+
| `ticket.commented` | Commentaire ajoute | ticketId, ticketNumber, commenterName, commentPreview |
|
|
109
|
+
| `ticket.sla-warning` | SLA proche expiration | ticketId, ticketNumber, deadline, remainingMinutes |
|
|
110
|
+
| `ticket.sla-breached` | SLA depasse | ticketId, ticketNumber, breachedAt |
|
|
111
|
+
|
|
112
|
+
### Ajouter un Nouveau Trigger
|
|
113
|
+
|
|
114
|
+
```csharp
|
|
115
|
+
// 1. Infrastructure/Persistence/Configurations/Communications/WorkflowTriggerConfiguration.cs
|
|
116
|
+
// Dans la methode GetSeedData(), ajouter:
|
|
117
|
+
|
|
118
|
+
new
|
|
119
|
+
{
|
|
120
|
+
Id = Guid.Parse("NEW-GUID-HERE"),
|
|
121
|
+
Code = "entity.event",
|
|
122
|
+
Name = "Entity Event",
|
|
123
|
+
Description = "Triggered when entity event occurs",
|
|
124
|
+
TriggerType = "EntityEvent",
|
|
125
|
+
AvailableVariablesJson = JsonSerializer.Serialize(new[]
|
|
126
|
+
{
|
|
127
|
+
new { Name = "entityId", Type = "Guid", Description = "Entity ID" },
|
|
128
|
+
new { Name = "entityName", Type = "string", Description = "Entity name" },
|
|
129
|
+
new { Name = "userEmail", Type = "string", Description = "User email" },
|
|
130
|
+
}),
|
|
131
|
+
IsActive = true,
|
|
132
|
+
CreatedAt = seedDate
|
|
133
|
+
},
|
|
134
|
+
|
|
135
|
+
// 2. Declencher dans le service/controller
|
|
136
|
+
await _workflowService.TriggerAsync(
|
|
137
|
+
"entity.event",
|
|
138
|
+
new Dictionary<string, object>
|
|
139
|
+
{
|
|
140
|
+
["entityId"] = entity.Id,
|
|
141
|
+
["entityName"] = entity.Name,
|
|
142
|
+
["userEmail"] = user.Email,
|
|
143
|
+
},
|
|
144
|
+
language: "fr");
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
---
|
|
148
|
+
|
|
149
|
+
## TYPES DE STEPS
|
|
150
|
+
|
|
151
|
+
### 1. SendEmail
|
|
152
|
+
|
|
153
|
+
Envoie un email en utilisant un template.
|
|
154
|
+
|
|
155
|
+
```csharp
|
|
156
|
+
// Creation via Factory Method
|
|
157
|
+
var step = WorkflowStep.CreateEmailStep(
|
|
158
|
+
name: "Welcome Email",
|
|
159
|
+
emailTemplateId: welcomeTemplateId,
|
|
160
|
+
order: 1);
|
|
161
|
+
|
|
162
|
+
// Configuration JSON (si specifique)
|
|
163
|
+
{
|
|
164
|
+
"templateId": "guid-template",
|
|
165
|
+
"recipientVariable": "userEmail", // Variable contenant l'email
|
|
166
|
+
"ccEmails": ["admin@company.com"], // CC optionnels
|
|
167
|
+
"bccEmails": []
|
|
168
|
+
}
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
**Variables automatiques:**
|
|
172
|
+
- Toutes les variables du trigger sont injectees dans le template
|
|
173
|
+
- `{{userName}}`, `{{userEmail}}`, `{{ticketNumber}}`, etc.
|
|
174
|
+
|
|
175
|
+
### 2. Wait
|
|
176
|
+
|
|
177
|
+
Ajoute un delai avant le step suivant.
|
|
178
|
+
|
|
179
|
+
```csharp
|
|
180
|
+
// Creation
|
|
181
|
+
var step = WorkflowStep.CreateWaitStep(
|
|
182
|
+
name: "Wait 24 hours",
|
|
183
|
+
delayMinutes: 24 * 60, // 1440 minutes
|
|
184
|
+
order: 2);
|
|
185
|
+
```
|
|
186
|
+
|
|
187
|
+
**Cas d'usage:**
|
|
188
|
+
- Rappel apres X heures sans reponse
|
|
189
|
+
- Sequence d'onboarding espacee
|
|
190
|
+
- Delai avant escalade
|
|
191
|
+
|
|
192
|
+
### 3. Condition
|
|
193
|
+
|
|
194
|
+
Branche conditionnelle basee sur les variables.
|
|
195
|
+
|
|
196
|
+
```csharp
|
|
197
|
+
// Creation
|
|
198
|
+
var step = WorkflowStep.CreateConditionStep(
|
|
199
|
+
name: "Check if premium user",
|
|
200
|
+
configurationJson: JsonSerializer.Serialize(new
|
|
201
|
+
{
|
|
202
|
+
condition = "{{userType}} == 'Premium'",
|
|
203
|
+
trueStepOrder = 3, // Si vrai, aller au step 3
|
|
204
|
+
falseStepOrder = 4 // Si faux, aller au step 4
|
|
205
|
+
}),
|
|
206
|
+
order: 2);
|
|
207
|
+
```
|
|
208
|
+
|
|
209
|
+
**Operateurs supportes:**
|
|
210
|
+
- `==`, `!=` (egalite)
|
|
211
|
+
- `>`, `<`, `>=`, `<=` (comparaison)
|
|
212
|
+
- `contains`, `startsWith`, `endsWith` (strings)
|
|
213
|
+
- `&&`, `||` (logique)
|
|
214
|
+
|
|
215
|
+
### 4. Webhook
|
|
216
|
+
|
|
217
|
+
Appelle une API externe.
|
|
218
|
+
|
|
219
|
+
```csharp
|
|
220
|
+
// Creation
|
|
221
|
+
var step = WorkflowStep.CreateWebhookStep(
|
|
222
|
+
name: "Notify CRM",
|
|
223
|
+
configurationJson: JsonSerializer.Serialize(new
|
|
224
|
+
{
|
|
225
|
+
url = "https://crm.example.com/api/events",
|
|
226
|
+
method = "POST",
|
|
227
|
+
headers = new Dictionary<string, string>
|
|
228
|
+
{
|
|
229
|
+
["Authorization"] = "Bearer {{crmApiKey}}",
|
|
230
|
+
["Content-Type"] = "application/json"
|
|
231
|
+
},
|
|
232
|
+
body = new
|
|
233
|
+
{
|
|
234
|
+
eventType = "user.registered",
|
|
235
|
+
userId = "{{userId}}",
|
|
236
|
+
email = "{{userEmail}}"
|
|
237
|
+
},
|
|
238
|
+
retryCount = 3,
|
|
239
|
+
timeoutSeconds = 30
|
|
240
|
+
}),
|
|
241
|
+
order: 3);
|
|
242
|
+
```
|
|
243
|
+
|
|
244
|
+
---
|
|
245
|
+
|
|
246
|
+
## WORKFLOW CREATION
|
|
247
|
+
|
|
248
|
+
### ETAPE 1: Definir le Trigger
|
|
249
|
+
|
|
250
|
+
```csharp
|
|
251
|
+
// Choisir un trigger existant ou en creer un nouveau
|
|
252
|
+
var triggerId = existingTriggerId; // ex: user.registered trigger ID
|
|
253
|
+
```
|
|
254
|
+
|
|
255
|
+
### ETAPE 2: Creer le Workflow
|
|
256
|
+
|
|
257
|
+
```csharp
|
|
258
|
+
// Domain/Communications/Workflow.cs via Factory Method
|
|
259
|
+
var workflow = Workflow.Create(
|
|
260
|
+
code: "welcome-sequence",
|
|
261
|
+
name: "Welcome Email Sequence",
|
|
262
|
+
description: "Sends welcome emails after registration",
|
|
263
|
+
triggerId: triggerId,
|
|
264
|
+
applicationId: null, // null = global
|
|
265
|
+
isSystem: false,
|
|
266
|
+
priority: 10); // Plus haut = execute en premier
|
|
267
|
+
```
|
|
268
|
+
|
|
269
|
+
### ETAPE 3: Ajouter les Steps
|
|
270
|
+
|
|
271
|
+
```csharp
|
|
272
|
+
// Step 1: Email de bienvenue immediat
|
|
273
|
+
workflow.AddStep(WorkflowStep.CreateEmailStep(
|
|
274
|
+
name: "Welcome Email",
|
|
275
|
+
emailTemplateId: welcomeTemplateId,
|
|
276
|
+
order: 1));
|
|
277
|
+
|
|
278
|
+
// Step 2: Attendre 24h
|
|
279
|
+
workflow.AddStep(WorkflowStep.CreateWaitStep(
|
|
280
|
+
name: "Wait 24h",
|
|
281
|
+
delayMinutes: 1440,
|
|
282
|
+
order: 2));
|
|
283
|
+
|
|
284
|
+
// Step 3: Email de suivi
|
|
285
|
+
workflow.AddStep(WorkflowStep.CreateEmailStep(
|
|
286
|
+
name: "Follow-up Email",
|
|
287
|
+
emailTemplateId: followUpTemplateId,
|
|
288
|
+
order: 3));
|
|
289
|
+
```
|
|
290
|
+
|
|
291
|
+
### ETAPE 4: Seed Data (Infrastructure)
|
|
292
|
+
|
|
293
|
+
```csharp
|
|
294
|
+
// Infrastructure/Persistence/Configurations/Communications/WorkflowConfiguration.cs
|
|
295
|
+
|
|
296
|
+
public void Configure(EntityTypeBuilder<Workflow> builder)
|
|
297
|
+
{
|
|
298
|
+
// ... configuration ...
|
|
299
|
+
|
|
300
|
+
builder.HasData(GetSeedData());
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
private static object[] GetSeedData()
|
|
304
|
+
{
|
|
305
|
+
var seedDate = new DateTime(2024, 1, 1, 0, 0, 0, DateTimeKind.Utc);
|
|
306
|
+
|
|
307
|
+
return new object[]
|
|
308
|
+
{
|
|
309
|
+
new
|
|
310
|
+
{
|
|
311
|
+
Id = Guid.Parse("WORKFLOW-GUID"),
|
|
312
|
+
Code = "welcome-sequence",
|
|
313
|
+
Name = "Welcome Email Sequence",
|
|
314
|
+
Description = "Sends welcome emails after registration",
|
|
315
|
+
TriggerId = Guid.Parse("TRIGGER-GUID"),
|
|
316
|
+
ApplicationId = (Guid?)null,
|
|
317
|
+
IsActive = true,
|
|
318
|
+
IsSystem = true,
|
|
319
|
+
Priority = 10,
|
|
320
|
+
CreatedAt = seedDate
|
|
321
|
+
}
|
|
322
|
+
};
|
|
323
|
+
}
|
|
324
|
+
```
|
|
325
|
+
|
|
326
|
+
---
|
|
327
|
+
|
|
328
|
+
## DECLENCHEMENT WORKFLOW
|
|
329
|
+
|
|
330
|
+
### Dans un Service
|
|
331
|
+
|
|
332
|
+
```csharp
|
|
333
|
+
public class UserService : IUserService
|
|
334
|
+
{
|
|
335
|
+
private readonly IWorkflowService _workflowService;
|
|
336
|
+
|
|
337
|
+
public async Task<Result> RegisterAsync(RegisterCommand command, CancellationToken ct)
|
|
338
|
+
{
|
|
339
|
+
// ... creation utilisateur ...
|
|
340
|
+
|
|
341
|
+
// Declencher le workflow
|
|
342
|
+
await _workflowService.TriggerAsync(
|
|
343
|
+
"user.registered",
|
|
344
|
+
new Dictionary<string, object>
|
|
345
|
+
{
|
|
346
|
+
["userId"] = user.Id,
|
|
347
|
+
["email"] = user.Email,
|
|
348
|
+
["displayName"] = user.DisplayName,
|
|
349
|
+
["confirmUrl"] = GenerateConfirmUrl(user.Id)
|
|
350
|
+
},
|
|
351
|
+
language: "fr",
|
|
352
|
+
cancellationToken: ct);
|
|
353
|
+
|
|
354
|
+
return Result.Success();
|
|
355
|
+
}
|
|
356
|
+
}
|
|
357
|
+
```
|
|
358
|
+
|
|
359
|
+
### Dans un Controller
|
|
360
|
+
|
|
361
|
+
```csharp
|
|
362
|
+
[HttpPost("tickets")]
|
|
363
|
+
public async Task<ActionResult> CreateTicket(CreateTicketRequest request, CancellationToken ct)
|
|
364
|
+
{
|
|
365
|
+
var ticket = await _ticketService.CreateAsync(request, ct);
|
|
366
|
+
|
|
367
|
+
// Declencher workflow
|
|
368
|
+
await _workflowService.TriggerAsync(
|
|
369
|
+
"ticket.created",
|
|
370
|
+
new Dictionary<string, object>
|
|
371
|
+
{
|
|
372
|
+
["ticketId"] = ticket.Id,
|
|
373
|
+
["ticketNumber"] = ticket.Number,
|
|
374
|
+
["title"] = ticket.Title,
|
|
375
|
+
["creatorEmail"] = _currentUser.Email,
|
|
376
|
+
["creatorName"] = _currentUser.DisplayName
|
|
377
|
+
});
|
|
378
|
+
|
|
379
|
+
return CreatedAtAction(nameof(GetById), new { id = ticket.Id }, ticket);
|
|
380
|
+
}
|
|
381
|
+
```
|
|
382
|
+
|
|
383
|
+
---
|
|
384
|
+
|
|
385
|
+
## EMAIL TEMPLATES
|
|
386
|
+
|
|
387
|
+
### Structure Template
|
|
388
|
+
|
|
389
|
+
```csharp
|
|
390
|
+
// Domain/Communications/EmailTemplate.cs
|
|
391
|
+
public class EmailTemplate : BaseEntity
|
|
392
|
+
{
|
|
393
|
+
public string Code { get; private set; } // "welcome-email"
|
|
394
|
+
public string Name { get; private set; } // "Welcome Email"
|
|
395
|
+
public string Category { get; private set; } // "Transactional"
|
|
396
|
+
public bool IsActive { get; private set; }
|
|
397
|
+
public bool IsSystem { get; private set; }
|
|
398
|
+
|
|
399
|
+
// Translations (multi-langue)
|
|
400
|
+
public ICollection<EmailTemplateTranslation> Translations { get; }
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
public class EmailTemplateTranslation
|
|
404
|
+
{
|
|
405
|
+
public string LanguageCode { get; set; } // "fr", "en"
|
|
406
|
+
public string Subject { get; set; } // "Bienvenue {{userName}}"
|
|
407
|
+
public string HtmlBody { get; set; } // HTML avec variables
|
|
408
|
+
public string TextBody { get; set; } // Version texte
|
|
409
|
+
}
|
|
410
|
+
```
|
|
411
|
+
|
|
412
|
+
### Variables dans Templates
|
|
413
|
+
|
|
414
|
+
```html
|
|
415
|
+
<!-- HtmlBody avec Handlebars-like syntax -->
|
|
416
|
+
<h1>Bienvenue {{userName}}!</h1>
|
|
417
|
+
<p>Votre compte a ete cree avec succes.</p>
|
|
418
|
+
<p>Email: {{userEmail}}</p>
|
|
419
|
+
<a href="{{confirmUrl}}">Confirmer votre email</a>
|
|
420
|
+
|
|
421
|
+
<!-- Conditions -->
|
|
422
|
+
{{#if isPremium}}
|
|
423
|
+
<p>Merci pour votre abonnement Premium!</p>
|
|
424
|
+
{{else}}
|
|
425
|
+
<p>Decouvrez nos offres Premium.</p>
|
|
426
|
+
{{/if}}
|
|
427
|
+
|
|
428
|
+
<!-- Boucles -->
|
|
429
|
+
{{#each items}}
|
|
430
|
+
<li>{{this.name}} - {{this.price}}</li>
|
|
431
|
+
{{/each}}
|
|
432
|
+
```
|
|
433
|
+
|
|
434
|
+
---
|
|
435
|
+
|
|
436
|
+
## FRONTEND WORKFLOW EDITOR
|
|
437
|
+
|
|
438
|
+
### API Workflows
|
|
439
|
+
|
|
440
|
+
```typescript
|
|
441
|
+
// services/api/workflowsApi.ts
|
|
442
|
+
export const workflowsApi = {
|
|
443
|
+
// CRUD Workflows
|
|
444
|
+
getAll: () => apiClient.get<WorkflowDto[]>('/admin/workflows'),
|
|
445
|
+
getById: (id: string) => apiClient.get<WorkflowDto>(`/admin/workflows/${id}`),
|
|
446
|
+
create: (data: CreateWorkflowRequest) => apiClient.post('/admin/workflows', data),
|
|
447
|
+
update: (id: string, data: UpdateWorkflowRequest) => apiClient.put(`/admin/workflows/${id}`, data),
|
|
448
|
+
delete: (id: string) => apiClient.delete(`/admin/workflows/${id}`),
|
|
449
|
+
|
|
450
|
+
// Triggers
|
|
451
|
+
getTriggers: () => apiClient.get<WorkflowTriggerDto[]>('/admin/workflows/triggers'),
|
|
452
|
+
|
|
453
|
+
// Steps
|
|
454
|
+
addStep: (workflowId: string, step: CreateStepRequest) =>
|
|
455
|
+
apiClient.post(`/admin/workflows/${workflowId}/steps`, step),
|
|
456
|
+
updateStep: (workflowId: string, stepId: string, step: UpdateStepRequest) =>
|
|
457
|
+
apiClient.put(`/admin/workflows/${workflowId}/steps/${stepId}`, step),
|
|
458
|
+
deleteStep: (workflowId: string, stepId: string) =>
|
|
459
|
+
apiClient.delete(`/admin/workflows/${workflowId}/steps/${stepId}`),
|
|
460
|
+
reorderSteps: (workflowId: string, stepIds: string[]) =>
|
|
461
|
+
apiClient.post(`/admin/workflows/${workflowId}/steps/reorder`, { stepIds }),
|
|
462
|
+
|
|
463
|
+
// Execution
|
|
464
|
+
execute: (workflowId: string, variables: Record<string, unknown>) =>
|
|
465
|
+
apiClient.post(`/admin/workflows/${workflowId}/execute`, { variables }),
|
|
466
|
+
};
|
|
467
|
+
```
|
|
468
|
+
|
|
469
|
+
### Composant WorkflowStepsEditor
|
|
470
|
+
|
|
471
|
+
```tsx
|
|
472
|
+
// components/admin/workflows/WorkflowStepsEditor.tsx
|
|
473
|
+
import { Mail, Clock, GitBranch, Webhook, Plus, GripVertical, Trash2 } from 'lucide-react';
|
|
474
|
+
|
|
475
|
+
const stepTypeConfig = {
|
|
476
|
+
SendEmail: { icon: Mail, color: 'blue', label: 'Email' },
|
|
477
|
+
Wait: { icon: Clock, color: 'amber', label: 'Delai' },
|
|
478
|
+
Condition: { icon: GitBranch, color: 'purple', label: 'Condition' },
|
|
479
|
+
Webhook: { icon: Webhook, color: 'green', label: 'Webhook' },
|
|
480
|
+
};
|
|
481
|
+
|
|
482
|
+
export function WorkflowStepsEditor({ workflowId, steps, onUpdate }) {
|
|
483
|
+
// Drag and drop pour reordonner
|
|
484
|
+
// Modal pour editer chaque step
|
|
485
|
+
// Bouton pour ajouter un step
|
|
486
|
+
|
|
487
|
+
return (
|
|
488
|
+
<div className="space-y-4">
|
|
489
|
+
{steps.map((step, index) => (
|
|
490
|
+
<WorkflowStepCard
|
|
491
|
+
key={step.id}
|
|
492
|
+
step={step}
|
|
493
|
+
index={index}
|
|
494
|
+
onEdit={() => openEditModal(step)}
|
|
495
|
+
onDelete={() => handleDelete(step.id)}
|
|
496
|
+
/>
|
|
497
|
+
))}
|
|
498
|
+
|
|
499
|
+
<button
|
|
500
|
+
onClick={() => openAddModal()}
|
|
501
|
+
className="w-full p-4 border-2 border-dashed rounded-lg"
|
|
502
|
+
>
|
|
503
|
+
<Plus className="w-5 h-5 mr-2" />
|
|
504
|
+
Ajouter une etape
|
|
505
|
+
</button>
|
|
506
|
+
</div>
|
|
507
|
+
);
|
|
508
|
+
}
|
|
509
|
+
```
|
|
510
|
+
|
|
511
|
+
---
|
|
512
|
+
|
|
513
|
+
## WORKFLOWS SYSTEME (SEED)
|
|
514
|
+
|
|
515
|
+
| Code | Trigger | Description |
|
|
516
|
+
|------|---------|-------------|
|
|
517
|
+
| `email-confirmation` | `user.registered` | Email de confirmation apres inscription |
|
|
518
|
+
| `welcome-email` | `user.email-confirmed` | Email de bienvenue apres confirmation |
|
|
519
|
+
| `password-reset` | `user.password-reset-requested` | Email avec lien de reset |
|
|
520
|
+
| `account-unlocked-notification` | `user.account-unlocked` | Notification compte debloque |
|
|
521
|
+
| `user-onboarding` | `user.onboarding-completed` | Email fin d'onboarding |
|
|
522
|
+
| `admin-password-reset` | `user.admin-password-reset` | Reset admin via lien |
|
|
523
|
+
| `admin-password-reset-direct` | `user.admin-password-reset-direct` | Reset admin direct |
|
|
524
|
+
|
|
525
|
+
---
|
|
526
|
+
|
|
527
|
+
## CHECKLIST CREATION WORKFLOW
|
|
528
|
+
|
|
529
|
+
```
|
|
530
|
+
□ Trigger identifie (existant ou nouveau)
|
|
531
|
+
□ Si nouveau trigger:
|
|
532
|
+
□ Ajoute dans WorkflowTriggerConfiguration.cs seed
|
|
533
|
+
□ Variables definies (AvailableVariablesJson)
|
|
534
|
+
□ Migration EF Core creee
|
|
535
|
+
□ Workflow cree avec:
|
|
536
|
+
□ Code unique (kebab-case)
|
|
537
|
+
□ Nom descriptif
|
|
538
|
+
□ Trigger lie
|
|
539
|
+
□ Priority definie
|
|
540
|
+
□ Steps configures:
|
|
541
|
+
□ SendEmail avec templateId valide
|
|
542
|
+
□ Wait avec delayMinutes
|
|
543
|
+
□ Condition avec expression valide
|
|
544
|
+
□ Webhook avec URL et payload
|
|
545
|
+
□ Email templates crees (si SendEmail steps)
|
|
546
|
+
□ Declenchement ajoute dans le code source
|
|
547
|
+
□ Tests:
|
|
548
|
+
□ Trigger declenche le workflow
|
|
549
|
+
□ Emails envoyes correctement
|
|
550
|
+
□ Variables substituees
|
|
551
|
+
□ Migration EF Core si seed data modifie
|
|
552
|
+
```
|
|
553
|
+
|
|
554
|
+
---
|
|
555
|
+
|
|
556
|
+
## REGLES ABSOLUES
|
|
557
|
+
|
|
558
|
+
1. **TOUJOURS** utiliser IWorkflowService.TriggerAsync (jamais execution directe)
|
|
559
|
+
2. **TOUJOURS** fournir toutes les variables attendues par le trigger
|
|
560
|
+
3. **TOUJOURS** specifier la langue pour les emails
|
|
561
|
+
4. **TOUJOURS** utiliser des codes kebab-case uniques
|
|
562
|
+
5. **TOUJOURS** definir une priority (10 = standard, 20+ = prioritaire)
|
|
563
|
+
6. **TOUJOURS** logger les executions de workflow
|
|
564
|
+
7. **JAMAIS** hardcoder les URLs dans les templates
|
|
565
|
+
8. **JAMAIS** exposer des secrets dans les variables
|
|
566
|
+
9. **JAMAIS** creer des boucles infinies (workflow A → trigger B → workflow A)
|
|
567
|
+
10. **JAMAIS** oublier les email templates pour les steps SendEmail
|
|
568
|
+
|
|
569
|
+
---
|
|
570
|
+
|
|
571
|
+
## FICHIERS CLES
|
|
572
|
+
|
|
573
|
+
| Fichier | Role |
|
|
574
|
+
|---------|------|
|
|
575
|
+
| `Domain/Communications/Workflow.cs` | Entite workflow |
|
|
576
|
+
| `Domain/Communications/WorkflowStep.cs` | Entite step |
|
|
577
|
+
| `Domain/Communications/WorkflowTrigger.cs` | Entite trigger |
|
|
578
|
+
| `Domain/Communications/Enums/WorkflowStepType.cs` | Types de steps |
|
|
579
|
+
| `Application/Common/Interfaces/IWorkflowService.cs` | Interface service |
|
|
580
|
+
| `Infrastructure/Services/Workflow/WorkflowExecutionService.cs` | Implementation |
|
|
581
|
+
| `Infrastructure/Persistence/Configurations/Communications/WorkflowConfiguration.cs` | EF Config + Seed |
|
|
582
|
+
| `web/src/components/admin/workflows/WorkflowStepsEditor.tsx` | UI Editor |
|