Maintenance Love :)

This commit is contained in:
lovebird 2026-03-23 19:33:01 +01:00
parent 784bd4d903
commit 021dc7b0c3

View File

@ -426,10 +426,12 @@ export function GadmPicker({ map, active, onClose, onSelectionChange, className
if (processingGids.current.has(gid)) return;
processingGids.current.add(gid);
const targetLevel = (forceLevel !== undefined) ? forceLevel : (resolutionOption > gadmLevel ? resolutionOption : gadmLevel);
let isDuplicate = false;
setSelectedRegions(prev => {
if (prev.some(r => r.gid === gid)) { isDuplicate = true; return prev; }
return [...prev, { gid, gadmName: name, level: gadmLevel, raw: region }];
return [...prev, { gid, gadmName: name, level: targetLevel, raw: region }];
});
if (isDuplicate) {
@ -441,12 +443,17 @@ export function GadmPicker({ map, active, onClose, onSelectionChange, className
setLoadingGid(gid, true);
try {
const targetLevel = (forceLevel !== undefined) ? forceLevel : (resolutionOption > gadmLevel ? resolutionOption : gadmLevel);
const geojson = await fetchRegionBoundary(
let geojson = await fetchRegionBoundary(
gid, name || '', targetLevel, enrich
);
if (!geojson.features || geojson.features.length === 0) {
console.warn(`No subdivisions found for ${gid} at level ${targetLevel}. Snapping back to level ${gadmLevel}`);
setSelectedRegions(prev => prev.map(r => r.gid === gid ? { ...r, level: gadmLevel } : r));
geojson = await fetchRegionBoundary(gid, name || '', gadmLevel, enrich);
}
let activeStats: Record<string, any> | undefined;
if (enrich && geojson.features && geojson.features.length > 0) {
const props = geojson.features[0].properties;
@ -477,6 +484,27 @@ export function GadmPicker({ map, active, onClose, onSelectionChange, className
});
};
const handleFlyToRegion = async (gid: string) => {
if (!map) return;
const gj = geojsons[gid];
if (!gj || !gj.features || gj.features.length === 0) return;
try {
const turf = await import('@turf/turf');
const bbox = turf.bbox(gj as any);
map.fitBounds(bbox as any, { padding: 50, duration: 800 });
} catch (e) {
console.error("Failed to fit bounds", e);
}
};
const handleReResolveRegion = (region: any, targetLevel: number) => {
if (loadingBoundaryIds.has(region.gid)) return;
if (targetLevel === region.level) return;
handleRemoveRegion(region.gid);
handleSelectRegion({ gid: region.gid, gadmName: region.gadmName, level: region.level }, targetLevel, true);
};
const handleCopyToClipboard = async () => {
const data = selectedRegions.map(r => ({ gid: r.gid, name: r.gadmName, level: r.level, raw: r.raw }));
await navigator.clipboard.writeText(JSON.stringify(data, null, 2));
@ -567,15 +595,7 @@ export function GadmPicker({ map, active, onClose, onSelectionChange, className
}, [updateMapFeatures]);
const sidebarContent = active ? (
<div className={`p-4 bg-white dark:bg-gray-800 border dark:border-gray-700 rounded-lg shadow-sm w-full h-full max-h-[80vh] flex flex-col space-y-4 ${className}`}>
<div className="flex justify-between items-center flex-shrink-0">
<h3 className="font-semibold text-gray-800 dark:text-gray-200">GADM Area Selector</h3>
{onClose && (
<button onClick={onClose} className="text-gray-500 hover:text-gray-700 dark:hover:text-gray-300">
<X size={18} />
</button>
)}
</div>
<div className={`w-full h-full flex flex-col space-y-4 ${className}`}>
<p className="text-sm text-gray-600 dark:text-gray-400 flex-shrink-0">
Click anywhere on the map to inspect administrative areas, or use the search below.
@ -677,7 +697,14 @@ export function GadmPicker({ map, active, onClose, onSelectionChange, className
{selectedRegions.map((region) => {
const loading = loadingBoundaryIds.has(region.gid);
return (
<div key={region.gid} className="flex flex-col bg-yellow-50 dark:bg-yellow-900/20 border border-yellow-200 dark:border-yellow-800/50 rounded-lg p-2">
<div
key={region.gid}
className="flex flex-col bg-yellow-50 dark:bg-yellow-900/20 border border-yellow-200 dark:border-yellow-800/50 rounded-lg p-2 cursor-pointer hover:bg-yellow-100 dark:hover:bg-yellow-900/40 transition-colors"
onClick={(e) => {
if ((e.target as HTMLElement).closest('button')) return;
handleFlyToRegion(region.gid);
}}
>
<div className="flex items-center gap-1.5 justify-between">
<div className="flex items-center gap-1.5 font-medium text-yellow-800 dark:text-yellow-300 text-sm">
{loading && <Loader2 className="w-3 h-3 animate-spin text-yellow-600"/>}
@ -686,8 +713,8 @@ export function GadmPicker({ map, active, onClose, onSelectionChange, className
<span className="text-xs opacity-70">({region.gid})</span>
</div>
<button
onClick={() => handleRemoveRegion(region.gid)}
className="text-yellow-600 hover:text-red-500 flex-shrink-0 focus:outline-none ml-2 bg-yellow-100/50 dark:bg-yellow-800/30 p-1 rounded"
onClick={(e) => { e.stopPropagation(); handleRemoveRegion(region.gid); }}
className="text-yellow-600 hover:text-red-500 flex-shrink-0 focus:outline-none ml-2 bg-yellow-100/50 dark:bg-yellow-800/30 p-1 rounded transition-colors"
>
<X className="w-3 h-3" />
</button>
@ -698,6 +725,28 @@ export function GadmPicker({ map, active, onClose, onSelectionChange, className
<span><strong className="font-semibold">Pop:</strong> {region.stats.ghsPopulation ? Math.round(region.stats.ghsPopulation).toLocaleString() : 'N/A'}</span>
</div>
)}
<div className="mt-2 flex items-center justify-between border-t border-yellow-200/50 dark:border-yellow-800/50 pt-2 px-1">
<span className="text-[10px] uppercase font-bold text-yellow-600/70 dark:text-yellow-500/70 tracking-wider">Change Level</span>
<div className="flex items-center bg-yellow-100/50 dark:bg-yellow-900/30 p-0.5 rounded border border-yellow-200 dark:border-yellow-700/50">
{[0, 1, 2, 3, 4, 5].map((lvl) => {
const isSelected = lvl === region.level;
return (
<button
key={lvl}
onClick={(e) => { e.stopPropagation(); handleReResolveRegion(region, lvl); }}
className={`px-2 py-0.5 text-[10px] font-bold rounded transition-colors ${
isSelected
? 'bg-yellow-400 text-yellow-900 shadow-sm'
: 'text-yellow-700 dark:text-yellow-400 hover:bg-yellow-200/50 dark:hover:bg-yellow-800/50'
}`}
title={`Change to Level ${lvl}`}
>
L{lvl}
</button>
);
})}
</div>
</div>
</div>
);
})}