Skip to content
shadcn.io

shadcn.io is not affiliated with official shadcn/ui

Contract State Workflow CRUD Block for Vue

One contract and its state graph: a strip of five states with the allowed next ones outlined, buttons for those moves only, a reason required to reject and a history you can pop with Undo.

Framework
UI library

Preview

Preview width

Installation

Part of Pro
Pro unlocks the code and one-line install of every Pro button and text effect, plus the MCP for your AI editor.
Have Pro already? Sign in to open it.Opens here as soon as you're Pro.

Overview

Make the rules of a state machine visible, with this contract tracker for Vue with Ark UI. A strip of five nodes, Draft, Review, Approved, Published and Rejected, fills the current state and outlines the ones it may move to; only those moves have a button, so "Move to Review" exists and "Move to Published" does not until Approved. A move names what it needs and stays aria-disabled until it has it: a summary of twenty characters before Review, a signed-by name before Published. Move to Rejected opens a Popover that requires a reason. Every move adds an entry to the history (who, from, to, note, day), and Undo last transition pops your newest entry and puts the contract back. Published is final. The summary Textarea and signed-by Input edit in place, and the first move button is the one Shine Button. Not a list with dates: that is the draft and publish desk.

FAQ

Where is the state graph defined?

In nextOf, one small function: Draft goes to Review; Review to Approved, Rejected or back to Draft; Approved to Published or back to Review; Rejected back to Draft; Published nowhere. The strip, the buttons and the history text all read it, so changing a rule there changes the whole block.

What happens when I press a move that is not ready?

The button is aria-disabled, not disabled, so it stays reachable: pressing it puts the reason in the polite line ("Needs a summary before Review") and focuses the field that fixes it. The same reasons are listed under the buttons and tied to them with aria-describedby.

What does Undo last transition undo?

Your own newest move only: it removes that history entry and returns the contract to the state it left. Entries made by other people (the seeded ones) are not yours to pop, so the button is aria-disabled until you have moved the contract once.

How do I use my own states?

Edit STATES for the nodes, nextOf for the allowed moves and needOf for the gates, and replace START with your record. The strip has one node per state and scrolls sideways when it is wider than the panel.

What stays local, and how do I wire a real workflow?

The contract and its history live in state in this tab and a reload restores them. In your app send the move to your API from record, check the transition on the server (never trust the button), store the history as rows and let Undo call a compensating move rather than deleting the row.

How does it look on a phone?

The strip scrolls sideways inside its band with the current state first in view, the move buttons wrap under it, and the history list keeps its height with its own scroll.