feat(web): URL-состояние вкладок/поиска/фильтров на страницах агентов, списков, правил и статистики

This commit is contained in:
Denozordec
2026-09-25 01:44:32 +07:00
parent 3d4edbad8b
commit 65434857bb
6 changed files with 269 additions and 70 deletions
+46 -12
View File
@@ -5,6 +5,7 @@ import { createFileRoute, useNavigate } from '@tanstack/react-router'
import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query'
import { toast } from 'sonner'
import type { Filter, FilterFieldConfig } from '@/components/reui/filters'
import { searchToFilters, searchToFlag, searchToQ } from '@/lib/search-params'
import { PageHeader, PageShell, ResourcePage } from '@/components/reui-kit'
import { DataGridColumnHeader } from '@/components/reui/data-grid/data-grid-column-header'
import { DataGridPrimaryCell } from '@/components/data-grid-cell'
@@ -28,11 +29,23 @@ import {
} from '@evofw/ui/components/sheet'
import type { PolicySet } from '@evofw/shared'
type RulesSearch = {
create?: boolean
q?: string
f?: Filter[]
}
/** URL-состояние страницы: create/q/f (deep-link и refresh сохраняют вид). */
function parseRulesSearch(search: Record<string, unknown>): RulesSearch {
return {
create: searchToFlag(search.create),
q: searchToQ(search.q),
f: searchToFilters(search.f),
}
}
export const Route = createFileRoute('/_auth/rules/')({
validateSearch: (search: Record<string, unknown>) => ({
/** Открыть sheet создания (из ⌘K-палитры). */
create: search.create === true || undefined,
}),
validateSearch: parseRulesSearch,
component: PolicySetsPage,
})
@@ -43,16 +56,37 @@ export const Route = createFileRoute('/_auth/rules/')({
* Create sheet: https://reui.io/preview/base/sheet-8
*/
function PolicySetsPage() {
const navigate = useNavigate()
const navigate = useNavigate({ from: Route.fullPath })
const qc = useQueryClient()
const setsQ = useQuery(policySetsQueryOptions())
const createParam = Route.useSearch().create === true
const { create: createParam, q: qParam, f: fParam } = Route.useSearch()
const searchQuery = qParam ?? ''
const filters = fParam ?? []
const [sheetOpenState, setSheetOpenState] = useState(false)
const sheetOpen = sheetOpenState || createParam
const sheetOpen = sheetOpenState || createParam === true
/** Обновление URL-состояния (q/f) без перезагрузки. */
const setRulesSearch = useCallback(
(next: { q?: string; f?: Filter[]; create?: boolean }) => {
void navigate({
search: (prev) => {
const base = parseRulesSearch(prev as Record<string, unknown>)
return {
create:
next.create !== undefined
? next.create || undefined
: base.create,
q: next.q !== undefined ? searchToQ(next.q) : base.q,
f: next.f !== undefined ? next.f : base.f,
}
},
replace: true,
})
},
[navigate],
)
const [name, setName] = useState('')
const [description, setDescription] = useState('')
const [filters, setFilters] = useState<Filter[]>([])
const [searchQuery, setSearchQuery] = useState('')
const [deleteId, setDeleteId] = useState<string | null>(null)
const create = useMutation({
@@ -231,11 +265,11 @@ function PolicySetsPage() {
getRowId={(r) => r.id}
filterFields={filterFields}
filters={filters}
onFiltersChange={setFilters}
onClearFilters={() => setFilters([])}
onFiltersChange={(f) => setRulesSearch({ f })}
onClearFilters={() => setRulesSearch({ f: [], q: '' })}
getFilterFieldValue={getFilterFieldValue}
searchQuery={searchQuery}
onSearchChange={setSearchQuery}
onSearchChange={(q) => setRulesSearch({ q })}
searchPlaceholder="Поиск наборов…"
getSearchText={getSearchText}
isLoading={setsQ.isLoading}