@atlashub/smartstack-cli 1.11.0 → 1.13.1
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 +7 -2
- package/.documentation/apex.html +7 -2
- package/.documentation/business-analyse.html +7 -2
- package/.documentation/cli-commands.html +871 -0
- package/.documentation/commands.html +7 -2
- package/.documentation/efcore.html +7 -2
- package/.documentation/gitflow.html +7 -2
- package/.documentation/hooks.html +7 -2
- package/.documentation/index.html +7 -2
- package/.documentation/init.html +7 -2
- package/.documentation/installation.html +7 -2
- package/.documentation/ralph-loop.html +7 -2
- package/.documentation/test-web.html +7 -2
- package/dist/index.js +1932 -336
- package/dist/index.js.map +1 -1
- package/package.json +8 -2
- package/templates/agents/efcore/squash.md +67 -31
- package/templates/agents/gitflow/finish.md +68 -56
- package/templates/commands/business-analyse/0-orchestrate.md +72 -556
- package/templates/commands/business-analyse/1-init.md +23 -193
- package/templates/commands/business-analyse/2-discover.md +85 -462
- package/templates/commands/business-analyse/3-analyse.md +40 -342
- package/templates/commands/business-analyse/4-specify.md +72 -537
- package/templates/commands/business-analyse/5-validate.md +43 -237
- package/templates/commands/business-analyse/6-handoff.md +93 -682
- package/templates/commands/business-analyse/7-doc-html.md +45 -544
- package/templates/commands/business-analyse/_shared.md +176 -0
- package/templates/commands/business-analyse/bug.md +50 -257
- package/templates/commands/business-analyse/change-request.md +59 -283
- package/templates/commands/business-analyse/hotfix.md +36 -120
- package/templates/commands/business-analyse.md +55 -574
- package/templates/commands/efcore/_shared.md +206 -0
- package/templates/commands/efcore/conflicts.md +39 -201
- package/templates/commands/efcore/db-deploy.md +28 -237
- package/templates/commands/efcore/db-reset.md +41 -390
- package/templates/commands/efcore/db-seed.md +44 -323
- package/templates/commands/efcore/db-status.md +31 -210
- package/templates/commands/efcore/migration.md +45 -368
- package/templates/commands/efcore/rebase-snapshot.md +38 -241
- package/templates/commands/efcore/scan.md +35 -204
- package/templates/commands/efcore/squash.md +158 -251
- package/templates/commands/efcore.md +49 -177
- package/templates/commands/gitflow/1-init.md +94 -1318
- package/templates/commands/gitflow/10-start.md +86 -990
- package/templates/commands/gitflow/11-finish.md +264 -454
- package/templates/commands/gitflow/12-cleanup.md +40 -213
- package/templates/commands/gitflow/2-status.md +51 -386
- package/templates/commands/gitflow/3-commit.md +108 -801
- package/templates/commands/gitflow/4-plan.md +42 -13
- package/templates/commands/gitflow/5-exec.md +60 -5
- package/templates/commands/gitflow/6-abort.md +54 -277
- package/templates/commands/gitflow/7-pull-request.md +74 -717
- package/templates/commands/gitflow/8-review.md +51 -178
- package/templates/commands/gitflow/9-merge.md +74 -404
- package/templates/commands/gitflow/_shared.md +196 -0
- package/templates/commands/quickstart.md +154 -0
- package/templates/commands/ralph-loop/ralph-loop.md +104 -2
- package/templates/hooks/hooks.json +13 -0
- package/templates/hooks/ralph-mcp-logger.sh +46 -0
- package/templates/hooks/ralph-session-end.sh +69 -0
- package/templates/ralph/README.md +91 -0
- package/templates/ralph/ralph.config.yaml +113 -0
- package/templates/scripts/setup-ralph-loop.sh +173 -0
- package/templates/skills/_shared.md +117 -0
- package/templates/skills/ai-prompt/SKILL.md +87 -654
- package/templates/skills/application/SKILL.md +76 -499
- package/templates/skills/controller/SKILL.md +38 -165
- package/templates/skills/documentation/SKILL.md +2 -1
- package/templates/skills/feature-full/SKILL.md +107 -732
- package/templates/skills/notification/SKILL.md +85 -474
- package/templates/skills/ui-components/SKILL.md +62 -762
- package/templates/skills/workflow/SKILL.md +85 -489
- package/templates/commands/gitflow/rescue.md +0 -867
- package/templates/skills/business-analyse/SKILL.md +0 -191
|
@@ -12,561 +12,159 @@ description: |
|
|
|
12
12
|
|
|
13
13
|
# Skill Workflow SmartStack
|
|
14
14
|
|
|
15
|
-
> **Architecture:**
|
|
16
|
-
> Les workflows sont executes automatiquement lors d'evenements applicatifs.
|
|
15
|
+
> **Architecture:** Trigger → Steps → Actions (Email/Wait/Condition/Webhook)
|
|
17
16
|
|
|
18
|
-
|
|
17
|
+
**Référence:** [_shared.md](../_shared.md) pour services communs
|
|
19
18
|
|
|
20
|
-
|
|
19
|
+
## QUAND CE SKILL S'ACTIVE
|
|
21
20
|
|
|
22
21
|
| Declencheur | Exemple |
|
|
23
22
|
|-------------|---------|
|
|
24
23
|
| Demande explicite | "Cree un workflow pour l'inscription" |
|
|
25
24
|
| Email automatique | "Envoie un email quand un ticket est cree" |
|
|
26
|
-
| Automatisation | "Automatise le processus d'onboarding" |
|
|
27
25
|
| Enchainement | "Apres 24h sans reponse, envoyer un rappel" |
|
|
28
26
|
| Mots-cles | "workflow", "trigger", "automatisation", "email template" |
|
|
29
27
|
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
## ARCHITECTURE WORKFLOW
|
|
28
|
+
## FLOW
|
|
33
29
|
|
|
34
30
|
```
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
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
|
-
└─────────────────────────────────────────────────────────────────────────────┘
|
|
31
|
+
EVENT → WorkflowTrigger("user.registered")
|
|
32
|
+
↓
|
|
33
|
+
IWorkflowService.TriggerAsync(code, variables)
|
|
34
|
+
↓
|
|
35
|
+
Workflow Steps: SendEmail → Wait → Condition → Webhook
|
|
81
36
|
```
|
|
82
37
|
|
|
83
|
-
---
|
|
84
|
-
|
|
85
38
|
## TRIGGERS DISPONIBLES
|
|
86
39
|
|
|
87
40
|
### User Events
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
| `user.
|
|
92
|
-
| `user.
|
|
93
|
-
| `user.
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
|
97
|
-
|
|
98
|
-
| `
|
|
99
|
-
| `
|
|
100
|
-
|
|
101
|
-
|
|
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
|
-
|
|
41
|
+
| Trigger | Variables |
|
|
42
|
+
|---------|-----------|
|
|
43
|
+
| `user.registered` | userId, email, displayName, confirmUrl |
|
|
44
|
+
| `user.email-confirmed` | userId, email, displayName |
|
|
45
|
+
| `user.password-reset-requested` | userId, email, resetUrl |
|
|
46
|
+
| `user.account-locked/unlocked` | userId, email, lockReason |
|
|
47
|
+
|
|
48
|
+
### Ticket Events
|
|
49
|
+
| Trigger | Variables |
|
|
50
|
+
|---------|-----------|
|
|
51
|
+
| `ticket.created` | ticketId, ticketNumber, title, creatorEmail |
|
|
52
|
+
| `ticket.resolved/assigned` | ticketId, ticketNumber, assigneeName |
|
|
53
|
+
| `ticket.sla-warning/breached` | ticketId, deadline, remainingMinutes |
|
|
54
|
+
|
|
55
|
+
### Ajouter un Trigger
|
|
114
56
|
```csharp
|
|
115
|
-
//
|
|
116
|
-
|
|
57
|
+
// WorkflowTriggerConfiguration.cs - GetSeedData()
|
|
58
|
+
new { Id = Guid.Parse("..."), Code = "entity.event", Name = "Entity Event",
|
|
59
|
+
AvailableVariablesJson = JsonSerializer.Serialize(new[]{ ... }), ... }
|
|
117
60
|
|
|
118
|
-
|
|
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");
|
|
61
|
+
// Declenchement
|
|
62
|
+
await _workflowService.TriggerAsync("entity.event", new Dictionary<string, object>{...}, language: "fr");
|
|
145
63
|
```
|
|
146
64
|
|
|
147
|
-
---
|
|
148
|
-
|
|
149
65
|
## TYPES DE STEPS
|
|
150
66
|
|
|
151
67
|
### 1. SendEmail
|
|
152
|
-
|
|
153
|
-
Envoie un email en utilisant un template.
|
|
154
|
-
|
|
155
68
|
```csharp
|
|
156
|
-
|
|
157
|
-
|
|
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
|
-
}
|
|
69
|
+
WorkflowStep.CreateEmailStep("Welcome Email", emailTemplateId, order: 1);
|
|
70
|
+
// Config: templateId, recipientVariable, ccEmails
|
|
169
71
|
```
|
|
170
72
|
|
|
171
|
-
**Variables automatiques:**
|
|
172
|
-
- Toutes les variables du trigger sont injectees dans le template
|
|
173
|
-
- `{{userName}}`, `{{userEmail}}`, `{{ticketNumber}}`, etc.
|
|
174
|
-
|
|
175
73
|
### 2. Wait
|
|
176
|
-
|
|
177
|
-
Ajoute un delai avant le step suivant.
|
|
178
|
-
|
|
179
74
|
```csharp
|
|
180
|
-
|
|
181
|
-
var step = WorkflowStep.CreateWaitStep(
|
|
182
|
-
name: "Wait 24 hours",
|
|
183
|
-
delayMinutes: 24 * 60, // 1440 minutes
|
|
184
|
-
order: 2);
|
|
75
|
+
WorkflowStep.CreateWaitStep("Wait 24h", delayMinutes: 1440, order: 2);
|
|
185
76
|
```
|
|
186
77
|
|
|
187
|
-
**Cas d'usage:**
|
|
188
|
-
- Rappel apres X heures sans reponse
|
|
189
|
-
- Sequence d'onboarding espacee
|
|
190
|
-
- Delai avant escalade
|
|
191
|
-
|
|
192
78
|
### 3. Condition
|
|
193
|
-
|
|
194
|
-
Branche conditionnelle basee sur les variables.
|
|
195
|
-
|
|
196
79
|
```csharp
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
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);
|
|
80
|
+
WorkflowStep.CreateConditionStep("Check premium", JsonSerializer.Serialize(new {
|
|
81
|
+
condition = "{{userType}} == 'Premium'",
|
|
82
|
+
trueStepOrder = 3, falseStepOrder = 4
|
|
83
|
+
}), order: 2);
|
|
84
|
+
// Operateurs: ==, !=, >, <, contains, &&, ||
|
|
207
85
|
```
|
|
208
86
|
|
|
209
|
-
**Operateurs supportes:**
|
|
210
|
-
- `==`, `!=` (egalite)
|
|
211
|
-
- `>`, `<`, `>=`, `<=` (comparaison)
|
|
212
|
-
- `contains`, `startsWith`, `endsWith` (strings)
|
|
213
|
-
- `&&`, `||` (logique)
|
|
214
|
-
|
|
215
87
|
### 4. Webhook
|
|
216
|
-
|
|
217
|
-
Appelle une API externe.
|
|
218
|
-
|
|
219
88
|
```csharp
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
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);
|
|
89
|
+
WorkflowStep.CreateWebhookStep("Notify CRM", JsonSerializer.Serialize(new {
|
|
90
|
+
url = "https://...", method = "POST",
|
|
91
|
+
headers = new { Authorization = "Bearer {{apiKey}}" },
|
|
92
|
+
body = new { eventType = "...", userId = "{{userId}}" },
|
|
93
|
+
retryCount = 3
|
|
94
|
+
}), order: 3);
|
|
242
95
|
```
|
|
243
96
|
|
|
244
|
-
---
|
|
245
|
-
|
|
246
97
|
## WORKFLOW CREATION
|
|
247
98
|
|
|
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
99
|
```csharp
|
|
258
|
-
//
|
|
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
|
-
```
|
|
100
|
+
// 1. Creer workflow
|
|
101
|
+
var workflow = Workflow.Create("welcome-sequence", "Welcome Sequence", description, triggerId, null, false, 10);
|
|
268
102
|
|
|
269
|
-
|
|
103
|
+
// 2. Ajouter steps
|
|
104
|
+
workflow.AddStep(WorkflowStep.CreateEmailStep("Welcome", templateId, 1));
|
|
105
|
+
workflow.AddStep(WorkflowStep.CreateWaitStep("Wait 24h", 1440, 2));
|
|
106
|
+
workflow.AddStep(WorkflowStep.CreateEmailStep("Follow-up", followUpTemplateId, 3));
|
|
270
107
|
|
|
271
|
-
|
|
272
|
-
|
|
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));
|
|
108
|
+
// 3. Seed data (WorkflowConfiguration.cs)
|
|
109
|
+
builder.HasData(new { Id = ..., Code = "welcome-sequence", TriggerId = ..., IsActive = true, ... });
|
|
289
110
|
```
|
|
290
111
|
|
|
291
|
-
|
|
112
|
+
## DECLENCHEMENT
|
|
292
113
|
|
|
293
114
|
```csharp
|
|
294
|
-
//
|
|
295
|
-
|
|
296
|
-
public void Configure(EntityTypeBuilder<Workflow> builder)
|
|
297
|
-
{
|
|
298
|
-
// ... configuration ...
|
|
299
|
-
|
|
300
|
-
builder.HasData(GetSeedData());
|
|
301
|
-
}
|
|
302
|
-
|
|
303
|
-
private static object[] GetSeedData()
|
|
115
|
+
// Dans Service ou Controller
|
|
116
|
+
await _workflowService.TriggerAsync("user.registered", new Dictionary<string, object>
|
|
304
117
|
{
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
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
|
-
}
|
|
118
|
+
["userId"] = user.Id,
|
|
119
|
+
["email"] = user.Email,
|
|
120
|
+
["confirmUrl"] = GenerateConfirmUrl(user.Id)
|
|
121
|
+
}, language: "fr", ct);
|
|
324
122
|
```
|
|
325
123
|
|
|
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
124
|
## EMAIL TEMPLATES
|
|
386
125
|
|
|
387
|
-
### Structure
|
|
388
|
-
|
|
126
|
+
### Structure
|
|
389
127
|
```csharp
|
|
390
|
-
|
|
391
|
-
|
|
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
|
-
}
|
|
128
|
+
EmailTemplate: Code, Name, Category, IsActive, Translations[]
|
|
129
|
+
EmailTemplateTranslation: LanguageCode, Subject, HtmlBody, TextBody
|
|
410
130
|
```
|
|
411
131
|
|
|
412
|
-
###
|
|
413
|
-
|
|
132
|
+
### Syntaxe (Handlebars)
|
|
414
133
|
```html
|
|
415
|
-
<!-- HtmlBody avec Handlebars-like syntax -->
|
|
416
134
|
<h1>Bienvenue {{userName}}!</h1>
|
|
417
|
-
<p>
|
|
418
|
-
<
|
|
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}}
|
|
135
|
+
{{#if isPremium}}<p>Merci Premium!</p>{{else}}<p>Offres Premium...</p>{{/if}}
|
|
136
|
+
{{#each items}}<li>{{this.name}}</li>{{/each}}
|
|
432
137
|
```
|
|
433
138
|
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
## FRONTEND WORKFLOW EDITOR
|
|
437
|
-
|
|
438
|
-
### API Workflows
|
|
139
|
+
## FRONTEND API
|
|
439
140
|
|
|
440
141
|
```typescript
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
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
|
-
}
|
|
142
|
+
workflowsApi.getAll() / getById(id) / create(data) / update(id, data)
|
|
143
|
+
workflowsApi.getTriggers()
|
|
144
|
+
workflowsApi.addStep(workflowId, step) / updateStep / deleteStep / reorderSteps
|
|
145
|
+
workflowsApi.execute(workflowId, variables)
|
|
509
146
|
```
|
|
510
147
|
|
|
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
|
|
148
|
+
## CHECKLIST
|
|
528
149
|
|
|
529
150
|
```
|
|
530
|
-
□ Trigger identifie (existant ou nouveau)
|
|
531
|
-
□
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
□
|
|
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
|
|
151
|
+
□ Trigger identifie (existant ou nouveau dans WorkflowTriggerConfiguration.cs)
|
|
152
|
+
□ Workflow cree: Code unique, Trigger lie, Priority (10=standard, 20+=prioritaire)
|
|
153
|
+
□ Steps: SendEmail+templateId, Wait+delayMinutes, Condition+expression, Webhook+url
|
|
154
|
+
□ Email templates crees (si SendEmail)
|
|
155
|
+
□ Declenchement ajoute dans code source
|
|
156
|
+
□ Tests: trigger, emails, variables
|
|
552
157
|
```
|
|
553
158
|
|
|
554
|
-
---
|
|
555
|
-
|
|
556
159
|
## REGLES ABSOLUES
|
|
557
160
|
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
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
|
-
---
|
|
161
|
+
| DO | DON'T |
|
|
162
|
+
|----|-------|
|
|
163
|
+
| IWorkflowService.TriggerAsync | Execution directe |
|
|
164
|
+
| Toutes variables du trigger | URLs hardcodees dans templates |
|
|
165
|
+
| Specifier langue pour emails | Secrets dans variables |
|
|
166
|
+
| Codes kebab-case uniques | Boucles infinies (A→B→A) |
|
|
167
|
+
| Logger executions | Oublier email templates |
|
|
570
168
|
|
|
571
169
|
## FICHIERS CLES
|
|
572
170
|
|
|
@@ -575,8 +173,6 @@ export function WorkflowStepsEditor({ workflowId, steps, onUpdate }) {
|
|
|
575
173
|
| `Domain/Communications/Workflow.cs` | Entite workflow |
|
|
576
174
|
| `Domain/Communications/WorkflowStep.cs` | Entite step |
|
|
577
175
|
| `Domain/Communications/WorkflowTrigger.cs` | Entite trigger |
|
|
578
|
-
| `
|
|
579
|
-
| `Application/Common/Interfaces/IWorkflowService.cs` | Interface service |
|
|
176
|
+
| `Application/Common/Interfaces/IWorkflowService.cs` | Interface |
|
|
580
177
|
| `Infrastructure/Services/Workflow/WorkflowExecutionService.cs` | Implementation |
|
|
581
|
-
| `Infrastructure
|
|
582
|
-
| `web/src/components/admin/workflows/WorkflowStepsEditor.tsx` | UI Editor |
|
|
178
|
+
| `Infrastructure/.../WorkflowConfiguration.cs` | EF Config + Seed |
|