diff --git a/.dev_session/SESSION_INFO b/.dev_session/SESSION_INFO index 8216acd3..067d6a68 100644 --- a/.dev_session/SESSION_INFO +++ b/.dev_session/SESSION_INFO @@ -1 +1 @@ -{"task_id": "2f488f42-8544-802f-8311-ee72ef1aac2f", "token": "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", "session_start_time": "2026-01-27T10:44:04.509525"} \ No newline at end of file +{"task_id": "2f388f42-8544-80b7-9c71-e7c8f319990a", "token": "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", "session_start_time": "2026-01-27T11:18:14.201030"} \ No newline at end of file diff --git a/company-explorer/backend/app.py b/company-explorer/backend/app.py index 8261a591..881fe9d0 100644 --- a/company-explorer/backend/app.py +++ b/company-explorer/backend/app.py @@ -107,6 +107,22 @@ def list_companies( query = query.order_by(Company.name.asc()) items = query.offset(skip).limit(limit).all() + + # Efficiently check for pending mistakes + company_ids = [c.id for c in items] + if company_ids: + pending_mistakes = db.query(ReportedMistake.company_id).filter( + ReportedMistake.company_id.in_(company_ids), + ReportedMistake.status == 'PENDING' + ).distinct().all() + companies_with_pending_mistakes = {row[0] for row in pending_mistakes} + else: + companies_with_pending_mistakes = set() + + # Add the flag to each company object + for company in items: + company.has_pending_mistakes = company.id in companies_with_pending_mistakes + return {"total": total, "items": items} except Exception as e: logger.error(f"List Companies Error: {e}", exc_info=True) @@ -251,6 +267,7 @@ def list_job_roles(db: Session = Depends(get_db)): @app.get("/api/mistakes") def list_reported_mistakes( status: Optional[str] = Query(None), + company_id: Optional[int] = Query(None), skip: int = 0, limit: int = 50, db: Session = Depends(get_db) @@ -260,6 +277,9 @@ def list_reported_mistakes( if status: query = query.filter(ReportedMistake.status == status.upper()) + if company_id: + query = query.filter(ReportedMistake.company_id == company_id) + total = query.count() items = query.order_by(ReportedMistake.created_at.desc()).offset(skip).limit(limit).all() diff --git a/company-explorer/frontend/src/components/CompanyTable.tsx b/company-explorer/frontend/src/components/CompanyTable.tsx index 30526482..5698652e 100644 --- a/company-explorer/frontend/src/components/CompanyTable.tsx +++ b/company-explorer/frontend/src/components/CompanyTable.tsx @@ -2,7 +2,7 @@ import { useState, useEffect } from 'react' import axios from 'axios' import { Building, Search, Upload, Globe, MapPin, Play, Search as SearchIcon, Loader2, - LayoutGrid, List, ChevronLeft, ChevronRight, ArrowDownUp + LayoutGrid, List, ChevronLeft, ChevronRight, ArrowDownUp, Flag } from 'lucide-react' import clsx from 'clsx' @@ -16,6 +16,7 @@ interface Company { industry_ai: string | null created_at: string updated_at: string + has_pending_mistakes: boolean } interface CompanyTableProps { @@ -124,7 +125,10 @@ export function CompanyTable({ apiBase, onRowClick, refreshKey, onImportClick }: style={{ borderLeftColor: c.status === 'ENRICHED' ? '#22c55e' : c.status === 'DISCOVERED' ? '#3b82f6' : '#94a3b8' }}>
-
{c.name}
+
+ +
{c.name}
+
{c.city && c.country ? (<> {c.city} ({c.country})) : (-)}
@@ -163,7 +167,12 @@ export function CompanyTable({ apiBase, onRowClick, refreshKey, onImportClick }: {data.map((c) => ( onRowClick(c.id)} className="hover:bg-slate-50 dark:hover:bg-slate-800/50 cursor-pointer"> - {c.name} + +
+ + {c.name} +
+ {c.city && c.country ? `${c.city}, (${c.country})` : '-'} diff --git a/company-explorer/frontend/src/components/Inspector.tsx b/company-explorer/frontend/src/components/Inspector.tsx index f9789f23..0237a7b6 100644 --- a/company-explorer/frontend/src/components/Inspector.tsx +++ b/company-explorer/frontend/src/components/Inspector.tsx @@ -1,6 +1,6 @@ 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, Calculator, Ruler, Database, Trash2, Flag } from 'lucide-react' +import { X, ExternalLink, Bot, Briefcase, Calendar, Globe, Users, DollarSign, MapPin, Tag, RefreshCw as RefreshCwIcon, Search as SearchIcon, Pencil, Check, Download, Clock, Lock, Unlock, Calculator, Ruler, Database, Trash2, Flag, AlertTriangle } from 'lucide-react' import clsx from 'clsx' import { ContactsManager, Contact } from './ContactsManager' @@ -48,6 +48,20 @@ type CompanyDetail = { metric_confidence_reason: string | null } +// NEW +type ReportedMistake = { + id: number; + field_name: string; + wrong_value: string | null; + corrected_value: string | null; + source_url: string | null; + quote: string | null; + user_comment: string | null; + status: 'PENDING' | 'APPROVED' | 'REJECTED'; + created_at: string; +}; + + export function Inspector({ companyId, initialContactId, onClose, apiBase }: InspectorProps) { const [data, setData] = useState(null) const [loading, setLoading] = useState(false) @@ -56,6 +70,7 @@ export function Inspector({ companyId, initialContactId, onClose, apiBase }: Ins // NEW: Report Mistake State const [isReportingMistake, setIsReportingMistake] = useState(false) + const [existingMistakes, setExistingMistakes] = useState([]) const [reportedFieldName, setReportedFieldName] = useState("") const [reportedWrongValue, setReportedWrongValue] = useState("") const [reportedCorrectedValue, setReportedCorrectedValue] = useState("") @@ -100,11 +115,14 @@ export function Inspector({ companyId, initialContactId, onClose, apiBase }: Ins if (!companyId) return if (!silent) setLoading(true) - axios.get(`${apiBase}/companies/${companyId}`) - .then(res => { - const newData = res.data - console.log("FETCHED COMPANY DATA:", newData) // DEBUG: Log raw data from API + const companyRequest = axios.get(`${apiBase}/companies/${companyId}`) + const mistakesRequest = axios.get(`${apiBase}/mistakes?company_id=${companyId}`) + + Promise.all([companyRequest, mistakesRequest]) + .then(([companyRes, mistakesRes]) => { + const newData = companyRes.data setData(newData) + setExistingMistakes(mistakesRes.data.items) // Auto-stop processing if status changes to ENRICHED or we see data if (isProcessing) { @@ -296,7 +314,15 @@ export function Inspector({ companyId, initialContactId, onClose, apiBase }: Ins } } - const handleLockToggle = async (sourceType: string, currentLockStatus: boolean) => {\n if (!companyId) return\n try {\n await axios.post(`${apiBase}/enrichment/${companyId}/${sourceType}/lock?locked=${!currentLockStatus}`)\n fetchData(true) // Silent refresh\n } catch (e) {\n console.error(\"Lock toggle failed\", e)\n }\n }\n\n // NEW: Interface for reporting mistakes\n interface ReportedMistakeRequest {\n field_name: string;\n wrong_value?: string | null;\n corrected_value?: string | null;\n source_url?: string | null;\n quote?: string | null;\n user_comment?: string | null;\n }\n\n const handleReportMistake = async () => {\n if (!companyId) return;\n if (!reportedFieldName) {\n alert(\"Field Name is required.\");\n return;\n }\n\n setIsProcessing(true);\n try {\n const payload: ReportedMistakeRequest = {\n field_name: reportedFieldName,\n wrong_value: reportedWrongValue || null,\n corrected_value: reportedCorrectedValue || null,\n source_url: reportedSourceUrl || null,\n quote: reportedQuote || null,\n user_comment: reportedComment || null,\n };\n\n await axios.post(`${apiBase}/companies/${companyId}/report-mistake`, payload);\n alert(\"Mistake reported successfully!\");\n setIsReportingMistake(false);\n // Reset form fields\n setReportedFieldName(\"\");\n setReportedWrongValue(\"\");\ setReportedCorrectedValue(\"\");\n setReportedSourceUrl(\"\");\n setReportedQuote(\"\");\n setReportedComment(\"\");\n } catch (e) {\n alert(\"Failed to report mistake.\");\n console.error(e);\n } finally {\n setIsProcessing(false);\n }\n }; + 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) + } + } // NEW: Interface for reporting mistakes interface ReportedMistakeRequest { @@ -336,6 +362,7 @@ export function Inspector({ companyId, initialContactId, onClose, apiBase }: Ins setReportedSourceUrl(""); setReportedQuote(""); setReportedComment(""); + fetchData(true); // Re-fetch to show the new mistake } catch (e) { alert("Failed to report mistake."); console.error(e); @@ -478,6 +505,36 @@ export function Inspector({ companyId, initialContactId, onClose, apiBase }: Ins )}
+ {/* Reported Mistakes Section */} + {existingMistakes.length > 0 && ( +
+

+ + Existing Correction Proposals +

+
+ {existingMistakes.map(mistake => ( +
+
+ {mistake.field_name} + + {mistake.status} + +
+

+ {mistake.wrong_value || 'N/A'}{mistake.corrected_value || 'N/A'} +

+ {mistake.user_comment &&

"{mistake.user_comment}"

} +
+ ))} +
+
+ )} + {/* Tab Navigation */}