import { router, usePage } from '@inertiajs/react'; import { useEffect, useRef, useState } from 'react'; import CatalogueFilterContext from './catalogue-filter-context'; // Shared feedback for existing Inertia search consumers; form state is never reset. const collections = [ 'results', 'records', 'resources', 'partnerships', 'opportunities', 'interests', 'investors', 'leads', 'articles', 'services', 'items', 'entries', 'documents', 'requests', 'batches', 'users', 'assignments', 'zones', 'matches', 'businesses', ]; function count(props: Record): number | null { if (props.demands && props.capacities) return ( Number((props.demands as { total: number }).total) + Number((props.capacities as { total: number }).total) ); if (props.plan && Array.isArray((props.plan as { rows: unknown[] }).rows)) return (props.plan as { rows: unknown[] }).rows.length; if (typeof props.total === 'number') return props.total; if (typeof props.match === 'boolean' && Array.isArray(props.articles)) return props.match ? props.articles.length : 0; for (const key of collections) { const value = props[key] as | { total?: number; meta?: { total?: number }; data?: unknown[] } | unknown[] | undefined; if (!value) continue; if (Array.isArray(value)) return value.length; if (typeof value.total === 'number') return value.total; if (typeof value.meta?.total === 'number') return value.meta.total; if (Array.isArray(value.data)) return value.data.length; } return null; } export default function SearchFeedback() { const page = usePage(); const handledLocally = [ 'investment/catalogue', 'investment/zones', ].includes(page.component); const [state, setState] = useState<'idle' | 'loading' | 'error'>('idle'); const latest = useRef<{ url: string; data: Record; } | null>(null); const active = useRef(false); const searchable = page.props.filters !== undefined || [ 'portal/legacy', 'desk/catalogue', 'investment/zones', 'partnerships/catalogue', 'education/index', 'management/territory-geometry-import', ].includes(page.component); // A page may contain several independent catalogues. Never infer a public // result universe by whichever collection happens to occur first. const descriptors: Record = { 'portfolio/index': ['records', 'Fichas da carteira'], 'investment/zones': ['records', 'Zonas e parques'], 'investment/business-marketplace': ['results', 'Anúncios de negócios'], 'portal/legacy': ['articles', 'Artigos'], }; const descriptor = page.component === 'portal/services' ? page.props.section === 'documents' || page.props.section === 'legislation' ? ['resources', 'Documentos'] : page.props.section === 'sectors' ? ['sectors', 'Sectores'] : null : descriptors[page.component]; const total = descriptor ? page.component === 'portal/legacy' && page.props.match === false ? 0 : count({ results: page.props[descriptor[0]] }) : searchable ? count(page.props) : null; const label = descriptor?.[1]; useEffect(() => { if (handledLocally) return; const off = [ router.on('before', (event) => { const visit = event.detail.visit; if (visit.method !== 'get') { active.current = false; return; } const data = visit.data as Record; const url = visit.url; const isSearch = total !== null || ['q', 'query', 'search', 'filter', 'status', 'sector'].some( (key) => key in data || url.searchParams.has(key), ); active.current = isSearch; if (isSearch) { latest.current = { url: url.toString(), data }; setState('loading'); } }), router.on('success', () => { if (active.current) { active.current = false; setState('idle'); } }), router.on('error', () => { if (active.current) setState('error'); }), router.on('httpException', (event) => { if (active.current) { event.preventDefault(); setState('error'); } }), router.on('networkError', (event) => { if (active.current) { event.preventDefault(); setState('error'); } }), ]; return () => off.forEach((remove) => remove()); }, [total, handledLocally]); if (handledLocally) return null; if (total === null && state === 'idle') return null; return ( <> {descriptor && } ); }