feat(web): upgrade lists to ReUI Table v9 and EventCalendar
quality / commitlint (push) Skipped
quality / changes (push) Successful in 9s
quality / docker-check (push) Skipped
quality / openapi (push) Successful in 39s
quality / web (push) Successful in 1m29s
quality / go (push) Successful in 1m3s
quality / bird2 (push) Successful in 15s
CD / quality (push) Successful in 3m43s
CD / publish (push) Failing after 8m29s

Единый kitDataGridTableLayout и ResourcePage/FrameDataGrid на всех списках.
Календарь задач переведён на EventCalendar, lookup — на cascader, у операций появился вид доски.
Удалены settings-7 и самописные DataGridSection/toolbar.

Co-authored-by: Cursor <[email protected]>
This commit is contained in:
Denozordec
2026-08-23 01:38:56 +07:00
co-authored by Cursor
parent 927e27640a
commit d56405af7b
157 changed files with 39067 additions and 6110 deletions
@@ -1,33 +1,56 @@
import { ColumnDef } from '@tanstack/react-table'
import { RefreshCw } from 'lucide-react'
import { useMemo } from 'react'
import { useMemo, useState } from 'react'
import { Boxes, RefreshCw, SearchIcon } from 'lucide-react'
import { CategoryBadge, ModeBadge } from '@/components/category-badge'
import { DataGridMutedCell, DataGridPrimaryCell } from '@/components/data-grid-cell'
import { DataGridSection } from '@/components/data-grid-shell'
import { DataGridMonoCell, DataGridMutedCell, DataGridNameCell } from '@/components/data-grid-cell'
import { LoadingButton } from '@/components/loading-button'
import { DataGridColumnHeader } from '@/components/reui/data-grid/data-grid-column-header'
import { useClientDataGrid } from '@/hooks/use-client-data-grid'
import type { FilterField, FilterQuery } from '@/components/reui/filters/filters-types'
import {
ResourcePage,
createTextFilterQuery,
type DataGridColumnDef,
} from '@/components/reui-kit'
import { moduleTypeRu } from '@/lib/ui-labels'
import type { ModuleRow } from '@/types/api'
const filterFields: FilterField[] = [
{
id: 'search',
label: 'Поиск',
icon: <SearchIcon className="size-3.5" aria-hidden />,
type: 'text',
placeholder: 'Поиск модулей…',
},
]
export function ScheduleModulesGrid({
items,
refreshing,
onRefresh,
isLoading = false,
isError = false,
error = null,
onRetry,
}: {
items: ModuleRow[]
refreshing: Record<string, boolean>
onRefresh: (id: string) => void
isLoading?: boolean
isError?: boolean
error?: Error | null
onRetry?: () => void
}) {
const columns = useMemo<ColumnDef<ModuleRow>[]>(
const [filterQuery, setFilterQuery] = useState<FilterQuery>(() =>
createTextFilterQuery('search'),
)
const columns = useMemo<DataGridColumnDef<ModuleRow>[]>(
() => [
{
accessorKey: 'name',
header: ({ column }) => <DataGridColumnHeader column={column} title="Модуль" />,
cell: ({ row }) => <DataGridPrimaryCell title={row.original.name} accent="primary" />,
cell: ({ row }) => <DataGridNameCell icon={Boxes} title={row.original.name} />,
meta: { headerTitle: 'Модуль' },
},
{
@@ -41,10 +64,10 @@ export function ScheduleModulesGrid({
accessorFn: (row) => row.cron_expr ?? String(row.refresh_interval_sec ?? ''),
header: ({ column }) => <DataGridColumnHeader column={column} title="Расписание" />,
cell: ({ row }) => (
<span className="font-mono text-xs text-muted-foreground">
<DataGridMonoCell className="text-xs text-muted-foreground">
{row.original.cron_expr ??
(row.original.refresh_interval_sec ? `${row.original.refresh_interval_sec} с` : '—')}
</span>
</DataGridMonoCell>
),
meta: { headerTitle: 'Расписание' },
},
@@ -93,22 +116,27 @@ export function ScheduleModulesGrid({
[onRefresh, refreshing],
)
const { table, globalFilter, setGlobalFilter, filteredCount } = useClientDataGrid({
data: items,
columns,
getSearchText: (row) => `${row.name} ${row.type} ${row.enabled ? 'вкл' : 'выкл'}`,
getRowId: (row) => row.id,
})
return (
<DataGridSection
table={table}
recordCount={filteredCount}
<ResourcePage
title="Модули"
description="Расписание обновления и ручной запуск"
filterFields={filterFields}
filterQuery={filterQuery}
onFilterQueryChange={setFilterQuery}
onClearFilters={() => setFilterQuery(createTextFilterQuery('search'))}
getFilterFieldValue={(item, field) => {
if (field !== 'search') return undefined
return `${item.name} ${item.type} ${item.enabled ? 'вкл' : 'выкл'}`
}}
columns={columns}
data={items}
getRowId={(row) => row.id}
isLoading={isLoading}
emptyMessage="Нет модулей"
searchValue={globalFilter}
onSearchChange={setGlobalFilter}
searchPlaceholder="Поиск модулей…"
isError={isError}
error={error}
onRetry={onRetry}
pinLastColumn
emptyState={{ title: 'Нет модулей' }}
/>
)
}