feat: Enhanced Outreach UI - Top 5 default + specific role generation
- market_intel_orchestrator.py: Updated generate_outreach_campaign to identify all relevant roles, generate top 5, and return remaining as suggestions. Added specific_role mode. - types.ts: Added OutreachResponse interface. - geminiService.ts: Updated to handle new response structure and specificRole parameter. - StepOutreach.tsx: Added sidebar section for Suggested Roles with on-demand generation buttons.
This commit is contained in:
@@ -1,7 +1,4 @@
|
||||
import { LeadStatus, AnalysisResult, Competitor, Language, Tier, EmailDraft, SearchStrategy, SearchSignal } from "../types";
|
||||
|
||||
// const apiKey = process.env.API_KEY; // Nicht mehr direkt im Frontend verwendet
|
||||
// const ai = new GoogleGenAI({ apiKey: apiKey || '' }); // Nicht mehr direkt im Frontend verwendet
|
||||
import { LeadStatus, AnalysisResult, Competitor, Language, Tier, EmailDraft, SearchStrategy, SearchSignal, OutreachResponse } from "../types";
|
||||
|
||||
// URL Konfiguration:
|
||||
// Im Production-Build (Docker/Nginx) nutzen wir den relativen Pfad '/api', da Nginx als Reverse Proxy fungiert.
|
||||
@@ -19,7 +16,7 @@ const extractJson = (text: string): any => {
|
||||
if (jsonMatch && jsonMatch[1]) {
|
||||
try { return JSON.parse(jsonMatch[1]); } catch (e2) {}
|
||||
}
|
||||
const arrayMatch = text.match(/\[\s*[\s\S]*\s*\]/);
|
||||
const arrayMatch = text.match(/\\[\s\S]*\\s*\]/);
|
||||
if (arrayMatch) {
|
||||
try { return JSON.parse(arrayMatch[0]); } catch (e3) {}
|
||||
}
|
||||
@@ -129,7 +126,7 @@ export const analyzeCompanyWithStrategy = async (
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify({
|
||||
body: JSON.stringify({
|
||||
companyName,
|
||||
strategy,
|
||||
targetMarket: language === 'de' ? 'Germany' : 'USA' // Einfache Ableitung, kann verfeinert werden
|
||||
@@ -175,9 +172,10 @@ export const generateOutreachCampaign = async (
|
||||
companyData: AnalysisResult,
|
||||
knowledgeBase: string,
|
||||
language: Language,
|
||||
referenceUrl: string
|
||||
): Promise<EmailDraft[]> => {
|
||||
console.log(`Frontend: Starte Outreach-Generierung für ${companyData.companyName}...`);
|
||||
referenceUrl: string,
|
||||
specificRole?: string
|
||||
): Promise<OutreachResponse> => {
|
||||
console.log(`Frontend: Starte Outreach-Generierung für ${companyData.companyName} (Role: ${specificRole || "All"})...`);
|
||||
|
||||
try {
|
||||
const response = await fetch(`${API_BASE_URL}/generate-outreach`, {
|
||||
@@ -185,10 +183,11 @@ export const generateOutreachCampaign = async (
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify({
|
||||
body: JSON.stringify({
|
||||
companyData,
|
||||
knowledgeBase,
|
||||
referenceUrl
|
||||
referenceUrl,
|
||||
specific_role: specificRole
|
||||
}),
|
||||
});
|
||||
|
||||
@@ -197,45 +196,57 @@ export const generateOutreachCampaign = async (
|
||||
throw new Error(`Backend-Fehler: ${errorData.error || response.statusText}`);
|
||||
}
|
||||
|
||||
const result = await response.json();
|
||||
console.log(`Frontend: Outreach-Generierung für ${companyData.companyName} erfolgreich.`);
|
||||
const result = await response.json(); // Expected: { campaigns: [...], available_roles: [...] } or single object
|
||||
console.log(`Frontend: Outreach-Generierung erfolgreich.`, result);
|
||||
|
||||
// Transform new backend structure to match frontend EmailDraft interface
|
||||
if (Array.isArray(result)) {
|
||||
return result.map((item: any) => {
|
||||
// Construct a body that shows the sequence
|
||||
let fullBody = "";
|
||||
const firstSubject = item.emails?.[0]?.subject || "No Subject";
|
||||
|
||||
if (item.emails && Array.isArray(item.emails)) {
|
||||
item.emails.forEach((mail: any, idx: number) => {
|
||||
fullBody += `### Email ${idx + 1}: ${mail.subject}\n\n`;
|
||||
fullBody += `${mail.body}\n\n`;
|
||||
if (idx < item.emails.length - 1) fullBody += `\n---\n\n`;
|
||||
});
|
||||
} else {
|
||||
// Fallback for flat structure or error
|
||||
fullBody = item.body || "No content generated.";
|
||||
}
|
||||
let rawCampaigns: any[] = [];
|
||||
let availableRoles: string[] = [];
|
||||
|
||||
return {
|
||||
persona: item.target_role || "Unknown Role",
|
||||
subject: firstSubject,
|
||||
body: fullBody,
|
||||
keyPoints: item.rationale ? [item.rationale] : []
|
||||
};
|
||||
});
|
||||
} else if (result.campaign && Array.isArray(result.campaign)) {
|
||||
return result.campaign as EmailDraft[];
|
||||
// Handle Mode A (Batch) vs Mode B (Single) response structures
|
||||
if (result.campaigns && Array.isArray(result.campaigns)) {
|
||||
rawCampaigns = result.campaigns;
|
||||
availableRoles = result.available_roles || [];
|
||||
} else if (result.target_role && result.emails) {
|
||||
// Single campaign response (Mode B)
|
||||
rawCampaigns = [result];
|
||||
} else if (Array.isArray(result)) {
|
||||
// Legacy fallback
|
||||
rawCampaigns = result;
|
||||
}
|
||||
|
||||
return [];
|
||||
|
||||
const processedCampaigns: EmailDraft[] = rawCampaigns.map((item: any) => {
|
||||
let fullBody = "";
|
||||
const firstSubject = item.emails?.[0]?.subject || "No Subject";
|
||||
|
||||
if (item.emails && Array.isArray(item.emails)) {
|
||||
item.emails.forEach((mail: any, idx: number) => {
|
||||
fullBody += `### Email ${idx + 1}: ${mail.subject}\n\n`;
|
||||
fullBody += `${mail.body}\n\n`;
|
||||
if (idx < item.emails.length - 1) fullBody += `\n---\n\n`;
|
||||
});
|
||||
} else {
|
||||
fullBody = item.body || "No content generated.";
|
||||
}
|
||||
|
||||
return {
|
||||
persona: item.target_role || "Unknown Role",
|
||||
subject: firstSubject,
|
||||
body: fullBody,
|
||||
keyPoints: item.rationale ? [item.rationale] : []
|
||||
};
|
||||
});
|
||||
|
||||
return {
|
||||
campaigns: processedCampaigns,
|
||||
available_roles: availableRoles
|
||||
};
|
||||
|
||||
} catch (error) {
|
||||
console.error(`Frontend: Outreach-Generierung fehlgeschlagen für ${companyData.companyName}`, error);
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
|
||||
export const translateEmailDrafts = async (drafts: EmailDraft[], targetLanguage: Language): Promise<EmailDraft[]> => {
|
||||
// Dieser Teil muss noch im Python-Backend oder direkt im Frontend implementiert werden
|
||||
console.warn("translateEmailDrafts ist noch nicht im Python-Backend implementiert.");
|
||||
|
||||
Reference in New Issue
Block a user