Files
cloudflare-domain-manager/apps/web/src/components/reui-kit/quick-action-grid.tsx
T
DenozordecandCursor 2418344551
CD / update-wiki (push) Successful in 6s
quality / commitlint (push) Skipped
quality / changes (push) Successful in 11s
quality / api (push) Skipped
quality / docker-check (push) Skipped
quality / web (push) Failing after 52s
CD / quality (push) Failing after 1m5s
CD / publish (push) Skipped
feat(web): собрать экраны из блоков ReUI PRO
Подключить stats-12, card-12, settings-3 и empty-state-12 к оболочке, спискам, карточкам и настройкам, чтобы метрики и пустые состояния шли из одних блоков.

Co-authored-by: Cursor <[email protected]>
2026-11-02 19:40:18 +07:00

81 lines
2.6 KiB
TypeScript

import type { ReactNode } from 'react'
import { Link } from '@tanstack/react-router'
import {
Frame,
FrameDescription,
FrameHeader,
FramePanel,
FrameTitle,
} from '@/components/reui/frame'
import { Badge } from '@/components/reui/badge'
import { cn } from '@cfdm/ui/lib/utils'
export interface QuickActionItem {
id: string
title: string
description: string
to: string
search?: Record<string, unknown>
icon?: ReactNode
iconClassName?: string
badgeVariant?: 'info-light' | 'warning-light'
}
interface QuickActionGridProps {
actions: QuickActionItem[]
title?: string
description?: string
className?: string
}
/**
* Quick links from card-12, with the artboard title, description and «Перейти».
* Preview: https://reui.io/preview/base/card-12
* Docs: https://reui.io/blocks/application/card/card-12
*/
export function QuickActionGrid({
actions,
title = 'Быстрые действия',
description,
className,
}: QuickActionGridProps) {
if (actions.length === 0) return null
return (
<Frame className={cn('w-full', className)}>
{title || description ? (
<FrameHeader>
{title ? <FrameTitle>{title}</FrameTitle> : null}
{description ? <FrameDescription>{description}</FrameDescription> : null}
</FrameHeader>
) : null}
<div className="grid gap-1 sm:grid-cols-2 lg:grid-cols-3">
{actions.map((action) => (
<Link
key={action.id}
to={action.to}
search={action.search}
aria-label={`${action.title}: ${action.description}`}
className="group/card focus-visible:ring-ring/50 hover:[&_svg]:text-primary block h-full min-w-0 cursor-pointer rounded-(--frame-radius) outline-none focus-visible:ring-[3px] [&_svg]:size-5 [&_svg]:opacity-60 hover:[&_svg]:opacity-100"
>
<FramePanel className="flex h-full flex-col gap-3 p-3.5">
<span className={cn('[&_svg]:size-5', action.iconClassName)} aria-hidden="true">
{action.icon}
</span>
<div className="flex items-start justify-between gap-2">
<h3 className="group-hover/card:text-primary text-sm leading-relaxed transition-colors">
{action.title}
</h3>
<Badge variant={action.badgeVariant ?? 'info-light'} size="sm">
Перейти
</Badge>
</div>
<p className="text-muted-foreground text-xs leading-relaxed">{action.description}</p>
</FramePanel>
</Link>
))}
</div>
</Frame>
)
}