diff --git a/company-explorer/frontend/src/components/Inspector.tsx b/company-explorer/frontend/src/components/Inspector.tsx index dac734fd..5e9e146b 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 } from 'lucide-react' +import { X, ExternalLink, Bot, Briefcase, Calendar, Globe, Users, DollarSign, MapPin, Tag, RefreshCw as RefreshCwIcon, Search as SearchIcon, Pencil, Check, Download, Clock } from 'lucide-react' import clsx from 'clsx' interface InspectorProps { @@ -20,6 +20,7 @@ type EnrichmentData = { source_type: string content: any is_locked?: boolean + created_at?: string } type CompanyDetail = { @@ -110,6 +111,38 @@ export function Inspector({ companyId, onClose, apiBase }: InspectorProps) { } } + const handleExport = () => { + if (!data) return; + + // Prepare full export object + const exportData = { + metadata: { + id: data.id, + exported_at: new Date().toISOString(), + source: "Company Explorer (Robotics Edition)" + }, + company: { + name: data.name, + website: data.website, + status: data.status, + industry_ai: data.industry_ai, + created_at: data.created_at + }, + enrichment: data.enrichment_data, + signals: data.signals + }; + + const blob = new Blob([JSON.stringify(exportData, null, 2)], { type: 'application/json' }); + const url = URL.createObjectURL(blob); + const a = document.createElement('a'); + a.href = url; + a.download = `company-export-${data.id}-${data.name.replace(/[^a-z0-9]/gi, '_').toLowerCase()}.json`; + document.body.appendChild(a); + a.click(); + document.body.removeChild(a); + URL.revokeObjectURL(url); + }; + const handleWikiOverride = async () => { if (!companyId) return setIsProcessing(true) @@ -145,11 +178,16 @@ export function Inspector({ companyId, onClose, apiBase }: InspectorProps) { const wikiEntry = data?.enrichment_data?.find(e => e.source_type === 'wikipedia') const wiki = wikiEntry?.content const isLocked = wikiEntry?.is_locked + const wikiDate = wikiEntry?.created_at - const aiAnalysis = data?.enrichment_data?.find(e => e.source_type === 'ai_analysis')?.content + const aiAnalysisEntry = data?.enrichment_data?.find(e => e.source_type === 'ai_analysis') + const aiAnalysis = aiAnalysisEntry?.content + const aiDate = aiAnalysisEntry?.created_at - const scrapeData = data?.enrichment_data?.find(e => e.source_type === 'website_scrape')?.content + const scrapeEntry = data?.enrichment_data?.find(e => e.source_type === 'website_scrape') + const scrapeData = scrapeEntry?.content const impressum = scrapeData?.impressum + const scrapeDate = scrapeEntry?.created_at return (
@@ -164,6 +202,13 @@ export function Inspector({ companyId, onClose, apiBase }: InspectorProps) {

{data.name}

+ - ) : ( -
+ +
+ {wikiDate && ( +
+ {new Date(wikiDate).toLocaleDateString()} +
+ )} + {!isEditingWiki ? ( - -
- )} + ) : ( +
+ + +
+ )} +
{isEditingWiki && ( diff --git a/gtm-architect/App.tsx b/gtm-architect/App.tsx index 8e795e13..13d7ca4d 100644 --- a/gtm-architect/App.tsx +++ b/gtm-architect/App.tsx @@ -208,8 +208,8 @@ const App: React.FC = () => { // Session Management const [sessions, setSessions] = useState([]); - const [viewingSessions, setViewingSessions] = useState(true); // Start in session view - + const [viewingSessions, setViewingSessions] = useState(false); // Start in input view + // Local state for adding new items (Human in the Loop inputs) // Phase 1 const [newFeatureInput, setNewFeatureInput] = useState(""); @@ -237,6 +237,10 @@ const App: React.FC = () => { const [brushColor, setBrushColor] = useState('#ef4444'); // Red for annotations const [brushSize, setBrushSize] = useState(4); + // Specs Editing + const [isEditingSpecs, setIsEditingSpecs] = useState(false); + const [specsJsonInput, setSpecsJsonInput] = useState(""); + const labels = TRANSLATIONS[language]; // Apply theme to body @@ -918,6 +922,31 @@ const App: React.FC = () => { })); }; + const handleSaveSpecs = async () => { + if (!state.projectId) return; + try { + const parsedSpecs = JSON.parse(specsJsonInput); + setState(s => ({ ...s, isLoading: true })); + + await Gemini.updateSpecs(state.projectId, parsedSpecs); + + setState(s => ({ + ...s, + isLoading: false, + phase1Result: s.phase1Result ? { + ...s.phase1Result, + specs: parsedSpecs + } : undefined + })); + setIsEditingSpecs(false); + setError(null); + } catch (e: any) { + console.error("Failed to save specs", e); + setError("Invalid JSON or Save Failed: " + e.message); + setState(s => ({ ...s, isLoading: false })); + } + }; + // --- Render Helpers --- const renderInputPhase = () => ( @@ -1074,7 +1103,7 @@ const App: React.FC = () => {

{labels.features}

    - {state.phase1Result?.features.map((f, i) => ( + {(state.phase1Result?.features || []).map((f, i) => (
  • {labels.constraints}
      - {state.phase1Result?.constraints.map((c, i) => ( + {(state.phase1Result?.constraints || []).map((c, i) => (
    • -

      - Technical Specifications (Hard Facts) -

      - +
      +

      + Technical Specifications (Hard Facts) +

      + {!isEditingSpecs ? ( + + ) : ( +
      + + +
      + )} +
      + + {isEditingSpecs ? ( +
      +

      + Edit the raw JSON data below. Be careful with the syntax. This data is used for the Strategy Report. +

      +