Files
Investidor-V2/resources/js/components/investment-map-panel.tsx
T

101 lines
3.7 KiB
TypeScript

import SearchState from '@/components/search-state';
import { useEffect, useState } from 'react';
import TerritoryGeometryPreview from './territory-geometry-preview';
import InteractiveInvestmentMap, {
type InvestmentMapFeature,
type InvestmentMapArea,
} from './interactive-investment-map';
export default function InvestmentMapPanel({
filters,
territoryId,
}: {
filters: Record<string, string | number>;
territoryId?: string;
}) {
const key = JSON.stringify(filters);
const [points, setPoints] = useState<InvestmentMapFeature[] | null>(null);
const [missing, setMissing] = useState<
{ id: number; title: string; url: string }[]
>([]);
const [error, setError] = useState('');
const [retry, setRetry] = useState(0);
const [areas, setAreas] = useState<InvestmentMapArea[]>([]);
useEffect(() => {
const controller = new AbortController();
const params = new URLSearchParams(JSON.parse(key));
setPoints(null);
setError('');
setMissing([]);
setAreas([]);
fetch(`/api/v1/investment/map?${params}`, {
signal: controller.signal,
headers: { Accept: 'application/json' },
})
.then(async (response) => {
if (!response.ok) throw new Error('Mapa indisponível.');
return response.json();
})
.then((data) => {
if (controller.signal.aborted) return;
setPoints(data.features);
setAreas(data.areas ?? []);
setMissing(data.unlocated ?? []);
setError(
data.truncated
? 'Cobertura limitada: refine os filtros.'
: '',
);
})
.catch(() => {
if (!controller.signal.aborted)
setError('Não foi possível concluir a pesquisa.');
});
return () => controller.abort();
}, [key, retry]);
return (
<section className="space-y-3">
<SearchState
scope="Registos consultados pelo mapa"
countLabel="registos, incluindo os que não têm coordenadas"
loading={points === null && !error}
error={error}
total={(points?.length ?? 0) + missing.length}
retry={() => setRetry((value) => value + 1)}
/>
<InteractiveInvestmentMap points={points} areas={areas} />
<TerritoryGeometryPreview
territoryId={
territoryId ||
String(
filters.locality_id ||
filters.admin_post_id ||
filters.district_id ||
filters.province_id ||
filters.geo_location_id ||
'',
)
}
/>
{missing.length > 0 && (
<div>
<p>
{missing.length} investimentos sem ponto público
utilizável. Consulte as fichas para localizações
documentadas sem coordenadas:
</p>
<ul>
{missing.map((record) => (
<li key={record.id}>
<a href={record.url} className="underline">
{record.title}
</a>
</li>
))}
</ul>
</div>
)}
</section>
);
}