Skip to content
EspañolEnglish
All components (244)

Approval chain

Management · ApprovalFlow

What it is and when to use it

An approval chain: the steps in order, who has to decide in each one, what they decided and when, and with what note.

When to use it

  • Approving an expense, a holiday, a contract or a publication that goes through several people.
  • steps are the steps and each carries its approvers with their decision (pending, approved, rejected, skipped). mode="any" makes one enough; by default everyone approves.
  • The first pending step is the current one and the following ones wait. A single rejection rejects the whole.
  • With currentUserId and onDecide, whoever has the decision pending in the current step sees "Approve" and "Reject"; rejecting asks for a reason (requireReason).

When not to

  • A step wizard for a form: Wizard.
  • The component does not change steps: it calls onDecide and the application changes it. The server has to check again that the person can decide.

Keyboard: they are normal buttons and a text field; the current step carries aria-current="step".

Accessibility: the state of each step and the decision of each person are written out, so colour is not the only thing that informs. Each decision is announced, and if it fails it is said in an alert.

Fine appearance: each step is a box with a stripe on the left: accent for the current one, success for the approved one, error for the rejected one.

What it looks like

In progress1 of 2 steps approved

  1. Step 1Purchasing managerApproved

    • Marta Ruiz Approved

      It fits the budget.

  2. Step 2AddressTheir turn

    • Aitor Mena Pending
    • Carla Gil Pending

<ApprovalFlow
  label="€4,200 purchase of materials"
  steps={pasos}
  currentUserId="aitor"
  requireReason
  onDecide={(paso, decision, comentario) => guardarDecision(paso, decision, comentario)}
/>

Props

Import it with import { ApprovalFlow } from '@amset/ui'. Required props are marked.

PropTypeWhat it does
labelrequiredstringWhat is being approved («Approval of expense 2026-114»). Required.
stepsrequiredreadonly ApprovalStep[]—
classNamestringMerged with the library classes; it does not replace them.
currentUserIdstringWho is looking: if it is their turn to decide in the current step, their buttons appear. Without this it is read-only.
onDecide(stepId: string, decision: ApprovalDecision, comment: string) => void | Promise<void>Record the decision of `currentUserId` in a step. If it throws or rejects, it says so and the buttons stay where they were. The library does not change `steps`: the application does, and the server has to check that the person can decide.
requireReasonbooleanRequire a reason to reject. On by default.

It also accepts the attributes of the element it renders: className, style, data-*, aria-* and its events.

In the test bench

Every state and variant, to try one at a time, on its page at ui.amset.tech: