Skip to main content

Status Components

The Status Components provide status badges, workflow indicators, and approval tracking functionality for the WorkPayCore Frontend application. These components handle various status states with consistent styling, colors, and behavioral patterns across different modules.

Overview

This document covers status-related components that display approval workflows, process states, and status indicators throughout the application with WorkPay's branded styling and interaction patterns.

Components Overview

Core Status Components

Workflow Components

Specialized Status Components


StatusFormatter

The main status badge component with enhanced styling, dot indicators, and tooltip support.

Component Location

import StatusFormatter from 'components/Status/StatusFormatter';

Props

PropTypeRequiredDefaultDescription
statusstring-Status value to display
showTooltipboolean-trueWhether to show tooltip
tooltipstring--Custom tooltip text

TypeScript Interface

interface StatusProps {
status: string;
showTooltip?: boolean;
tooltip?: string;
}

Supported Status Values

The component supports a wide range of status values with predefined colors:

Positive/Success States

  • active - Green background, white text
  • approved - Green background, white text
  • completed - Green background, white text
  • successful - Green background, white text
  • paid - Green background, white text

Pending/Processing States

  • pending - Chrome background, dark text
  • processing - Chrome background, dark text
  • awaiting_approval - Chrome background, dark text
  • sent - Chrome background, dark text

Negative/Error States

  • failed - Red background, white text
  • denied - Red background, white text
  • cancelled - Red background, white text
  • rejected - Red background, white text
  • disapproved - Red background, white text

Neutral/Inactive States

  • inactive - Gray background, dark text
  • draft - Purple background, white text
  • on_hold - Sky background, white text

Usage Examples

Basic Status Display

import StatusFormatter from 'components/Status/StatusFormatter';

function EmployeeStatus({ employee }) {
return (
<StatusFormatter
status={employee.status}
showTooltip
tooltip='Employee current status'
/>
);
}

Leave Request Status

import StatusFormatter from 'components/Status/StatusFormatter';

function LeaveRequestStatus({ request }) {
return (
<StatusFormatter
status={request.status}
showTooltip
tooltip={`Leave request ${request.status} on ${request.updatedAt}`}
/>
);
}

Payment Status with Dot Indicator

import StatusFormatter from 'components/Status/StatusFormatter';

function PaymentStatus({ payment }) {
return (
<StatusFormatter
status={payment.status}
showTooltip
tooltip={`Payment ${payment.status} - ${payment.amount}`}
/>
);
}

Multiple Status Display

import StatusFormatter from 'components/Status/StatusFormatter';
import { HStack } from '@chakra-ui/react';

function MultipleStatusDisplay({ items }) {
return (
<HStack spacing={2}>
{items.map(item => (
<StatusFormatter
key={item.id}
status={item.status}
showTooltip
tooltip={`${item.type}: ${item.status}`}
/>
))}
</HStack>
);
}

WPStatusFormatter

Legacy status formatter component with basic styling and color mapping.

Component Location

import WPStatusFormatter from 'components/Notifications/StatusFormatter';

Props

PropTypeRequiredDefaultDescription
statusstring-Status value to display
colorCodestring--Custom color override

Usage Examples

Basic Legacy Status

import WPStatusFormatter from 'components/Notifications/StatusFormatter';

function LegacyStatusDisplay({ status }) {
return <WPStatusFormatter status={status} />;
}

Custom Color Status

import WPStatusFormatter from 'components/Notifications/StatusFormatter';

function CustomColorStatus({ status }) {
return <WPStatusFormatter status={status} colorCode='#FF6B6B' />;
}

WpBaseStatusFormatter

Base reusable status component with full customization support.

Component Location

import WpBaseStatusFormatter from 'components/Status/WpBaseStatusFormatter';

Props

PropTypeRequiredDefaultDescription
statusstring-Status value
statusMapRecord<string, object>--Custom status mapping
titlestring--Custom title override
hasDotboolean-trueShow status dot indicator
leftIconobject--Custom left icon with tooltip
rightIconobject--Custom right icon with tooltip
customColorsobject--Color overrides
sizestring-'md'Badge size

TypeScript Interface

interface StatusEntry {
bgColor: string;
color: string;
dotColor: string;
defaultTitle: string;
}

interface WpBaseStatusBadgeProps<T extends string> {
status: T;
statusMap?: Record<T, StatusEntry>;
title?: string;
hasDot?: boolean;
leftIcon?: {
icon: ReactNode;
tooltip?: string;
};
rightIcon?: {
icon?: ReactNode;
tooltip?: string;
};
customColors?: Partial&lt;StatusEntry&gt;;
size?: 'sm' | 'md' | 'lg';
}

Usage Examples

Custom Status Mapping

import WpBaseStatusFormatter from 'components/Status/WpBaseStatusFormatter';

function CustomStatusBadge({ status }) {
const customStatusMap = {
'in-progress': {
bgColor: 'blue.100',
color: 'blue.800',
dotColor: 'blue.500',
defaultTitle: 'In Progress',
},
'under-review': {
bgColor: 'orange.100',
color: 'orange.800',
dotColor: 'orange.500',
defaultTitle: 'Under Review',
},
};

return (
<WpBaseStatusFormatter
status={status}
statusMap={customStatusMap}
hasDot
size='md'
/>
);
}

Status with Custom Icons

import WpBaseStatusFormatter from 'components/Status/WpBaseStatusFormatter';
import { CheckIcon, WarningIcon } from '@chakra-ui/icons';

function IconStatusBadge({ status }) {
const leftIcon =
status === 'completed'
? {
icon: <CheckIcon />,
tooltip: 'Task completed successfully',
}
: undefined;

const rightIcon =
status === 'warning'
? {
icon: <WarningIcon />,
tooltip: 'Attention required',
}
: undefined;

return (
<WpBaseStatusFormatter
status={status}
leftIcon={leftIcon}
rightIcon={rightIcon}
size='lg'
/>
);
}

Payment Status Example

function PaymentStatusBadge({ status }) {
const paymentStatusMap = {
pending: {
bgColor: 'yellow.100',
color: 'yellow.800',
dotColor: 'yellow.500',
defaultTitle: 'Payment Pending',
},
completed: {
bgColor: 'green.100',
color: 'green.800',
dotColor: 'green.500',
defaultTitle: 'Payment Completed',
},
failed: {
bgColor: 'red.100',
color: 'red.800',
dotColor: 'red.500',
defaultTitle: 'Payment Failed',
},
};

return (
<WpBaseStatusFormatter
status={status}
statusMap={paymentStatusMap}
hasDot
/>
);
}

PolicyViolationBadge

Specialized badge for displaying policy violations with tooltip information.

Component Location

import PolicyViolationBadge from 'components/Status/PolicyViolationBadge';

Props

PropTypeRequiredDefaultDescription
statusTextstring-Status text display
tooltipstring--Tooltip information

Usage Examples

Basic Policy Violation

import PolicyViolationBadge from 'components/Status/PolicyViolationBadge';

function AttendanceViolation({ violation }) {
return (
<PolicyViolationBadge
statusText='Policy Violation'
tooltip={violation.description}
/>
);
}

Leave Policy Violation

import PolicyViolationBadge from 'components/Status/PolicyViolationBadge';

function LeaveViolationBadge({ violation }) {
return (
<PolicyViolationBadge
statusText='Leave Policy Violation'
tooltip={`Exceeded ${violation.type} limit by ${violation.days} days`}
/>
);
}

ApprovalWorkflowStatus

Complex component for displaying approval workflow stages and progress.

Component Location

import { ApprovalWorkflowStatus } from 'components/Status';

Props

PropTypeRequiredDefaultDescription
originalobject-Data object with approval information
modulestring-Module name (e.g., 'Leaves', 'Expenses')
isNewDesignboolean-falseUse new design layout

TypeScript Interface

interface ApprovalWorkflowStatusProps {
original: Record<string, any>;
module: string;
isNewDesign?: boolean;
}

Supported Modules

  • Leaves - Leave request approvals
  • Expenses - Expense claim approvals
  • Loans - Loan application approvals
  • Salary Advance - Salary advance approvals
  • Overtime - Overtime request approvals
  • Payroll - Payroll approvals
  • Global Payroll - Global payroll approvals

Usage Examples

Leave Approval Workflow

import { ApprovalWorkflowStatus } from 'components/Status';

function LeaveApprovalDisplay({ leaveRequest }) {
return (
<ApprovalWorkflowStatus
original={leaveRequest}
module='Leaves'
isNewDesign
/>
);
}

Expense Approval Workflow

import { ApprovalWorkflowStatus } from 'components/Status';

function ExpenseApprovalDisplay({ expense }) {
return <ApprovalWorkflowStatus original={expense} module='Expenses' />;
}

Loan Approval Workflow

import { ApprovalWorkflowStatus } from 'components/Status';

function LoanApprovalDisplay({ loan }) {
return <ApprovalWorkflowStatus original={loan} module='Loans' isNewDesign />;
}

ApprovalActionStageHistory

Component for displaying approval stages timeline with detailed history.

Component Location

import { ApprovalActionStageHistory } from 'components/Status';

Props

PropTypeRequiredDefaultDescription
stagesarray-Array of approval stages
modulestring-Module name
isNewDesignboolean-falseUse new design layout

Usage Examples

Approval History Tooltip

import { ApprovalActionStageHistory } from 'components/Status';

function ApprovalHistoryTooltip({ stages, module }) {
return (
<ApprovalActionStageHistory stages={stages} module={module} isNewDesign />
);
}

Specialized Status Components

ExpenseStatusFormatter

Expense-specific status formatter with expense workflow states.

import ExpenseStatusFormatter from 'containers/ExpensesRedesign/Tables/ExpensesStatusFormatter';

function ExpenseRow({ expense }) {
return <ExpenseStatusFormatter row={expense} />;
}

LoansStatusFormatter

Loan-specific status formatter with loan lifecycle states.

import LoansStatusFormatter from 'containers/Loans/statusFormatter';

function LoanRow({ loan }) {
return <LoansStatusFormatter row={loan} />;
}

DisciplinaryStatusBadge

Disciplinary tracking status with specialized workflow states.

import DisciplinaryStatusBadge from 'containers/DisciplinaryTracking/Page/table/columns/DisciplinaryStatusBadge';

function DisciplinaryRow({ disciplinary }) {
return (
<DisciplinaryStatusBadge
status={disciplinary.status}
statusText={disciplinary.statusText}
/>
);
}

BatchStatusBadge

Payment batch status indicator with success/failure counts.

import BatchStatusBadge from 'containers/PaymentsRedesign/Components/BatchStatusBadge';

function PaymentBatchRow({ batch, activeTab }) {
return <BatchStatusBadge row={{ original: batch }} activeTab={activeTab} />;
}

Status Patterns

Basic Status Display Pattern

import StatusFormatter from 'components/Status/StatusFormatter';
import { HStack, VStack, Text } from '@chakra-ui/react';

function StatusDisplayPattern({ item }) {
return (
<HStack spacing={4} align='center'>
<VStack align='start' spacing={0}>
<Text fontWeight='medium'>{item.name}</Text>
<Text fontSize='sm' color='gray.600'>
{item.description}
</Text>
</VStack>

<StatusFormatter
status={item.status}
showTooltip
tooltip={`${item.type} status: ${item.status}`}
/>
</HStack>
);
}

Multi-Stage Approval Pattern

import { ApprovalWorkflowStatus } from 'components/Status';
import StatusFormatter from 'components/Status/StatusFormatter';
import { VStack, HStack, Text, Progress } from '@chakra-ui/react';

function MultiStageApprovalPattern({ request }) {
const approvalProgress =
(request.approved_attempts_count / request.approval_stages.length) * 100;

return (
<VStack spacing={4} align='stretch'>
{/* Current Status */}
<HStack justify='space-between' align='center'>
<Text fontWeight='medium'>Current Status</Text>
<StatusFormatter
status={request.status}
showTooltip
tooltip='Current approval status'
/>
</HStack>

{/* Progress Bar */}
<VStack align='stretch' spacing={2}>
<HStack justify='space-between'>
<Text fontSize='sm'>Approval Progress</Text>
<Text fontSize='sm' color='gray.600'>
{request.approved_attempts_count} of{' '}
{request.approval_stages.length} stages
</Text>
</HStack>
<Progress value={approvalProgress} colorScheme='green' />
</VStack>

{/* Workflow Status */}
<ApprovalWorkflowStatus original={request} module='Leaves' isNewDesign />
</VStack>
);
}

Status with Actions Pattern

import StatusFormatter from 'components/Status/StatusFormatter';
import {
HStack,
Button,
Menu,
MenuButton,
MenuList,
MenuItem,
} from '@chakra-ui/react';

function StatusWithActionsPattern({ item, onStatusChange }) {
const canApprove = item.status === 'pending' && item.canApprove;
const canReject = item.status === 'pending' && item.canReject;

return (
<HStack spacing={4} align='center'>
<StatusFormatter
status={item.status}
showTooltip
tooltip={`Request ${item.status} on ${item.updatedAt}`}
/>

{(canApprove || canReject) && (
&lt;Menu&gt;
<MenuButton as={Button} variant='ghost' size='sm'>
Actions
</MenuButton>
&lt;MenuList&gt;
{canApprove && (
<MenuItem onClick={() => onStatusChange('approved')}>
Approve
</MenuItem>
)}
{canReject && (
<MenuItem onClick={() => onStatusChange('rejected')}>
Reject
</MenuItem>
)}
</MenuList>
</Menu>
)}
</HStack>
);
}

Conditional Status Display Pattern

import StatusFormatter from 'components/Status/StatusFormatter';
import PolicyViolationBadge from 'components/Status/PolicyViolationBadge';
import { HStack } from '@chakra-ui/react';

function ConditionalStatusPattern({ item }) {
const hasPolicyViolation = item.policyViolations?.length > 0;

return (
<HStack spacing={2}>
<StatusFormatter
status={item.status}
showTooltip
tooltip={`${item.type} status`}
/>

{hasPolicyViolation && (
<PolicyViolationBadge
statusText='Policy Violation'
tooltip={item.policyViolations.map(v => v.description).join(', ')}
/>
)}
</HStack>
);
}

Best Practices

Status Selection

  1. Use Appropriate Components

    • StatusFormatter for modern, enhanced status display
    • WPStatusFormatter for legacy compatibility
    • WpBaseStatusFormatter for custom status mappings
    • PolicyViolationBadge for policy violations
  2. Consistent Status Values

    • Use standardized status values across modules
    • Maintain consistent color coding
    • Provide meaningful tooltips
  3. Module-Specific Formatters

    • Use specialized formatters for complex workflows
    • Maintain consistency within modules
    • Document custom status mappings

Accessibility

  1. Color Accessibility

    • Don't rely solely on color for status information
    • Provide sufficient contrast ratios
    • Include text labels for all status states
  2. Screen Reader Support

    • Provide meaningful aria-labels
    • Include status descriptions in tooltips
    • Use semantic HTML structure
  3. Keyboard Navigation

    • Ensure tooltips are accessible via keyboard
    • Provide keyboard shortcuts for status actions
    • Handle focus management properly

Performance

  1. Component Optimization

    • Memoize status components with stable props
    • Avoid unnecessary re-renders
    • Use efficient status lookups
  2. Status Mapping

    • Cache status mappings for repeated use
    • Use efficient data structures
    • Minimize prop drilling

Testing

Unit Tests

import { render, screen } from '@testing-library/react';
import StatusFormatter from 'components/Status/StatusFormatter';

describe('StatusFormatter', () => {
it('renders status correctly', () => {
render(<StatusFormatter status='approved' />);
expect(screen.getByText('approved')).toBeInTheDocument();
});

it('shows tooltip when provided', async () => {
const { user } = render(
<StatusFormatter
status='pending'
tooltip='Request is pending approval'
/>,
);

await user.hover(screen.getByText('pending'));
expect(screen.getByText('Request is pending approval')).toBeInTheDocument();
});

it('applies correct styling for different statuses', () => {
const { rerender } = render(<StatusFormatter status='approved' />);
expect(screen.getByText('approved')).toHaveClass('chakra-badge');

rerender(<StatusFormatter status='rejected' />);
expect(screen.getByText('rejected')).toHaveClass('chakra-badge');
});
});

Integration Tests

describe('ApprovalWorkflowStatus', () => {
it('displays correct approval stage', () => {
const mockRequest = {
approval_stages: [{ id: 1 }, { id: 2 }],
approved_attempts: [{ id: 1 }],
approved_attempts_count: 1,
status: 'NOT_APPROVED',
};

render(<ApprovalWorkflowStatus original={mockRequest} module='Leaves' />);

expect(screen.getByText('2nd approval stage')).toBeInTheDocument();
});

it('handles completed approval workflow', () => {
const mockRequest = {
approval_stages: [{ id: 1 }, { id: 2 }],
approved_attempts: [{ id: 1 }, { id: 2 }],
approved_attempts_count: 2,
status: 'APPROVED',
};

render(<ApprovalWorkflowStatus original={mockRequest} module='Leaves' />);

expect(screen.getByText('approved')).toBeInTheDocument();
});
});

Migration Guide

From Legacy Status Components

  1. Update Import Paths

    // Old
    import StatusBadge from 'components/OldStatusBadge';

    // New
    import StatusFormatter from 'components/Status/StatusFormatter';
  2. Update Props Structure

    // Old
    <StatusBadge status="approved" color="green" />

    // New
    <StatusFormatter
    status="approved"
    showTooltip
    tooltip="Status information"
    />
  3. Update Status Values

    // Old inconsistent values
    status: 'APPROVED' | 'REJECTED' | 'PENDING';

    // New consistent values
    status: 'approved' | 'rejected' | 'pending';

Custom Status Mappings

// Old manual color mapping
const getStatusColor = status => {
switch (status) {
case 'approved':
return 'green';
case 'rejected':
return 'red';
default:
return 'gray';
}
};

// New status mapping
const customStatusMap = {
approved: {
bgColor: 'green.100',
color: 'green.800',
dotColor: 'green.500',
defaultTitle: 'Approved',
},
rejected: {
bgColor: 'red.100',
color: 'red.800',
dotColor: 'red.500',
defaultTitle: 'Rejected',
},
};

Advanced Usage

Dynamic Status Updates

import { useState, useEffect } from 'react';
import StatusFormatter from 'components/Status/StatusFormatter';

function DynamicStatusDisplay({ itemId }) {
const [status, setStatus] = useState('pending');
const [statusHistory, setStatusHistory] = useState([]);

useEffect(() => {
// Subscribe to status updates
const subscription = subscribeToStatusUpdates(itemId, newStatus => {
setStatusHistory(prev => [...prev, { status, timestamp: Date.now() }]);
setStatus(newStatus);
});

return () => subscription.unsubscribe();
}, [itemId]);

return (
<VStack spacing={2} align='stretch'>
<HStack justify='space-between'>
&lt;Text&gt;Current Status</Text>
<StatusFormatter
status={status}
showTooltip
tooltip={`Status updated ${new Date().toLocaleString()}`}
/>
</HStack>

{statusHistory.length > 0 && (
<VStack align='stretch' spacing={1}>
<Text fontSize='sm' fontWeight='medium'>
Status History
</Text>
{statusHistory.slice(-3).map((item, index) => (
<HStack key={index} justify='space-between' fontSize='xs'>
<StatusFormatter status={item.status} />
<Text color='gray.600'>
{new Date(item.timestamp).toLocaleString()}
</Text>
</HStack>
))}
</VStack>
)}
</VStack>
);
}

Bulk Status Operations

import StatusFormatter from 'components/Status/StatusFormatter';
import { useState } from 'react';

function BulkStatusOperations({ items, onBulkStatusChange }) {
const [selectedItems, setSelectedItems] = useState([]);
const [bulkStatus, setBulkStatus] = useState('');

const statusCounts = items.reduce((acc, item) => {
acc[item.status] = (acc[item.status] || 0) + 1;
return acc;
}, {});

const handleBulkApproval = () => {
onBulkStatusChange(selectedItems, 'approved');
setSelectedItems([]);
};

return (
<VStack spacing={4} align='stretch'>
{/* Status Summary */}
<HStack spacing={4}>
{Object.entries(statusCounts).map(([status, count]) => (
<VStack key={status} spacing={1}>
<StatusFormatter status={status} />
<Text fontSize='sm'>{count} items</Text>
</VStack>
))}
</HStack>

{/* Bulk Actions */}
{selectedItems.length > 0 && (
<HStack spacing={2}>
<Button size='sm' colorScheme='green' onClick={handleBulkApproval}>
Approve Selected ({selectedItems.length})
</Button>
<Button
size='sm'
variant='outline'
onClick={() => setSelectedItems([])}
>
Clear Selection
</Button>
</HStack>
)}
</VStack>
);
}

Status Workflow Visualization

import StatusFormatter from 'components/Status/StatusFormatter';
import { HStack, VStack, Box, Text } from '@chakra-ui/react';

function StatusWorkflowVisualization({ workflow, currentStatus }) {
const currentIndex = workflow.findIndex(
step => step.status === currentStatus,
);

return (
<VStack spacing={4} align='stretch'>
<Text fontWeight='bold'>Workflow Progress</Text>

<HStack spacing={4} align='center'>
{workflow.map((step, index) => (
<VStack key={step.status} spacing={2} align='center'>
<StatusFormatter
status={step.status}
showTooltip
tooltip={step.description}
/>

<Text fontSize='xs' textAlign='center'>
{step.label}
</Text>

{index < workflow.length - 1 && (
<Box
w='20px'
h='2px'
bg={index < currentIndex ? 'green.400' : 'gray.300'}
ml={4}
/>
)}
</VStack>
))}
</HStack>

<Text fontSize='sm' color='gray.600'>
Step {currentIndex + 1} of {workflow.length}
</Text>
</VStack>
);
}