npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@fickou/quasar-workflow

v1.0.2

Published

Workflow components and composables for Quasar v2 / Vue 3

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

  1. Installation
  2. Enregistrement du plugin
  3. Usage de base — formulaire avec workflow
  4. Directive v-workflow-field
  5. WorkflowActionBar — boutons d'action
  6. WorkflowStepper — visualisation
  7. WorkflowHistory — journal
  8. useWorkflow — composable principal
  9. useWorkflowWebSocket — temps réel
  10. useWorkflowFieldControl — contrôle des champs
  11. Composants de suivi et monitoring
  12. Composants d'administration
  13. useWorkflowDelegation — délégations
  14. useWorkflowStats — statistiques
  15. Référence des composables
  16. 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-workflow

Vé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 version actuel)
  • 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/join affiché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 = false de 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:install

2. Lancer l'exemple en mode développement

Depuis la racine du package packages/quasar-workflow :

npm run example:dev

L'application sera accessible sur http://localhost:9000.

Note : L'exemple Quasar nécessite que le backend adonis-example soit lancé pour fonctionner correctement (API et WebSockets).