feat: add StatusBadge component with configurable status display
This commit is contained in:
@@ -0,0 +1,231 @@
|
||||
import React from 'react';
|
||||
import { Badge, BadgeProps } from '@mantine/core';
|
||||
import {
|
||||
CheckCircle2,
|
||||
XCircle,
|
||||
Clock,
|
||||
Edit2,
|
||||
Loader2,
|
||||
Package,
|
||||
AlertCircle,
|
||||
RotateCcw,
|
||||
Check,
|
||||
CreditCard,
|
||||
Send,
|
||||
Users,
|
||||
} from 'lucide-react';
|
||||
|
||||
export enum STATUS_DATA {
|
||||
DRAFT = 'draft',
|
||||
|
||||
ACTIVE = 'active',
|
||||
ACTIVATED = 'activated',
|
||||
|
||||
INACTIVE = 'inactive',
|
||||
DEACTIVATED = 'deactivated',
|
||||
|
||||
REQUEST = 'request',
|
||||
REQUESTING = 'requesting',
|
||||
REQUESTED = 'requested',
|
||||
|
||||
APPROVAL = 'approval',
|
||||
APPROVED = 'approved',
|
||||
|
||||
OPEN = 'open',
|
||||
OPENED = 'opened',
|
||||
|
||||
PENDING = 'pending',
|
||||
|
||||
WAIT = 'wait',
|
||||
WAITING = 'waiting',
|
||||
WAITING_LIST = 'waiting-list',
|
||||
|
||||
PROCESS = 'process',
|
||||
PROCESSING = 'processing',
|
||||
IN_PROCESS = 'in-process',
|
||||
PROCESSED = 'processed',
|
||||
|
||||
CLOSE = 'close',
|
||||
CLOSED = 'closed',
|
||||
|
||||
CANCEL = 'cancel',
|
||||
CANCELLED = 'cancelled',
|
||||
|
||||
COMPLETE = 'complete',
|
||||
COMPLETED = 'completed',
|
||||
|
||||
TODO = 'todo',
|
||||
DONE = 'done',
|
||||
PARTIAL_DONE = 'partial-done',
|
||||
|
||||
ON_HOLD = 'on-hold',
|
||||
|
||||
SHIPMENT = 'shipment',
|
||||
SHIPPING = 'shipping',
|
||||
SHIPPED = 'shipped',
|
||||
|
||||
EXPIRED = 'expired',
|
||||
|
||||
REFUND = 'refund',
|
||||
REFUNDED = 'refunded',
|
||||
PARTIAL_REFUND = 'partial-refund',
|
||||
PARTIALLY_REFUNDED = 'partially-refunded',
|
||||
REFUND_PROCESSING = 'refund-processing',
|
||||
CANCEL_REFUNDED = 'cancel-refunded',
|
||||
REFUND_PROCESS = 'refund-processing',
|
||||
|
||||
TRANSFERRED = 'transferred',
|
||||
|
||||
SETTLEMENT = 'settlement',
|
||||
SETTLED = 'settled',
|
||||
SETTLED_FREE = 'settled-free',
|
||||
|
||||
REJECTION = 'rejection',
|
||||
REJECTED = 'rejected',
|
||||
|
||||
PAYMENT = 'payment',
|
||||
PAID = 'paid',
|
||||
|
||||
ISSUE = 'issue',
|
||||
PROBLEM = 'problem',
|
||||
RESOLVED = 'resolved',
|
||||
|
||||
CHECKOUT = 'checkout',
|
||||
INVOICE = 'invoice',
|
||||
INVOICED = 'invoiced',
|
||||
|
||||
JOINED = 'joined',
|
||||
INVITED = 'invited',
|
||||
|
||||
PRESENT = 'present',
|
||||
}
|
||||
|
||||
export type StatusBadgeConfigCallback = (status: string) => Partial<BadgeProps>;
|
||||
|
||||
export interface StatusBadgeProps extends Omit<BadgeProps, 'color' | 'leftSection'> {
|
||||
/** The status string or STATUS_DATA enum value */
|
||||
status: STATUS_DATA | string;
|
||||
/** Optional custom label, overrides default status string */
|
||||
label?: string;
|
||||
/** Callback to provide custom badge props (color, icon) for a specific status */
|
||||
getCustomConfig?: StatusBadgeConfigCallback;
|
||||
/** Overrides default and custom color */
|
||||
color?: string;
|
||||
/** Overrides default and custom leftSection icon */
|
||||
leftSection?: React.ReactNode;
|
||||
}
|
||||
|
||||
const ICON_SIZE = 12;
|
||||
const STROKE_WIDTH = 2.5;
|
||||
|
||||
const getIcon = (IconCmp: any) => <IconCmp size={ICON_SIZE} strokeWidth={STROKE_WIDTH} />;
|
||||
|
||||
export const DEFAULT_STATUS_MAP: Record<string, BadgeProps> = {
|
||||
// #B6B6B6
|
||||
[STATUS_DATA.DRAFT]: { color: '#B6B6B6', leftSection: getIcon(Edit2) },
|
||||
[STATUS_DATA.CHECKOUT]: { color: '#B6B6B6', leftSection: getIcon(CreditCard) },
|
||||
|
||||
// #66BB6A
|
||||
[STATUS_DATA.ACTIVE]: { color: '#66BB6A', leftSection: getIcon(CheckCircle2) },
|
||||
[STATUS_DATA.ACTIVATED]: { color: '#66BB6A', leftSection: getIcon(CheckCircle2) },
|
||||
[STATUS_DATA.APPROVAL]: { color: '#66BB6A', leftSection: getIcon(CheckCircle2) },
|
||||
[STATUS_DATA.APPROVED]: { color: '#66BB6A', leftSection: getIcon(CheckCircle2) },
|
||||
[STATUS_DATA.COMPLETE]: { color: '#66BB6A', leftSection: getIcon(CheckCircle2) },
|
||||
[STATUS_DATA.COMPLETED]: { color: '#66BB6A', leftSection: getIcon(CheckCircle2) },
|
||||
[STATUS_DATA.DONE]: { color: '#66BB6A', leftSection: getIcon(CheckCircle2) },
|
||||
[STATUS_DATA.PARTIAL_DONE]: { color: '#66BB6A', leftSection: getIcon(Clock) },
|
||||
[STATUS_DATA.JOINED]: { color: '#66BB6A', leftSection: getIcon(Users) },
|
||||
[STATUS_DATA.RESOLVED]: { color: '#66BB6A', leftSection: getIcon(CheckCircle2) },
|
||||
[STATUS_DATA.PAID]: { color: '#66BB6A', leftSection: getIcon(CreditCard) },
|
||||
|
||||
// #EF5350
|
||||
[STATUS_DATA.INACTIVE]: { color: '#EF5350', leftSection: getIcon(XCircle) },
|
||||
[STATUS_DATA.DEACTIVATED]: { color: '#EF5350', leftSection: getIcon(XCircle) },
|
||||
[STATUS_DATA.CLOSE]: { color: '#EF5350', leftSection: getIcon(XCircle) },
|
||||
[STATUS_DATA.CLOSED]: { color: '#EF5350', leftSection: getIcon(XCircle) },
|
||||
[STATUS_DATA.CANCELLED]: { color: '#EF5350', leftSection: getIcon(XCircle) },
|
||||
[STATUS_DATA.CANCEL_REFUNDED]: { color: '#EF5350', leftSection: getIcon(XCircle) },
|
||||
[STATUS_DATA.REJECTION]: { color: '#EF5350', leftSection: getIcon(XCircle) },
|
||||
[STATUS_DATA.REJECTED]: { color: '#EF5350', leftSection: getIcon(XCircle) },
|
||||
[STATUS_DATA.ISSUE]: { color: '#EF5350', leftSection: getIcon(AlertCircle) },
|
||||
[STATUS_DATA.PROBLEM]: { color: '#EF5350', leftSection: getIcon(AlertCircle) },
|
||||
|
||||
// #42A5F5
|
||||
[STATUS_DATA.REQUEST]: { color: '#42A5F5', leftSection: getIcon(Send) },
|
||||
[STATUS_DATA.REQUESTING]: { color: '#42A5F5', leftSection: getIcon(Send) },
|
||||
[STATUS_DATA.REQUESTED]: { color: '#42A5F5', leftSection: getIcon(Send) },
|
||||
[STATUS_DATA.OPEN]: { color: '#42A5F5', leftSection: getIcon(CheckCircle2) },
|
||||
[STATUS_DATA.OPENED]: { color: '#42A5F5', leftSection: getIcon(CheckCircle2) },
|
||||
[STATUS_DATA.PROCESS]: { color: '#42A5F5', leftSection: getIcon(Loader2) },
|
||||
[STATUS_DATA.PROCESSING]: { color: '#42A5F5', leftSection: getIcon(Loader2) },
|
||||
[STATUS_DATA.IN_PROCESS]: { color: '#42A5F5', leftSection: getIcon(Loader2) },
|
||||
[STATUS_DATA.PROCESSED]: { color: '#42A5F5', leftSection: getIcon(Check) },
|
||||
[STATUS_DATA.SHIPMENT]: { color: '#42A5F5', leftSection: getIcon(Package) },
|
||||
[STATUS_DATA.SHIPPING]: { color: '#42A5F5', leftSection: getIcon(Package) },
|
||||
[STATUS_DATA.SHIPPED]: { color: '#42A5F5', leftSection: getIcon(Check) },
|
||||
[STATUS_DATA.PAYMENT]: { color: '#42A5F5', leftSection: getIcon(CreditCard) },
|
||||
[STATUS_DATA.INVOICE]: { color: '#42A5F5', leftSection: getIcon(CreditCard) },
|
||||
[STATUS_DATA.INVOICED]: { color: '#42A5F5', leftSection: getIcon(CreditCard) },
|
||||
|
||||
// #DFAB45
|
||||
[STATUS_DATA.PENDING]: { color: '#DFAB45', leftSection: getIcon(Clock) },
|
||||
|
||||
// #6C5DD0
|
||||
[STATUS_DATA.WAIT]: { color: '#6C5DD0', leftSection: getIcon(Clock) },
|
||||
[STATUS_DATA.WAITING]: { color: '#6C5DD0', leftSection: getIcon(Clock) },
|
||||
[STATUS_DATA.WAITING_LIST]: { color: '#6C5DD0', leftSection: getIcon(Clock) },
|
||||
|
||||
// #D26DA9
|
||||
[STATUS_DATA.CANCEL]: { color: '#D26DA9', leftSection: getIcon(XCircle) },
|
||||
[STATUS_DATA.TRANSFERRED]: { color: '#D26DA9', leftSection: getIcon(Send) },
|
||||
|
||||
// #9575CD
|
||||
[STATUS_DATA.TODO]: { color: '#9575CD', leftSection: getIcon(Clock) },
|
||||
|
||||
// #FFB74D
|
||||
[STATUS_DATA.ON_HOLD]: { color: '#FFB74D', leftSection: getIcon(Clock) },
|
||||
|
||||
// #787878
|
||||
[STATUS_DATA.EXPIRED]: { color: '#787878', leftSection: getIcon(AlertCircle) },
|
||||
|
||||
// #FE725E
|
||||
[STATUS_DATA.REFUND]: { color: '#FE725E', leftSection: getIcon(RotateCcw) },
|
||||
[STATUS_DATA.REFUNDED]: { color: '#FE725E', leftSection: getIcon(RotateCcw) },
|
||||
[STATUS_DATA.PARTIAL_REFUND]: { color: '#FE725E', leftSection: getIcon(RotateCcw) },
|
||||
[STATUS_DATA.PARTIALLY_REFUNDED]: { color: '#FE725E', leftSection: getIcon(RotateCcw) },
|
||||
[STATUS_DATA.INVITED]: { color: '#FE725E', leftSection: getIcon(Users) },
|
||||
|
||||
// #D7A29A
|
||||
[STATUS_DATA.REFUND_PROCESSING]: { color: '#D7A29A', leftSection: getIcon(Loader2) },
|
||||
|
||||
// #5DADD0
|
||||
[STATUS_DATA.SETTLEMENT]: { color: '#5DADD0', leftSection: getIcon(CheckCircle2) },
|
||||
[STATUS_DATA.SETTLED]: { color: '#5DADD0', leftSection: getIcon(CheckCircle2) },
|
||||
[STATUS_DATA.SETTLED_FREE]: { color: '#5DADD0', leftSection: getIcon(CheckCircle2) },
|
||||
|
||||
// #E2B43E
|
||||
[STATUS_DATA.PRESENT]: { color: '#E2B43E', leftSection: getIcon(Users) },
|
||||
};
|
||||
|
||||
export function StatusBadge({ status, label, getCustomConfig, color, leftSection, ...rest }: StatusBadgeProps) {
|
||||
if (!status) return null;
|
||||
|
||||
const normalizedStatus = status?.toLowerCase() || '';
|
||||
const defaultConfig = DEFAULT_STATUS_MAP[normalizedStatus] || { color: 'gray' };
|
||||
const customConfig = getCustomConfig ? getCustomConfig(normalizedStatus) : {};
|
||||
|
||||
return (
|
||||
<Badge
|
||||
size="sm"
|
||||
radius="sm"
|
||||
fw={600}
|
||||
style={{ textTransform: 'capitalize' }}
|
||||
variant={customConfig.variant ? customConfig.variant : 'light'}
|
||||
color={color || customConfig.color || defaultConfig.color}
|
||||
leftSection={leftSection || customConfig.leftSection || defaultConfig.leftSection}
|
||||
{...rest}
|
||||
>
|
||||
{label || (status ? status.replace(/-/g, ' ') : 'Unknown')}
|
||||
</Badge>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user