This commit introduces the foundational elements for the new "Company Explorer" web application, marking a significant step away from the legacy Google Sheets / CLI system. Key changes include: - Project Structure: A new directory with separate (FastAPI) and (React/Vite) components. - Data Persistence: Migration from Google Sheets to a local SQLite database () using SQLAlchemy. - Core Utilities: Extraction and cleanup of essential helper functions (LLM wrappers, text utilities) into . - Backend Services: , , for AI-powered analysis, and logic. - Frontend UI: Basic React application with company table, import wizard, and dynamic inspector sidebar. - Docker Integration: Updated and for multi-stage builds and sideloading. - Deployment & Access: Integrated into central Nginx proxy and dashboard, accessible via . Lessons Learned & Fixed during development: - Frontend Asset Loading: Addressed issues with Vite's path and FastAPI's . - TypeScript Configuration: Added and . - Database Schema Evolution: Solved errors by forcing a new database file and correcting override. - Logging: Implemented robust file-based logging (). This new foundation provides a powerful and maintainable platform for future B2B robotics lead generation.
86 lines
2.8 KiB
TypeScript
86 lines
2.8 KiB
TypeScript
import { useState } from 'react'
|
|
import axios from 'axios'
|
|
import { X, UploadCloud } from 'lucide-react'
|
|
|
|
interface ImportWizardProps {
|
|
isOpen: boolean
|
|
onClose: () => void
|
|
onSuccess: () => void
|
|
apiBase: string
|
|
}
|
|
|
|
export function ImportWizard({ isOpen, onClose, onSuccess, apiBase }: ImportWizardProps) {
|
|
const [text, setText] = useState("")
|
|
const [loading, setLoading] = useState(false)
|
|
|
|
if (!isOpen) return null
|
|
|
|
const handleImport = async () => {
|
|
const lines = text.split('\n').map(l => l.trim()).filter(l => l.length > 0)
|
|
if (lines.length === 0) return
|
|
|
|
setLoading(true)
|
|
try {
|
|
await axios.post(`${apiBase}/companies/bulk`, { names: lines })
|
|
setText("")
|
|
onSuccess()
|
|
onClose()
|
|
} catch (e: any) {
|
|
console.error(e)
|
|
const msg = e.response?.data?.detail || e.message || "Unknown Error"
|
|
alert(`Import failed: ${msg}`)
|
|
} finally {
|
|
setLoading(false)
|
|
}
|
|
}
|
|
|
|
return (
|
|
<div className="fixed inset-0 bg-black/70 backdrop-blur-sm z-50 flex items-center justify-center p-4">
|
|
<div className="bg-slate-900 border border-slate-700 rounded-xl w-full max-w-lg shadow-2xl">
|
|
|
|
{/* Header */}
|
|
<div className="flex items-center justify-between p-4 border-b border-slate-800">
|
|
<h3 className="text-lg font-semibold text-white flex items-center gap-2">
|
|
<UploadCloud className="h-5 w-5 text-blue-400" />
|
|
Quick Import
|
|
</h3>
|
|
<button onClick={onClose} className="text-slate-400 hover:text-white">
|
|
<X className="h-5 w-5" />
|
|
</button>
|
|
</div>
|
|
|
|
{/* Body */}
|
|
<div className="p-4 space-y-4">
|
|
<p className="text-sm text-slate-400">
|
|
Paste company names below (one per line). Duplicates in the database will be skipped automatically.
|
|
</p>
|
|
<textarea
|
|
className="w-full h-64 bg-slate-950 border border-slate-700 rounded-lg p-3 text-sm text-slate-200 focus:ring-2 focus:ring-blue-600 outline-none font-mono"
|
|
placeholder="Company A Company B Company C..."
|
|
value={text}
|
|
onChange={e => setText(e.target.value)}
|
|
/>
|
|
</div>
|
|
|
|
{/* Footer */}
|
|
<div className="p-4 border-t border-slate-800 flex justify-end gap-3">
|
|
<button
|
|
onClick={onClose}
|
|
className="px-4 py-2 text-sm font-medium text-slate-400 hover:text-white"
|
|
>
|
|
Cancel
|
|
</button>
|
|
<button
|
|
onClick={handleImport}
|
|
disabled={loading || !text.trim()}
|
|
className="px-4 py-2 bg-blue-600 hover:bg-blue-500 text-white rounded-md text-sm font-medium disabled:opacity-50 disabled:cursor-not-allowed"
|
|
>
|
|
{loading ? "Importing..." : "Import Companies"}
|
|
</button>
|
|
</div>
|
|
|
|
</div>
|
|
</div>
|
|
)
|
|
}
|