import { useEffect, useState } from 'react' import axios from 'axios' import { X, ExternalLink, Bot, Briefcase, Calendar, Globe, Users, DollarSign, MapPin, Tag, RefreshCw as RefreshCwIcon, Search as SearchIcon, Pencil, Check, Download, Clock, Lock, Unlock } from 'lucide-react' import clsx from 'clsx' import { ContactsManager, Contact } from './ContactsManager' interface InspectorProps { companyId: number | null initialContactId?: number | null // NEW onClose: () => void apiBase: string } type Signal = { signal_type: string confidence: number value: string proof_text: string } type EnrichmentData = { source_type: string content: any is_locked?: boolean created_at?: string } type CompanyDetail = { id: number name: string website: string | null industry_ai: string | null status: string created_at: string signals: Signal[] enrichment_data: EnrichmentData[] contacts?: Contact[] } export function Inspector({ companyId, initialContactId, onClose, apiBase }: InspectorProps) { const [data, setData] = useState(null) const [loading, setLoading] = useState(false) const [isProcessing, setIsProcessing] = useState(false) const [activeTab, setActiveTab] = useState<'overview' | 'contacts'>('overview') // Polling Logic useEffect(() => { let interval: NodeJS.Timeout; if (isProcessing) { interval = setInterval(() => { fetchData(true) // Silent fetch }, 2000) } return () => clearInterval(interval) }, [isProcessing, companyId]) // Dependencies // Auto-switch to contacts tab if initialContactId is present useEffect(() => { if (initialContactId) { setActiveTab('contacts') } else { setActiveTab('overview') } }, [initialContactId, companyId]) // Manual Override State const [isEditingWiki, setIsEditingWiki] = useState(false) const [wikiUrlInput, setWikiUrlInput] = useState("") const [isEditingWebsite, setIsEditingWebsite] = useState(false) const [websiteInput, setWebsiteInput] = useState("") const [isEditingImpressum, setIsEditingImpressum] = useState(false) const [impressumUrlInput, setImpressumUrlInput] = useState("") const fetchData = (silent = false) => { if (!companyId) return if (!silent) setLoading(true) axios.get(`${apiBase}/companies/${companyId}`) .then(res => { const newData = res.data setData(newData) // Auto-stop processing if status changes to ENRICHED or we see data if (isProcessing) { const hasWiki = newData.enrichment_data?.some((e:any) => e.source_type === 'wikipedia') const hasAnalysis = newData.enrichment_data?.some((e:any) => e.source_type === 'ai_analysis') // If we were waiting for Discover (Wiki) or Analyze (AI) if ((hasWiki && newData.status === 'DISCOVERED') || (hasAnalysis && newData.status === 'ENRICHED')) { setIsProcessing(false) } } }) .catch(console.error) .finally(() => { if (!silent) setLoading(false) }) } useEffect(() => { fetchData() setIsEditingWiki(false) setIsEditingWebsite(false) setIsEditingImpressum(false) setIsProcessing(false) // Reset on ID change }, [companyId]) const handleDiscover = async () => { if (!companyId) return setIsProcessing(true) try { await axios.post(`${apiBase}/enrich/discover`, { company_id: companyId }) // Polling effect will handle the rest } catch (e) { console.error(e) setIsProcessing(false) } } const handleAnalyze = async () => { if (!companyId) return setIsProcessing(true) try { await axios.post(`${apiBase}/enrich/analyze`, { company_id: companyId }) // Polling effect will handle the rest } catch (e) { console.error(e) setIsProcessing(false) } } const handleExport = () => { if (!data) return; // Prepare full export object const exportData = { metadata: { id: data.id, exported_at: new Date().toISOString(), source: "Company Explorer (Robotics Edition)" }, company: { name: data.name, website: data.website, status: data.status, industry_ai: data.industry_ai, created_at: data.created_at }, enrichment: data.enrichment_data, signals: data.signals }; const blob = new Blob([JSON.stringify(exportData, null, 2)], { type: 'application/json' }); const url = URL.createObjectURL(blob); const a = document.createElement('a'); a.href = url; a.download = `company-export-${data.id}-${data.name.replace(/[^a-z0-9]/gi, '_').toLowerCase()}.json`; document.body.appendChild(a); a.click(); document.body.removeChild(a); URL.revokeObjectURL(url); }; const handleWikiOverride = async () => { if (!companyId) return setIsProcessing(true) try { await axios.post(`${apiBase}/companies/${companyId}/override/wiki?url=${encodeURIComponent(wikiUrlInput)}`) setIsEditingWiki(false) fetchData() } catch (e) { alert("Update failed") console.error(e) } finally { setIsProcessing(false) } } const handleWebsiteOverride = async () => { if (!companyId) return setIsProcessing(true) try { await axios.post(`${apiBase}/companies/${companyId}/override/website?url=${encodeURIComponent(websiteInput)}`) setIsEditingWebsite(false) fetchData() } catch (e) { alert("Update failed") console.error(e) } finally { setIsProcessing(false) } } const handleImpressumOverride = async () => { if (!companyId) return setIsProcessing(true) try { await axios.post(`${apiBase}/companies/${companyId}/override/impressum?url=${encodeURIComponent(impressumUrlInput)}`) setIsEditingImpressum(false) fetchData() } catch (e) { alert("Impressum update failed") console.error(e) } finally { setIsProcessing(false) } } const handleLockToggle = async (sourceType: string, currentLockStatus: boolean) => { if (!companyId) return try { await axios.post(`${apiBase}/enrichment/${companyId}/${sourceType}/lock?locked=${!currentLockStatus}`) fetchData(true) // Silent refresh } catch (e) { console.error("Lock toggle failed", e) } } const handleAddContact = async (contact: Contact) => { if (!companyId) return try { await axios.post(`${apiBase}/contacts`, { ...contact, company_id: companyId }) fetchData(true) } catch (e) { alert("Failed to add contact") console.error(e) } } const handleEditContact = async (contact: Contact) => { if (!contact.id) return try { await axios.put(`${apiBase}/contacts/${contact.id}`, contact) fetchData(true) } catch (e) { alert("Failed to update contact") console.error(e) } } if (!companyId) return null const wikiEntry = data?.enrichment_data?.find(e => e.source_type === 'wikipedia') const wiki = wikiEntry?.content const isLocked = wikiEntry?.is_locked const wikiDate = wikiEntry?.created_at const aiAnalysisEntry = data?.enrichment_data?.find(e => e.source_type === 'ai_analysis') const aiAnalysis = aiAnalysisEntry?.content const aiDate = aiAnalysisEntry?.created_at const scrapeEntry = data?.enrichment_data?.find(e => e.source_type === 'website_scrape') const scrapeData = scrapeEntry?.content const impressum = scrapeData?.impressum const scrapeDate = scrapeEntry?.created_at return (
{loading ? (
Loading details...
) : !data ? (
Failed to load data.
) : (
{/* Header */}

{data.name}

{!isEditingWebsite ? (
{data.website && data.website !== "k.A." ? ( {new URL(data.website).hostname.replace('www.', '')} ) : ( No website )}
) : (
setWebsiteInput(e.target.value)} placeholder="https://..." className="bg-white dark:bg-slate-800 border border-slate-300 dark:border-slate-700 rounded px-2 py-0.5 text-xs text-slate-900 dark:text-white focus:ring-1 focus:ring-blue-500 outline-none w-48" autoFocus />
)} {data.industry_ai && ( {data.industry_ai} )} {data.status}
{/* Tab Navigation */}
{activeTab === 'overview' && ( <> {/* Action Bar (Only for Overview) */}
{/* Impressum / Legal Data */}
Official Legal Data
{scrapeDate && (
{new Date(scrapeDate).toLocaleDateString()}
)} {/* Lock Button for Impressum */} {scrapeEntry && ( )} {!isEditingImpressum ? ( ) : (
)}
{isEditingImpressum && (
setImpressumUrlInput(e.target.value)} placeholder="https://.../impressum" className="w-full bg-white dark:bg-slate-900 border border-slate-300 dark:border-slate-700 rounded px-2 py-1 text-xs text-slate-900 dark:text-white focus:ring-1 focus:ring-blue-500 outline-none" autoFocus />
)} {impressum ? ( <>
{impressum.legal_name || "Unknown Legal Name"}
{impressum.street}
{impressum.zip} {impressum.city}
{(impressum.email || impressum.phone) && (
{impressum.email && {impressum.email}} {impressum.phone && {impressum.phone}} {impressum.vat_id && VAT: {impressum.vat_id}}
)} ) : !isEditingImpressum && (
No legal data found. Click pencil to provide direct Impressum link.
)}
{/* AI Analysis Dossier */} {aiAnalysis && (

AI Strategic Dossier

{aiDate && (
{new Date(aiDate).toLocaleDateString()}
)}
Business Model

{aiAnalysis.business_model || "No summary available."}

{aiAnalysis.infrastructure_evidence && (
Infrastructure Evidence

"{aiAnalysis.infrastructure_evidence}"

)}
)} {/* Wikipedia Section */}

Company Profile (Wikipedia)

{wikiDate && (
{new Date(wikiDate).toLocaleDateString()}
)} {/* Lock Button for Wiki */} {wikiEntry && ( )} {!isEditingWiki ? ( ) : (
)}
{isEditingWiki && (
setWikiUrlInput(e.target.value)} placeholder="Paste Wikipedia URL here..." className="w-full bg-white dark:bg-slate-800 border border-slate-300 dark:border-slate-700 rounded px-2 py-1 text-sm text-slate-900 dark:text-white focus:ring-1 focus:ring-blue-500 outline-none" />

Paste a valid URL. Saving will trigger a re-scan.

)} {wiki && wiki.url !== 'k.A.' && !isEditingWiki ? (
{/* ... existing wiki content ... */}
{isLocked && (
Manual Override
)}

"{wiki.first_paragraph}"

Employees
{wiki.mitarbeiter || 'k.A.'}
Revenue
{wiki.umsatz && wiki.umsatz !== 'k.A.' ? `${wiki.umsatz} Mio. €` : 'k.A.'}
Headquarters
{wiki.sitz_stadt}{wiki.sitz_land ? `, ${wiki.sitz_land}` : ''}
Wiki Industry
{wiki.branche || 'k.A.'}
{wiki.categories && wiki.categories !== 'k.A.' && (
Categories
{wiki.categories.split(',').map((cat: string) => ( {cat.trim()} ))}
)}
) : !isEditingWiki ? (

No Wikipedia profile found yet.

) : null}
{/* Robotics Scorecard */}

Robotics Potential

{['cleaning', 'transport', 'security', 'service'].map(type => { const sig = data.signals.find(s => s.signal_type.includes(type)) const score = sig ? sig.confidence : 0 return (
{type} 70 ? "text-green-600 dark:text-green-400" : score > 30 ? "text-yellow-600 dark:text-yellow-400" : "text-slate-500")}> {score}%
70 ? "bg-green-500" : score > 30 ? "bg-yellow-500" : "bg-slate-500")} style={{ width: `${score}%` }} />
{sig?.proof_text && (

"{sig.proof_text}"

)}
) })}
{/* Meta Info */}
Added: {new Date(data.created_at).toLocaleDateString()}
ID: CE-{data.id.toString().padStart(4, '0')}
)} {activeTab === 'contacts' && ( )}
)}
) }