Build, Test, and Push CFDM Docker Image / test (push) Failing after 49s
Build, Test, and Push CFDM Docker Image / build-and-push (push) Has been skipped
Build, Test, and Push CFDM Docker Image / update-wiki (push) Has been skipped
Build, Test, and Push CFDM Docker Image / create-release (push) Has been skipped
- Added ReUI configuration to .mcp.json and .cursor/mcp.json for component integration. - Updated pnpm-lock.yaml with new dependencies including react-phone-number-input and adjustments to existing packages. - Enhanced SKILL.md documentation for ReUI to clarify usage and features. - Removed unused components (ChartCard, DataGridCard, etc.) to streamline the codebase. - Adjusted domain-related components and filters for improved functionality and UI consistency. Co-authored-by: Cursor <[email protected]>
222 lines
6.5 KiB
TypeScript
222 lines
6.5 KiB
TypeScript
import { useMemo } from 'react'
|
||
import { Link } from '@tanstack/react-router'
|
||
import type { ColumnDef } from '@tanstack/react-table'
|
||
import { MoreHorizontalIcon, SearchIcon } from 'lucide-react'
|
||
|
||
import { Badge } from '@/components/reui/badge'
|
||
import { DataGridColumnHeader } from '@/components/reui/data-grid/data-grid-column-header'
|
||
import { createFilter, type FilterFieldConfig } from '@/components/reui/filters'
|
||
import { StatusBadge } from '@/components/status-badge'
|
||
import type { DomainListItem } from '@/lib/schemas'
|
||
import { renderSingleSelectedLabel } from '@/components/reui-kit/filter-utils'
|
||
import { Button } from '@cfdm/ui/components/button'
|
||
import {
|
||
DropdownMenu,
|
||
DropdownMenuContent,
|
||
DropdownMenuItem,
|
||
DropdownMenuTrigger,
|
||
} from '@cfdm/ui/components/dropdown-menu'
|
||
|
||
export const DOMAIN_TABS = [
|
||
{ id: 'all', label: 'Все' },
|
||
{ id: 'with_group', label: 'С группой' },
|
||
{ id: 'without_group', label: 'Без группы' },
|
||
] as const
|
||
|
||
export function domainTabFilter(item: DomainListItem, tabId: string) {
|
||
if (tabId === 'with_group') return item.group_id != null
|
||
if (tabId === 'without_group') return item.group_id == null
|
||
return true
|
||
}
|
||
|
||
export function createDefaultDomainFilters() {
|
||
return [createFilter('zone_name', 'contains', [''])]
|
||
}
|
||
|
||
export function useDomainFilterFields(
|
||
groupOptions: { value: string; label: string }[],
|
||
) {
|
||
return useMemo<FilterFieldConfig[]>(
|
||
() => [
|
||
{
|
||
key: 'zone_name',
|
||
label: 'Зона',
|
||
icon: <SearchIcon className="size-3.5" aria-hidden />,
|
||
type: 'text',
|
||
className: 'w-52',
|
||
placeholder: 'Поиск…',
|
||
},
|
||
{
|
||
key: 'group_id',
|
||
label: 'Группа',
|
||
type: 'select',
|
||
searchable: true,
|
||
className: 'w-[168px]',
|
||
options: groupOptions,
|
||
customValueRenderer: (values) =>
|
||
renderSingleSelectedLabel(values, groupOptions),
|
||
},
|
||
],
|
||
[groupOptions],
|
||
)
|
||
}
|
||
|
||
export function domainFilterFieldValue(item: DomainListItem, field: string) {
|
||
switch (field) {
|
||
case 'zone_name':
|
||
return `${item.zone_name} ${item.group_name ?? ''}`.toLowerCase()
|
||
case 'group_id':
|
||
return item.group_id != null ? String(item.group_id) : 'none'
|
||
default:
|
||
return ''
|
||
}
|
||
}
|
||
|
||
export function useDomainColumns({
|
||
onRequestDelete,
|
||
isDeleting,
|
||
}: {
|
||
onRequestDelete: (domain: DomainListItem) => void
|
||
isDeleting?: boolean
|
||
}) {
|
||
const columns = useMemo<ColumnDef<DomainListItem>[]>(
|
||
() => [
|
||
{
|
||
id: 'zone_name',
|
||
accessorKey: 'zone_name',
|
||
header: ({ column }) => (
|
||
<DataGridColumnHeader column={column} title="Зона" />
|
||
),
|
||
cell: ({ row }) => (
|
||
<Button
|
||
variant="link"
|
||
className="h-auto p-0 font-medium"
|
||
nativeButton={false}
|
||
render={
|
||
<Link
|
||
to="/domains/$domainId"
|
||
params={{ domainId: String(row.original.id) }}
|
||
/>
|
||
}
|
||
>
|
||
{row.original.zone_name}
|
||
</Button>
|
||
),
|
||
},
|
||
{
|
||
id: 'group',
|
||
header: ({ column }) => (
|
||
<DataGridColumnHeader column={column} title="Группа" />
|
||
),
|
||
cell: ({ row }) => {
|
||
const domain = row.original
|
||
if (domain.group_id && domain.group_name) {
|
||
return (
|
||
<Button
|
||
variant="link"
|
||
className="h-auto p-0"
|
||
nativeButton={false}
|
||
render={
|
||
<Link
|
||
to="/groups/$groupId"
|
||
params={{ groupId: String(domain.group_id) }}
|
||
/>
|
||
}
|
||
>
|
||
{domain.group_name}
|
||
</Button>
|
||
)
|
||
}
|
||
return <Badge variant="outline">Без группы</Badge>
|
||
},
|
||
},
|
||
{
|
||
id: 'service_count',
|
||
accessorKey: 'service_count',
|
||
header: ({ column }) => (
|
||
<DataGridColumnHeader column={column} title="Сервисы" />
|
||
),
|
||
cell: ({ row }) => (
|
||
<Button
|
||
variant="link"
|
||
className="h-auto p-0 tabular-nums"
|
||
nativeButton={false}
|
||
render={
|
||
<Link to="/services" search={{ domainId: row.original.id }} />
|
||
}
|
||
>
|
||
{row.original.service_count}
|
||
</Button>
|
||
),
|
||
},
|
||
{
|
||
id: 'status',
|
||
header: 'Статус',
|
||
cell: ({ row }) => <StatusBadge status={row.original.status} />,
|
||
},
|
||
{
|
||
id: 'last_synced_at',
|
||
accessorKey: 'last_synced_at',
|
||
header: ({ column }) => (
|
||
<DataGridColumnHeader column={column} title="Синхронизация" />
|
||
),
|
||
cell: ({ row }) => (
|
||
<span className="text-muted-foreground tabular-nums">
|
||
{row.original.last_synced_at ?? '—'}
|
||
</span>
|
||
),
|
||
},
|
||
{
|
||
id: 'actions',
|
||
header: '',
|
||
enableHiding: false,
|
||
cell: ({ row }) => (
|
||
<div className="text-right">
|
||
<DropdownMenu>
|
||
<DropdownMenuTrigger
|
||
render={<Button variant="outline" size="icon" className="size-8" />}
|
||
>
|
||
<MoreHorizontalIcon />
|
||
<span className="sr-only">Действия</span>
|
||
</DropdownMenuTrigger>
|
||
<DropdownMenuContent align="end">
|
||
<DropdownMenuItem
|
||
render={
|
||
<Link
|
||
to="/domains/$domainId"
|
||
params={{ domainId: String(row.original.id) }}
|
||
/>
|
||
}
|
||
>
|
||
Обзор
|
||
</DropdownMenuItem>
|
||
<DropdownMenuItem
|
||
render={
|
||
<Link
|
||
to="/domains/$domainId/dns"
|
||
params={{ domainId: String(row.original.id) }}
|
||
search={{ host: undefined }}
|
||
/>
|
||
}
|
||
>
|
||
DNS
|
||
</DropdownMenuItem>
|
||
<DropdownMenuItem
|
||
variant="destructive"
|
||
disabled={isDeleting}
|
||
onClick={() => onRequestDelete(row.original)}
|
||
>
|
||
Удалить
|
||
</DropdownMenuItem>
|
||
</DropdownMenuContent>
|
||
</DropdownMenu>
|
||
</div>
|
||
),
|
||
},
|
||
],
|
||
[isDeleting, onRequestDelete],
|
||
)
|
||
|
||
return { columns }
|
||
}
|