From 2561cd7d30d3fb9b24d218fdf04c282b20332c42 Mon Sep 17 00:00:00 2001 From: PhatPhuckDave Date: Sun, 10 Aug 2025 22:18:17 +0200 Subject: [PATCH] feat(frontend): load systems from PocketBase and add system focus to region page --- frontend/src/components/SearchDialog.tsx | 37 ++++++++++++------------ frontend/src/pages/RegionPage.tsx | 5 +++- 2 files changed, 23 insertions(+), 19 deletions(-) diff --git a/frontend/src/components/SearchDialog.tsx b/frontend/src/components/SearchDialog.tsx index c9bf01a..dee537f 100644 --- a/frontend/src/components/SearchDialog.tsx +++ b/frontend/src/components/SearchDialog.tsx @@ -3,6 +3,7 @@ import { useNavigate } from 'react-router-dom'; import { Dialog, DialogContent, DialogHeader, DialogTitle } from '@/components/ui/dialog'; import { Input } from '@/components/ui/input'; import { AhoCorasick } from '@/lib/aho'; +import pb from '@/lib/pocketbase'; interface SearchResult { system: string; @@ -10,21 +11,25 @@ interface SearchResult { } async function loadAllSystems(): Promise> { - // Fetch the list of regions from universe.json then fetch each region JSON for systems - const res = await fetch('/universe.json'); - if (!res.ok) return []; - const regions: Array<{ regionName: string } & Record> = await res.json(); + // Fetch system names with regions from PocketBase, paginated and minimal fields + const perPage = 1000; + let page = 1; const out: Array = []; - await Promise.all(regions.map(async (r) => { - try { - const rr = await fetch(`/${encodeURIComponent(r.regionName)}.json`); - if (!rr.ok) return; - const systems: Array<{ solarSystemName: string } & Record> = await rr.json(); - for (const s of systems) { - out.push({ system: s.solarSystemName, region: r.regionName }); + const seen = new Set(); + // loop pages until fewer than perPage items + while (true) { + const res = await pb.collection('regionview').getList(page, perPage, { fields: 'sysname,sysregion' }); + for (const item of res.items as any[]) { + const system: string = item.sysname; + const region: string = item.sysregion; + if (!seen.has(system)) { + seen.add(system); + out.push({ system, region }); } - } catch (_) { /* noop */ } - })); + } + if (res.items.length < perPage) break; + page += 1; + } return out; } @@ -36,7 +41,6 @@ export const SearchDialog: React.FC = () => { const [results, setResults] = useState>([]); const inputRef = useRef(null); - // Build AC automaton of single query to enable contains search efficiently (case-insensitive) const automaton = useMemo(() => { const ac = new AhoCorasick(); if (query.trim().length > 0) ac.add(query.toLowerCase()); @@ -60,17 +64,14 @@ export const SearchDialog: React.FC = () => { useEffect(() => { if (!open) return; if (all.length === 0) { - // lazy load once loadAllSystems().then(setAll); } }, [open, all.length]); useEffect(() => { if (query.trim().length === 0) { setResults([]); return; } - const q = query.toLowerCase(); const out: Array = []; for (const r of all) { - // contains via Aho–Corasick over the single query pattern if (automaton.searchHas(r.system.toLowerCase())) { out.push(r); if (out.length >= 10) break; @@ -82,7 +83,7 @@ export const SearchDialog: React.FC = () => { const onSelect = (r: SearchResult) => { setOpen(false); setQuery(''); - navigate(`/regions/${encodeURIComponent(r.region)}/${encodeURIComponent(r.system)}`); + navigate(`/regions/${encodeURIComponent(r.region)}?focus=${encodeURIComponent(r.system)}`); }; return ( diff --git a/frontend/src/pages/RegionPage.tsx b/frontend/src/pages/RegionPage.tsx index 6e07001..4d78903 100644 --- a/frontend/src/pages/RegionPage.tsx +++ b/frontend/src/pages/RegionPage.tsx @@ -1,4 +1,4 @@ -import { useParams } from 'react-router-dom'; +import { useParams, useSearchParams } from 'react-router-dom'; import { RegionMap } from '@/components/RegionMap'; import { Button } from '@/components/ui/button'; import { ArrowLeft } from 'lucide-react'; @@ -6,6 +6,8 @@ import { useNavigate } from 'react-router-dom'; export const RegionPage = () => { const { region } = useParams<{ region: string }>(); + const [sp] = useSearchParams(); + const focus = sp.get('focus') || undefined; const navigate = useNavigate(); if (!region) { @@ -31,6 +33,7 @@ export const RegionPage = () => { );