From 54cad18fc869470dd2490b808af9fd161ed6825c Mon Sep 17 00:00:00 2001 From: Floke Date: Sat, 20 Dec 2025 20:57:09 +0000 Subject: [PATCH] Dateien nach "general-market-intelligence" hochladen --- general-market-intelligence/.env.local | 1 + general-market-intelligence/.gitignore | 24 ++++ general-market-intelligence/App.tsx | 188 +++++++++++++++++++++++++ general-market-intelligence/index.html | 35 +++++ general-market-intelligence/index.tsx | 15 ++ 5 files changed, 263 insertions(+) create mode 100644 general-market-intelligence/.env.local create mode 100644 general-market-intelligence/.gitignore create mode 100644 general-market-intelligence/App.tsx create mode 100644 general-market-intelligence/index.html create mode 100644 general-market-intelligence/index.tsx diff --git a/general-market-intelligence/.env.local b/general-market-intelligence/.env.local new file mode 100644 index 00000000..be86fc11 --- /dev/null +++ b/general-market-intelligence/.env.local @@ -0,0 +1 @@ +GEMINI_API_KEY=PLACEHOLDER_API_KEY diff --git a/general-market-intelligence/.gitignore b/general-market-intelligence/.gitignore new file mode 100644 index 00000000..a547bf36 --- /dev/null +++ b/general-market-intelligence/.gitignore @@ -0,0 +1,24 @@ +# Logs +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* +pnpm-debug.log* +lerna-debug.log* + +node_modules +dist +dist-ssr +*.local + +# Editor directories and files +.vscode/* +!.vscode/extensions.json +.idea +.DS_Store +*.suo +*.ntvs* +*.njsproj +*.sln +*.sw? diff --git a/general-market-intelligence/App.tsx b/general-market-intelligence/App.tsx new file mode 100644 index 00000000..53c20f81 --- /dev/null +++ b/general-market-intelligence/App.tsx @@ -0,0 +1,188 @@ + +import React, { useState, useCallback } from 'react'; +import { Header } from './components/Header'; +import { StepInput } from './components/StepInput'; +import { StepStrategy } from './components/StepStrategy'; +import { StepReview } from './components/StepReview'; +import { StepProcessing } from './components/StepProcessing'; +import { StepReport } from './components/StepReport'; +import { StepOutreach } from './components/StepOutreach'; +import { AppStep, Competitor, AnalysisResult, AnalysisState, Language, SearchStrategy } from './types'; +import { identifyCompetitors, analyzeCompanyWithStrategy, generateSearchStrategy } from './services/geminiService'; + +const generateId = () => Math.random().toString(36).substr(2, 9); + +const App: React.FC = () => { + const [step, setStep] = useState(AppStep.INPUT); + const [isLoading, setIsLoading] = useState(false); + const [language, setLanguage] = useState('de'); + const [referenceUrl, setReferenceUrl] = useState(''); + const [targetMarket, setTargetMarket] = useState(''); + + // Data States + const [strategy, setStrategy] = useState(null); + const [competitors, setCompetitors] = useState([]); + const [analysisResults, setAnalysisResults] = useState([]); + const [processingState, setProcessingState] = useState({ + currentCompany: '', progress: 0, total: 0, completed: 0 + }); + + const [selectedCompanyForOutreach, setSelectedCompanyForOutreach] = useState(null); + + const handleBack = () => { + if (step === AppStep.STRATEGY) setStep(AppStep.INPUT); + else if (step === AppStep.REVIEW_LIST) setStep(AppStep.STRATEGY); + else if (step === AppStep.REPORT) setStep(AppStep.REVIEW_LIST); + else if (step === AppStep.OUTREACH) { + setStep(AppStep.REPORT); + setSelectedCompanyForOutreach(null); + } + }; + + const handleInitialInput = async (url: string, productContext: string, market: string, selectedLang: Language) => { + setIsLoading(true); + setLanguage(selectedLang); + setReferenceUrl(url); + setTargetMarket(market); + try { + // 1. Generate Strategy first + const generatedStrategy = await generateSearchStrategy(url, productContext, selectedLang); + setStrategy(generatedStrategy); + setStep(AppStep.STRATEGY); + } catch (error) { + alert("Failed to generate strategy. Please try again."); + console.error(error); + } finally { + setIsLoading(false); + } + }; + + const handleLoadReport = (loadedStrategy: SearchStrategy, loadedResults: AnalysisResult[]) => { + setStrategy(loadedStrategy); + setAnalysisResults(loadedResults); + // Reconstruct competitors list from results for consistency if user goes back + const reconstructedCompetitors = loadedResults.map(r => ({ + id: generateId(), + name: r.companyName, + dataSource: r.dataSource + })); + setCompetitors(reconstructedCompetitors); + setStep(AppStep.REPORT); + }; + + const handleStrategyConfirm = async (finalStrategy: SearchStrategy) => { + setStrategy(finalStrategy); + setIsLoading(true); + try { + // 2. Identify Competitors based on Reference + const results = await identifyCompetitors(referenceUrl, targetMarket, language); + const mappedCompetitors: Competitor[] = results.map(c => ({ + id: generateId(), + name: c.name || "Unknown", + url: c.url, + description: c.description + })); + setCompetitors(mappedCompetitors); + setStep(AppStep.REVIEW_LIST); + } catch (e) { + alert("Failed to find companies."); + } finally { + setIsLoading(false); + } + }; + + const handleRemoveCompetitor = (id: string) => { + setCompetitors(prev => prev.filter(c => c.id !== id)); + }; + + const handleAddCompetitor = (name: string) => { + setCompetitors(prev => [...prev, { id: generateId(), name }]); + }; + + const runAnalysis = useCallback(async () => { + if (!strategy) return; + setStep(AppStep.ANALYSIS); + setAnalysisResults([]); + setProcessingState({ currentCompany: '', progress: 0, total: competitors.length, completed: 0 }); + + const results: AnalysisResult[] = []; + + for (let i = 0; i < competitors.length; i++) { + const comp = competitors[i]; + setProcessingState(prev => ({ ...prev, currentCompany: comp.name })); + + try { + // 3. Analyze using the specific strategy + const result = await analyzeCompanyWithStrategy(comp.name, strategy, language); + results.push(result); + } catch (e) { + console.error(`Failed to analyze ${comp.name}`); + } + setProcessingState(prev => ({ ...prev, completed: i + 1 })); + } + + setAnalysisResults(results); + setStep(AppStep.REPORT); + }, [competitors, language, strategy]); + + const handleRestart = () => { + setCompetitors([]); + setAnalysisResults([]); + setStrategy(null); + setStep(AppStep.INPUT); + }; + + return ( +
+
+
+ {step === AppStep.INPUT && ( + + )} + + {step === AppStep.STRATEGY && strategy && ( + + )} + + {step === AppStep.REVIEW_LIST && ( + 0} + onShowReport={() => setStep(AppStep.REPORT)} + /> + )} + + {step === AppStep.ANALYSIS && ( + + )} + + {step === AppStep.REPORT && strategy && ( + { + setSelectedCompanyForOutreach(company); + setStep(AppStep.OUTREACH); + }} + /> + )} + + {step === AppStep.OUTREACH && selectedCompanyForOutreach && ( + + )} +
+
+ ); +}; + +export default App; diff --git a/general-market-intelligence/index.html b/general-market-intelligence/index.html new file mode 100644 index 00000000..dfbddfa8 --- /dev/null +++ b/general-market-intelligence/index.html @@ -0,0 +1,35 @@ + + + + + + parcelLab Market Intelligence + + + + + + + +
+ + + \ No newline at end of file diff --git a/general-market-intelligence/index.tsx b/general-market-intelligence/index.tsx new file mode 100644 index 00000000..6ca5361e --- /dev/null +++ b/general-market-intelligence/index.tsx @@ -0,0 +1,15 @@ +import React from 'react'; +import ReactDOM from 'react-dom/client'; +import App from './App'; + +const rootElement = document.getElementById('root'); +if (!rootElement) { + throw new Error("Could not find root element to mount to"); +} + +const root = ReactDOM.createRoot(rootElement); +root.render( + + + +); \ No newline at end of file