@fickou/quasar-workflow 1.0.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/LICENSE +21 -0
- package/README.md +535 -0
- package/dist/composables/useWorkflow.d.ts +108 -0
- package/dist/composables/useWorkflow.js +183 -0
- package/dist/composables/useWorkflow.js.map +1 -0
- package/dist/composables/useWorkflowAdmin.d.ts +51 -0
- package/dist/composables/useWorkflowAdmin.js +140 -0
- package/dist/composables/useWorkflowAdmin.js.map +1 -0
- package/dist/composables/useWorkflowDashboard.d.ts +72 -0
- package/dist/composables/useWorkflowDashboard.js +40 -0
- package/dist/composables/useWorkflowDashboard.js.map +1 -0
- package/dist/composables/useWorkflowDelegation.d.ts +76 -0
- package/dist/composables/useWorkflowDelegation.js +87 -0
- package/dist/composables/useWorkflowDelegation.js.map +1 -0
- package/dist/composables/useWorkflowFieldControl.d.ts +19 -0
- package/dist/composables/useWorkflowFieldControl.js +111 -0
- package/dist/composables/useWorkflowFieldControl.js.map +1 -0
- package/dist/composables/useWorkflowStats.d.ts +21 -0
- package/dist/composables/useWorkflowStats.js +87 -0
- package/dist/composables/useWorkflowStats.js.map +1 -0
- package/dist/composables/useWorkflowWebSocket.d.ts +26 -0
- package/dist/composables/useWorkflowWebSocket.js +140 -0
- package/dist/composables/useWorkflowWebSocket.js.map +1 -0
- package/dist/directives/vWorkflowField.d.ts +2 -0
- package/dist/directives/vWorkflowField.js +59 -0
- package/dist/directives/vWorkflowField.js.map +1 -0
- package/dist/index.d.ts +23 -0
- package/dist/index.js +29 -0
- package/dist/index.js.map +1 -0
- package/dist/plugins/WorkflowPlugin.d.ts +6 -0
- package/dist/plugins/WorkflowPlugin.js +36 -0
- package/dist/plugins/WorkflowPlugin.js.map +1 -0
- package/dist/types/index.d.ts +47 -0
- package/dist/types/index.js +3 -0
- package/dist/types/index.js.map +1 -0
- package/package.json +63 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2024 fickou
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,535 @@
|
|
|
1
|
+
# @fickou/quasar-workflow
|
|
2
|
+
|
|
3
|
+
Composants et composables Vue 3 pour intégrer le moteur de workflow dans une application **Quasar v2**.
|
|
4
|
+
|
|
5
|
+
Dépend uniquement de l'API REST exposée par `adonis-workflow`. Aucune dépendance directe sur le backend.
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## Table des matières
|
|
10
|
+
|
|
11
|
+
1. [Installation](#1-installation)
|
|
12
|
+
2. [Enregistrement du plugin](#2-enregistrement-du-plugin)
|
|
13
|
+
3. [Usage de base — formulaire avec workflow](#3-usage-de-base--formulaire-avec-workflow)
|
|
14
|
+
4. [Directive `v-workflow-field`](#4-directive-v-workflow-field)
|
|
15
|
+
5. [WorkflowActionBar — boutons d'action](#5-workflowactionbar--boutons-daction)
|
|
16
|
+
6. [WorkflowStepper — visualisation](#6-workflowstepper--visualisation)
|
|
17
|
+
7. [WorkflowHistory — journal](#7-workflowhistory--journal)
|
|
18
|
+
8. [useWorkflow — composable principal](#8-useworkflow--composable-principal)
|
|
19
|
+
9. [useWorkflowWebSocket — temps réel](#9-useworkflowwebsocket--temps-réel)
|
|
20
|
+
10. [useWorkflowFieldControl — contrôle des champs](#10-useworkflowfieldcontrol--contrôle-des-champs)
|
|
21
|
+
11. [Composants de suivi et monitoring](#11-composants-de-suivi-et-monitoring)
|
|
22
|
+
12. [Composants d'administration](#12-composants-dadministration)
|
|
23
|
+
13. [useWorkflowDelegation — délégations](#13-useworkflowdelegation--délégations)
|
|
24
|
+
14. [useWorkflowStats — statistiques](#14-useworkflowstats--statistiques)
|
|
25
|
+
15. [Référence des composables](#15-référence-des-composables)
|
|
26
|
+
16. [Tableau formMode / canDelete](#16-tableau-formmode--candelete)
|
|
27
|
+
|
|
28
|
+
---
|
|
29
|
+
|
|
30
|
+
## 1. Installation
|
|
31
|
+
|
|
32
|
+
Ce package s'installe par **copie directe** dans le projet hôte (pas de registry npm).
|
|
33
|
+
|
|
34
|
+
```bash
|
|
35
|
+
# Via npm (recommandé)
|
|
36
|
+
npm install @fickou/quasar-workflow
|
|
37
|
+
|
|
38
|
+
# Ou via le chemin local (monorepo)
|
|
39
|
+
npm install file:packages/quasar-workflow
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
Vérifier que les peer dependencies sont présentes :
|
|
43
|
+
|
|
44
|
+
```json
|
|
45
|
+
"vue": "^3.0.0",
|
|
46
|
+
"quasar": "^2.0.0",
|
|
47
|
+
"axios": "^1.0.0"
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
---
|
|
51
|
+
|
|
52
|
+
## 2. Enregistrement du plugin
|
|
53
|
+
|
|
54
|
+
Créer un fichier boot dédié dans votre projet Quasar :
|
|
55
|
+
|
|
56
|
+
```typescript
|
|
57
|
+
// src/boot/workflow.ts
|
|
58
|
+
import { boot } from 'quasar/wrappers'
|
|
59
|
+
import { WorkflowPlugin } from '@fickou/quasar-workflow'
|
|
60
|
+
|
|
61
|
+
export default boot(({ app }) => {
|
|
62
|
+
app.use(WorkflowPlugin, {
|
|
63
|
+
// URL de base de l'API workflow (le préfixe doit correspondre à routePrefix dans adonis-workflow)
|
|
64
|
+
apiBase: '/api/workflow',
|
|
65
|
+
|
|
66
|
+
// URL du serveur WebSocket AdonisJS (optionnel — uniquement si @adonisjs/websocket est installé)
|
|
67
|
+
wsUrl: 'ws://localhost:3333',
|
|
68
|
+
})
|
|
69
|
+
})
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
Déclarer le boot dans `quasar.config.ts` :
|
|
73
|
+
|
|
74
|
+
```typescript
|
|
75
|
+
// quasar.config.ts
|
|
76
|
+
boot: [
|
|
77
|
+
'axios',
|
|
78
|
+
'workflow', // ← ajouter
|
|
79
|
+
],
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
Le plugin enregistre automatiquement **tous les composants** (`WorkflowFormGuard`, `WorkflowActionBar`, etc.) et la **directive `v-workflow-field`** de façon globale. Aucun import supplémentaire n'est nécessaire dans vos pages.
|
|
83
|
+
|
|
84
|
+
---
|
|
85
|
+
|
|
86
|
+
## 3. Usage de base — formulaire avec workflow
|
|
87
|
+
|
|
88
|
+
Le pattern standard consiste à envelopper votre formulaire dans `WorkflowFormGuard`, qui gère automatiquement le mode du formulaire selon l'état du workflow.
|
|
89
|
+
|
|
90
|
+
```vue
|
|
91
|
+
<!-- src/pages/InvoicePage.vue -->
|
|
92
|
+
<template>
|
|
93
|
+
<q-page padding>
|
|
94
|
+
<WorkflowFormGuard
|
|
95
|
+
entity-type="invoice"
|
|
96
|
+
:entity-id="invoiceId"
|
|
97
|
+
:current-user-id="currentUser.id"
|
|
98
|
+
@action-executed="onActionExecuted"
|
|
99
|
+
>
|
|
100
|
+
<template #default="{ formMode, canEdit, canDelete, canSubmit, canResubmit, hookRejectionReason }">
|
|
101
|
+
|
|
102
|
+
<!-- Alerte si une action a été bloquée par un hook métier -->
|
|
103
|
+
<q-banner v-if="hookRejectionReason" type="warning" class="q-mb-md">
|
|
104
|
+
{{ hookRejectionReason }}
|
|
105
|
+
</q-banner>
|
|
106
|
+
|
|
107
|
+
<!-- Champs du formulaire — v-workflow-field gère readonly/disable automatiquement -->
|
|
108
|
+
<q-input
|
|
109
|
+
v-workflow-field="'amount'"
|
|
110
|
+
v-model="invoice.amount"
|
|
111
|
+
label="Montant"
|
|
112
|
+
type="number"
|
|
113
|
+
/>
|
|
114
|
+
|
|
115
|
+
<q-input
|
|
116
|
+
v-workflow-field="'description'"
|
|
117
|
+
v-model="invoice.description"
|
|
118
|
+
label="Description"
|
|
119
|
+
type="textarea"
|
|
120
|
+
/>
|
|
121
|
+
|
|
122
|
+
<!-- Boutons selon le mode -->
|
|
123
|
+
<div class="row q-gutter-sm q-mt-md">
|
|
124
|
+
<q-btn
|
|
125
|
+
v-if="formMode === 'create'"
|
|
126
|
+
label="Créer la facture"
|
|
127
|
+
color="primary"
|
|
128
|
+
@click="createInvoice"
|
|
129
|
+
/>
|
|
130
|
+
<q-btn
|
|
131
|
+
v-if="canEdit"
|
|
132
|
+
label="Enregistrer"
|
|
133
|
+
color="primary"
|
|
134
|
+
@click="saveInvoice"
|
|
135
|
+
/>
|
|
136
|
+
<q-btn
|
|
137
|
+
v-if="canDelete"
|
|
138
|
+
label="Supprimer"
|
|
139
|
+
color="negative"
|
|
140
|
+
@click="deleteInvoice"
|
|
141
|
+
/>
|
|
142
|
+
</div>
|
|
143
|
+
|
|
144
|
+
<!-- Barre d'actions workflow (boutons approve, reject, etc.) -->
|
|
145
|
+
<WorkflowActionBar
|
|
146
|
+
entity-type="invoice"
|
|
147
|
+
:entity-id="invoiceId"
|
|
148
|
+
class="q-mt-md"
|
|
149
|
+
/>
|
|
150
|
+
|
|
151
|
+
<!-- Visualisation de l'avancement -->
|
|
152
|
+
<WorkflowStepper
|
|
153
|
+
entity-type="invoice"
|
|
154
|
+
:entity-id="invoiceId"
|
|
155
|
+
class="q-mt-lg"
|
|
156
|
+
/>
|
|
157
|
+
|
|
158
|
+
</template>
|
|
159
|
+
</WorkflowFormGuard>
|
|
160
|
+
</q-page>
|
|
161
|
+
</template>
|
|
162
|
+
|
|
163
|
+
<script setup lang="ts">
|
|
164
|
+
import { ref, computed } from 'vue'
|
|
165
|
+
import { useAuthStore } from 'src/stores/auth'
|
|
166
|
+
|
|
167
|
+
const props = defineProps<{ invoiceId: string | null }>()
|
|
168
|
+
const authStore = useAuthStore()
|
|
169
|
+
const currentUser = computed(() => authStore.user)
|
|
170
|
+
const invoice = ref({ amount: 0, description: '' })
|
|
171
|
+
|
|
172
|
+
const onActionExecuted = (action: string) => {
|
|
173
|
+
// Rafraîchir les données après une action workflow
|
|
174
|
+
console.log('Action exécutée :', action)
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
async function createInvoice() { /* ... */ }
|
|
178
|
+
async function saveInvoice() { /* ... */ }
|
|
179
|
+
async function deleteInvoice() { /* ... */ }
|
|
180
|
+
</script>
|
|
181
|
+
```
|
|
182
|
+
|
|
183
|
+
### Props de `WorkflowFormGuard`
|
|
184
|
+
|
|
185
|
+
| Prop | Type | Requis | Description |
|
|
186
|
+
|---|---|---|---|
|
|
187
|
+
| `entity-type` | `string` | ✅ | Type d'entité (`invoice`, `purchase_order`…) |
|
|
188
|
+
| `entity-id` | `string \| null` | ✅ | ID du document. `null` → mode création |
|
|
189
|
+
| `current-user-id` | `string` | ✅ | ID de l'utilisateur courant |
|
|
190
|
+
|
|
191
|
+
### Slot `#default`
|
|
192
|
+
|
|
193
|
+
| Variable | Type | Description |
|
|
194
|
+
|---|---|---|
|
|
195
|
+
| `formMode` | `FormMode` | Mode courant du formulaire |
|
|
196
|
+
| `canEdit` | `boolean` | L'utilisateur peut modifier les champs |
|
|
197
|
+
| `canDelete` | `boolean` | L'utilisateur peut supprimer le document |
|
|
198
|
+
| `canSubmit` | `boolean` | Le workflow peut être démarré |
|
|
199
|
+
| `canResubmit` | `boolean` | Le document peut être resoumis après correction |
|
|
200
|
+
| `hookRejectionReason` | `string \| null` | Message de refus d'un ActionHook |
|
|
201
|
+
| `instance` | `WorkflowInstanceDTO \| null` | Instance de workflow courante |
|
|
202
|
+
|
|
203
|
+
---
|
|
204
|
+
|
|
205
|
+
## 4. Directive `v-workflow-field`
|
|
206
|
+
|
|
207
|
+
La directive `v-workflow-field` applique automatiquement `readonly` ou `disable` sur un champ selon les droits de l'étape courante.
|
|
208
|
+
|
|
209
|
+
```vue
|
|
210
|
+
<!-- readonly si l'étape n'autorise pas la modification du champ -->
|
|
211
|
+
<q-input v-workflow-field="'amount'" v-model="form.amount" />
|
|
212
|
+
<q-select v-workflow-field="'category'" v-model="form.category" :options="categories" />
|
|
213
|
+
```
|
|
214
|
+
|
|
215
|
+
**Prérequis** : la directive doit être utilisée à l'intérieur d'un `WorkflowFormGuard`. Celui-ci expose via `provide()` la fonction `workflowFieldState` consommée par la directive.
|
|
216
|
+
|
|
217
|
+
Les champs éditables sont définis dans la propriété `editable_fields` de chaque étape de la définition workflow.
|
|
218
|
+
|
|
219
|
+
---
|
|
220
|
+
|
|
221
|
+
## 5. WorkflowActionBar — boutons d'action
|
|
222
|
+
|
|
223
|
+
Affiche les boutons d'action disponibles pour l'étape courante (approve, reject, transfer, etc.).
|
|
224
|
+
|
|
225
|
+
```vue
|
|
226
|
+
<WorkflowActionBar
|
|
227
|
+
entity-type="invoice"
|
|
228
|
+
:entity-id="invoiceId"
|
|
229
|
+
@action-executed="onActionExecuted"
|
|
230
|
+
@hook-rejected="onHookRejected"
|
|
231
|
+
/>
|
|
232
|
+
```
|
|
233
|
+
|
|
234
|
+
### Props
|
|
235
|
+
|
|
236
|
+
| Prop | Type | Description |
|
|
237
|
+
|---|---|---|
|
|
238
|
+
| `entity-type` | `string` | Type d'entité |
|
|
239
|
+
| `entity-id` | `string \| null` | ID du document |
|
|
240
|
+
|
|
241
|
+
### Événements émis
|
|
242
|
+
|
|
243
|
+
| Événement | Payload | Description |
|
|
244
|
+
|---|---|---|
|
|
245
|
+
| `action-executed` | `{ action: string }` | Après une action réussie |
|
|
246
|
+
| `hook-rejected` | `{ reason: string }` | Quand un ActionHook bloque |
|
|
247
|
+
|
|
248
|
+
### Actions affichées automatiquement selon l'étape
|
|
249
|
+
|
|
250
|
+
Les actions sont filtrées selon la configuration `WorkflowStepAction` de l'étape courante. La `WorkflowActionBar` gère automatiquement :
|
|
251
|
+
|
|
252
|
+
- Les **dialogs de saisie de raison** pour les actions qui la requièrent
|
|
253
|
+
- Le **champ "Transférer à"** pour l'action `transfer`
|
|
254
|
+
- Le **verrouillage optimiste** (passage automatique du `version` actuel)
|
|
255
|
+
- Le **bouton Soumettre** (démarrage du workflow) quand pas d'instance
|
|
256
|
+
- Le **bouton Re-soumettre** en mode correction
|
|
257
|
+
|
|
258
|
+
---
|
|
259
|
+
|
|
260
|
+
## 6. WorkflowStepper — visualisation
|
|
261
|
+
|
|
262
|
+
Affiche la progression du workflow avec le statut de chaque étape.
|
|
263
|
+
|
|
264
|
+
```vue
|
|
265
|
+
<WorkflowStepper
|
|
266
|
+
entity-type="invoice"
|
|
267
|
+
:entity-id="invoiceId"
|
|
268
|
+
/>
|
|
269
|
+
```
|
|
270
|
+
|
|
271
|
+
Gère automatiquement les trois topologies :
|
|
272
|
+
- **Séquentielle** : étapes affichées en ligne
|
|
273
|
+
- **Parallèle** : étapes `fork`/`join` affichées côte à côte
|
|
274
|
+
- **Conditionnelle** : étapes skippées affichées en grisé
|
|
275
|
+
|
|
276
|
+
---
|
|
277
|
+
|
|
278
|
+
## 7. WorkflowHistory — journal
|
|
279
|
+
|
|
280
|
+
Affiche l'historique complet des actions sous forme de timeline.
|
|
281
|
+
|
|
282
|
+
```vue
|
|
283
|
+
<WorkflowHistory
|
|
284
|
+
entity-type="invoice"
|
|
285
|
+
:entity-id="invoiceId"
|
|
286
|
+
/>
|
|
287
|
+
```
|
|
288
|
+
|
|
289
|
+
Chaque entrée affiche : action, acteur, date, raison (si présente).
|
|
290
|
+
|
|
291
|
+
---
|
|
292
|
+
|
|
293
|
+
## 8. useWorkflow — composable principal
|
|
294
|
+
|
|
295
|
+
Pour un contrôle fin, utiliser le composable directement :
|
|
296
|
+
|
|
297
|
+
```typescript
|
|
298
|
+
import { useWorkflow } from '@fickou/quasar-workflow'
|
|
299
|
+
import { ref } from 'vue'
|
|
300
|
+
|
|
301
|
+
const entityId = ref<string | null>('inv-123')
|
|
302
|
+
|
|
303
|
+
const {
|
|
304
|
+
instance, // Ref<WorkflowInstanceDTO | null>
|
|
305
|
+
definition, // Ref<WorkflowDefinitionDTO | null>
|
|
306
|
+
loading, // Ref<boolean>
|
|
307
|
+
error, // Ref<string | null>
|
|
308
|
+
hookRejectionReason, // Ref<string | null>
|
|
309
|
+
|
|
310
|
+
fetchInstanceAndDefinition, // () => Promise<void>
|
|
311
|
+
submitWorkflow, // () => Promise<void> — démarre le workflow
|
|
312
|
+
resubmit, // () => Promise<void> — resoumet après correction
|
|
313
|
+
executeAction, // (action, payload) => Promise<void>
|
|
314
|
+
} = useWorkflow('invoice', entityId)
|
|
315
|
+
|
|
316
|
+
// Charger au montage
|
|
317
|
+
await fetchInstanceAndDefinition()
|
|
318
|
+
|
|
319
|
+
// Exécuter une action
|
|
320
|
+
await executeAction('approve', {
|
|
321
|
+
version: instance.value!.steps[0].version,
|
|
322
|
+
reason: 'Conforme au budget',
|
|
323
|
+
})
|
|
324
|
+
```
|
|
325
|
+
|
|
326
|
+
---
|
|
327
|
+
|
|
328
|
+
## 9. useWorkflowWebSocket — temps réel
|
|
329
|
+
|
|
330
|
+
Le WebSocket permet de recevoir les mises à jour en temps réel sans rafraîchir la page.
|
|
331
|
+
|
|
332
|
+
```typescript
|
|
333
|
+
import { useWorkflowWebSocket } from '@fickou/quasar-workflow'
|
|
334
|
+
import { ref } from 'vue'
|
|
335
|
+
|
|
336
|
+
const instanceId = ref<string | null>('inst-456')
|
|
337
|
+
|
|
338
|
+
const { isConnected } = useWorkflowWebSocket(instanceId, {
|
|
339
|
+
onStepChanged: (data) => {
|
|
340
|
+
console.log('Étape changée :', data)
|
|
341
|
+
// Rafraîchir l'état de l'instance
|
|
342
|
+
fetchInstanceAndDefinition()
|
|
343
|
+
},
|
|
344
|
+
onActionExecuted: (data) => {
|
|
345
|
+
console.log('Action exécutée par un autre utilisateur :', data)
|
|
346
|
+
},
|
|
347
|
+
onConflict: (data) => {
|
|
348
|
+
// Un autre utilisateur a agi en même temps
|
|
349
|
+
Notify.create({ type: 'warning', message: 'L\'état a changé. Rechargement...' })
|
|
350
|
+
fetchInstanceAndDefinition()
|
|
351
|
+
},
|
|
352
|
+
})
|
|
353
|
+
```
|
|
354
|
+
|
|
355
|
+
La reconnexion est gérée automatiquement avec un **backoff exponentiel** (base 1s, ×2 à chaque tentative, max 30s).
|
|
356
|
+
|
|
357
|
+
---
|
|
358
|
+
|
|
359
|
+
## 10. useWorkflowFieldControl — contrôle des champs
|
|
360
|
+
|
|
361
|
+
Pour contrôler l'état des champs sans passer par la directive :
|
|
362
|
+
|
|
363
|
+
```typescript
|
|
364
|
+
import { useWorkflowFieldControl } from '@fickou/quasar-workflow'
|
|
365
|
+
import { ref } from 'vue'
|
|
366
|
+
|
|
367
|
+
const entityId = ref<string | null>('inv-123')
|
|
368
|
+
|
|
369
|
+
const {
|
|
370
|
+
formMode, // ComputedRef<FormMode>
|
|
371
|
+
canEdit, // ComputedRef<boolean>
|
|
372
|
+
canDelete, // ComputedRef<boolean>
|
|
373
|
+
isLoading, // Ref<boolean>
|
|
374
|
+
fieldState, // (fieldName: string) => FieldState ('editable' | 'readonly' | 'disabled')
|
|
375
|
+
} = useWorkflowFieldControl('invoice', entityId, {
|
|
376
|
+
currentUserId: 'user-123',
|
|
377
|
+
creatorId: 'user-123',
|
|
378
|
+
})
|
|
379
|
+
|
|
380
|
+
// Utilisation manuelle sans directive
|
|
381
|
+
const amountState = computed(() => fieldState('amount'))
|
|
382
|
+
```
|
|
383
|
+
|
|
384
|
+
---
|
|
385
|
+
|
|
386
|
+
## 11. Composants de suivi et monitoring
|
|
387
|
+
|
|
388
|
+
### WorkflowDashboard — documents en attente d'action
|
|
389
|
+
|
|
390
|
+
Affiche la liste de tous les documents sur lesquels l'utilisateur courant doit agir.
|
|
391
|
+
|
|
392
|
+
```vue
|
|
393
|
+
<WorkflowDashboard :user-id="currentUser.id" />
|
|
394
|
+
```
|
|
395
|
+
|
|
396
|
+
### WorkflowTracking — suivi créateur
|
|
397
|
+
|
|
398
|
+
Permet au créateur de suivre l'avancement de ses propres documents.
|
|
399
|
+
|
|
400
|
+
```vue
|
|
401
|
+
<WorkflowTracking entity-type="invoice" :entity-id="invoiceId" />
|
|
402
|
+
```
|
|
403
|
+
|
|
404
|
+
### WorkflowAdminMonitor — monitoring global (workflow_manager)
|
|
405
|
+
|
|
406
|
+
Tableau de bord global avec filtres par statut, entity_type et période.
|
|
407
|
+
|
|
408
|
+
```vue
|
|
409
|
+
<WorkflowAdminMonitor />
|
|
410
|
+
```
|
|
411
|
+
|
|
412
|
+
---
|
|
413
|
+
|
|
414
|
+
## 12. Composants d'administration
|
|
415
|
+
|
|
416
|
+
### WorkflowAdminBuilder — constructeur de workflow (workflow_admin)
|
|
417
|
+
|
|
418
|
+
Interface complète pour créer et modifier les définitions de workflow (étapes + transitions).
|
|
419
|
+
|
|
420
|
+
```vue
|
|
421
|
+
<!-- Page d'administration -->
|
|
422
|
+
<WorkflowAdminBuilder entity-type="invoice" />
|
|
423
|
+
```
|
|
424
|
+
|
|
425
|
+
Inclut deux onglets :
|
|
426
|
+
- **Étapes** : ajouter/modifier/supprimer les étapes, configurer deadline, validateurs, actions autorisées
|
|
427
|
+
- **Transitions** : définir les conditions de passage entre étapes via `RuleBuilderWidget`
|
|
428
|
+
|
|
429
|
+
### WorkflowTransitionEditor — éditeur de transitions
|
|
430
|
+
|
|
431
|
+
```vue
|
|
432
|
+
<WorkflowTransitionEditor :definition-id="defId" />
|
|
433
|
+
```
|
|
434
|
+
|
|
435
|
+
### RuleBuilderWidget — éditeur visuel de règles
|
|
436
|
+
|
|
437
|
+
Interface visuelle pour créer des conditions AND/OR imbriquées. Supporte les 15 opérateurs du `RuleEngineService`.
|
|
438
|
+
|
|
439
|
+
```vue
|
|
440
|
+
<RuleBuilderWidget
|
|
441
|
+
v-model="ruleSet"
|
|
442
|
+
:available-fields="['amount', 'department', 'status']"
|
|
443
|
+
/>
|
|
444
|
+
```
|
|
445
|
+
|
|
446
|
+
---
|
|
447
|
+
|
|
448
|
+
## 13. useWorkflowDelegation — délégations
|
|
449
|
+
|
|
450
|
+
```typescript
|
|
451
|
+
import { useWorkflowDelegation } from '@fickou/quasar-workflow'
|
|
452
|
+
|
|
453
|
+
const {
|
|
454
|
+
myDelegations, // Ref<WorkflowDelegation[]>
|
|
455
|
+
receivedDelegations, // Ref<WorkflowDelegation[]>
|
|
456
|
+
loading,
|
|
457
|
+
fetchMyDelegations,
|
|
458
|
+
fetchReceivedDelegations,
|
|
459
|
+
createDelegation,
|
|
460
|
+
revokeDelegation,
|
|
461
|
+
} = useWorkflowDelegation()
|
|
462
|
+
|
|
463
|
+
// Créer une délégation
|
|
464
|
+
await createDelegation({
|
|
465
|
+
delegateId: 'user-456',
|
|
466
|
+
scope: 'workflow',
|
|
467
|
+
workflowDefinitionId: 'def-789',
|
|
468
|
+
startDate: new Date().toISOString(),
|
|
469
|
+
endDate: new Date(Date.now() + 7 * 86400000).toISOString(),
|
|
470
|
+
type: 'delegation',
|
|
471
|
+
})
|
|
472
|
+
```
|
|
473
|
+
|
|
474
|
+
Ou utiliser directement le composant :
|
|
475
|
+
|
|
476
|
+
```vue
|
|
477
|
+
<WorkflowDelegationManager :user-id="currentUser.id" />
|
|
478
|
+
```
|
|
479
|
+
|
|
480
|
+
---
|
|
481
|
+
|
|
482
|
+
## 14. useWorkflowStats — statistiques
|
|
483
|
+
|
|
484
|
+
Disponible uniquement pour les utilisateurs `workflow_manager` et `workflow_admin`.
|
|
485
|
+
|
|
486
|
+
```typescript
|
|
487
|
+
import { useWorkflowStats } from '@fickou/quasar-workflow'
|
|
488
|
+
|
|
489
|
+
const {
|
|
490
|
+
delays, // Ref<DelayStats[]>
|
|
491
|
+
rejectionRates, // Ref<RejectionRateStats[]>
|
|
492
|
+
volumes, // Ref<VolumeStats[]>
|
|
493
|
+
loading,
|
|
494
|
+
fetchDelays,
|
|
495
|
+
fetchRejectionRates,
|
|
496
|
+
fetchVolumes,
|
|
497
|
+
} = useWorkflowStats()
|
|
498
|
+
|
|
499
|
+
await fetchDelays({ entityType: 'invoice', period: '30d' })
|
|
500
|
+
```
|
|
501
|
+
|
|
502
|
+
Ou utiliser le composant intégré :
|
|
503
|
+
|
|
504
|
+
```vue
|
|
505
|
+
<WorkflowStats entity-type="invoice" />
|
|
506
|
+
```
|
|
507
|
+
|
|
508
|
+
---
|
|
509
|
+
|
|
510
|
+
## 15. Référence des composables
|
|
511
|
+
|
|
512
|
+
| Composable | Paramètres | Usage |
|
|
513
|
+
|---|---|---|
|
|
514
|
+
| `useWorkflow(entityType, entityId, options?)` | `entityType: string`, `entityId: Ref<string\|null>` | Composable principal — état + actions |
|
|
515
|
+
| `useWorkflowFieldControl(entityType, entityId, options?)` | idem + `currentUserId`, `creatorId` | formMode, canEdit, fieldState |
|
|
516
|
+
| `useWorkflowWebSocket(instanceId, callbacks?)` | `instanceId: Ref<string\|null>` | Connexion WS + callbacks |
|
|
517
|
+
| `useWorkflowAdmin()` | — | CRUD définitions/steps/transitions |
|
|
518
|
+
| `useWorkflowDashboard()` | — | Liste des instances en attente |
|
|
519
|
+
| `useWorkflowStats()` | — | Statistiques (manager+) |
|
|
520
|
+
| `useWorkflowDelegation()` | — | Gestion des délégations |
|
|
521
|
+
|
|
522
|
+
---
|
|
523
|
+
|
|
524
|
+
## 16. Tableau formMode / canDelete
|
|
525
|
+
|
|
526
|
+
| Contexte | `formMode` | `canEdit` | `canDelete` |
|
|
527
|
+
|---|---|---|---|
|
|
528
|
+
| `entity_id = null` | `create` | `true` | `false` |
|
|
529
|
+
| Pas d'instance active | `view` | `false` | `true` si `ever_started = false` |
|
|
530
|
+
| Instance `in_progress` | `edit` | `true` (champs editable_fields) | `false` |
|
|
531
|
+
| Instance `suspended` | `suspended` | `true` (champs editable_fields) | `false` |
|
|
532
|
+
| `correction_pending` (créateur) | `correction` | `true` | `false` |
|
|
533
|
+
| `completed / cancelled / rejected` | `locked` | `false` | `false` |
|
|
534
|
+
|
|
535
|
+
> **Règle absolue** : dès que `ever_started = true`, `canDelete = false` de façon **permanente**, même si l'instance est terminée. Toujours vérifier côté backend avant toute suppression.
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
import { Ref } from 'vue';
|
|
2
|
+
import { WorkflowInstanceDTO, WorkflowAction, ActionPayload, WorkflowDefinitionDTO } from '../types';
|
|
3
|
+
interface UseWorkflowOptions {
|
|
4
|
+
apiBase?: string;
|
|
5
|
+
wsUrl?: string;
|
|
6
|
+
}
|
|
7
|
+
export declare function useWorkflow(entityType: string, entityId: Ref<string | null>, options?: UseWorkflowOptions): {
|
|
8
|
+
instance: Ref<{
|
|
9
|
+
id: string;
|
|
10
|
+
entity_type: string;
|
|
11
|
+
entity_id: string;
|
|
12
|
+
status: import("../types").WorkflowStatus;
|
|
13
|
+
ever_started: boolean;
|
|
14
|
+
snapshot_id: string;
|
|
15
|
+
steps: {
|
|
16
|
+
id: string;
|
|
17
|
+
instance_id: string;
|
|
18
|
+
step_id: string;
|
|
19
|
+
status: "pending" | "active" | "completed" | "skipped" | "rejected";
|
|
20
|
+
validation_count: number;
|
|
21
|
+
version: number;
|
|
22
|
+
}[];
|
|
23
|
+
snapshot?: {
|
|
24
|
+
snapshot_data: {
|
|
25
|
+
id: string;
|
|
26
|
+
entity_type: string;
|
|
27
|
+
auto_start: boolean;
|
|
28
|
+
steps: {
|
|
29
|
+
id: string;
|
|
30
|
+
order: number;
|
|
31
|
+
step_type: import("../types").StepType;
|
|
32
|
+
validation_threshold: number;
|
|
33
|
+
name: string;
|
|
34
|
+
description?: string | undefined;
|
|
35
|
+
editable_fields?: string[] | undefined;
|
|
36
|
+
}[];
|
|
37
|
+
};
|
|
38
|
+
} | undefined;
|
|
39
|
+
} | null, WorkflowInstanceDTO | {
|
|
40
|
+
id: string;
|
|
41
|
+
entity_type: string;
|
|
42
|
+
entity_id: string;
|
|
43
|
+
status: import("../types").WorkflowStatus;
|
|
44
|
+
ever_started: boolean;
|
|
45
|
+
snapshot_id: string;
|
|
46
|
+
steps: {
|
|
47
|
+
id: string;
|
|
48
|
+
instance_id: string;
|
|
49
|
+
step_id: string;
|
|
50
|
+
status: "pending" | "active" | "completed" | "skipped" | "rejected";
|
|
51
|
+
validation_count: number;
|
|
52
|
+
version: number;
|
|
53
|
+
}[];
|
|
54
|
+
snapshot?: {
|
|
55
|
+
snapshot_data: {
|
|
56
|
+
id: string;
|
|
57
|
+
entity_type: string;
|
|
58
|
+
auto_start: boolean;
|
|
59
|
+
steps: {
|
|
60
|
+
id: string;
|
|
61
|
+
order: number;
|
|
62
|
+
step_type: import("../types").StepType;
|
|
63
|
+
validation_threshold: number;
|
|
64
|
+
name: string;
|
|
65
|
+
description?: string | undefined;
|
|
66
|
+
editable_fields?: string[] | undefined;
|
|
67
|
+
}[];
|
|
68
|
+
};
|
|
69
|
+
} | undefined;
|
|
70
|
+
} | null>;
|
|
71
|
+
definition: Ref<{
|
|
72
|
+
id: string;
|
|
73
|
+
entity_type: string;
|
|
74
|
+
auto_start: boolean;
|
|
75
|
+
steps: {
|
|
76
|
+
id: string;
|
|
77
|
+
order: number;
|
|
78
|
+
step_type: import("../types").StepType;
|
|
79
|
+
validation_threshold: number;
|
|
80
|
+
name: string;
|
|
81
|
+
description?: string | undefined;
|
|
82
|
+
editable_fields?: string[] | undefined;
|
|
83
|
+
}[];
|
|
84
|
+
} | null, WorkflowDefinitionDTO | {
|
|
85
|
+
id: string;
|
|
86
|
+
entity_type: string;
|
|
87
|
+
auto_start: boolean;
|
|
88
|
+
steps: {
|
|
89
|
+
id: string;
|
|
90
|
+
order: number;
|
|
91
|
+
step_type: import("../types").StepType;
|
|
92
|
+
validation_threshold: number;
|
|
93
|
+
name: string;
|
|
94
|
+
description?: string | undefined;
|
|
95
|
+
editable_fields?: string[] | undefined;
|
|
96
|
+
}[];
|
|
97
|
+
} | null>;
|
|
98
|
+
loading: Ref<boolean, boolean>;
|
|
99
|
+
error: Ref<string | null, string | null>;
|
|
100
|
+
hookRejectionReason: Ref<string | null, string | null>;
|
|
101
|
+
isWsConnected: Ref<boolean, boolean>;
|
|
102
|
+
fetchInstance: () => Promise<void>;
|
|
103
|
+
executeAction: (action: WorkflowAction, payload?: Partial<ActionPayload>) => Promise<void>;
|
|
104
|
+
submitWorkflow: () => Promise<void>;
|
|
105
|
+
resubmit: () => Promise<void>;
|
|
106
|
+
fetchHistory: () => Promise<any>;
|
|
107
|
+
};
|
|
108
|
+
export {};
|