Files
EvoBGP/apps/web/src/components/status-badge.tsx
T
DenozordecandCursor 094f75913b
quality / commitlint (push) Skipped
quality / changes (push) Successful in 8s
quality / go (push) Skipped
quality / bird2 (push) Skipped
quality / docker-check (push) Skipped
quality / openapi (push) Successful in 22s
quality / web (push) Successful in 1m0s
CD / quality (push) Successful in 1m35s
CD / publish (push) Successful in 2m59s
fix(web): unify ReUI chrome and stop tab underline overlap
Секции страниц переведены на segmented Tabs (c-tabs-9), статус грида — ToggleGroup в toolbar. Убраны календарь задач, пустые вкладки мониторинга и demo-деревья ReUI. KPI overflow как в CFDM, DetailPanel на карточке модуля, один FrameSection.

Co-authored-by: Cursor <[email protected]>
2026-08-23 12:46:20 +07:00

118 lines
2.8 KiB
TypeScript

import type { ComponentProps, ReactNode } from 'react'
import { cn } from '@evobgp/ui/lib/utils'
import { Badge } from '@/components/reui/badge'
import { jobStatusRu } from '@/lib/ui-labels'
type BadgeVariant = NonNullable<ComponentProps<typeof Badge>['variant']>
const STATUS_VARIANT: Record<string, BadgeVariant> = {
active: 'success-light',
ok: 'success-light',
paid: 'success-light',
established: 'success-light',
succeeded: 'success-light',
healthy: 'success-light',
approved: 'success-light',
accept: 'success-light',
paused: 'invert-light',
disabled: 'invert-light',
archived: 'outline',
error: 'destructive-light',
failed: 'destructive-light',
revoked: 'destructive-light',
block: 'destructive-light',
cancelled: 'destructive-light',
running: 'info-light',
queued: 'info-light',
overdue: 'warning-light',
stale: 'warning-light',
warning: 'warning-light',
mismatch: 'warning-light',
pending: 'warning-light',
}
const DOT_COLOR: Record<string, string> = {
'success-light': 'bg-success',
'info-light': 'bg-info',
'warning-light': 'bg-warning',
'destructive-light': 'bg-destructive',
'invert-light': 'bg-muted-foreground',
outline: 'bg-muted-foreground',
}
const TONE_VARIANT: Record<string, BadgeVariant> = {
neutral: 'outline',
info: 'info-light',
warning: 'warning-light',
success: 'success-light',
}
export function jobStatusVariant(status: string): BadgeVariant {
return STATUS_VARIANT[status.toLowerCase()] ?? 'outline'
}
export function StatusBadge({
status,
label,
hint,
className,
}: {
status: string
label?: string
hint?: string
className?: string
}) {
const variant = jobStatusVariant(status)
const dotColor = DOT_COLOR[variant] ?? 'bg-muted-foreground'
return (
<div className={cn('flex flex-col gap-0.5', className)}>
<Badge variant={variant} size="sm" radius="full" className="gap-1.5">
<span className={cn('size-1.5 shrink-0 rounded-full', dotColor)} aria-hidden />
{label ?? jobStatusRu(status)}
</Badge>
{hint ? <span className="text-xs text-muted-foreground">{hint}</span> : null}
</div>
)
}
export function ModeBadge({
enabled,
onLabel = 'включён',
offLabel = 'выключен',
className,
}: {
enabled: boolean
onLabel?: string
offLabel?: string
className?: string
}) {
return enabled ? (
<Badge variant="success-light" size="sm" radius="full" className={className}>
{onLabel}
</Badge>
) : (
<Badge variant="secondary" size="sm" radius="full" className={className}>
{offLabel}
</Badge>
)
}
export function CategoryBadge({
children,
tone = 'neutral',
className,
}: {
children: ReactNode
tone?: keyof typeof TONE_VARIANT
className?: string
}) {
return (
<Badge variant={TONE_VARIANT[tone]} size="sm" radius="full" className={className}>
{children}
</Badge>
)
}