feat(b2b-assistant): Implement Step 7 (Customer Journey) with tactical focus on Buying Center, Deal-Breakers, and Assets. Add restart functionality for individual steps.
This commit is contained in:
@@ -67,7 +67,7 @@ const App: React.FC = () => {
|
|||||||
|
|
||||||
const t = translations[inputData.language];
|
const t = translations[inputData.language];
|
||||||
const STEP_TITLES = t.stepTitles;
|
const STEP_TITLES = t.stepTitles;
|
||||||
const STEP_KEYS: (keyof AnalysisData)[] = ['offer', 'targetGroups', 'personas', 'painPoints', 'gains', 'messages'];
|
const STEP_KEYS: (keyof AnalysisData)[] = ['offer', 'targetGroups', 'personas', 'painPoints', 'gains', 'messages', 'customerJourney'];
|
||||||
|
|
||||||
// --- AUTO-SAVE EFFECT ---
|
// --- AUTO-SAVE EFFECT ---
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@@ -260,9 +260,16 @@ const App: React.FC = () => {
|
|||||||
};
|
};
|
||||||
if (newAnalysisData.messages.rows.length === 0) newAnalysisData.messages.rows = parseSection("Step 6");
|
if (newAnalysisData.messages.rows.length === 0) newAnalysisData.messages.rows = parseSection("Step 6");
|
||||||
|
|
||||||
|
newAnalysisData.customerJourney = {
|
||||||
|
summary: [],
|
||||||
|
headers: [],
|
||||||
|
rows: parseSection("Schritt 7")
|
||||||
|
};
|
||||||
|
if (newAnalysisData.customerJourney.rows.length === 0) newAnalysisData.customerJourney.rows = parseSection("Step 7");
|
||||||
|
|
||||||
|
|
||||||
setAnalysisData(newAnalysisData);
|
setAnalysisData(newAnalysisData);
|
||||||
setGenerationStep(6); // Jump to end
|
setGenerationStep(7); // Jump to end (now 7)
|
||||||
setProjectName(file.name.replace('.md', ' (Imported)'));
|
setProjectName(file.name.replace('.md', ' (Imported)'));
|
||||||
setProjectId(null); // Treat as new project
|
setProjectId(null); // Treat as new project
|
||||||
|
|
||||||
@@ -321,7 +328,7 @@ const App: React.FC = () => {
|
|||||||
}, [inputData]);
|
}, [inputData]);
|
||||||
|
|
||||||
const handleGenerateNextStep = useCallback(async () => {
|
const handleGenerateNextStep = useCallback(async () => {
|
||||||
if (generationStep >= 6) return;
|
if (generationStep >= 7) return;
|
||||||
|
|
||||||
if (generationStep === 5 && !selectedIndustry) {
|
if (generationStep === 5 && !selectedIndustry) {
|
||||||
setError('Bitte wählen Sie eine Fokus-Branche aus.');
|
setError('Bitte wählen Sie eine Fokus-Branche aus.');
|
||||||
@@ -340,7 +347,7 @@ const App: React.FC = () => {
|
|||||||
language: inputData.language,
|
language: inputData.language,
|
||||||
channels: inputData.channels,
|
channels: inputData.channels,
|
||||||
generationStep: generationStep + 1, // Pass the step we want to generate
|
generationStep: generationStep + 1, // Pass the step we want to generate
|
||||||
focusIndustry: generationStep === 5 ? selectedIndustry : undefined,
|
focusIndustry: (generationStep === 5 || generationStep === 6) ? selectedIndustry : undefined,
|
||||||
}),
|
}),
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -443,6 +450,45 @@ const App: React.FC = () => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const handleStepRestart = (stepKey: keyof AnalysisData) => {
|
||||||
|
const stepIndex = STEP_KEYS.indexOf(stepKey);
|
||||||
|
if (stepIndex === -1) return;
|
||||||
|
|
||||||
|
// Reset steps from this index onwards
|
||||||
|
// But specifically, if I restart Step 2, I want generationStep to be 1 (so I can generate Step 2 again)
|
||||||
|
// If I restart Step 1 (index 0), I want generationStep to be 0
|
||||||
|
|
||||||
|
// But wait, if I restart Step 1 (Offer), that is the "Start Generation" button usually.
|
||||||
|
// Actually, if I restart Step 1, I basically want to clear everything and go back to step 0.
|
||||||
|
// If I restart Step 2, I want to keep Step 1, clear Step 2+, and go back to step 1 (ready to generate step 2).
|
||||||
|
|
||||||
|
const newGenerationStep = stepIndex;
|
||||||
|
|
||||||
|
setGenerationStep(newGenerationStep);
|
||||||
|
setAnalysisData(prev => {
|
||||||
|
const newData: Partial<AnalysisData> = { ...prev };
|
||||||
|
// Remove all keys starting from this step
|
||||||
|
for (let i = stepIndex; i < STEP_KEYS.length; i++) {
|
||||||
|
delete newData[STEP_KEYS[i]];
|
||||||
|
}
|
||||||
|
return newData;
|
||||||
|
});
|
||||||
|
|
||||||
|
// Also reset other state related to progress
|
||||||
|
if (stepKey === 'messages' || stepKey === 'targetGroups') {
|
||||||
|
// If we are resetting before Step 6, clear selection
|
||||||
|
// Actually if we reset Step 6 (messages), we clear Step 6 data.
|
||||||
|
if (stepIndex <= 5) {
|
||||||
|
setBatchStatus(null);
|
||||||
|
// We might want to keep selectedIndustry if we are just retrying Step 6?
|
||||||
|
// But the prompt implies "resetting", so clearing selection is safer.
|
||||||
|
setSelectedIndustry('');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// If resetting Step 7, clear its specific state if any (none currently)
|
||||||
|
};
|
||||||
|
|
||||||
const handleDownloadMarkdown = () => {
|
const handleDownloadMarkdown = () => {
|
||||||
if (!analysisData) return;
|
if (!analysisData) return;
|
||||||
const markdownContent = generateMarkdown(analysisData as AnalysisData, STEP_TITLES, t.summaryTitle);
|
const markdownContent = generateMarkdown(analysisData as AnalysisData, STEP_TITLES, t.summaryTitle);
|
||||||
@@ -478,6 +524,7 @@ const App: React.FC = () => {
|
|||||||
onEnrichRow={canAdd ? handleManualAdd : undefined}
|
onEnrichRow={canAdd ? handleManualAdd : undefined}
|
||||||
isEnriching={false}
|
isEnriching={false}
|
||||||
canDeleteRows={canDelete}
|
canDeleteRows={canDelete}
|
||||||
|
onRestart={() => handleStepRestart(stepKey)}
|
||||||
t={t}
|
t={t}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
@@ -661,9 +708,11 @@ const App: React.FC = () => {
|
|||||||
{generationStep >= 5 && renderStep('gains', STEP_TITLES.gains)}
|
{generationStep >= 5 && renderStep('gains', STEP_TITLES.gains)}
|
||||||
{renderContinueButton(6)}
|
{renderContinueButton(6)}
|
||||||
{generationStep >= 6 && renderStep('messages', STEP_TITLES.messages)}
|
{generationStep >= 6 && renderStep('messages', STEP_TITLES.messages)}
|
||||||
|
{renderContinueButton(7)}
|
||||||
|
{generationStep >= 7 && renderStep('customerJourney', STEP_TITLES.customerJourney)}
|
||||||
|
|
||||||
|
|
||||||
{generationStep === 6 && !isLoading && (
|
{generationStep === 7 && !isLoading && (
|
||||||
<div className="p-8 bg-sky-50 dark:bg-sky-900/20 rounded-2xl border border-sky-100 dark:border-sky-800 text-center print:hidden">
|
<div className="p-8 bg-sky-50 dark:bg-sky-900/20 rounded-2xl border border-sky-100 dark:border-sky-800 text-center print:hidden">
|
||||||
<div className="inline-flex items-center justify-center w-12 h-12 rounded-full bg-sky-100 dark:bg-sky-800 mb-4">
|
<div className="inline-flex items-center justify-center w-12 h-12 rounded-full bg-sky-100 dark:bg-sky-800 mb-4">
|
||||||
<SparklesIcon className="h-6 w-6 text-sky-600 dark:text-sky-300" />
|
<SparklesIcon className="h-6 w-6 text-sky-600 dark:text-sky-300" />
|
||||||
|
|||||||
@@ -83,3 +83,9 @@ export const XMarkIcon: React.FC<{ className?: string }> = ({ className = '' })
|
|||||||
<line x1="6" y1="6" x2="18" y2="18" />
|
<line x1="6" y1="6" x2="18" y2="18" />
|
||||||
</svg>
|
</svg>
|
||||||
);
|
);
|
||||||
|
|
||||||
|
export const RefreshIcon: React.FC<{ className?: string }> = ({ className = '' }) => (
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" strokeWidth={1.5} stroke="currentColor" className={`h-6 w-6 ${className}`}>
|
||||||
|
<path strokeLinecap="round" strokeLinejoin="round" d="M16.023 9.348h4.992v-.001M2.985 19.644v-4.992m0 0h4.992m-4.993 0l3.181 3.183a8.25 8.25 0 0013.803-3.7M4.031 9.865a8.25 8.25 0 0113.803-3.7l3.181 3.182m0-4.991v4.99" />
|
||||||
|
</svg>
|
||||||
|
);
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
|
|
||||||
import React, { useState, useMemo, useRef, useEffect } from 'react';
|
import React, { useState, useMemo, useRef, useEffect } from 'react';
|
||||||
import { CopyIcon, ClipboardTableIcon, SearchIcon, TrashIcon, LoadingSpinner, CheckIcon, XMarkIcon } from './Icons';
|
import { CopyIcon, ClipboardTableIcon, SearchIcon, TrashIcon, LoadingSpinner, CheckIcon, XMarkIcon, RefreshIcon } from './Icons';
|
||||||
import { convertArrayToTsv } from '../services/export';
|
import { convertArrayToTsv } from '../services/export';
|
||||||
import { translations } from '../constants';
|
import { translations } from '../constants';
|
||||||
|
|
||||||
@@ -14,16 +14,18 @@ interface StepDisplayProps {
|
|||||||
canDeleteRows?: boolean;
|
canDeleteRows?: boolean;
|
||||||
onEnrichRow?: (productName: string, productUrl?: string) => Promise<void>;
|
onEnrichRow?: (productName: string, productUrl?: string) => Promise<void>;
|
||||||
isEnriching?: boolean;
|
isEnriching?: boolean;
|
||||||
|
onRestart?: () => void;
|
||||||
t: typeof translations.de;
|
t: typeof translations.de;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const StepDisplay: React.FC<StepDisplayProps> = ({ title, summary, headers, rows, onDataChange, canAddRows = false, canDeleteRows = false, onEnrichRow, isEnriching = false, t }) => {
|
export const StepDisplay: React.FC<StepDisplayProps> = ({ title, summary, headers, rows, onDataChange, canAddRows = false, canDeleteRows = false, onEnrichRow, isEnriching = false, onRestart, t }) => {
|
||||||
const [copySuccess, setCopySuccess] = useState('');
|
const [copySuccess, setCopySuccess] = useState('');
|
||||||
const [filterQuery, setFilterQuery] = useState('');
|
const [filterQuery, setFilterQuery] = useState('');
|
||||||
const [isAddingRow, setIsAddingRow] = useState(false);
|
const [isAddingRow, setIsAddingRow] = useState(false);
|
||||||
const [newRowValue, setNewRowValue] = useState('');
|
const [newRowValue, setNewRowValue] = useState('');
|
||||||
const [newRowUrl, setNewRowUrl] = useState('');
|
const [newRowUrl, setNewRowUrl] = useState('');
|
||||||
const inputRef = useRef<HTMLInputElement>(null);
|
const inputRef = useRef<HTMLInputElement>(null);
|
||||||
|
const [showRestartConfirm, setShowRestartConfirm] = useState(false);
|
||||||
|
|
||||||
const filteredRows = useMemo(() => {
|
const filteredRows = useMemo(() => {
|
||||||
if (!filterQuery) {
|
if (!filterQuery) {
|
||||||
@@ -141,6 +143,15 @@ export const StepDisplay: React.FC<StepDisplayProps> = ({ title, summary, header
|
|||||||
onDataChange(newRows);
|
onDataChange(newRows);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const handleRestartClick = () => {
|
||||||
|
setShowRestartConfirm(true);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleRestartConfirm = () => {
|
||||||
|
setShowRestartConfirm(false);
|
||||||
|
if (onRestart) onRestart();
|
||||||
|
};
|
||||||
|
|
||||||
const getColumnStyle = (header: string): React.CSSProperties => {
|
const getColumnStyle = (header: string): React.CSSProperties => {
|
||||||
const lowerHeader = header.toLowerCase();
|
const lowerHeader = header.toLowerCase();
|
||||||
|
|
||||||
@@ -169,9 +180,40 @@ export const StepDisplay: React.FC<StepDisplayProps> = ({ title, summary, header
|
|||||||
const isLoadingCell = (cell: string) => cell.toLowerCase().includes(loadingText.toLowerCase());
|
const isLoadingCell = (cell: string) => cell.toLowerCase().includes(loadingText.toLowerCase());
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<section className="bg-white dark:bg-slate-800/50 rounded-2xl shadow-lg p-6 md:p-8 border border-slate-200 dark:border-slate-700 print:shadow-none print:border-none print:[break-inside:avoid]">
|
<section className="bg-white dark:bg-slate-800/50 rounded-2xl shadow-lg p-6 md:p-8 border border-slate-200 dark:border-slate-700 print:shadow-none print:border-none print:[break-inside:avoid] relative">
|
||||||
<div className="flex flex-col sm:flex-row justify-between sm:items-start mb-6 gap-4">
|
<div className="flex flex-col sm:flex-row justify-between sm:items-start mb-6 gap-4">
|
||||||
|
<div className="flex items-center gap-4">
|
||||||
<h2 className="text-2xl md:text-3xl font-bold text-slate-900 dark:text-white">{title}</h2>
|
<h2 className="text-2xl md:text-3xl font-bold text-slate-900 dark:text-white">{title}</h2>
|
||||||
|
{onRestart && (
|
||||||
|
<div className="relative print:hidden">
|
||||||
|
{!showRestartConfirm ? (
|
||||||
|
<button
|
||||||
|
onClick={handleRestartClick}
|
||||||
|
className="p-2 text-slate-400 hover:text-red-500 hover:bg-red-50 dark:hover:bg-red-900/20 rounded-full transition-colors"
|
||||||
|
title="Diesen Schritt neu starten (löscht nachfolgende Schritte)"
|
||||||
|
>
|
||||||
|
<RefreshIcon className="h-5 w-5" />
|
||||||
|
</button>
|
||||||
|
) : (
|
||||||
|
<div className="absolute top-0 left-0 z-10 flex items-center bg-white dark:bg-slate-800 border border-slate-200 dark:border-slate-700 shadow-lg rounded-lg p-1 animate-fade-in whitespace-nowrap">
|
||||||
|
<span className="text-xs font-semibold text-slate-700 dark:text-slate-300 mx-2">Wirklich neu starten?</span>
|
||||||
|
<button
|
||||||
|
onClick={handleRestartConfirm}
|
||||||
|
className="p-1 text-green-600 hover:bg-green-50 dark:hover:bg-green-900/30 rounded"
|
||||||
|
>
|
||||||
|
<CheckIcon className="h-4 w-4" />
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
onClick={() => setShowRestartConfirm(false)}
|
||||||
|
className="p-1 text-red-600 hover:bg-red-50 dark:hover:bg-red-900/30 rounded"
|
||||||
|
>
|
||||||
|
<XMarkIcon className="h-4 w-4" />
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
<div className="relative ml-auto">
|
<div className="relative ml-auto">
|
||||||
<button
|
<button
|
||||||
onClick={handleCopyTable}
|
onClick={handleCopyTable}
|
||||||
|
|||||||
@@ -53,6 +53,7 @@ export const translations = {
|
|||||||
painPoints: 'Schritt 4: Painpoints je Rolle (WARUM)',
|
painPoints: 'Schritt 4: Painpoints je Rolle (WARUM)',
|
||||||
gains: 'Schritt 5: Gains & Nutzen je Rolle (WARUM wechseln)',
|
gains: 'Schritt 5: Gains & Nutzen je Rolle (WARUM wechseln)',
|
||||||
messages: 'Schritt 6: Marketingbotschaften je Segment & Rolle (WIE sprechen)',
|
messages: 'Schritt 6: Marketingbotschaften je Segment & Rolle (WIE sprechen)',
|
||||||
|
customerJourney: 'Schritt 7: Customer Journey & Buying Center',
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
en: {
|
en: {
|
||||||
@@ -101,6 +102,7 @@ export const translations = {
|
|||||||
painPoints: 'Step 4: Pain Points per Role (WHY)',
|
painPoints: 'Step 4: Pain Points per Role (WHY)',
|
||||||
gains: 'Step 5: Gains & Benefits per Role (WHY switch)',
|
gains: 'Step 5: Gains & Benefits per Role (WHY switch)',
|
||||||
messages: 'Step 6: Marketing Messages per Segment & Role (HOW to speak)',
|
messages: 'Step 6: Marketing Messages per Segment & Role (HOW to speak)',
|
||||||
|
customerJourney: 'Step 7: Customer Journey & Buying Center',
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ export const tableToMarkdown = (step: AnalysisStep): string => {
|
|||||||
export const generateMarkdown = (data: AnalysisData, titles: Record<keyof AnalysisData, string>, summaryTitle: string): string => {
|
export const generateMarkdown = (data: AnalysisData, titles: Record<keyof AnalysisData, string>, summaryTitle: string): string => {
|
||||||
let markdownContent = '# B2B Marketing Analysis Report\n\n';
|
let markdownContent = '# B2B Marketing Analysis Report\n\n';
|
||||||
|
|
||||||
const stepOrder: (keyof AnalysisData)[] = ['offer', 'targetGroups', 'personas', 'painPoints', 'gains', 'messages'];
|
const stepOrder: (keyof AnalysisData)[] = ['offer', 'targetGroups', 'personas', 'painPoints', 'gains', 'messages', 'customerJourney'];
|
||||||
|
|
||||||
for (const key of stepOrder) {
|
for (const key of stepOrder) {
|
||||||
const step = data[key];
|
const step = data[key];
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ export interface AnalysisData {
|
|||||||
painPoints?: AnalysisStep;
|
painPoints?: AnalysisStep;
|
||||||
gains?: AnalysisStep;
|
gains?: AnalysisStep;
|
||||||
messages?: AnalysisStep;
|
messages?: AnalysisStep;
|
||||||
|
customerJourney?: AnalysisStep;
|
||||||
searchStrategyICP?: AnalysisStep;
|
searchStrategyICP?: AnalysisStep;
|
||||||
digitalSignals?: AnalysisStep;
|
digitalSignals?: AnalysisStep;
|
||||||
targetPages?: AnalysisStep;
|
targetPages?: AnalysisStep;
|
||||||
|
|||||||
@@ -22,6 +22,16 @@ Der 6-stufige Prozess der App wird im Backend abgebildet, wobei die ersten Schri
|
|||||||
2. **Schritt 3-6 (Personas, Pains, Gains, Messages):**
|
2. **Schritt 3-6 (Personas, Pains, Gains, Messages):**
|
||||||
* Diese Schritte bauen auf den validierten, faktenbasierten Ergebnissen aus Schritt 1 & 2 auf. Die gesamte Logikkette wird dadurch stabiler und konsistenter.
|
* Diese Schritte bauen auf den validierten, faktenbasierten Ergebnissen aus Schritt 1 & 2 auf. Die gesamte Logikkette wird dadurch stabiler und konsistenter.
|
||||||
|
|
||||||
|
3. **Schritt 7 (Customer Journey & Buying Center):**
|
||||||
|
* **NEU:** Als finaler Schritt wird die hypothetische Kaufentscheidung simuliert.
|
||||||
|
* **Fokus:** Analyse der "Journey" vom ersten Touchpoint bis zum Vertragsabschluss.
|
||||||
|
* **Kernfragen:**
|
||||||
|
* Wie sieht der Entscheidungsprozess aus? (Spontan vs. Gremium)
|
||||||
|
* Welche Stationen durchläuft der Käufer?
|
||||||
|
* Wie sehen typische Verträge aus (Laufzeit, Lock-in)?
|
||||||
|
* Welche spezifischen Fragen stellen sich die unterschiedlichen Entscheider (Buying Center) in den verschiedenen Phasen?
|
||||||
|
* **Ziel:** Tiefe Einblicke in das Käuferverhalten, spezifisch getrimmt auf das ermittelte Produkt (SaaS vs. Hardware), unter Einbeziehung der Pains & Gains.
|
||||||
|
|
||||||
## 3. Strategische Vision: Integration der Tools
|
## 3. Strategische Vision: Integration der Tools
|
||||||
|
|
||||||
Dieses Projekt ist der erste Schritt zur Schaffung eines einheitlichen "Strategy & Audit"-Workflows.
|
Dieses Projekt ist der erste Schritt zur Schaffung eines einheitlichen "Strategy & Audit"-Workflows.
|
||||||
@@ -258,102 +268,26 @@ Für eine schnelle Entwicklung ist "Sideloading" für die Python-Logik aktiviert
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
### Priorität 2: Asset Factory (Schritt 7)
|
### Priorität 2: Customer Journey (Schritt 7) [ABGESCHLOSSEN]
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
* **UI:** Neuer Bereich "Assets generieren" nach Abschluss von Schritt 6.
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
* **Status:** Erfolgreich implementiert.
|
||||||
|
* **Funktion:** Tiefe Analyse der Kaufentscheidung mit Fokus auf Buying Center Dynamik.
|
||||||
|
* **Mehrwert:** Liefert konkrete "Deal-Breaker" und definiert benötigte Assets (Munition) für jede Phase.
|
||||||
|
* **UI:** Tabellarische Darstellung mit neuer "Schritt neu starten"-Funktion für iterative Optimierung.
|
||||||
|
|
||||||
|
### Priorität 3: Asset Factory (Schritt 8)
|
||||||
|
|
||||||
|
* **UI:** Neuer Bereich "Assets generieren" nach Abschluss von Schritt 7.
|
||||||
* **Funktion:** Auswahl einer Persona und eines Formats (z.B. "LinkedIn Vernetzungsanfrage", "Cold Mail Sequenz").
|
* **Funktion:** Auswahl einer Persona und eines Formats (z.B. "LinkedIn Vernetzungsanfrage", "Cold Mail Sequenz").
|
||||||
|
* **Input:** Nutzt die in Schritt 7 definierten "Benötigten Assets" als Blaupause.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
* **Output:** Generierung von Copy-Paste-fertigen Texten basierend auf den Painpoints/Gains der Analyse.
|
* **Output:** Generierung von Copy-Paste-fertigen Texten basierend auf den Painpoints/Gains der Analyse.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
* **Export:** Als separater "Marketing Kit" Download oder Anhang im Markdown.
|
* **Export:** Als separater "Marketing Kit" Download oder Anhang im Markdown.
|
||||||
|
|
||||||
|
### Priorität 4: Erweiterung des Finalen Reports [ABGESCHLOSSEN]
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
### Priorität 3: Erweiterung des Finalen Reports [ABGESCHLOSSEN]
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
* **Status:** Vollständig in den Workflow integriert.
|
* **Status:** Vollständig in den Workflow integriert.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
* **Search Strategy Beschreibung ICP:** Detaillierte Profile werden generiert.
|
* **Search Strategy Beschreibung ICP:** Detaillierte Profile werden generiert.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
* **Digital Signals:** Konkrete Signale (Technographic, Growth, Strategy) identifiziert.
|
* **Digital Signals:** Konkrete Signale (Technographic, Growth, Strategy) identifiziert.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
* **Target Pages:** Relevante URLs für die Akquise im Report gelistet.
|
* **Target Pages:** Relevante URLs für die Akquise im Report gelistet.
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -150,6 +150,22 @@ Fuehre fuer jede **[Rolle]** innerhalb der **[Fokus-Branche: {{focus_industry}}]
|
|||||||
Erstelle ONLY die finale Markdown-Tabelle.
|
Erstelle ONLY die finale Markdown-Tabelle.
|
||||||
* **Table Columns:** *Fokus-Branche | Rolle | Kernbotschaft (2-3 sentences) | {{channels}}*.
|
* **Table Columns:** *Fokus-Branche | Rolle | Kernbotschaft (2-3 sentences) | {{channels}}*.
|
||||||
* **Requirement:** Your response must start with the heading \"## Schritt 6: Botschaften\" and contain ONLY die vollstaendige Markdown-Tabelle.""",
|
* **Requirement:** Your response must start with the heading \"## Schritt 6: Botschaften\" and contain ONLY die vollstaendige Markdown-Tabelle.""",
|
||||||
|
"""# Aufgabe
|
||||||
|
Fuehre **Schritt 7 - Customer Journey & Buying Center** durch.
|
||||||
|
|
||||||
|
# Kontext: Validierte Ergebnisse aus vorherigen Schritten
|
||||||
|
{{previous_steps_data}}
|
||||||
|
|
||||||
|
# Fokus
|
||||||
|
Beziehe dich auf die **Fokus-Branche: {{focus_industry}}**.
|
||||||
|
|
||||||
|
# Anweisungen fuer Schritt 7
|
||||||
|
* Analysiere die Kaufreise ("Journey") vom ersten Trigger bis zum Vertrag.
|
||||||
|
* Identifiziere fuer jede Phase die **Dynamik im Buying Center**: Wer treibt an (Champion), wer bremst oder prueft (Gatekeeper/Evaluator), wer entscheidet (Decider)?
|
||||||
|
* Gehe besonders auf **technische und organisatorische Barrieren** ein (z.B. IT-Sicherheit, Schnittstellen wie Aufzugssteuerung, Prozessintegration).
|
||||||
|
* Definiere **konkrete Assets**, die die jeweilige Rolle in dieser Phase benoetigt, um Einwaende zu entkraeften oder interne Mitstreiter zu ueberzeugen (z.B. "API-Dokumentation fuer Aufzugsbauer", "ROI-Rechner fuer CFO", "Sicherheits-Whitepaper").
|
||||||
|
* **Output:** Erstelle eine Markdown-Tabelle mit exakt diesen Spalten: *Phase | Rolle | Funktion (Buying Center) | Zentrale Frage / Beduerfnis | Moeglicher Deal-Breaker | Benoetigtes Asset / Format*.
|
||||||
|
* **Format-Anforderung:** Antworte NUR mit den Ergebnissen fuer diesen einen Schritt. Deine Antwort muss mit der Ueberschrift \"## Schritt 7: Customer Journey\" beginnen."""
|
||||||
]
|
]
|
||||||
|
|
||||||
SYSTEM_PROMPT_EN = """# System Role
|
SYSTEM_PROMPT_EN = """# System Role
|
||||||
@@ -256,7 +272,23 @@ For each **[Role]** within the **[Focus Industry: {{focus_industry}}]**, perform
|
|||||||
# Output Format
|
# Output Format
|
||||||
Create ONLY the final Markdown table.
|
Create ONLY the final Markdown table.
|
||||||
* **Table Columns:** *Focus Industry | Role | Core Message (2-3 sentences) | {{channels}}*.
|
* **Table Columns:** *Focus Industry | Role | Core Message (2-3 sentences) | {{channels}}*.
|
||||||
* **Requirement:** Your response must start with the heading \"## Step 6: Messages\" and contain ONLY the complete Markdown table."""
|
* **Requirement:** Your response must start with the heading \"## Step 6: Messages\" and contain ONLY the complete Markdown table.""",
|
||||||
|
"""# Task
|
||||||
|
Perform **Step 7 - Customer Journey & Buying Center**.
|
||||||
|
|
||||||
|
# Context: Validated results from previous steps
|
||||||
|
{{previous_steps_data}}
|
||||||
|
|
||||||
|
# Focus
|
||||||
|
Refer to the **Focus Industry: {{focus_industry}}**.
|
||||||
|
|
||||||
|
# Instructions for Step 7
|
||||||
|
* Analyze the purchase journey ("Journey") from the first trigger to the contract.
|
||||||
|
* Identify the **Buying Center dynamics** for each phase: Who drives it (Champion), who slows it down or audits (Gatekeeper/Evaluator), who decides (Decider)?
|
||||||
|
* Focus specifically on **technical and organizational barriers** (e.g., IT security, interfaces like elevator control, process integration).
|
||||||
|
* Define **concrete assets** that each role needs in this phase to invalidate objections or convince internal stakeholders (e.g., "API documentation for elevator manufacturers", "ROI calculator for CFO", "Security Whitepaper").
|
||||||
|
* **Output:** Create a Markdown table with exactly these columns: *Phase | Role | Function (Buying Center) | Key Question / Need | Potential Deal-Breaker | Needed Asset / Format*.
|
||||||
|
* **Format Requirement:** Respond ONLY with the results for this single step. Your response must start with the heading \"## Step 7: Customer Journey\"."""
|
||||||
]
|
]
|
||||||
|
|
||||||
PROMPTS = {
|
PROMPTS = {
|
||||||
@@ -270,6 +302,7 @@ PROMPTS = {
|
|||||||
'painPoints': 'Schritt 4: Painpoints je Rolle (WARUM)',
|
'painPoints': 'Schritt 4: Painpoints je Rolle (WARUM)',
|
||||||
'gains': 'Schritt 5: Gains & Nutzen je Rolle (WARUM wechseln)',
|
'gains': 'Schritt 5: Gains & Nutzen je Rolle (WARUM wechseln)',
|
||||||
'messages': 'Schritt 6: Marketingbotschaften je Segment & Rolle (WIE sprechen)',
|
'messages': 'Schritt 6: Marketingbotschaften je Segment & Rolle (WIE sprechen)',
|
||||||
|
'customerJourney': 'Schritt 7: Customer Journey & Buying Center',
|
||||||
},
|
},
|
||||||
'SUMMARY_TITLE': 'Kurzresuemee:',
|
'SUMMARY_TITLE': 'Kurzresuemee:',
|
||||||
'SUMMARY_TEXT_FOR_STEP1': [
|
'SUMMARY_TEXT_FOR_STEP1': [
|
||||||
@@ -287,6 +320,7 @@ PROMPTS = {
|
|||||||
'painPoints': 'Step 4: Pain Points per Role (WHY)',
|
'painPoints': 'Step 4: Pain Points per Role (WHY)',
|
||||||
'gains': 'Step 5: Gains & Benefits per Role (WHY switch)',
|
'gains': 'Step 5: Gains & Benefits per Role (WHY switch)',
|
||||||
'messages': 'Step 6: Marketing Messages per Segment & Role (HOW to speak)',
|
'messages': 'Step 6: Marketing Messages per Segment & Role (HOW to speak)',
|
||||||
|
'customerJourney': 'Step 7: Customer Journey & Buying Center',
|
||||||
},
|
},
|
||||||
'SUMMARY_TITLE': 'Summary:',
|
'SUMMARY_TITLE': 'Summary:',
|
||||||
'SUMMARY_TEXT_FOR_STEP1': [
|
'SUMMARY_TEXT_FOR_STEP1': [
|
||||||
@@ -432,7 +466,7 @@ def format_context_for_prompt(analysis_data, language):
|
|||||||
context = ""
|
context = ""
|
||||||
current_prompts = PROMPTS[language]
|
current_prompts = PROMPTS[language]
|
||||||
step_titles = current_prompts['STEP_TITLES']
|
step_titles = current_prompts['STEP_TITLES']
|
||||||
step_keys = ['offer', 'targetGroups', 'personas', 'painPoints', 'gains', 'messages']
|
step_keys = ['offer', 'targetGroups', 'personas', 'painPoints', 'gains', 'messages', 'customerJourney']
|
||||||
for i, step_key in enumerate(step_keys):
|
for i, step_key in enumerate(step_keys):
|
||||||
step_data = analysis_data.get(step_key)
|
step_data = analysis_data.get(step_key)
|
||||||
if step_data:
|
if step_data:
|
||||||
@@ -530,7 +564,7 @@ def next_step(language, context_file, generation_step, channels, focus_industry=
|
|||||||
# Log the full response
|
# Log the full response
|
||||||
save_detailed_log(f"step{generation_step}", "response", response_text)
|
save_detailed_log(f"step{generation_step}", "response", response_text)
|
||||||
|
|
||||||
step_key = ['offer', 'targetGroups', 'personas', 'painPoints', 'gains', 'messages'][generation_step - 1]
|
step_key = ['offer', 'targetGroups', 'personas', 'painPoints', 'gains', 'messages', 'customerJourney'][generation_step - 1]
|
||||||
expected_title = current_prompts['STEP_TITLES'][step_key]
|
expected_title = current_prompts['STEP_TITLES'][step_key]
|
||||||
title_match = re.search(rf'## {re.escape(expected_title)}\s*', response_text, re.IGNORECASE)
|
title_match = re.search(rf'## {re.escape(expected_title)}\s*', response_text, re.IGNORECASE)
|
||||||
content = response_text[title_match.end():].strip() if title_match else response_text
|
content = response_text[title_match.end():].strip() if title_match else response_text
|
||||||
|
|||||||
Reference in New Issue
Block a user