quality / commitlint (push) Skipped
quality / changes (push) Successful in 7s
quality / go (push) Skipped
quality / bird2 (push) Skipped
quality / docker-check (push) Skipped
quality / openapi (push) Successful in 24s
quality / web (push) Successful in 1m1s
CD / quality (push) Successful in 1m37s
CD / publish (push) Successful in 2m53s
- Introduced new properties for status toggle items, including `icon` and `tone`, to improve visual representation. - Updated the `StatusToggleGroup` component to utilize these new properties for better user feedback. - Enhanced filter utilities with additional functions for managing filter queries, including `getPrimaryTextField`, `getExtraFilterFields`, and `setFilterTextValue`. - Improved the `ResourcePageFiltered` component to integrate search functionality and manage filter states more effectively. This update aims to streamline the user experience in managing statuses and filters across the application.
134 lines
3.5 KiB
TypeScript
134 lines
3.5 KiB
TypeScript
import type { ComponentProps } from 'react'
|
|
import type { LucideIcon } from 'lucide-react'
|
|
import { CircleCheck, CircleOff, Clock, ListFilter } from 'lucide-react'
|
|
|
|
import { ToggleGroup, ToggleGroupItem } from '@evobgp/ui/components/toggle-group'
|
|
import { cn } from '@evobgp/ui/lib/utils'
|
|
|
|
import { Badge } from '@/components/reui/badge'
|
|
|
|
type BadgeVariant = NonNullable<ComponentProps<typeof Badge>['variant']>
|
|
|
|
export type StatusToggleTone = 'neutral' | 'success' | 'warning' | 'info' | 'destructive'
|
|
|
|
export type StatusToggleItem = {
|
|
id: string
|
|
label: string
|
|
count?: number
|
|
icon?: LucideIcon
|
|
tone?: StatusToggleTone
|
|
}
|
|
|
|
interface StatusToggleGroupProps {
|
|
items: StatusToggleItem[]
|
|
value: string
|
|
onValueChange: (value: string) => void
|
|
className?: string
|
|
'aria-label'?: string
|
|
}
|
|
|
|
const TONE_BY_ID: Record<string, StatusToggleTone> = {
|
|
all: 'neutral',
|
|
enabled: 'success',
|
|
online: 'success',
|
|
established: 'success',
|
|
succeeded: 'success',
|
|
pending: 'warning',
|
|
queued: 'warning',
|
|
running: 'info',
|
|
active: 'info',
|
|
disabled: 'destructive',
|
|
offline: 'destructive',
|
|
failed: 'destructive',
|
|
}
|
|
|
|
const ICON_BY_ID: Record<string, LucideIcon> = {
|
|
all: ListFilter,
|
|
enabled: CircleCheck,
|
|
online: CircleCheck,
|
|
established: CircleCheck,
|
|
succeeded: CircleCheck,
|
|
pending: Clock,
|
|
queued: Clock,
|
|
running: Clock,
|
|
active: Clock,
|
|
disabled: CircleOff,
|
|
offline: CircleOff,
|
|
failed: CircleOff,
|
|
}
|
|
|
|
const BADGE_VARIANT: Record<StatusToggleTone, BadgeVariant> = {
|
|
neutral: 'outline',
|
|
success: 'success-light',
|
|
warning: 'warning-light',
|
|
info: 'info-light',
|
|
destructive: 'destructive-light',
|
|
}
|
|
|
|
const ICON_CLASS: Record<StatusToggleTone, string> = {
|
|
neutral: 'text-muted-foreground',
|
|
success: 'text-success',
|
|
warning: 'text-warning',
|
|
info: 'text-info',
|
|
destructive: 'text-destructive',
|
|
}
|
|
|
|
function resolveTone(item: StatusToggleItem): StatusToggleTone {
|
|
return item.tone ?? TONE_BY_ID[item.id] ?? 'neutral'
|
|
}
|
|
|
|
function resolveIcon(item: StatusToggleItem): LucideIcon {
|
|
return item.icon ?? ICON_BY_ID[item.id] ?? ListFilter
|
|
}
|
|
|
|
/**
|
|
* Grid status filter — native ToggleGroup in the ResourcePage toolbar.
|
|
* @see https://reui.io/preview/base/components/c-toggle-group-5
|
|
* @see https://reui.io/preview/base/components/c-toggle-group-13
|
|
* @see https://reui.io/components/toggle-group
|
|
* @see https://reui.io/docs/components/base/badge
|
|
*/
|
|
export function StatusToggleGroup({
|
|
items,
|
|
value,
|
|
onValueChange,
|
|
className,
|
|
'aria-label': ariaLabel = 'Статус',
|
|
}: StatusToggleGroupProps) {
|
|
return (
|
|
<ToggleGroup
|
|
multiple={false}
|
|
value={[value]}
|
|
onValueChange={(values) => {
|
|
const next = values[0]
|
|
if (next) onValueChange(next)
|
|
}}
|
|
variant="outline"
|
|
size="default"
|
|
spacing={0}
|
|
aria-label={ariaLabel}
|
|
className={cn('w-fit', className)}
|
|
>
|
|
{items.map((item) => {
|
|
const tone = resolveTone(item)
|
|
const Icon = resolveIcon(item)
|
|
return (
|
|
<ToggleGroupItem key={item.id} value={item.id} className="gap-1.5 px-2.5">
|
|
<Icon
|
|
className={cn('size-3.5', ICON_CLASS[tone])}
|
|
data-icon="inline-start"
|
|
aria-hidden="true"
|
|
/>
|
|
<span>{item.label}</span>
|
|
{item.count !== undefined ? (
|
|
<Badge variant={BADGE_VARIANT[tone]} size="xs" radius="full" className="tabular-nums">
|
|
{item.count}
|
|
</Badge>
|
|
) : null}
|
|
</ToggleGroupItem>
|
|
)
|
|
})}
|
|
</ToggleGroup>
|
|
)
|
|
}
|