fix: handle missing isochron better
This commit is contained in:
parent
43a1c71dd1
commit
09d23e6666
1 changed files with 25 additions and 4 deletions
|
|
@ -40,6 +40,7 @@ export default function HomePage() {
|
||||||
// Pin / location rating
|
// Pin / location rating
|
||||||
const [pinLocation, setPinLocation] = useState<{ lat: number; lng: number } | null>(null);
|
const [pinLocation, setPinLocation] = useState<{ lat: number; lng: number } | null>(null);
|
||||||
const [pinData, setPinData] = useState<LocationScoreData | null>(null);
|
const [pinData, setPinData] = useState<LocationScoreData | null>(null);
|
||||||
|
const [pinScoreError, setPinScoreError] = useState(false);
|
||||||
const [pinAddress, setPinAddress] = useState<string | undefined>(undefined);
|
const [pinAddress, setPinAddress] = useState<string | undefined>(undefined);
|
||||||
const [pinEstateValue, setPinEstateValue] = useState<number | null>(null);
|
const [pinEstateValue, setPinEstateValue] = useState<number | null>(null);
|
||||||
const [pinEstatePercentile, setPinEstatePercentile] = useState<number | null>(null);
|
const [pinEstatePercentile, setPinEstatePercentile] = useState<number | null>(null);
|
||||||
|
|
@ -100,11 +101,13 @@ export default function HomePage() {
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!pinLocation || !selectedCity) {
|
if (!pinLocation || !selectedCity) {
|
||||||
setPinData(null);
|
setPinData(null);
|
||||||
|
setPinScoreError(false);
|
||||||
setPinAddress(undefined);
|
setPinAddress(undefined);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
let cancelled = false;
|
let cancelled = false;
|
||||||
|
setPinScoreError(false);
|
||||||
|
|
||||||
const params = new URLSearchParams({
|
const params = new URLSearchParams({
|
||||||
lat: String(pinLocation.lat),
|
lat: String(pinLocation.lat),
|
||||||
|
|
@ -128,14 +131,15 @@ export default function HomePage() {
|
||||||
.then(([scoreData, address]) => {
|
.then(([scoreData, address]) => {
|
||||||
if (cancelled) return;
|
if (cancelled) return;
|
||||||
if (scoreData?.error) {
|
if (scoreData?.error) {
|
||||||
// No grid data for this location — clear the pin so the skeleton doesn't persist.
|
// No grid data for this mode — keep the pin (isochrone may still show) but
|
||||||
setPinLocation(null);
|
// display an error state instead of an infinite loading skeleton.
|
||||||
|
setPinScoreError(true);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
setPinData(scoreData as LocationScoreData);
|
setPinData(scoreData as LocationScoreData);
|
||||||
setPinAddress(address);
|
setPinAddress(address);
|
||||||
})
|
})
|
||||||
.catch(() => { if (!cancelled) setPinLocation(null); });
|
.catch(() => { if (!cancelled) setPinScoreError(true); });
|
||||||
|
|
||||||
return () => { cancelled = true; };
|
return () => { cancelled = true; };
|
||||||
}, [pinLocation, selectedCity, mode, threshold, profile]);
|
}, [pinLocation, selectedCity, mode, threshold, profile]);
|
||||||
|
|
@ -252,6 +256,7 @@ export default function HomePage() {
|
||||||
// Clear pin when profile changes so scores are re-fetched with new profile
|
// Clear pin when profile changes so scores are re-fetched with new profile
|
||||||
setPinLocation(null);
|
setPinLocation(null);
|
||||||
setPinData(null);
|
setPinData(null);
|
||||||
|
setPinScoreError(false);
|
||||||
setPinAddress(undefined);
|
setPinAddress(undefined);
|
||||||
setPinEstateValue(null);
|
setPinEstateValue(null);
|
||||||
setPinEstatePercentile(null);
|
setPinEstatePercentile(null);
|
||||||
|
|
@ -262,6 +267,7 @@ export default function HomePage() {
|
||||||
function handleLocationClick(lat: number, lng: number, estateValue: number | null) {
|
function handleLocationClick(lat: number, lng: number, estateValue: number | null) {
|
||||||
setPinLocation({ lat, lng });
|
setPinLocation({ lat, lng });
|
||||||
setPinData(null);
|
setPinData(null);
|
||||||
|
setPinScoreError(false);
|
||||||
setPinAddress(undefined);
|
setPinAddress(undefined);
|
||||||
setPinEstateValue(estateValue);
|
setPinEstateValue(estateValue);
|
||||||
setPinEstatePercentile(null);
|
setPinEstatePercentile(null);
|
||||||
|
|
@ -272,6 +278,7 @@ export default function HomePage() {
|
||||||
function handlePinClose() {
|
function handlePinClose() {
|
||||||
setPinLocation(null);
|
setPinLocation(null);
|
||||||
setPinData(null);
|
setPinData(null);
|
||||||
|
setPinScoreError(false);
|
||||||
setPinAddress(undefined);
|
setPinAddress(undefined);
|
||||||
setPinEstateValue(null);
|
setPinEstateValue(null);
|
||||||
setPinEstatePercentile(null);
|
setPinEstatePercentile(null);
|
||||||
|
|
@ -351,7 +358,21 @@ export default function HomePage() {
|
||||||
hasPinData={!!pinData}
|
hasPinData={!!pinData}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
{pinLocation && !pinData && (
|
{pinLocation && !pinData && pinScoreError && (
|
||||||
|
<div className="absolute bottom-8 right-4 z-20 bg-white rounded-xl shadow-lg border border-gray-100 w-72 p-4">
|
||||||
|
<button
|
||||||
|
onClick={handlePinClose}
|
||||||
|
className="absolute top-3 right-3 text-gray-300 hover:text-gray-500 text-xl leading-none"
|
||||||
|
aria-label="Close"
|
||||||
|
>×</button>
|
||||||
|
<p className="text-sm text-gray-500 text-center py-4">
|
||||||
|
No score data for this mode yet.<br />
|
||||||
|
<span className="text-xs text-gray-400">Re-run ingest to compute new modes.</span>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{pinLocation && !pinData && !pinScoreError && (
|
||||||
<div className="absolute bottom-8 right-4 z-20 bg-white rounded-xl shadow-lg border border-gray-100 w-72 p-4">
|
<div className="absolute bottom-8 right-4 z-20 bg-white rounded-xl shadow-lg border border-gray-100 w-72 p-4">
|
||||||
<button
|
<button
|
||||||
onClick={handlePinClose}
|
onClick={handlePinClose}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue