chore: reorganize frontend into app and admin roots
This commit is contained in:
@@ -0,0 +1,135 @@
|
||||
import React, { useEffect, useMemo } from 'react';
|
||||
import { Map, Marker, useMap } from '@vis.gl/react-google-maps';
|
||||
import type { DeepResearchPreview } from '@/shared/types';
|
||||
import { cleanMapOptions } from '../lib/map-styles';
|
||||
|
||||
interface DeepResearchPreviewMapProps {
|
||||
pin: google.maps.LatLngLiteral | null;
|
||||
preview: DeepResearchPreview | null;
|
||||
onPinChange: (nextPin: google.maps.LatLngLiteral) => void;
|
||||
}
|
||||
|
||||
const ringPalette = ['#059669', '#10b981', '#34d399', '#6ee7b7', '#a7f3d0', '#d1fae5'];
|
||||
|
||||
export function DeepResearchPreviewMap({ pin, preview, onPinChange }: DeepResearchPreviewMapProps) {
|
||||
const defaultCenter = useMemo(() => {
|
||||
if (pin) {
|
||||
return pin;
|
||||
}
|
||||
|
||||
const baseArea = preview?.baseArea;
|
||||
if (baseArea?.centroidLat != null && baseArea?.centroidLng != null) {
|
||||
return { lat: baseArea.centroidLat, lng: baseArea.centroidLng };
|
||||
}
|
||||
|
||||
return { lat: 39.5, lng: -98.35 };
|
||||
}, [pin, preview]);
|
||||
|
||||
return (
|
||||
<div className="relative h-[280px] overflow-hidden rounded-3xl border border-stone-200 bg-stone-100 shadow-sm sm:h-[360px] lg:h-[440px]">
|
||||
<Map
|
||||
defaultCenter={defaultCenter}
|
||||
defaultZoom={5}
|
||||
style={{ width: '100%', height: '100%' }}
|
||||
gestureHandling="cooperative"
|
||||
{...cleanMapOptions}
|
||||
onClick={(event) => {
|
||||
const latLng = event.detail.latLng;
|
||||
if (latLng) {
|
||||
onPinChange(latLng);
|
||||
}
|
||||
}}
|
||||
>
|
||||
<PreviewOverlay overlay={preview?.overlay ?? null} pin={pin} preview={preview} />
|
||||
{pin && (
|
||||
<Marker
|
||||
position={pin}
|
||||
icon={{
|
||||
path: google.maps.SymbolPath.CIRCLE,
|
||||
fillColor: '#059669',
|
||||
fillOpacity: 1,
|
||||
strokeColor: '#064e3b',
|
||||
strokeWeight: 2,
|
||||
scale: 7,
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
</Map>
|
||||
|
||||
{!pin && (
|
||||
<div className="pointer-events-none absolute inset-x-4 top-4 rounded-2xl border border-white/20 bg-white/90 p-3 text-sm text-stone-600 shadow-lg backdrop-blur-sm sm:inset-x-6 sm:top-6 sm:p-4">
|
||||
Click anywhere on the map to drop a pin and preview the ZIP/FSA areas included in the deep research run.
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function PreviewOverlay({
|
||||
overlay,
|
||||
pin,
|
||||
preview,
|
||||
}: {
|
||||
overlay: DeepResearchPreview['overlay'] | null;
|
||||
pin: google.maps.LatLngLiteral | null;
|
||||
preview: DeepResearchPreview | null;
|
||||
}) {
|
||||
const map = useMap();
|
||||
|
||||
useEffect(() => {
|
||||
if (!map || !overlay) {
|
||||
return;
|
||||
}
|
||||
|
||||
const addedFeatures = map.data.addGeoJson(overlay as never);
|
||||
map.data.setStyle((feature) => {
|
||||
const ring = Number(feature.getProperty('propagationRing') ?? 0);
|
||||
const color = ringPalette[Math.min(ring, ringPalette.length - 1)];
|
||||
|
||||
return {
|
||||
fillColor: color,
|
||||
fillOpacity: ring === 0 ? 0.28 : 0.16,
|
||||
strokeColor: color,
|
||||
strokeWeight: ring === 0 ? 3 : 2,
|
||||
clickable: false,
|
||||
};
|
||||
});
|
||||
|
||||
return () => {
|
||||
addedFeatures.forEach((feature) => map.data.remove(feature));
|
||||
};
|
||||
}, [map, overlay]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!map) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (pin && (!preview || preview.areas.length === 0)) {
|
||||
map.panTo(pin);
|
||||
map.setZoom(15);
|
||||
return;
|
||||
}
|
||||
|
||||
const bounds = new google.maps.LatLngBounds();
|
||||
let hasBounds = false;
|
||||
|
||||
if (pin) {
|
||||
bounds.extend(pin);
|
||||
hasBounds = true;
|
||||
}
|
||||
|
||||
preview?.areas.forEach((area) => {
|
||||
if (area.centroidLat != null && area.centroidLng != null) {
|
||||
bounds.extend({ lat: area.centroidLat, lng: area.centroidLng });
|
||||
hasBounds = true;
|
||||
}
|
||||
});
|
||||
|
||||
if (hasBounds) {
|
||||
map.fitBounds(bounds, 40);
|
||||
}
|
||||
}, [map, pin, preview]);
|
||||
|
||||
return null;
|
||||
}
|
||||
Reference in New Issue
Block a user