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:
42
company-explorer/backend/repositories/mock.py
Normal file
42
company-explorer/backend/repositories/mock.py
Normal file
@@ -0,0 +1,42 @@
|
||||
import logging
|
||||
import uuid
|
||||
from typing import Optional
|
||||
from ..interfaces import CRMRepository, LeadData, TaskData
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
class MockRepository(CRMRepository):
|
||||
"""
|
||||
Simulates a CRM. Use this for local dev or tests.
|
||||
Stores data in memory (lost on restart).
|
||||
"""
|
||||
def __init__(self):
|
||||
self._store = {}
|
||||
|
||||
def get_name(self) -> str:
|
||||
return "Local Mock CRM"
|
||||
|
||||
def find_company(self, name: str, email: str = None) -> Optional[str]:
|
||||
# Simple Exact Match Simulation
|
||||
for lead_id, lead in self._store.items():
|
||||
if lead.name.lower() == name.lower():
|
||||
logger.info(f"[MockCRM] Found existing company '{name}' with ID {lead_id}")
|
||||
return lead_id
|
||||
return None
|
||||
|
||||
def create_lead(self, lead: LeadData) -> str:
|
||||
new_id = f"MOCK_{uuid.uuid4().hex[:8]}"
|
||||
self._store[new_id] = lead
|
||||
logger.info(f"[MockCRM] Created company '{lead.name}' (ID: {new_id}). Total records: {len(self._store)}")
|
||||
return new_id
|
||||
|
||||
def update_lead(self, external_id: str, lead: LeadData) -> bool:
|
||||
if external_id in self._store:
|
||||
self._store[external_id] = lead
|
||||
logger.info(f"[MockCRM] Updated company {external_id} with robotics score: {lead.robotics_potential_score}")
|
||||
return True
|
||||
return False
|
||||
|
||||
def create_task(self, external_id: str, task: TaskData) -> bool:
|
||||
logger.info(f"[MockCRM] 🔔 TASK CREATED for {external_id}: '{task.subject}'")
|
||||
return True
|
||||
40
company-explorer/backend/repositories/superoffice.py
Normal file
40
company-explorer/backend/repositories/superoffice.py
Normal file
@@ -0,0 +1,40 @@
|
||||
import logging
|
||||
import requests
|
||||
from typing import Optional
|
||||
from ..interfaces import CRMRepository, LeadData, TaskData
|
||||
from ..config import settings
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
class SuperOfficeRepository(CRMRepository):
|
||||
def __init__(self, tenant_id: str, api_token: str):
|
||||
self.base_url = f"https://{tenant_id}.superoffice.com/api/v1"
|
||||
self.headers = {
|
||||
"Authorization": f"Bearer {api_token}",
|
||||
"Accept": "application/json"
|
||||
}
|
||||
|
||||
def get_name(self) -> str:
|
||||
return "SuperOffice"
|
||||
|
||||
def find_company(self, name: str, email: str = None) -> Optional[str]:
|
||||
# TODO: Implement actual OData query
|
||||
# Example: GET /Contact?$filter=Name eq '{name}'
|
||||
logger.info(f"[SuperOffice] Searching for '{name}'...")
|
||||
return None
|
||||
|
||||
def create_lead(self, lead: LeadData) -> str:
|
||||
logger.info(f"[SuperOffice] Creating Lead: {lead.name}")
|
||||
# TODO: POST /Contact
|
||||
# Payload mapping: lead.industry -> SuperOffice BusinessId
|
||||
return "SO_DUMMY_ID_123"
|
||||
|
||||
def update_lead(self, external_id: str, lead: LeadData) -> bool:
|
||||
logger.info(f"[SuperOffice] Updating Lead {external_id} with Score {lead.robotics_potential_score}")
|
||||
# TODO: PUT /Contact/{id}
|
||||
# Wir schreiben das Robotics-Potential z.B. in ein benutzerdefiniertes Feld (UserDefinedField)
|
||||
return True
|
||||
|
||||
def create_task(self, external_id: str, task: TaskData) -> bool:
|
||||
logger.info(f"[SuperOffice] Creating Task for {external_id}: {task.subject}")
|
||||
return True
|
||||
Reference in New Issue
Block a user