feat(company-explorer): Initial Web UI & Backend with Enrichment Flow
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.
This commit is contained in:
85
company-explorer/frontend/src/components/ImportWizard.tsx
Normal file
85
company-explorer/frontend/src/components/ImportWizard.tsx
Normal file
@@ -0,0 +1,85 @@
|
||||
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>
|
||||
)
|
||||
}
|
||||
Reference in New Issue
Block a user