feat(frontend): load systems from PocketBase and add system focus to region page

This commit is contained in:
2025-08-10 22:18:17 +02:00
parent 9c40135102
commit 2561cd7d30
2 changed files with 23 additions and 19 deletions

View File

@@ -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<Array<SearchResult>> {
// 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<string, any>> = await res.json();
// Fetch system names with regions from PocketBase, paginated and minimal fields
const perPage = 1000;
let page = 1;
const out: Array<SearchResult> = [];
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<string, any>> = await rr.json();
for (const s of systems) {
out.push({ system: s.solarSystemName, region: r.regionName });
const seen = new Set<string>();
// 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 });
}
}
if (res.items.length < perPage) break;
page += 1;
}
} catch (_) { /* noop */ }
}));
return out;
}
@@ -36,7 +41,6 @@ export const SearchDialog: React.FC = () => {
const [results, setResults] = useState<Array<SearchResult>>([]);
const inputRef = useRef<HTMLInputElement | null>(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<SearchResult> = [];
for (const r of all) {
// contains via AhoCorasick 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 (

View File

@@ -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 = () => {
<RegionMap
regionName={region}
isWormholeRegion={region === "Wormhole"}
focusSystem={focus}
/>
</div>
);