Five stages of enterprise React workflow with Fluent UI cards: Design & UX, Development, Component Library, Testing, Deployment.

Technical Blog Article — App 54: Approval System with React, TypeScript, Fluent UI, and Enterprise Workflow Architecture

Introduction

In modern enterprise applications, approval workflows are everywhere. Before a purchase is finalized, before a document is published, before a ticket is escalated, or before a financial transaction is executed, there is usually an approval process controlling the flow.

This is exactly the focus of App 54 — Approval System.

Enterprise workflows are state machines.

Enterprise workflows are state machines.

Enterprise workflows are state machines.

  • enterprise workflow state
  • approval/rejection transitions
  • conditional rendering
  • immutable state updates
  • dynamic card rendering
  • derived UI
  • Microsoft Fluent UI enterprise composition
  • state-driven business interfaces

The central architectural idea is:

Workflow state changes
→ React re-renders
→ Enterprise UI updates automatically

This app simulates a corporate approval dashboard similar to what you might find in:

  • SharePoint approval systems
  • Microsoft Teams workflows
  • ERP systems
  • financial approval chains
  • HR systems
  • procurement portals
  • administrative dashboards
  • enterprise CRMs

Official references:


1. Creating the Project

Create the application with Vite

mkdir bloco03
cd bloco03
npm create vite@latest app54-approval-system -- --template react-ts
cd app54-approval-system
npm install

Install Fluent UI:

npm install @fluentui/react-components @fluentui/react-icons

2. Creating the Folder Structure

Professional React applications should separate responsibilities from the beginning.

Create folders:

mkdir src\components
mkdir src\models
mkdir src\data
mkdir src\styles

Create files:

New-Item src\models\ApprovalRequest.ts -ItemType File
New-Item src\data\approvalRequests.ts -ItemType File
New-Item src\components\ApprovalCard.tsx -ItemType File
New-Item src\components\ApprovalBoard.tsx -ItemType File
New-Item artigo.md -ItemType File

3. Final Project Structure

app54-approval-system/
src/
components/
ApprovalCard.tsx
ApprovalBoard.tsx
data/
approvalRequests.ts
models/
ApprovalRequest.ts
styles/
App.tsx
main.tsx
index.css

This structure follows the ReactLab architectural organization model.


4. Understanding the Enterprise Workflow

Before writing code, we must understand the business logic.

The application simulates approval requests.

Each request can be:

  • Pending
  • Approved
  • Rejected

The user interacts with buttons:

Approve
Reject

These buttons update React state.

React then automatically updates:

  • badges
  • icons
  • actions
  • card appearance

The architecture is completely declarative.

We never manually manipulate the DOM.


5. Create the Data Model

src\models\ApprovalRequest.ts

export type ApprovalStatus =
| "Pending"
| "Approved"
| "Rejected";
export interface ApprovalRequest {
id: number;
title: string;
requester: string;
department: string;
status: ApprovalStatus;
description: string;
}

6. Why the Model Layer Matters

This interface defines the exact structure of enterprise approval requests.

Benefits:

  • predictable architecture
  • safer refactoring
  • autocomplete
  • enterprise consistency
  • validation through TypeScript

Without models, enterprise applications become difficult to scale.

TypeScript ensures every request always contains:

  • id
  • title
  • requester
  • department
  • status
  • description

This is critical in professional React systems.


7. Create the Static Data Source

src\data\approvalRequests.ts

import type { ApprovalRequest } from "../models/ApprovalRequest";
export const approvalRequests: ApprovalRequest[] = [
{
id: 1,
title: "Budget Expansion",
requester: "Maria Johnson",
department: "Finance",
status: "Pending",
description:
"Request for additional quarterly operational budget.",
},
{
id: 2,
title: "Hardware Purchase",
requester: "David Wilson",
department: "Infrastructure",
status: "Pending",
description:
"Approval required for enterprise workstation acquisition.",
},
{
id: 3,
title: "Marketing Campaign",
requester: "Sophia Miller",
department: "Marketing",
status: "Approved",
description:
"Digital campaign approved for Q4 execution.",
},
];

8. Why Static Data Is Important First

At this stage, we intentionally avoid APIs.

Why?

Because the goal is learning:

  • rendering
  • state updates
  • component composition
  • enterprise workflow logic

without adding:

  • async complexity
  • loading states
  • fetch logic
  • effects

This follows the official React learning progression.


9. Create the Approval Card Component

src\components\ApprovalCard.tsx

import {
Badge,
Button,
Card,
CardHeader,
Text,
Title3,
} from "@fluentui/react-components";
import {
CheckmarkCircle24Regular,
DismissCircle24Regular,
Clock24Regular,
} from "@fluentui/react-icons";
import type {
ApprovalRequest,
ApprovalStatus,
} from "../models/ApprovalRequest";
interface ApprovalCardProps {
request: ApprovalRequest;
onUpdateStatus: (
id: number,
status: ApprovalStatus
) => void;
}
function getStatusIcon(status: ApprovalStatus) {
if (status === "Approved") {
return <CheckmarkCircle24Regular />;
}
if (status === "Rejected") {
return <DismissCircle24Regular />;
}
return <Clock24Regular />;
}
function getBadgeAppearance(status: ApprovalStatus) {
if (status === "Approved") {
return "filled" as const;
}
if (status === "Rejected") {
return "outline" as const;
}
return "tint" as const;
}
export function ApprovalCard({
request,
onUpdateStatus,
}: ApprovalCardProps) {
return (
<Card
style={{
padding: "24px",
display: "flex",
flexDirection: "column",
gap: "20px",
}}
>
<CardHeader
image={getStatusIcon(request.status)}
header={<Title3>{request.title}</Title3>}
description={
<Text>
{request.requester} — {request.department}
</Text>
}
/>
<Text>{request.description}</Text>
<Badge appearance={getBadgeAppearance(request.status)}>
{request.status}
</Badge>
{request.status === "Pending" && (
<div
style={{
display: "flex",
gap: "12px",
}}
>
<Button
appearance="primary"
onClick={() =>
onUpdateStatus(request.id, "Approved")
}
>
Approve
</Button>
<Button
appearance="secondary"
onClick={() =>
onUpdateStatus(request.id, "Rejected")
}
>
Reject
</Button>
</div>
)}
</Card>
);
}

10. Understanding Enterprise Component Composition

This component is responsible only for rendering one request.

It does not:

  • own global state
  • manage arrays
  • control the application

Its responsibility is:

Render one approval request.

This follows React component architecture principles.


11. Understanding Conditional Rendering

This section is fundamental:

{request.status === "Pending" && (

This means:

Only show the action buttons
if the request is still pending.

This is declarative rendering.

We do not manually hide elements.

Instead:

  • state changes
  • React re-renders
  • UI updates automatically

Official documentation:


12. Understanding Derived UI

The icon and badge appearance are derived from state.

Example:

getBadgeAppearance(request.status)

The UI derives from data.

This is the core React philosophy.

We do not manually style elements after rendering.

Instead:

  • state determines appearance
  • React computes the UI

13. Create the Approval Board

src\components\ApprovalBoard.tsx

import { useState } from "react";
import {
Text,
Title1,
} from "@fluentui/react-components";
import { approvalRequests } from "../data/approvalRequests";
import { ApprovalCard } from "./ApprovalCard";
import type {
ApprovalRequest,
ApprovalStatus,
} from "../models/ApprovalRequest";
export function ApprovalBoard() {
const [requests, setRequests] =
useState<ApprovalRequest[]>(
approvalRequests
);
function handleUpdateStatus(
id: number,
status: ApprovalStatus
) {
setRequests((currentRequests) =>
currentRequests.map((request) => {
if (request.id === id) {
return {
...request,
status,
};
}
return request;
})
);
}
return (
<section
style={{
maxWidth: "1200px",
margin: "0 auto",
}}
>
<Title1>Enterprise Approval System</Title1>
<Text>
Enterprise approval workflow built with React and Fluent UI.
</Text>
<div
style={{
display: "grid",
gridTemplateColumns:
"repeat(auto-fit, minmax(320px, 1fr))",
gap: "24px",
marginTop: "32px",
}}
>
{requests.map((request) => (
<ApprovalCard
key={request.id}
request={request}
onUpdateStatus={handleUpdateStatus}
/>
))}
</div>
</section>
);
}

14. Understanding useState

This is the core of the application:

const [requests, setRequests]

React state stores the approval workflow.

The UI derives entirely from this array.

This means:

State changes
→ React re-renders
→ Workflow UI changes automatically

Official documentation:


15. Understanding Immutable Updates

This logic is critical:

return {
...request,
status,
};

We never mutate state directly.

Incorrect:

request.status = status;

Correct:

return {
...request,
status,
};

This creates:

  • safer rendering
  • predictable updates
  • better debugging
  • React-friendly architecture

Official documentation:


16. Why map() Is So Important

React heavily relies on arrays.

This section:

requests.map(...)

transforms:

  • enterprise workflow data
    into:
  • React UI components

Conceptually:

Data
→ Components
→ Rendered UI

This is one of the most important React patterns.


17. Create App.tsx

src\App.tsx

import {
FluentProvider,
webLightTheme,
} from "@fluentui/react-components";
import { ApprovalBoard } from "./components/ApprovalBoard";
function App() {
return (
<FluentProvider theme={webLightTheme}>
<main
style={{
minHeight: "100vh",
backgroundColor: "#f5f5f5",
padding: "48px",
boxSizing: "border-box",
}}
>
<ApprovalBoard />
</main>
</FluentProvider>
);
}
export default App;

18. Understanding FluentProvider

The provider activates the Microsoft Fluent UI design system globally.

Without it:

  • components lose theme styling
  • typography becomes inconsistent
  • enterprise spacing disappears

This provider injects:

  • colors
  • typography
  • accessibility
  • spacing tokens
  • Microsoft visual identity

19. Create main.tsx

src\main.tsx

import React from "react";
import ReactDOM from "react-dom/client";
import App from "./App";
import "./index.css";
ReactDOM.createRoot(
document.getElementById("root")!
).render(
<React.StrictMode>
<App />
</React.StrictMode>
);

20. Create index.css

src\index.css

body {
margin: 0;
font-family: "Segoe UI", Arial, sans-serif;
}
* {
box-sizing: border-box;
}

21. Run the Application

Development server:

npm run dev

Production validation:

npm run build

Preview production build:

npm run preview

22. Complete Rendering Flow

User clicks Approve
→ handleUpdateStatus executes
→ requests state updates
→ React re-renders
→ ApprovalCard receives new status
→ Badge changes
→ Buttons disappear
→ UI reflects workflow state

This is pure React architecture.


23. Why There Is No useEffect

This application intentionally avoids effects.

There is no:

  • API synchronization
  • external systems
  • timers
  • subscriptions

Everything is internal UI state.

Therefore:

useState is correct.
useEffect would be unnecessary.

Official documentation:


24. Technical Summary

ConceptExplanation
useStateStores approval requests
Immutable updatesArrays updated safely
map() renderingDynamic card rendering
Conditional renderingActions hidden after approval
Fluent UI CardEnterprise request container
Derived UIBadge/icon depends on status
TypeScript modelPredictable enterprise data
Callback propsChild triggers parent updates
React renderingUI derived from state
FluentProviderGlobal Microsoft theme

25. Concept Table

ConceptFilePurpose
Approval modelApprovalRequest.tsDefines enterprise workflow data
Static dataapprovalRequests.tsInitial request source
Approval cardApprovalCard.tsxRenders one approval item
Approval boardApprovalBoard.tsxOwns workflow state
Conditional renderingApprovalCard.tsxControls button visibility
Immutable updatesApprovalBoard.tsxSafe state updates
Enterprise layoutApp.tsxMain dashboard shell
Global stylingindex.cssRemoves browser defaults

26. Official Documentation

TopicDocumentation
React LearnReact Learn
Rendering ListsRendering Lists
Conditional RenderingConditional Rendering
Updating Arrays in StateUpdating Arrays in State
State: A Component’s MemoryState: A Component’s Memory
Fluent UI ComponentsFluent UI React Components
Fluent UI CardFluent UI Card
Fluent UI BadgeFluent UI Badge
Vite GuideVite Guide
TypeScript DocsTypeScript Docs

27. Final Architectural Insight

The most important lesson from App 54 is:

Enterprise workflows are state machines.

Each request transitions between states:

  • Pending
  • Approved
  • Rejected

React simply reflects the current workflow state visually.

This same architecture appears later in:

  • SharePoint workflows
  • ticket systems
  • ERP dashboards
  • approval chains
  • CRM systems
  • enterprise admin portals
  • Microsoft ecosystem dashboards

Mastering this pattern is essential for enterprise React development.


Current Project Progress

BlockAppNameStatus
Block 101Hello React FluentCompleted
Block 102Profile CardCompleted
Block 103Product ListCompleted
Block 104Microsoft Style User CardCompleted
Block 105Static DashboardCompleted
Block 106Corporate Sidebar MenuCompleted
Block 107Visual Task ListCompleted
Block 108Timeline EventsCompleted
Block 109Employee TableCompleted
Block 110Email ListCompleted
Block 111Grid of CardsCompleted
Block 112Image GalleryCompleted
Block 113Movie CatalogCompleted
Block 114Football TeamsCompleted
Block 115News PageCompleted
Block 116Financial DashboardCompleted
Block 117SharePoint Style LayoutCompleted
Block 118File ExplorerCompleted
Block 119Corporate PortalCompleted
Block 120Microsoft Style Landing PageCompleted
Block 221Modern CounterCompleted
Block 222Toggle ThemeCompleted
Block 223React CalculatorCompleted
Block 224Login FormCompleted
Block 225User RegistrationCompleted
Block 226Complete ToDo ListCompleted
Block 227Shopping ListCompleted
Block 228Product FilterCompleted
Block 229Employee SearchCompleted
Block 230Shopping CartCompleted
Block 231Grade SimulatorCompleted
Block 232Inventory ControlCompleted
Block 233Contact AgendaCompleted
Block 234Currency ConverterCompleted
Block 235BMI CalculatorCompleted
Block 236Installment SimulatorCompleted
Block 237Voting PanelCompleted
Block 238Interactive QuizCompleted
Block 239Team ManagerCompleted
Block 240Dynamic DashboardCompleted
Block 341Microsoft Style LoginCompleted
Block 342Corporate FormCompleted
Block 343Tabs NavigationCompleted
Block 344Dialog ManagerCompleted
Block 345Executive DashboardCompleted
Block 346DataGrid CatalogCompleted
Block 347Enterprise User ListCompleted
Block 348Sidebar NavigationCompleted
Block 349Corporate HeaderCompleted
Block 350Professional ToolbarCompleted
Block 351Notification CenterCompleted
Block 352Administrative PanelCompleted
Block 353Ticket ManagerCompleted
Block 354Approval SystemCurrent
Block 355Corporate AgendaNext
Edvaldo Guimrães Filho Avatar

Published by