Files
Brancheneinstufung2/competitor-analysis-app/components/Step3_Competitors.tsx
Floke 249b06463b feat(competitor-analysis): Fix 404, SDK compatibility, and update docs
Resolved multiple issues preventing the 'competitor-analysis' app from running and serving its frontend:

1.  **Fixed Python SyntaxError in Prompts:** Corrected unterminated string literals and ensure proper multi-line string formatting (using .format() instead of f-strings for complex prompts) in .
2.  **Addressed Python SDK Compatibility (google-generativeai==0.3.0):**
    *   Removed  for  and  by adapting the orchestrator to pass JSON schemas as direct Python dictionaries, as required by the older SDK version.
    *   Updated  with detailed guidance on handling / imports and dictionary-based schema definitions for older SDKs.
3.  **Corrected Frontend Build Dependencies:** Moved critical build dependencies (like , , ) from  to  in .
    *   Updated  to include this  pitfall, ensuring frontend build tools are installed in Docker.
4.  **Updated Documentation:**
    *   : Added comprehensive lessons learned regarding  dependencies, Python SDK versioning (specifically  and  imports for ), and robust multi-line prompt handling.
    *   : Integrated specific details of the encountered errors and their solutions, making the migration report a more complete historical record and guide.

These changes collectively fix the 404 error by ensuring the Python backend starts correctly and serves the frontend assets after a successful build.
2026-01-10 09:10:00 +00:00

57 lines
2.8 KiB
TypeScript

import React from 'react';
import type { CompetitorCandidate } from '../types';
import EvidencePopover from './EvidencePopover';
import { EditableCard } from './EditableCard';
interface Step3CompetitorsProps {
candidates: CompetitorCandidate[];
onCandidatesChange: (candidates: CompetitorCandidate[]) => void;
maxCompetitors: number;
t: any;
}
const Step3Competitors: React.FC<Step3CompetitorsProps> = ({ candidates, onCandidatesChange, maxCompetitors, t }) => {
const sortedCandidates = [...candidates].sort((a, b) => b.confidence - a.confidence);
return (
<div>
<h2 className="text-2xl font-bold mb-4">{t.title}</h2>
<p className="text-light-subtle dark:text-brand-light mb-6">
{t.subtitle(maxCompetitors)}
</p>
<EditableCard<CompetitorCandidate>
title={t.cardTitle}
items={sortedCandidates}
onItemsChange={onCandidatesChange}
fieldConfigs={[
{ key: 'name', label: t.nameLabel, type: 'text' },
{ key: 'url', label: 'URL', type: 'text' },
{ key: 'why', label: t.whyLabel, type: 'textarea' },
]}
newItemTemplate={{ name: '', url: '', confidence: 0.8, why: '', evidence: [] }}
renderDisplay={(item, index) => (
<div>
<div className="flex items-center justify-between">
<div className="flex items-center">
<strong className={`text-light-text dark:text-white ${index < maxCompetitors ? 'text-green-600 dark:text-green-400' : ''}`}>{item.name}</strong>
<a href={item.url} target="_blank" rel="noopener noreferrer" className="ml-2 text-blue-500 dark:text-blue-400 hover:underline text-sm">
{t.visitButton}
</a>
<EvidencePopover evidence={item.evidence} />
</div>
<span className="text-xs font-mono bg-light-accent dark:bg-brand-accent text-light-text dark:text-white py-1 px-2 rounded-full">
{(item.confidence * 100).toFixed(0)}%
</span>
</div>
<p className="text-light-subtle dark:text-brand-light text-sm mt-1">{item.why}</p>
{index === maxCompetitors - 1 && <div className="border-t-2 border-dashed border-red-500 mt-4 pt-2 text-red-500 dark:text-red-400 text-xs text-center font-bold">{t.shortlistBoundary}</div>}
</div>
)}
t={t.editableCard}
/>
</div>
);
};
export default Step3Competitors;