@fickou/quasar-workflow
v1.0.2
Published
Workflow components and composables for Quasar v2 / Vue 3
Maintainers
Readme
@fickou/quasar-workflow
Composants et composables Vue 3 pour intégrer le moteur de workflow dans une application Quasar v2.
Dépend uniquement de l'API REST exposée par adonis-workflow. Aucune dépendance directe sur le backend.
Table des matières
- Installation
- Enregistrement du plugin
- Usage de base — formulaire avec workflow
- Directive
v-workflow-field - WorkflowActionBar — boutons d'action
- WorkflowStepper — visualisation
- WorkflowHistory — journal
- useWorkflow — composable principal
- useWorkflowWebSocket — temps réel
- useWorkflowFieldControl — contrôle des champs
- Composants de suivi et monitoring
- Composants d'administration
- useWorkflowDelegation — délégations
- useWorkflowStats — statistiques
- Référence des composables
- Tableau formMode / canDelete
1. Installation
Ce package s'installe par copie directe dans le projet hôte (pas de registry npm).
# Via npm (recommandé)
npm install @fickou/quasar-workflow
# Ou via le chemin local (monorepo)
npm install file:packages/quasar-workflowVérifier que les peer dependencies sont présentes :
"vue": "^3.0.0",
"quasar": "^2.0.0",
"axios": "^1.0.0"2. Enregistrement du plugin
Créer un fichier boot dédié dans votre projet Quasar :
// src/boot/workflow.ts
import { boot } from 'quasar/wrappers'
import { WorkflowPlugin } from '@fickou/quasar-workflow'
export default boot(({ app }) => {
app.use(WorkflowPlugin, {
// URL de base de l'API workflow (le préfixe doit correspondre à routePrefix dans adonis-workflow)
apiBase: '/api/workflow',
// URL du serveur WebSocket AdonisJS (optionnel — uniquement si @adonisjs/websocket est installé)
wsUrl: 'ws://localhost:3333',
})
})Déclarer le boot dans quasar.config.ts :
// quasar.config.ts
boot: [
'axios',
'workflow', // ← ajouter
],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.
3. Usage de base — formulaire avec workflow
Le pattern standard consiste à envelopper votre formulaire dans WorkflowFormGuard, qui gère automatiquement le mode du formulaire selon l'état du workflow.
<!-- src/pages/InvoicePage.vue -->
<template>
<q-page padding>
<WorkflowFormGuard
entity-type="invoice"
:entity-id="invoiceId"
:current-user-id="currentUser.id"
@action-executed="onActionExecuted"
>
<template #default="{ formMode, canEdit, canDelete, canSubmit, canResubmit, hookRejectionReason }">
<!-- Alerte si une action a été bloquée par un hook métier -->
<q-banner v-if="hookRejectionReason" type="warning" class="q-mb-md">
{{ hookRejectionReason }}
</q-banner>
<!-- Champs du formulaire — v-workflow-field gère readonly/disable automatiquement -->
<q-input
v-workflow-field="'amount'"
v-model="invoice.amount"
label="Montant"
type="number"
/>
<q-input
v-workflow-field="'description'"
v-model="invoice.description"
label="Description"
type="textarea"
/>
<!-- Boutons selon le mode -->
<div class="row q-gutter-sm q-mt-md">
<q-btn
v-if="formMode === 'create'"
label="Créer la facture"
color="primary"
@click="createInvoice"
/>
<q-btn
v-if="canEdit"
label="Enregistrer"
color="primary"
@click="saveInvoice"
/>
<q-btn
v-if="canDelete"
label="Supprimer"
color="negative"
@click="deleteInvoice"
/>
</div>
<!-- Barre d'actions workflow (boutons approve, reject, etc.) -->
<WorkflowActionBar
entity-type="invoice"
:entity-id="invoiceId"
class="q-mt-md"
/>
<!-- Visualisation de l'avancement -->
<WorkflowStepper
entity-type="invoice"
:entity-id="invoiceId"
class="q-mt-lg"
/>
</template>
</WorkflowFormGuard>
</q-page>
</template>
<script setup lang="ts">
import { ref, computed } from 'vue'
import { useAuthStore } from 'src/stores/auth'
const props = defineProps<{ invoiceId: string | null }>()
const authStore = useAuthStore()
const currentUser = computed(() => authStore.user)
const invoice = ref({ amount: 0, description: '' })
const onActionExecuted = (action: string) => {
// Rafraîchir les données après une action workflow
console.log('Action exécutée :', action)
}
async function createInvoice() { /* ... */ }
async function saveInvoice() { /* ... */ }
async function deleteInvoice() { /* ... */ }
</script>Props de WorkflowFormGuard
| Prop | Type | Requis | Description |
|---|---|---|---|
| entity-type | string | ✅ | Type d'entité (invoice, purchase_order…) |
| entity-id | string \| null | ✅ | ID du document. null → mode création |
| current-user-id | string | ✅ | ID de l'utilisateur courant |
Slot #default
| Variable | Type | Description |
|---|---|---|
| formMode | FormMode | Mode courant du formulaire |
| canEdit | boolean | L'utilisateur peut modifier les champs |
| canDelete | boolean | L'utilisateur peut supprimer le document |
| canSubmit | boolean | Le workflow peut être démarré |
| canResubmit | boolean | Le document peut être resoumis après correction |
| hookRejectionReason | string \| null | Message de refus d'un ActionHook |
| instance | WorkflowInstanceDTO \| null | Instance de workflow courante |
4. Directive v-workflow-field
La directive v-workflow-field applique automatiquement readonly ou disable sur un champ selon les droits de l'étape courante.
<!-- readonly si l'étape n'autorise pas la modification du champ -->
<q-input v-workflow-field="'amount'" v-model="form.amount" />
<q-select v-workflow-field="'category'" v-model="form.category" :options="categories" />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.
Les champs éditables sont définis dans la propriété editable_fields de chaque étape de la définition workflow.
5. WorkflowActionBar — boutons d'action
Affiche les boutons d'action disponibles pour l'étape courante (approve, reject, transfer, etc.).
<WorkflowActionBar
entity-type="invoice"
:entity-id="invoiceId"
@action-executed="onActionExecuted"
@hook-rejected="onHookRejected"
/>Props
| Prop | Type | Description |
|---|---|---|
| entity-type | string | Type d'entité |
| entity-id | string \| null | ID du document |
Événements émis
| Événement | Payload | Description |
|---|---|---|
| action-executed | { action: string } | Après une action réussie |
| hook-rejected | { reason: string } | Quand un ActionHook bloque |
Actions affichées automatiquement selon l'étape
Les actions sont filtrées selon la configuration WorkflowStepAction de l'étape courante. La WorkflowActionBar gère automatiquement :
- Les dialogs de saisie de raison pour les actions qui la requièrent
- Le champ "Transférer à" pour l'action
transfer - Le verrouillage optimiste (passage automatique du
versionactuel) - Le bouton Soumettre (démarrage du workflow) quand pas d'instance
- Le bouton Re-soumettre en mode correction
6. WorkflowStepper — visualisation
Affiche la progression du workflow avec le statut de chaque étape.
<WorkflowStepper
entity-type="invoice"
:entity-id="invoiceId"
/>Gère automatiquement les trois topologies :
- Séquentielle : étapes affichées en ligne
- Parallèle : étapes
fork/joinaffichées côte à côte - Conditionnelle : étapes skippées affichées en grisé
7. WorkflowHistory — journal
Affiche l'historique complet des actions sous forme de timeline.
<WorkflowHistory
entity-type="invoice"
:entity-id="invoiceId"
/>Chaque entrée affiche : action, acteur, date, raison (si présente).
8. useWorkflow — composable principal
Pour un contrôle fin, utiliser le composable directement :
import { useWorkflow } from '@fickou/quasar-workflow'
import { ref } from 'vue'
const entityId = ref<string | null>('inv-123')
const {
instance, // Ref<WorkflowInstanceDTO | null>
definition, // Ref<WorkflowDefinitionDTO | null>
loading, // Ref<boolean>
error, // Ref<string | null>
hookRejectionReason, // Ref<string | null>
fetchInstanceAndDefinition, // () => Promise<void>
submitWorkflow, // () => Promise<void> — démarre le workflow
resubmit, // () => Promise<void> — resoumet après correction
executeAction, // (action, payload) => Promise<void>
} = useWorkflow('invoice', entityId)
// Charger au montage
await fetchInstanceAndDefinition()
// Exécuter une action
await executeAction('approve', {
version: instance.value!.steps[0].version,
reason: 'Conforme au budget',
})9. useWorkflowWebSocket — temps réel
Le WebSocket permet de recevoir les mises à jour en temps réel sans rafraîchir la page.
import { useWorkflowWebSocket } from '@fickou/quasar-workflow'
import { ref } from 'vue'
const instanceId = ref<string | null>('inst-456')
const { isConnected } = useWorkflowWebSocket(instanceId, {
onStepChanged: (data) => {
console.log('Étape changée :', data)
// Rafraîchir l'état de l'instance
fetchInstanceAndDefinition()
},
onActionExecuted: (data) => {
console.log('Action exécutée par un autre utilisateur :', data)
},
onConflict: (data) => {
// Un autre utilisateur a agi en même temps
Notify.create({ type: 'warning', message: 'L\'état a changé. Rechargement...' })
fetchInstanceAndDefinition()
},
})La reconnexion est gérée automatiquement avec un backoff exponentiel (base 1s, ×2 à chaque tentative, max 30s).
10. useWorkflowFieldControl — contrôle des champs
Pour contrôler l'état des champs sans passer par la directive :
import { useWorkflowFieldControl } from '@fickou/quasar-workflow'
import { ref } from 'vue'
const entityId = ref<string | null>('inv-123')
const {
formMode, // ComputedRef<FormMode>
canEdit, // ComputedRef<boolean>
canDelete, // ComputedRef<boolean>
isLoading, // Ref<boolean>
fieldState, // (fieldName: string) => FieldState ('editable' | 'readonly' | 'disabled')
} = useWorkflowFieldControl('invoice', entityId, {
currentUserId: 'user-123',
creatorId: 'user-123',
})
// Utilisation manuelle sans directive
const amountState = computed(() => fieldState('amount'))11. Composants de suivi et monitoring
WorkflowDashboard — documents en attente d'action
Affiche la liste de tous les documents sur lesquels l'utilisateur courant doit agir.
<WorkflowDashboard :user-id="currentUser.id" />WorkflowTracking — suivi créateur
Permet au créateur de suivre l'avancement de ses propres documents.
<WorkflowTracking entity-type="invoice" :entity-id="invoiceId" />WorkflowAdminMonitor — monitoring global (workflow_manager)
Tableau de bord global avec filtres par statut, entity_type et période.
<WorkflowAdminMonitor />12. Composants d'administration
WorkflowAdminBuilder — constructeur de workflow (workflow_admin)
Interface complète pour créer et modifier les définitions de workflow (étapes + transitions).
<!-- Page d'administration -->
<WorkflowAdminBuilder entity-type="invoice" />Inclut deux onglets :
- Étapes : ajouter/modifier/supprimer les étapes, configurer deadline, validateurs, actions autorisées
- Transitions : définir les conditions de passage entre étapes via
RuleBuilderWidget
WorkflowTransitionEditor — éditeur de transitions
<WorkflowTransitionEditor :definition-id="defId" />RuleBuilderWidget — éditeur visuel de règles
Interface visuelle pour créer des conditions AND/OR imbriquées. Supporte les 15 opérateurs du RuleEngineService.
<RuleBuilderWidget
v-model="ruleSet"
:available-fields="['amount', 'department', 'status']"
/>13. useWorkflowDelegation — délégations
import { useWorkflowDelegation } from '@fickou/quasar-workflow'
const {
myDelegations, // Ref<WorkflowDelegation[]>
receivedDelegations, // Ref<WorkflowDelegation[]>
loading,
fetchMyDelegations,
fetchReceivedDelegations,
createDelegation,
revokeDelegation,
} = useWorkflowDelegation()
// Créer une délégation
await createDelegation({
delegateId: 'user-456',
scope: 'workflow',
workflowDefinitionId: 'def-789',
startDate: new Date().toISOString(),
endDate: new Date(Date.now() + 7 * 86400000).toISOString(),
type: 'delegation',
})Ou utiliser directement le composant :
<WorkflowDelegationManager :user-id="currentUser.id" />14. useWorkflowStats — statistiques
Disponible uniquement pour les utilisateurs workflow_manager et workflow_admin.
import { useWorkflowStats } from '@fickou/quasar-workflow'
const {
delays, // Ref<DelayStats[]>
rejectionRates, // Ref<RejectionRateStats[]>
volumes, // Ref<VolumeStats[]>
loading,
fetchDelays,
fetchRejectionRates,
fetchVolumes,
} = useWorkflowStats()
await fetchDelays({ entityType: 'invoice', period: '30d' })Ou utiliser le composant intégré :
<WorkflowStats entity-type="invoice" />15. Référence des composables
| Composable | Paramètres | Usage |
|---|---|---|
| useWorkflow(entityType, entityId, options?) | entityType: string, entityId: Ref<string\|null> | Composable principal — état + actions |
| useWorkflowFieldControl(entityType, entityId, options?) | idem + currentUserId, creatorId | formMode, canEdit, fieldState |
| useWorkflowWebSocket(instanceId, callbacks?) | instanceId: Ref<string\|null> | Connexion WS + callbacks |
| useWorkflowAdmin() | — | CRUD définitions/steps/transitions |
| useWorkflowDashboard() | — | Liste des instances en attente |
| useWorkflowStats() | — | Statistiques (manager+) |
| useWorkflowDelegation() | — | Gestion des délégations |
16. Tableau formMode / canDelete
| Contexte | formMode | canEdit | canDelete |
|---|---|---|---|
| entity_id = null | create | true | false |
| Pas d'instance active | view | false | true si ever_started = false |
| Instance in_progress | edit | true (champs editable_fields) | false |
| Instance suspended | suspended | true (champs editable_fields) | false |
| correction_pending (créateur) | correction | true | false |
| completed / cancelled / rejected | locked | false | false |
Règle absolue : dès que
ever_started = true,canDelete = falsede façon permanente, même si l'instance est terminée. Toujours vérifier côté backend avant toute suppression.
Développement et Exemple
Le package contient un exemple complet d'intégration dans examples/quasar-example.
1. Installation de l'exemple
Depuis la racine du package packages/quasar-workflow :
npm run example:install2. Lancer l'exemple en mode développement
Depuis la racine du package packages/quasar-workflow :
npm run example:devL'application sera accessible sur http://localhost:9000.
Note : L'exemple Quasar nécessite que le backend
adonis-examplesoit lancé pour fonctionner correctement (API et WebSockets).
