import React, { useState, useCallback } from 'react'; import { InputForm } from './components/InputForm'; import { StepDisplay } from './components/StepDisplay'; import { LoadingSpinner, BotIcon, SparklesIcon, MarkdownIcon, PrintIcon } from './components/Icons'; import { ExportMenu } from './components/ExportMenu'; import { translations } from './constants'; import type { AnalysisStep, AnalysisData, InputData } from './types'; import { generateMarkdown, downloadFile } from './services/export'; const API_BASE_URL = 'api'; const App: React.FC = () => { const [inputData, setInputData] = useState({ companyUrl: '', language: 'de', regions: '', focus: '', channels: ['LinkedIn', 'Kaltmail', 'Landingpage'] }); const [analysisData, setAnalysisData] = useState>({}); const [isLoading, setIsLoading] = useState(false); const [error, setError] = useState(null); const [generationStep, setGenerationStep] = useState(0); // 0: idle, 1-6: step X is complete const [selectedIndustry, setSelectedIndustry] = useState(''); const [batchStatus, setBatchStatus] = useState<{ current: number; total: number; industry: string } | null>(null); const t = translations[inputData.language]; const STEP_TITLES = t.stepTitles; const STEP_KEYS: (keyof AnalysisData)[] = ['offer', 'targetGroups', 'personas', 'painPoints', 'gains', 'messages']; const handleStartGeneration = useCallback(async () => { if (!inputData.companyUrl) { setError('Bitte geben Sie eine Unternehmens-URL ein.'); return; } setIsLoading(true); setError(null); setAnalysisData({}); setGenerationStep(0); setSelectedIndustry(''); setBatchStatus(null); try { const response = await fetch(`${API_BASE_URL}/start-generation`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ companyUrl: inputData.companyUrl, language: inputData.language, regions: inputData.regions, focus: inputData.focus, }), }); if (!response.ok) { const errorData = await response.json(); throw new Error(errorData.details || `HTTP error! status: ${response.status}`); } const parsedData = await response.json(); if (parsedData.error) { throw new Error(parsedData.error); } setAnalysisData(parsedData); setGenerationStep(1); } catch (e) { console.error(e); setError(e instanceof Error ? `Ein Fehler ist aufgetreten: ${e.message}` : 'Ein unbekannter Fehler ist aufgetreten.'); } finally { setIsLoading(false); } }, [inputData]); const handleGenerateNextStep = useCallback(async () => { if (generationStep >= 6) return; if (generationStep === 5 && !selectedIndustry) { setError('Bitte wählen Sie eine Fokus-Branche aus.'); return; } setIsLoading(true); setError(null); try { const response = await fetch(`${API_BASE_URL}/next-step`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ analysisData, language: inputData.language, channels: inputData.channels, generationStep: generationStep + 1, // Pass the step we want to generate focusIndustry: generationStep === 5 ? selectedIndustry : undefined, }), }); if (!response.ok) { const errorData = await response.json(); throw new Error(errorData.details || `HTTP error! status: ${response.status}`); } const parsedData = await response.json(); if (parsedData.error) { throw new Error(parsedData.error); } setAnalysisData(prev => ({ ...prev, ...parsedData })); setGenerationStep(prev => prev + 1); } catch (e) { console.error(e); setError(e instanceof Error ? `Ein Fehler ist aufgetreten: ${e.message}` : 'Ein unbekannter Fehler ist aufgetreten.'); setGenerationStep(prev => prev); // Stay on the current step if error } finally { setIsLoading(false); } }, [analysisData, generationStep, inputData.channels, inputData.language, selectedIndustry]); const handleBatchGenerate = useCallback(async () => { if (!analysisData.targetGroups?.rows) return; const industries = analysisData.targetGroups.rows.map(row => row[0]); if (industries.length === 0) return; setIsLoading(true); setError(null); setGenerationStep(6); // Show the Step 6 container (will be filled incrementally) // Initialize Step 6 data container setAnalysisData(prev => ({ ...prev, messages: { summary: ["Batch-Analyse aller Branchen läuft..."], headers: ["Fokus-Branche", "Rolle", "Kernbotschaft", "Kanäle"], // Default headers, will be overwritten/verified rows: [] } })); let aggregatedRows: string[][] = []; let capturedHeaders: string[] = []; for (let i = 0; i < industries.length; i++) { const industry = industries[i]; setBatchStatus({ current: i + 1, total: industries.length, industry }); try { const response = await fetch(`${API_BASE_URL}/next-step`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ analysisData, // Pass full context language: inputData.language, channels: inputData.channels, generationStep: 6, focusIndustry: industry, }), }); if (!response.ok) throw new Error(`HTTP error for ${industry}`); const data = await response.json(); if (data.messages && data.messages.rows) { if (capturedHeaders.length === 0 && data.messages.headers) { capturedHeaders = data.messages.headers; } aggregatedRows = [...aggregatedRows, ...data.messages.rows]; // Update state incrementally so user sees results growing setAnalysisData(prev => ({ ...prev, messages: { summary: ["Vollständige Analyse über alle identifizierten Branchen."], headers: capturedHeaders.length > 0 ? capturedHeaders : (prev?.messages?.headers || []), rows: aggregatedRows } })); } } catch (e) { console.error(`Error processing industry ${industry}:`, e); // We continue with next industry even if one fails } } setIsLoading(false); setBatchStatus(null); }, [analysisData, inputData]); const handleDataChange = (step: K, newData: AnalysisData[K]) => { if (analysisData[step]) { setAnalysisData(prev => prev ? { ...prev, [step]: newData } : { [step]: newData }); } }; const handleDownloadMarkdown = () => { if (!analysisData) return; const markdownContent = generateMarkdown(analysisData as AnalysisData, STEP_TITLES, t.summaryTitle); downloadFile(markdownContent, 'b2b-marketing-analysis.md', 'text/markdown;charset=utf-8'); }; const handlePrint = () => { window.print(); }; const renderStep = (stepKey: keyof AnalysisData, title: string) => { const step = analysisData[stepKey] as AnalysisStep | undefined; if (!step) return null; const canDelete = ['offer', 'targetGroups', 'personas'].includes(stepKey); return ( handleDataChange(stepKey, { ...step, rows: newRows })} canAddRows={false} // Disabled enrich functionality onEnrichRow={undefined} isEnriching={false} canDeleteRows={canDelete} t={t} /> ); }; const renderContinueButton = (stepNumber: number) => { if (isLoading || generationStep !== stepNumber - 1) return null; // Industry Selector Logic for Step 6 if (stepNumber === 6 && analysisData.targetGroups?.rows) { const industries = analysisData.targetGroups.rows.map(row => row[0]); // Assume Col 0 is Industry return (

Wählen Sie eine Fokus-Branche für Schritt 6 (Botschaften):

- ODER EINZELN -
{industries.map((ind, idx) => ( ))}
); } return (
); } return (

{t.appTitle}

{t.appSubtitle}

{error && (
{t.errorTitle} {error}
)}
{isLoading && (

{batchStatus ? `Analysiere Branche ${batchStatus.current} von ${batchStatus.total}: ${batchStatus.industry}...` : t.generatingStep.replace('{{stepTitle}}', STEP_TITLES[STEP_KEYS[generationStep]])}

{batchStatus && (
)}
)} {!isLoading && generationStep === 0 && !error && (

{t.readyTitle}

{t.readyText}

)} {generationStep > 0 && ( <> {generationStep >= 6 && (
)} {renderStep('offer', STEP_TITLES.offer)} {renderContinueButton(2)} {generationStep >= 2 && renderStep('targetGroups', STEP_TITLES.targetGroups)} {renderContinueButton(3)} {generationStep >= 3 && renderStep('personas', STEP_TITLES.personas)} {renderContinueButton(4)} {generationStep >= 4 && renderStep('painPoints', STEP_TITLES.painPoints)} {renderContinueButton(5)} {generationStep >= 5 && renderStep('gains', STEP_TITLES.gains)} {renderContinueButton(6)} {generationStep >= 6 && renderStep('messages', STEP_TITLES.messages)} {generationStep === 6 && !isLoading && (

{t.analysisCompleteTitle}

{t.analysisCompleteText.replace('{{otherLanguage}}', t.otherLanguage)}

Ergebnis verfeinern?

)} )}
); }; export default App;