import { useQuery } from '@tanstack/react-query' import { useEffect, useRef, useState } from 'react' import { Button } from '@/components/ui/button' import { countsUseLatest } from '@/lib/counts' import { fetchCountsCandidates } from '@/lib/countsDefinitions' import { countsKeys } from '@/lib/queryKeys' import { useLocale } from '@/lib/useLocale' import { useUITranslation } from '@/lib/useUITranslation' import { inputClass } from '@/lib/utils' import type { DialogueFormControls } from '@/lib/dialogues/useDialogue' import type { CountsResource } from '@/lib/counts' import type { CountsCandidate } from '@/lib/countsDefinitions' type CandidateSource = { resource: CountsResource; field: string; params?: URLSearchParams } const CandidatePicker = ( { source, choices = [], categories = [], initial, maximum, controls, onSelect }: { source?: CandidateSource; choices?: CountsCandidate[] categories?: { value: string; label: string }[] initial: (string | number | boolean)[]; maximum: number controls: DialogueFormControls; onSelect: (values: string[]) => void }, ) => { const { label, message } = useUITranslation ('counts') const locale = useLocale () const [selected, setSelected] = useState (() => new Set (initial.map (String))) const [columns, setColumns] = useState (['id', 'name', 'category', 'kind']) const [draft, setDraft] = useState ('') const [search, setSearch] = useState ('') const [category, setCategory] = useState ('') const [page, setPage] = useState (1) const params = source?.params ?? new URLSearchParams () const point = useRef (params.get ('execution_at')) const resource = source?.resource ?? 'posts' const request = new URLSearchParams ({ field: source?.field ?? '', query: search, page: String (page) }) for (const key of ['timezone', 'at', 'execution_at']) if (params.has (key)) request.set (key, params.get (key)!) if (countsUseLatest (params)) request.delete ('at') if (category) request.set ('category', category) const dynamic = source != null const { data, isFetching: fetching, isError: errorFlg } = useQuery ({ queryKey: countsKeys.candidates (resource, request.toString (), locale), enabled: dynamic, queryFn: ({ signal }) => { const pinned = new URLSearchParams (request) if (point.current != null) pinned.set ('execution_at', point.current) return fetchCountsCandidates (resource, pinned, locale, signal) }, retry: false, staleTime: 0, gcTime: 0 }) useEffect (() => { if (data != null) point.current = data.executionAt }, [data]) useEffect (() => { controls.setActions ([{ label: label ('counts:pattern.selection'), onSelect: () => onSelect ([...selected]) }]) }, [controls, label, onSelect, selected]) const rows = dynamic ? data?.rows ?? [] : choices.filter (row => row.name?.includes (search)) const headings: Record = { id: 'columns.resourceId', name: 'columns.name', category: 'common:categoryLabel', kind: 'columns.kind' } const referenceFlg = ['tags', 'tags_json', 'tags.id', 'members'].includes (source?.field ?? '') const available = dynamic ? (referenceFlg ? ['id', 'name', 'category', 'kind'] : ['id', 'name']) : ['name'] const visible = available.filter (column => columns.includes (column)) const cell = (row: CountsCandidate, column: string) => { if (column === 'category') return row.category == null ? '' : message (`common:categories.${row.category}`).text if (column === 'kind') { const key = row.kind === 'group' ? 'resources.tag_groups' : `kinds.${row.kind}` return row.kind == null ? '' : message (key).text } return column === 'id' ? row.id : row.name } return
{ const value = event.target.value if (new TextEncoder ().encode (value).length <= 1000) setDraft (value) }} onKeyDown={event => { if (event.key !== 'Enter') return event.preventDefault () setSearch (draft) setPage (1) }}/>
{categories.length > 0 && }
{label ('counts:pattern.columns')} {available.map (column => )}

{label ('counts:pattern.value')}{':'} {label (dynamic ? 'columns.resourceId' : 'common:categoryLabel')}

{errorFlg &&

{label ('common:fetchError')}

}
{visible.map (column => )} {rows.map (row => {visible.map (column => )} )}
{label ('counts:pattern.selection')} {label (headings[column])}
= maximum} onChange={event => setSelected (previous => { const next = new Set (maximum === 1 ? [] : previous) if (event.target.checked) next.add (row.value) else next.delete (row.value) return next })}/> {cell (row, column)}
{dynamic &&
{page}
}
} export default CandidatePicker export type { CandidateSource }