import React, { useEffect, useMemo, useState } from 'react'; import { Crosshair, Loader2, MapPinned, Sparkles } from 'lucide-react'; import type { DeepResearchPreview } from '@/shared/types'; import { createDeepResearchBatch, previewDeepResearch } from '../lib/database'; import { DeepResearchPreviewMap } from './DeepResearchPreviewMap'; import { Alert, Badge, Button, FieldLabel, Input, MetricPill, PageContainer, PageShell, SectionHeader, Surface } from './ui'; interface DeepResearchViewProps { onShowBatchOnMap: (jobIds: string[]) => void; topContent?: React.ReactNode; } export function DeepResearchView({ onShowBatchOnMap, topContent }: DeepResearchViewProps) { const [pin, setPin] = useState(null); const [businessType, setBusinessType] = useState(''); const [keywords, setKeywords] = useState(''); const [propagation, setPropagation] = useState(1); const [preview, setPreview] = useState(null); const [previewError, setPreviewError] = useState(null); const [isPreviewing, setIsPreviewing] = useState(false); const [isRunning, setIsRunning] = useState(false); useEffect(() => { setPreview(null); setPreviewError(null); }, [businessType, keywords, pin?.lat, pin?.lng, propagation]); const canPreview = Boolean(pin && businessType.trim()); const previewSummary = useMemo(() => { if (!preview) { return null; } return `${preview.totalAreas} postal areas across ${preview.countryCode} with ${preview.estimatedChildJobs} child researches.`; }, [preview]); const handlePreview = async () => { if (!pin || !businessType.trim()) { setPreviewError('Drop a pin and add a business type before previewing deep research.'); return; } setIsPreviewing(true); setPreviewError(null); try { const nextPreview = await previewDeepResearch({ lat: pin.lat, lng: pin.lng, propagation, businessType: businessType.trim(), keywords: keywords.trim() || undefined, }); setPreview(nextPreview); } catch (err) { setPreviewError(err instanceof Error ? err.message : 'Failed to preview deep research.'); } finally { setIsPreviewing(false); } }; const handleRunDeepResearch = async () => { if (!pin || !businessType.trim()) { setPreviewError('Drop a pin and add a business type before running deep research.'); return; } setIsRunning(true); setPreviewError(null); try { const batch = await createDeepResearchBatch({ lat: pin.lat, lng: pin.lng, propagation, businessType: businessType.trim(), keywords: keywords.trim() || undefined, }); if (batch.jobIds.length > 0) { onShowBatchOnMap(batch.jobIds); } } catch (err) { setPreviewError(err instanceof Error ? err.message : 'Failed to run deep research.'); } finally { setIsRunning(false); } }; return ( {topContent}
Business Type setBusinessType(event.target.value)} placeholder="e.g. dentists, HVAC, bakeries" />
Keywords setKeywords(event.target.value)} placeholder="Optional comma-separated keywords" />
Location
Click directly on the map to place the deep research center. The preview uses that active pin to find the base ZIP or FSA and expand outward.
{pin && (

{pin.lat.toFixed(5)}, {pin.lng.toFixed(5)}

)}
Propagation setPropagation(Number.parseInt(event.target.value, 10) || 0)} className="w-full accent-emerald-600" />
Base postal area only {propagation} hop{propagation === 1 ? '' : 's'} Expand outward
Pin placement

Click the map to drop a pin. The preview will find the containing ZIP or FSA and expand to adjacent postal areas based on the propagation depth.

{pin &&

Pin: {pin.lat.toFixed(5)}, {pin.lng.toFixed(5)}

}
{previewError && ( {previewError} )} {previewSummary && (

{previewSummary}

Base area: {preview?.baseArea.displayName}

)}
Map Controls

Preview center and coverage

Drop a pin directly on the map to define the base postal area. Preview colors and rings show how propagation expands the deep research batch into adjacent areas.

{preview && (

Preview coverage

These postal areas will become child researches in the batch.

{preview.totalAreas} areas
{preview.areas.map((area) => ( {area.displayName} ยท ring {area.propagationRing} ))}
)}
); }