Skip to content

Workflow Approval Page

Back to Regnify Product Docs

Previous Next


Overview

The Workflow Approval page (/declarations/:id/workflow) provides the interface for approvers to review a declaration and either approve, reject, or send it back for revision. The page is implemented in client/src/pages/WorkflowApprovalPage.tsx.

Page context: Wraps content in <PageEntityProvider entity={{ kind: 'workflow_for_declaration', declarationId: asDeclarationId(declaration.id) }}>.

The :id in the URL is the declaration ID (not a workflow instance ID). The page resolves the linked workflow instance via workflowInstances(declarationId) query.

Page Layout

PageHeader component: - Title: "Workflow Approval" - Subtitle: "Declaration #[id] - [Rep Name]" (from declaration.rep_name or form_data.personal_info.full_name) - Back link: "/dashboard" - Breadcrumbs: Dashboard > Workflows > Approval #[id] - Action buttons: PDF export, DOCX export (both call FastAPI export endpoints)

Workflow Stepper (Prominent Top)

If the workflow instance has steps, the WorkflowStepper component renders in a card with a blue top border:

Step 1      Step 2         Step 3        Step 4
------->  ------->       ------->      ------->
[Approved] [Current]     [Pending]     [Future]

Each step displays: - Circle: colored by status — green with checkmark (approved), blue pulsing ring (current), gray (pending future), red with X (rejected), orange (sent back) - Initial: first letter of approver name (or step number if name unavailable; fallback uses index step_index + 1) - Label: approver name (or "Approver N") - Status text: "Approved" / "Current Reviewer" / "Approver" / "Rejected" / "Sent Back" - Acted-at date: short SG format (if step has acted_at) - Current step extras: "Waiting for: [Approver Name]" badge + "Since: [date]" + idle-day indicator (green 0-3d, amber 4-7d, red 8+d) - Connector lines: green gradient (completed), red gradient (rejected), orange (sent-back), gray (future)

Idle days are computed from the previous step's acted_at or the submittedAt prop. The workflow is considered "terminal" (no current reviewer) when any step is sent_back or rejected, or when all steps are approved.

Declaration Summary Card

Two-column grid with:

Left column: - Representative: personal_info.full_name or declaration.rep_name - Company: general_info.principal_company_name - Rep Type: general_info.rep_type

Right column: - Form Type: via formTypeLabel() utility - Created: SG-locale datetime - Submitted: SG-locale datetime

Above both columns: StatusBadge showing current declaration status.

Footer: "View full declaration form" link navigating to the Form 3A page in view-only mode (/reps/:rep_id/form3a with state: { declarationId, viewOnly: true }).

Approval Comments Section

When workflow events with comments exist, rendered in a card with MessageSquare icon heading. Each comment is a colored bordered card:

Event Type Card Colors Icon Badge
step_approved / approved / approve Green success background + green border Green circle with CheckCircle2 + "Approved"
step_sent_back / sent_back / send_back Orange background + orange border Orange circle with RotateCcw + "Sent Back"
Other (rejected events) Red danger background + red border Red circle with XCircle + "Rejected"

Each comment card shows: - Avatar: initials of actor name - Actor name (bold) + status badge - Timestamp (right-aligned, SG-locale) - Comment text below (with left padding for alignment)

Comments are sorted chronologically (oldest first).

Your Decision Section (Conditional)

Shown only when the current user is the active pending approver. The isCurrentApprover check: the workflow exists, and there is a step where step_index === workflow.current_step_index AND approver_user_id === dbUser.id AND status === 'pending'.

Amber top border card with: - AlertCircle icon in amber circle + "Your Decision" heading - "Review the declaration and provide your decision with a comment." subtitle - Mandatory comment textarea (required field, red asterisk label) - Three action buttons: 1. Approve: green background, CheckCircle2 icon, bg-success-600 hover:bg-success-700 2. Request Changes (send-back): orange outline, RotateCcw icon, border-orange-400 text-orange-700 3. Reject: red danger variant, XCircle icon

Validation: comment must not be empty before Approve or Reject. An inline error message "Comment is required before approving or rejecting." (rendered as red text below the comment textarea) blocks the action modal from opening.

On Approve/Reject click: opens confirmation modal with declaration summary and comment preview. On confirm: calls approveWorkflowStep or rejectWorkflowStep mutation, refetches all data, clears comment.

On Send Back click: opens send-back modal with "What changes are needed?" textarea. On submit: calls sendBackWorkflowStep mutation, navigates back to /dashboard.

Status Info Card (When NOT Current Approver)

When the user is not the current approver, a card shows the workflow status:

Workflow Status Icon Message Border Color
rejected XCircle (red) "This declaration has been rejected." Red left border
sent_back RotateCcw (orange) "This declaration was sent back for revision." Orange left border
approved / completed CheckCheck (blue) "This workflow has been completed. All approvers have acted." Blue left border
Other (pending, not user's turn) Clock (blue) "You are not the current approver for this declaration. Please wait for your turn." Blue left border

Action Modals

Approve/Reject Confirmation Modal

Triggered by Approve or Reject buttons. Shows: - Declaration summary (ID, rep, form type, user's comment) - Confirm/Cancel buttons - On confirm: executes mutation, refetches, shows success toast

Send-Back Modal

Triggered by "Request Changes" button. Shows: - Declaration summary (ID, rep) - "What changes are needed?" mandatory textarea - Cancel / Send Back buttons (orange-themed) - On submit: executes sendBackWorkflowStep mutation, navigates to /dashboard

GraphQL Data Sources

Query/Mutation Purpose Key Parameters
declaration(id) Fetch declaration data id: number
workflowInstances(declarationId) Fetch linked workflow (first instance used) declarationId: number
workflowEvents(declarationId) Fetch all events for the declaration declarationId: number
approveWorkflowStep Approve current step instanceId, stepIndex, comment
rejectWorkflowStep Reject current step instanceId, stepIndex, comment
sendBackWorkflowStep Send back for revision instanceId, stepIndex, comment

Sequential Constraint

The workflow enforces step-by-step sequential progression. Step N+1 cannot begin until Step N is APPROVED. This constraint is enforced server-side by the NestJS workflow service. The frontend only displays approval actions for the user assigned to the current pending step.

What Happens After Each Action

  • Approve: advances current_step_index by 1. If it was the last step, workflow status changes to completed.
  • Reject: all prior approvals are discarded. Declaration status reverts; the representative must start over.
  • Send Back (Request Changes): returns the declaration to the representative with REVISION_REQUESTED status. Prior approvals are retained — when the rep resubmits, the workflow resumes at the step that sent it back.

Escalation Paths

The page itself does not implement reassignment or override. Escalation is handled externally: - If an approver is unavailable (on leave, unresponsive), an ORG_ADMIN or HR_ADMIN can reassign the workflow template's approver at /workflows (WorkflowManagementPage) — but this change applies to new declarations only, not existing instances - There is no in-page "reassign approver" button — the chatbot or an admin must use direct database/API operations for mid-workflow reassignment

Page Context Details

The page publishes { kind: 'workflow_for_declaration', declaration_id } to the chatbot. When responding to page-context queries, the chatbot should: - Read the workflow state to identify the current pending approver - Calculate idle days to determine urgency - Report which step is stuck and at which approver - Suggest appropriate next steps: nudge the approver (if still responsive), escalate to admin (if >5 days), or advise on send-back reasons


Previous Next

Back to Regnify Product Docs