Files
Brancheneinstufung2/company-explorer/frontend/src/App.tsx
Floke 95634d7bb6 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.
2026-01-07 17:55:08 +00:00

117 lines
4.1 KiB
TypeScript

import { useState, useEffect } from 'react'
import axios from 'axios'
import { CompanyTable } from './components/CompanyTable'
import { ImportWizard } from './components/ImportWizard'
import { Inspector } from './components/Inspector' // NEW
import { LayoutDashboard, UploadCloud, Search, RefreshCw } from 'lucide-react'
// Base URL detection (Production vs Dev)
const API_BASE = import.meta.env.BASE_URL === '/ce/' ? '/ce/api' : '/api';
interface Stats {
total: number;
}
function App() {
const [stats, setStats] = useState<Stats>({ total: 0 })
const [refreshKey, setRefreshKey] = useState(0)
const [isImportOpen, setIsImportOpen] = useState(false)
const [selectedCompanyId, setSelectedCompanyId] = useState<number | null>(null) // NEW
const fetchStats = async () => {
try {
const res = await axios.get(`${API_BASE}/companies?limit=1`)
setStats({ total: res.data.total })
} catch (e) {
console.error("Failed to fetch stats", e)
}
}
useEffect(() => {
fetchStats()
}, [refreshKey])
const handleCompanySelect = (id: number) => {
setSelectedCompanyId(id)
}
const handleCloseInspector = () => {
setSelectedCompanyId(null)
}
return (
<div className="min-h-screen bg-slate-950 text-slate-200 font-sans">
<ImportWizard
isOpen={isImportOpen}
onClose={() => setIsImportOpen(false)}
apiBase={API_BASE}
onSuccess={() => setRefreshKey(k => k + 1)}
/>
{/* Inspector Sidebar */}
<Inspector
companyId={selectedCompanyId}
onClose={handleCloseInspector}
apiBase={API_BASE}
/>
{/* Header */}
<header className="border-b border-slate-800 bg-slate-900/50 sticky top-0 z-10 backdrop-blur-md">
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 h-16 flex items-center justify-between">
<div className="flex items-center gap-3">
<div className="p-2 bg-blue-600 rounded-lg">
<LayoutDashboard className="h-6 w-6 text-white" />
</div>
<div>
<h1 className="text-xl font-bold text-white tracking-tight">Company Explorer</h1>
<p className="text-xs text-blue-400 font-medium">ROBOTICS EDITION <span className="text-slate-600 ml-2">v0.2.2 (New DB Path)</span></p>
</div>
</div>
<div className="flex items-center gap-4">
<div className="text-sm text-slate-400">
<span className="text-white font-bold">{stats.total}</span> Companies
</div>
<button
onClick={() => setRefreshKey(k => k + 1)}
className="p-2 hover:bg-slate-800 rounded-full transition-colors text-slate-400 hover:text-white"
title="Refresh Data"
>
<RefreshCw className="h-5 w-5" />
</button>
<button
className="flex items-center gap-2 bg-blue-600 hover:bg-blue-500 text-white px-4 py-2 rounded-md font-medium text-sm transition-all shadow-lg shadow-blue-900/20"
onClick={() => setIsImportOpen(true)}
>
<UploadCloud className="h-4 w-4" />
Import List
</button>
</div>
</div>
</header>
{/* Main Content */}
<main className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-8">
<div className="mb-6 flex gap-4">
<div className="relative flex-1 max-w-md">
<Search className="absolute left-3 top-2.5 h-5 w-5 text-slate-500" />
<input
type="text"
placeholder="Search companies..."
className="w-full bg-slate-900 border border-slate-700 text-slate-200 rounded-md pl-10 pr-4 py-2 focus:ring-2 focus:ring-blue-500 focus:border-transparent outline-none"
/>
</div>
</div>
<div className="bg-slate-900 border border-slate-800 rounded-xl overflow-hidden shadow-xl">
<CompanyTable key={refreshKey} apiBase={API_BASE} onRowClick={handleCompanySelect} /> {/* NEW PROP */}
</div>
</main>
</div>
)
}
export default App