45 lines
1.6 KiB
React
45 lines
1.6 KiB
React
|
|
import React, { useMemo } from 'react';
|
||
|
|
import { Loader2, AlertCircle } from 'lucide-react';
|
||
|
|
import { cn } from '@/lib/utils';
|
||
|
|
import { STATUS_META } from '@/lib/trackerUtils';
|
||
|
|
|
||
|
|
export const StatusBadge = React.memo(function StatusBadge({ status, clickable, onClick, loading }) {
|
||
|
|
const meta = useMemo(() => STATUS_META[status] || STATUS_META.upcoming, [status]);
|
||
|
|
|
||
|
|
const isSkipped = status === 'skipped';
|
||
|
|
const isUrgent = status === 'late' || status === 'missed';
|
||
|
|
const canClick = clickable && !isSkipped && !loading;
|
||
|
|
|
||
|
|
return (
|
||
|
|
<button
|
||
|
|
type="button"
|
||
|
|
disabled={!canClick || loading}
|
||
|
|
onClick={onClick}
|
||
|
|
className={cn(
|
||
|
|
'inline-flex items-center px-2.5 py-0.5 text-[11px] rounded-md font-semibold',
|
||
|
|
'uppercase tracking-wide whitespace-nowrap',
|
||
|
|
'transition-all duration-150',
|
||
|
|
isUrgent && 'gap-1.5 px-2.5 py-1 text-xs',
|
||
|
|
canClick && 'cursor-pointer hover:scale-105 hover:shadow-sm',
|
||
|
|
canClick && status === 'paid' && 'hover:bg-red-500/20 hover:text-red-600 hover:border-red-500/40',
|
||
|
|
canClick && status !== 'paid' && 'hover:bg-emerald-500/20 hover:text-emerald-600 hover:border-emerald-500/40',
|
||
|
|
loading && 'opacity-60 cursor-wait',
|
||
|
|
meta.cls,
|
||
|
|
)}
|
||
|
|
title={canClick ? (status === 'paid' || status === 'autodraft' ? 'Click to mark unpaid' : 'Click to mark paid') : undefined}
|
||
|
|
>
|
||
|
|
{loading ? (
|
||
|
|
<>
|
||
|
|
<Loader2 className="h-3 w-3 mr-1 animate-spin" />
|
||
|
|
{meta.label}
|
||
|
|
</>
|
||
|
|
) : (
|
||
|
|
<>
|
||
|
|
{isUrgent && <AlertCircle className="h-3.5 w-3.5" />}
|
||
|
|
{meta.label}
|
||
|
|
</>
|
||
|
|
)}
|
||
|
|
</button>
|
||
|
|
);
|
||
|
|
});
|