Dateien nach "b2b-marketing-assistant" hochladen
This commit is contained in:
24
b2b-marketing-assistant/.gitignore
vendored
Normal file
24
b2b-marketing-assistant/.gitignore
vendored
Normal file
@@ -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?
|
||||||
388
b2b-marketing-assistant/App.tsx
Normal file
388
b2b-marketing-assistant/App.tsx
Normal file
@@ -0,0 +1,388 @@
|
|||||||
|
|
||||||
|
import React, { useState, useCallback, useRef } from 'react';
|
||||||
|
import { GoogleGenAI, Chat } from "@google/genai";
|
||||||
|
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 { PROMPTS, translations } from './constants';
|
||||||
|
import type { AnalysisStep, AnalysisData, InputData } from './types';
|
||||||
|
import { parseGeminiStepResponse } from './services/parser';
|
||||||
|
import { generateMarkdown, downloadFile, tableToMarkdown } from './services/export';
|
||||||
|
|
||||||
|
const App: React.FC = () => {
|
||||||
|
const [inputData, setInputData] = useState<InputData>({
|
||||||
|
companyUrl: '',
|
||||||
|
language: 'de',
|
||||||
|
regions: '',
|
||||||
|
focus: '',
|
||||||
|
channels: ['LinkedIn', 'Kaltmail', 'Landingpage']
|
||||||
|
});
|
||||||
|
const [analysisData, setAnalysisData] = useState<Partial<AnalysisData>>({});
|
||||||
|
const [isLoading, setIsLoading] = useState<boolean>(false);
|
||||||
|
const [isEnriching, setIsEnriching] = useState<boolean>(false);
|
||||||
|
const [error, setError] = useState<string | null>(null);
|
||||||
|
const [generationStep, setGenerationStep] = useState<number>(0); // 0: idle, 1-6: step X is complete
|
||||||
|
const chatRef = useRef<Chat | 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);
|
||||||
|
|
||||||
|
try {
|
||||||
|
const ai = new GoogleGenAI({ apiKey: process.env.API_KEY });
|
||||||
|
const currentPrompts = PROMPTS[inputData.language];
|
||||||
|
|
||||||
|
const newChat = ai.chats.create({
|
||||||
|
model: 'gemini-2.5-pro',
|
||||||
|
config: {
|
||||||
|
systemInstruction: currentPrompts.SYSTEM_PROMPT.replace('{{language}}', inputData.language)
|
||||||
|
}
|
||||||
|
});
|
||||||
|
chatRef.current = newChat;
|
||||||
|
|
||||||
|
let prompt = currentPrompts.STEP_PROMPTS[0];
|
||||||
|
prompt = prompt.replace('{{company_url}}', inputData.companyUrl);
|
||||||
|
prompt = prompt.replace('{{language}}', inputData.language);
|
||||||
|
prompt = prompt.replace('{{regions}}', inputData.regions);
|
||||||
|
prompt = prompt.replace('{{focus}}', inputData.focus);
|
||||||
|
|
||||||
|
const response = await newChat.sendMessage({ message: prompt });
|
||||||
|
|
||||||
|
const parsedData = parseGeminiStepResponse(response.text);
|
||||||
|
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 (!chatRef.current || generationStep >= 6) return;
|
||||||
|
|
||||||
|
setIsLoading(true);
|
||||||
|
setError(null);
|
||||||
|
const nextStepIndex = generationStep;
|
||||||
|
|
||||||
|
try {
|
||||||
|
let context = '';
|
||||||
|
for (let i = 0; i < generationStep; i++) {
|
||||||
|
const stepKey = STEP_KEYS[i];
|
||||||
|
const stepObject = analysisData[stepKey];
|
||||||
|
if (stepObject) {
|
||||||
|
context += `\n\n## ${STEP_TITLES[stepKey]}\n\n`;
|
||||||
|
const summary = stepObject.summary && stepObject.summary.length > 0 ? `**${t.summaryTitle}**\n${stepObject.summary.map(s => `* ${s}`).join('\n')}\n\n` : '';
|
||||||
|
const table = tableToMarkdown(stepObject);
|
||||||
|
context += `${summary}${table}`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const currentPrompts = PROMPTS[inputData.language];
|
||||||
|
let prompt = currentPrompts.STEP_PROMPTS[nextStepIndex];
|
||||||
|
prompt = prompt.replace('{{previous_steps_data}}', context);
|
||||||
|
|
||||||
|
if (nextStepIndex === 5) { // Step 6 is index 5
|
||||||
|
prompt = prompt.replace('{{channels}}', inputData.channels.join(', '));
|
||||||
|
}
|
||||||
|
|
||||||
|
const response = await chatRef.current.sendMessage({ message: prompt });
|
||||||
|
const parsedData = parseGeminiStepResponse(response.text);
|
||||||
|
|
||||||
|
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, STEP_KEYS, STEP_TITLES, t.summaryTitle]);
|
||||||
|
|
||||||
|
const handleDataChange = <K extends keyof AnalysisData>(step: K, newData: AnalysisData[K]) => {
|
||||||
|
if (analysisData[step]) {
|
||||||
|
setAnalysisData(prev => prev ? { ...prev, [step]: newData } : { [step]: newData });
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const getEnrichPrompt = (lang: 'de' | 'en', url: string, productName: string) => {
|
||||||
|
if (lang === 'en') {
|
||||||
|
return `# Task
|
||||||
|
Fill in the information for the following product/solution based on the website ${url}. Respond ONLY with the content for the remaining 4 columns, separated by '|||'.
|
||||||
|
|
||||||
|
# Product/Solution
|
||||||
|
${productName}
|
||||||
|
|
||||||
|
# Column Order
|
||||||
|
1. Description (1–2 sentences)
|
||||||
|
2. Core Features
|
||||||
|
3. Differentiation
|
||||||
|
4. Primary Source (URL)
|
||||||
|
|
||||||
|
# Important
|
||||||
|
Respond *only* with the text for the 4 columns, separated by '|||'. Do not output any headers or explanations.`;
|
||||||
|
}
|
||||||
|
// German (original)
|
||||||
|
return `# Aufgabe
|
||||||
|
Fülle die Informationen für das folgende Produkt/Lösung basierend auf der Webseite ${url} aus. Antworte NUR mit dem Inhalt für die restlichen 4 Spalten, getrennt durch '|||'.
|
||||||
|
|
||||||
|
# Produkt/Lösung
|
||||||
|
${productName}
|
||||||
|
|
||||||
|
# Spalten-Reihenfolge
|
||||||
|
1. Beschreibung (1–2 Sätze)
|
||||||
|
2. Kernfunktionen
|
||||||
|
3. Differenzierung
|
||||||
|
4. Primäre Quelle (URL)
|
||||||
|
|
||||||
|
# Wichtig
|
||||||
|
Antworte *nur* mit dem Text für die 4 Spalten, getrennt durch '|||'. Gib keine Überschriften oder Erklärungen aus.`;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
const handleEnrichOfferRow = useCallback(async (productName: string, productUrl?: string) => {
|
||||||
|
if (!analysisData.offer) return;
|
||||||
|
|
||||||
|
setIsEnriching(true);
|
||||||
|
setError(null);
|
||||||
|
|
||||||
|
const loadingText = t.loadingButton.replace('...', '');
|
||||||
|
const placeholderRow = [productName, loadingText, loadingText, loadingText, loadingText];
|
||||||
|
|
||||||
|
setAnalysisData(prev => {
|
||||||
|
if (!prev.offer) return prev;
|
||||||
|
const currentOffer = prev.offer;
|
||||||
|
const updatedRows = [...currentOffer.rows, placeholderRow];
|
||||||
|
return {
|
||||||
|
...prev,
|
||||||
|
offer: {
|
||||||
|
...currentOffer,
|
||||||
|
rows: updatedRows,
|
||||||
|
}
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
try {
|
||||||
|
const ai = new GoogleGenAI({ apiKey: process.env.API_KEY });
|
||||||
|
// Use the specific product URL if provided, otherwise fallback to the main company URL
|
||||||
|
const targetUrl = productUrl && productUrl.trim() !== '' ? productUrl.trim() : inputData.companyUrl;
|
||||||
|
|
||||||
|
const enrichPrompt = getEnrichPrompt(inputData.language, targetUrl, productName);
|
||||||
|
|
||||||
|
const response = await ai.models.generateContent({
|
||||||
|
model: 'gemini-2.5-flash',
|
||||||
|
contents: enrichPrompt
|
||||||
|
});
|
||||||
|
const text = response.text || '';
|
||||||
|
const enrichedParts = text.split('|||').map(s => s.trim());
|
||||||
|
|
||||||
|
const finalEnrichedData = Array(4).fill('');
|
||||||
|
for (let i = 0; i < 4; i++) {
|
||||||
|
if (enrichedParts[i]) {
|
||||||
|
finalEnrichedData[i] = enrichedParts[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const newRow = [productName, ...finalEnrichedData];
|
||||||
|
|
||||||
|
setAnalysisData(prev => {
|
||||||
|
const offerData = prev.offer;
|
||||||
|
if (!offerData) return prev;
|
||||||
|
const finalRows = offerData.rows.map(row =>
|
||||||
|
row[0] === productName && row[1] === loadingText ? newRow : row
|
||||||
|
);
|
||||||
|
return { ...prev, offer: { ...offerData, rows: finalRows } };
|
||||||
|
});
|
||||||
|
|
||||||
|
} catch (e) {
|
||||||
|
console.error(e);
|
||||||
|
// On error, clear loading text so user can edit manually
|
||||||
|
setAnalysisData(prev => {
|
||||||
|
const offerData = prev.offer;
|
||||||
|
if (!offerData) return prev;
|
||||||
|
const emptyRow = [productName, '', '', '', ''];
|
||||||
|
const finalRows = offerData.rows.map(row =>
|
||||||
|
row[0] === productName && row[1] === loadingText ? emptyRow : row
|
||||||
|
);
|
||||||
|
return { ...prev, offer: { ...offerData, rows: finalRows } };
|
||||||
|
});
|
||||||
|
} finally {
|
||||||
|
setIsEnriching(false);
|
||||||
|
}
|
||||||
|
}, [analysisData.offer, inputData.companyUrl, inputData.language, t.loadingButton]);
|
||||||
|
|
||||||
|
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 (
|
||||||
|
<StepDisplay
|
||||||
|
key={stepKey}
|
||||||
|
title={title}
|
||||||
|
summary={step.summary}
|
||||||
|
headers={step.headers}
|
||||||
|
rows={step.rows}
|
||||||
|
onDataChange={(newRows) => handleDataChange(stepKey, { ...step, rows: newRows })}
|
||||||
|
canAddRows={stepKey === 'offer'}
|
||||||
|
onEnrichRow={stepKey === 'offer' ? handleEnrichOfferRow : undefined}
|
||||||
|
isEnriching={isEnriching}
|
||||||
|
canDeleteRows={canDelete}
|
||||||
|
t={t}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const renderContinueButton = (stepNumber: number) => {
|
||||||
|
if (isLoading || generationStep !== stepNumber - 1) return null;
|
||||||
|
return (
|
||||||
|
<div className="my-8 text-center print:hidden">
|
||||||
|
<button
|
||||||
|
onClick={handleGenerateNextStep}
|
||||||
|
className="flex items-center justify-center w-auto mx-auto px-6 py-3 border border-transparent text-base font-medium rounded-md shadow-sm text-white bg-green-600 hover:bg-green-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-green-500 disabled:bg-slate-400 dark:disabled:bg-slate-600 disabled:cursor-not-allowed transition-colors duration-200"
|
||||||
|
>
|
||||||
|
<SparklesIcon className="mr-2 h-5 w-5" />
|
||||||
|
{t.continueButton.replace('{{step}}', (stepNumber - 1).toString()).replace('{{nextStep}}', stepNumber.toString())}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="min-h-screen bg-slate-50 dark:bg-slate-900 text-slate-800 dark:text-slate-200 font-sans">
|
||||||
|
<main className="container mx-auto px-4 py-8 md:py-12">
|
||||||
|
<header className="text-center mb-10 print:hidden">
|
||||||
|
<h1 className="text-4xl md:text-5xl font-extrabold text-slate-900 dark:text-white">
|
||||||
|
{t.appTitle}
|
||||||
|
</h1>
|
||||||
|
<p className="mt-4 text-lg text-slate-600 dark:text-slate-400">
|
||||||
|
{t.appSubtitle}
|
||||||
|
</p>
|
||||||
|
</header>
|
||||||
|
|
||||||
|
<div className="max-w-4xl mx-auto 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:hidden">
|
||||||
|
<InputForm
|
||||||
|
inputData={inputData}
|
||||||
|
setInputData={setInputData}
|
||||||
|
onGenerate={handleStartGeneration}
|
||||||
|
isLoading={isLoading && generationStep === 0}
|
||||||
|
t={t}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{error && (
|
||||||
|
<div className="max-w-4xl mx-auto mt-8 bg-red-100 dark:bg-red-900/50 border border-red-400 dark:border-red-600 text-red-700 dark:text-red-200 px-4 py-3 rounded-lg print:hidden" role="alert">
|
||||||
|
<strong className="font-bold">{t.errorTitle} </strong>
|
||||||
|
<span className="block sm:inline">{error}</span>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
<div className="mt-12 space-y-12">
|
||||||
|
{isLoading && (
|
||||||
|
<div className="flex flex-col items-center justify-center text-center p-8">
|
||||||
|
<LoadingSpinner />
|
||||||
|
<p className="mt-4 text-lg text-slate-600 dark:text-slate-400 animate-pulse">
|
||||||
|
{t.generatingStep.replace('{{stepTitle}}', STEP_TITLES[STEP_KEYS[generationStep]])}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{!isLoading && generationStep === 0 && !error && (
|
||||||
|
<div className="text-center p-8 bg-white dark:bg-slate-800/50 rounded-2xl shadow-md border border-slate-200 dark:border-slate-700 print:hidden">
|
||||||
|
<BotIcon className="mx-auto h-16 w-16 text-sky-500" />
|
||||||
|
<h3 className="mt-4 text-xl font-semibold text-slate-900 dark:text-white">{t.readyTitle}</h3>
|
||||||
|
<p className="mt-2 text-slate-600 dark:text-slate-400">{t.readyText}</p>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{generationStep > 0 && (
|
||||||
|
<>
|
||||||
|
{generationStep >= 6 && (
|
||||||
|
<div className="flex justify-end mb-8 print:hidden">
|
||||||
|
<ExportMenu
|
||||||
|
onDownloadMarkdown={handleDownloadMarkdown}
|
||||||
|
onPrint={handlePrint}
|
||||||
|
t={t}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{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 && (
|
||||||
|
<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">
|
||||||
|
<SparklesIcon className="h-6 w-6 text-sky-600 dark:text-sky-300" />
|
||||||
|
</div>
|
||||||
|
<h3 className="text-xl font-bold text-slate-900 dark:text-white mb-2">
|
||||||
|
{t.analysisCompleteTitle}
|
||||||
|
</h3>
|
||||||
|
<p className="text-slate-600 dark:text-slate-300 mb-8 max-w-2xl mx-auto">
|
||||||
|
{t.analysisCompleteText.replace('{{otherLanguage}}', t.otherLanguage)}
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<div className="flex flex-col sm:flex-row justify-center items-center gap-4">
|
||||||
|
<button
|
||||||
|
onClick={handleDownloadMarkdown}
|
||||||
|
className="w-full sm:w-auto flex items-center justify-center px-6 py-3 border border-transparent text-base font-medium rounded-lg text-white bg-slate-900 dark:bg-sky-600 hover:bg-slate-800 dark:hover:bg-sky-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-slate-500 shadow-md transition-all"
|
||||||
|
>
|
||||||
|
<MarkdownIcon className="mr-2 h-5 w-5" />
|
||||||
|
{t.exportAsMarkdown}
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
onClick={handlePrint}
|
||||||
|
className="w-full sm:w-auto flex items-center justify-center px-6 py-3 border border-slate-300 dark:border-slate-600 text-base font-medium rounded-lg text-slate-700 dark:text-slate-200 bg-white dark:bg-slate-800 hover:bg-slate-50 dark:hover:bg-slate-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-slate-500 shadow-sm transition-all"
|
||||||
|
>
|
||||||
|
<PrintIcon className="mr-2 h-5 w-5" />
|
||||||
|
{t.exportAsPdf}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</main>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default App;
|
||||||
365
b2b-marketing-assistant/constants.ts
Normal file
365
b2b-marketing-assistant/constants.ts
Normal file
@@ -0,0 +1,365 @@
|
|||||||
|
|
||||||
|
export const LANGUAGE_OPTIONS = [
|
||||||
|
{ value: 'de', label: 'Deutsch' },
|
||||||
|
{ value: 'en', label: 'English' },
|
||||||
|
];
|
||||||
|
|
||||||
|
export const CHANNEL_OPTIONS = ['LinkedIn', 'Kaltmail', 'Landingpage'];
|
||||||
|
|
||||||
|
export const translations = {
|
||||||
|
de: {
|
||||||
|
appTitle: 'B2B Marketing Assistant',
|
||||||
|
appSubtitle: 'Analysiere eine URL, um gezielte Marketingstrategien zu erstellen.',
|
||||||
|
companyUrlLabel: 'Unternehmens-URL',
|
||||||
|
companyUrlPlaceholder: 'https://beispiel-unternehmen.de',
|
||||||
|
targetLanguageLabel: 'Zielsprache',
|
||||||
|
productFocusLabel: 'Produkt-/Lösungsfokus',
|
||||||
|
productFocusOptional: '(optional)',
|
||||||
|
productFocusPlaceholder: 'z.B. Cloud-Services',
|
||||||
|
regionsLabel: 'Region(en) / Märkte',
|
||||||
|
regionsOptional: '(optional)',
|
||||||
|
regionsPlaceholder: 'z.B. DACH, Nordamerika',
|
||||||
|
channelsLabel: 'Gewünschte Kanäle',
|
||||||
|
channelsOptional: '(optional)',
|
||||||
|
generateButton: 'Analyse generieren',
|
||||||
|
analyzingButton: 'Analysiere...',
|
||||||
|
errorTitle: 'Fehler:',
|
||||||
|
generatingStep: 'Generiere {{stepTitle}}... Dies kann einen Moment dauern.',
|
||||||
|
readyTitle: 'Bereit zur Analyse',
|
||||||
|
readyText: 'Geben Sie oben eine URL ein, um mit der Generierung Ihrer B2B-Marketingstrategie zu beginnen.',
|
||||||
|
continueButton: 'Ergebnisse für Schritt {{step}} korrekt. Weiter zu Schritt {{nextStep}}.',
|
||||||
|
analysisCompleteTitle: 'Analyse abgeschlossen. Nächste Schritte:',
|
||||||
|
analysisCompleteText: 'Möchtest du, dass ich eine Rolle/Branche priorisiere, die Botschaften nach Kanal verfeinere oder Beispiele in {{otherLanguage}} ausformuliere? Nenne optional Metriken (z.B. Zeit-zu-Einsatz, First-Contact-Resolution, Fehlerrate), die besonders wichtig sind.',
|
||||||
|
otherLanguage: 'Englisch',
|
||||||
|
exportButton: 'Exportieren',
|
||||||
|
exportAsMarkdown: 'Als Markdown (.md)',
|
||||||
|
exportAsPdf: 'Drucken / Als PDF',
|
||||||
|
summaryTitle: 'Kurzresümee:',
|
||||||
|
filterPlaceholder: 'Tabelle filtern...',
|
||||||
|
copyTableButton: 'Tabelle kopieren',
|
||||||
|
copySuccess: 'Gefilterte Tabelle kopiert!',
|
||||||
|
copyFailure: 'Kopieren fehlgeschlagen.',
|
||||||
|
addRowButton: '+ Zeile hinzufügen',
|
||||||
|
loadingButton: 'Lade...',
|
||||||
|
deleteRowAria: 'Zeile löschen',
|
||||||
|
copyToClipboardAria: 'In die Zwischenablage kopieren',
|
||||||
|
newProductPrompt: 'Name des Produkts/der Lösung',
|
||||||
|
productUrlPlaceholder: 'URL für dieses Produkt (optional)',
|
||||||
|
noFilterResults: 'Keine Ergebnisse für "{{query}}" gefunden.',
|
||||||
|
stepTitles: {
|
||||||
|
offer: 'Schritt 1: Angebot (WAS)',
|
||||||
|
targetGroups: 'Schritt 2: Zielgruppen (WER - Unternehmen)',
|
||||||
|
personas: 'Schritt 3: Zielpersonen/Rollen (WER - Personen)',
|
||||||
|
painPoints: 'Schritt 4: Painpoints je Rolle (WARUM)',
|
||||||
|
gains: 'Schritt 5: Gains & Nutzen je Rolle (WARUM wechseln)',
|
||||||
|
messages: 'Schritt 6: Marketingbotschaften je Segment & Rolle (WIE sprechen)',
|
||||||
|
}
|
||||||
|
},
|
||||||
|
en: {
|
||||||
|
appTitle: 'B2B Marketing Assistant',
|
||||||
|
appSubtitle: 'Analyze a URL to create targeted B2B marketing strategies.',
|
||||||
|
companyUrlLabel: 'Company URL',
|
||||||
|
companyUrlPlaceholder: 'https://example-company.com',
|
||||||
|
targetLanguageLabel: 'Target Language',
|
||||||
|
productFocusLabel: 'Product/Solution Focus',
|
||||||
|
productFocusOptional: '(optional)',
|
||||||
|
productFocusPlaceholder: 'e.g., Cloud Services',
|
||||||
|
regionsLabel: 'Region(s) / Markets',
|
||||||
|
regionsOptional: '(optional)',
|
||||||
|
regionsPlaceholder: 'e.g., EMEA, North America',
|
||||||
|
channelsLabel: 'Desired Channels',
|
||||||
|
channelsOptional: '(optional)',
|
||||||
|
generateButton: 'Generate Analysis',
|
||||||
|
analyzingButton: 'Analyzing...',
|
||||||
|
errorTitle: 'Error:',
|
||||||
|
generatingStep: 'Generating {{stepTitle}}... This may take a moment.',
|
||||||
|
readyTitle: 'Ready to Analyze',
|
||||||
|
readyText: 'Enter a URL above to start generating your B2B marketing strategy.',
|
||||||
|
continueButton: 'Results for step {{step}} are correct. Continue to step {{nextStep}}.',
|
||||||
|
analysisCompleteTitle: 'Analysis complete. Next steps:',
|
||||||
|
analysisCompleteText: 'Would you like me to prioritize a role/industry, refine messages by channel, or write examples in {{otherLanguage}}? Optionally, mention metrics (e.g., time-to-value, first-contact resolution, error rate) that are particularly important.',
|
||||||
|
otherLanguage: 'German',
|
||||||
|
exportButton: 'Export',
|
||||||
|
exportAsMarkdown: 'As Markdown (.md)',
|
||||||
|
exportAsPdf: 'Print / As PDF',
|
||||||
|
summaryTitle: 'Summary:',
|
||||||
|
filterPlaceholder: 'Filter table...',
|
||||||
|
copyTableButton: 'Copy Table',
|
||||||
|
copySuccess: 'Filtered table copied!',
|
||||||
|
copyFailure: 'Copy failed.',
|
||||||
|
addRowButton: '+ Add Row',
|
||||||
|
loadingButton: 'Loading...',
|
||||||
|
deleteRowAria: 'Delete row',
|
||||||
|
copyToClipboardAria: 'Copy to clipboard',
|
||||||
|
newProductPrompt: 'Product/Solution Name',
|
||||||
|
productUrlPlaceholder: 'URL for this product (optional)',
|
||||||
|
noFilterResults: 'No results found for "{{query}}".',
|
||||||
|
stepTitles: {
|
||||||
|
offer: 'Step 1: Offer (WHAT)',
|
||||||
|
targetGroups: 'Step 2: Target Groups (WHO - Companies)',
|
||||||
|
personas: 'Step 3: Personas/Roles (WHO - People)',
|
||||||
|
painPoints: 'Step 4: Pain Points per Role (WHY)',
|
||||||
|
gains: 'Step 5: Gains & Benefits per Role (WHY switch)',
|
||||||
|
messages: 'Step 6: Marketing Messages per Segment & Role (HOW to speak)',
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
const SYSTEM_PROMPT_DE = `
|
||||||
|
# Systemrolle
|
||||||
|
|
||||||
|
Du bist ein **B2B-Marketing-Researcher & Copywriter**. Du analysierst eine Unternehmens-URL, identifizierst Angebot, Zielgruppen, konkrete Zielrollen, deren Painpoints sowie Gains und formulierst darauf basierend eine wertschätzende, fachkundige Marketingbotschaft. **Antworte nur mit Ergebnissen, keine Gedankengänge.** Belege jede Aussage mit einer konkreten Seiten-URL der analysierten Domain. Kennzeichne Unsicherheiten explizit. Deine Antwort muss immer in der Zielsprache {{language}} sein.
|
||||||
|
|
||||||
|
# Arbeitsprinzipien
|
||||||
|
|
||||||
|
1. **Quellenpriorisierung:** Produktseite → Lösungsseite → Branchen/Industrien/Referenzen → Unternehmens-/Über-uns-Seite → Blog/News (nur zur Verifikation).
|
||||||
|
2. **Faktenpolicy:** Nur aus der Domain der bereitgestellten URL ableiten; bei Vermutungen: als *Hypothese* kennzeichnen.
|
||||||
|
3. **B2B-Ton:** sachkundig, respektvoll, nicht marktschreierisch.
|
||||||
|
4. **Klarheit & Struktur:** Jede Stufe als Markdown-Tabelle + optionales Kurzresümee dokumentieren.
|
||||||
|
5. **Mindestens 4 spezifische Rollen** je Zielgruppe (nicht generisch).
|
||||||
|
6. **Kompakt & nützlich:** Präzise Formulierungen; keine Floskeln.
|
||||||
|
`;
|
||||||
|
|
||||||
|
const STEP_PROMPTS_DE = [
|
||||||
|
// Step 1: Offer
|
||||||
|
`# Aufgabe
|
||||||
|
Führe **Schritt 1 – Angebot verstehen (WAS)** für das folgende Unternehmen durch.
|
||||||
|
|
||||||
|
# Eingaben
|
||||||
|
* **Unternehmens-URL:** \`{{company_url}}\`
|
||||||
|
* **Zielsprache der Ausgabe:** \`{{language}}\`
|
||||||
|
* **Region(en) / Märkte (optional):** \`{{regions}}\`
|
||||||
|
* **Produkt-/Lösungsfokus (optional):** \`{{focus}}\`
|
||||||
|
|
||||||
|
# Anweisungen für Schritt 1
|
||||||
|
* Extrahiere Produkt(e)/Leistung(en), Kernfunktionen, Differenzierung, relevante Werteversprechen.
|
||||||
|
* Erstelle ein kurzes Resümee (max. 4 Bulletpoints) der wichtigsten Erkenntnisse.
|
||||||
|
* **Output:** Tabelle mit Spalten: *Produkt/Lösung | Beschreibung (1–2 Sätze) | Kernfunktionen | Differenzierung | Primäre Quelle (URL)*.
|
||||||
|
* **Format-Anforderung:** Antworte NUR mit den Ergebnissen für diesen einen Schritt. Deine Antwort muss mit der Überschrift "## Schritt 1: Angebot (WAS)" beginnen und das Kurzresümee sowie die Markdown-Tabelle enthalten. Gib keine weiteren Erklärungen ab.`,
|
||||||
|
|
||||||
|
// Step 2: Target Groups
|
||||||
|
`# Aufgabe
|
||||||
|
Führe nun **Schritt 2 – Zielgruppen (WER – Unternehmen)** durch.
|
||||||
|
|
||||||
|
# Kontext: Validierte Ergebnisse aus vorherigen Schritten
|
||||||
|
{{previous_steps_data}}
|
||||||
|
|
||||||
|
# Anweisungen für Schritt 2
|
||||||
|
* Identifiziere B2B-Zielsegmente (Branchen/Unternehmensarten/Größen/Regionen) basierend auf dem gegebenen Angebot.
|
||||||
|
* **Output:** Tabelle: *Zielbranche/Segment | Typische Unternehmensmerkmale | Region(en) | Relevanzbeleg (URL)*.
|
||||||
|
* **Format-Anforderung:** Antworte NUR mit den Ergebnissen für diesen einen Schritt. Deine Antwort muss mit der Überschrift "## Schritt 2: Zielgruppen (Unternehmen)" beginnen und die Markdown-Tabelle enthalten.`,
|
||||||
|
|
||||||
|
// Step 3: Personas
|
||||||
|
`# Aufgabe
|
||||||
|
Führe nun **Schritt 3 – Zielpersonen/Rollen (WER – Personen)** durch.
|
||||||
|
|
||||||
|
# Kontext: Validierte Ergebnisse aus vorherigen Schritten
|
||||||
|
{{previous_steps_data}}
|
||||||
|
|
||||||
|
# Anweisungen für Schritt 3
|
||||||
|
* Für jede Zielbranche: mind. 4 **spezifische** Rollen mit Verantwortungsbereich und Kaufbeteiligung (E, I, D, U nach RACI-Logik). Erfinde **keine** Personen; leite Rollen logisch aus Problem-/Prozessbezug ab.
|
||||||
|
* **Output:** Tabelle: *Rolle (präzise) | Verantwortungsbereich | Warum relevant für Produkt | Kaufbeteiligung (E/I/D/U) | Quelle/Indiz (URL oder Referenz)*.
|
||||||
|
* **Format-Anforderung:** Antworte NUR mit den Ergebnissen für diesen einen Schritt. Deine Antwort muss mit der Überschrift "## Schritt 3: Zielpersonen (Rollen)" beginnen.`,
|
||||||
|
|
||||||
|
// Step 4: Pain Points
|
||||||
|
`# Aufgabe
|
||||||
|
Führe nun **Schritt 4 – Painpoints je Rolle (WARUM)** durch.
|
||||||
|
|
||||||
|
# Kontext: Validierte Ergebnisse aus vorherigen Schritten
|
||||||
|
{{previous_steps_data}}
|
||||||
|
|
||||||
|
# Anweisungen für Schritt 4
|
||||||
|
* Formuliere pro Rolle 3–5 konkrete Painpoints (Beobachtungen, keine Features).
|
||||||
|
* Tagge jeden Painpoint mit einer Kategorie: **Kosten | Zeit | Risiko | Compliance | Qualität | Mitarbeiterbindung.**
|
||||||
|
* Füge eine **Impact-Schätzung (€, h, %)** als Hypothese hinzu.
|
||||||
|
* **Output:** Tabelle: *Rolle | Painpoint (konkret, messbar/operativ) | Kategorie | Auswirkung (Kosten, Risiko, Zeit) | Impact-Schätzung (€, h, %) | Dringlichkeit (hoch/mittel/niedrig) | Quelle/Indiz (URL)*.
|
||||||
|
* **Format-Anforderung:** Antworte NUR mit den Ergebnissen für diesen einen Schritt. Deine Antwort muss mit der Überschrift "## Schritt 4: Painpoints je Rolle" beginnen.`,
|
||||||
|
|
||||||
|
// Step 5: Gains
|
||||||
|
`# Aufgabe
|
||||||
|
Führe nun **Schritt 5 – Gains & Nutzen je Rolle (WARUM wechseln)** durch.
|
||||||
|
|
||||||
|
# Kontext: Validierte Ergebnisse aus vorherigen Schritten
|
||||||
|
{{previous_steps_data}}
|
||||||
|
|
||||||
|
# Anweisungen für Schritt 5
|
||||||
|
* Basierend auf den identifizierten Painpoints, formuliere pro Rolle 2-3 konkrete Gains (Vorteile/Nutzen).
|
||||||
|
* Quantifiziere den Nutzen als Hypothese (z.B. Einsparung in €, Zeitgewinn in h, Effizienzsteigerung in %).
|
||||||
|
* **Output:** Tabelle: *Rolle | Gain (konkreter Nutzen) | Quantifizierung (Hypothese in €, h, %) | Quelle/Indiz (URL)*.
|
||||||
|
* **Format-Anforderung:** Antworte NUR mit den Ergebnissen für diesen einen Schritt. Deine Antwort muss mit der Überschrift "## Schritt 5: Gains & Nutzen je Rolle" beginnen.`,
|
||||||
|
|
||||||
|
// Step 6: Messages
|
||||||
|
`# Aufgabe
|
||||||
|
Führe nun **Schritt 6 – Marketingbotschaft (WIE sprechen)** durch.
|
||||||
|
|
||||||
|
# Kontext: Validierte Ergebnisse aus vorherigen Schritten
|
||||||
|
{{previous_steps_data}}
|
||||||
|
|
||||||
|
# Eingaben für diesen Schritt
|
||||||
|
* **Gewünschte Kanäle für die Botschaft:** \`{{channels}}\`
|
||||||
|
|
||||||
|
# Anweisungen für Schritt 6: Chain-of-Thought-Analyse & Texterstellung
|
||||||
|
|
||||||
|
**Dein Ziel ist es, für JEDE Zielbranche aus Schritt 2 eine EIGENE, spezifische Botschaft für JEDE Rolle aus Schritt 3 zu erstellen. Das Ergebnis MUSS eine vollständige Matrix sein (z.B. 3 Zielbranchen x 4 Rollen = 12 einzigartige Botschaften).**
|
||||||
|
|
||||||
|
Führe für jede Kombination aus **[Zielbranche/Segment]** und **[Rolle]** den folgenden Denkprozess durch, bevor du die finale Botschaft formulierst:
|
||||||
|
|
||||||
|
1. **Schritt 6.1 (Analyse): Produkt-Rollen-Fit.**
|
||||||
|
* Welches Produkt/welche Lösung aus der "Angebot"-Tabelle (Schritt 1) ist für die **[Rolle]** am relevantesten?
|
||||||
|
* *Beispiel-Gedanke:* "Für den Einsatzleiter im Kundenservice ist die Software zur Einsatzplanung relevanter als die mobile App, da er für die Disposition zuständig ist."
|
||||||
|
|
||||||
|
2. **Schritt 6.2 (Analyse): Branchen-Use-Case.**
|
||||||
|
* Was sind 1-2 typische Anwendungsfälle für das ausgewählte Produkt in der **[Zielbranche/Segment]**? Was macht die **[Rolle]** damit konkret?
|
||||||
|
* *Beispiel-Gedanke:* "Ein Servicetechniker im Maschinenbau nutzt die mobile App typischerweise, um auf Wartungsprotokolle zuzugreifen und digitale Serviceberichte direkt beim Kunden zu erstellen."
|
||||||
|
|
||||||
|
3. **Schritt 6.3 (Analyse): Nutzen-Quantifizierung.**
|
||||||
|
* Betrachte die Painpoints (Schritt 4) und Gains (Schritt 5) für die **[Rolle]**. Welcher Painpoint ist am dringendsten, welcher Gain am überzeugendsten?
|
||||||
|
* Leite daraus einen konkreten, für die **[Rolle]** relevanten KPI ab (z.B. First-Contact-Resolution-Rate, Zeit-bis-zur-Lösung, Anlagenausfallzeit).
|
||||||
|
* Formuliere den quantifizierbaren Nutzen aus Schritt 5 in Bezug auf diesen KPI.
|
||||||
|
* *Beispiel-Gedanke:* "Der Painpoint des Leiters Instandhaltung ist 'ungeplante Anlagenausfälle'. Der Gain ist 'Reduzierung der Ausfallzeiten um 15%'. Der relevante KPI ist die 'Overall Equipment Effectiveness (OEE)'. Der Nutzen ist die Steigerung der OEE durch vorausschauende Wartungsplanung."
|
||||||
|
|
||||||
|
4. **Schritt 6.4 (Synthese): Botschaft formulieren.**
|
||||||
|
* Synthetisiere die Erkenntnisse aus 6.1-6.3 zu einer prägnanten Kernbotschaft (2-3 Sätze) nach der Struktur: **Beobachtung (Problem) → Niedrigschwellige Lösungsidee → Produkt-Brücke → Quantifizierter Nutzen.**
|
||||||
|
* Erstelle Varianten dieser Botschaft für die Kanäle: {{channels}}.
|
||||||
|
|
||||||
|
# Output-Format
|
||||||
|
Erstelle NUR die finale Markdown-Tabelle. Der Denkprozess (6.1-6.3) ist eine interne Anweisung an dich und soll NICHT im Output erscheinen.
|
||||||
|
* **Tabelle-Spalten:** *Zielbranche/Segment | Rolle | Kernbotschaft (2–3 Sätze) | {{channels}}*.
|
||||||
|
* **Anforderung:** Deine Antwort muss mit der Überschrift "## Schritt 6: Botschaften" beginnen und NUR die vollständige Markdown-Tabelle enthalten.`,
|
||||||
|
];
|
||||||
|
|
||||||
|
|
||||||
|
const SYSTEM_PROMPT_EN = `
|
||||||
|
# System Role
|
||||||
|
|
||||||
|
You are a **B2B Marketing Researcher & Copywriter**. You analyze a company URL, identify the offer, target groups, specific target roles, their pain points and gains, and based on this, you formulate an appreciative, expert marketing message. **Answer only with results, no thought processes.** Support every statement with a specific page URL from the analyzed domain. Explicitly mark uncertainties. Your response must always be in the target language {{language}}.
|
||||||
|
|
||||||
|
# Working Principles
|
||||||
|
|
||||||
|
1. **Source Prioritization:** Product Page → Solutions Page → Industries/References → Company/About Us Page → Blog/News (for verification only).
|
||||||
|
2. **Fact Policy:** Only derive from the domain of the provided URL; for assumptions, mark them as a *hypothesis*.
|
||||||
|
3. **B2B Tone:** Knowledgeable, respectful, not salesy.
|
||||||
|
4. **Clarity & Structure:** Document each stage as a Markdown table + an optional short summary.
|
||||||
|
5. **At least 4 specific roles** per target group (not generic).
|
||||||
|
6. **Concise & Useful:** Precise wording; no clichés.
|
||||||
|
`;
|
||||||
|
|
||||||
|
const STEP_PROMPTS_EN = [
|
||||||
|
// Step 1: Offer
|
||||||
|
`# Task
|
||||||
|
Perform **Step 1 – Understand the Offer (WHAT)** for the following company.
|
||||||
|
|
||||||
|
# Inputs
|
||||||
|
* **Company URL:** \`{{company_url}}\`
|
||||||
|
* **Target Language of Output:** \`{{language}}\`
|
||||||
|
* **Region(s) / Markets (optional):** \`{{regions}}\`
|
||||||
|
* **Product/Solution Focus (optional):** \`{{focus}}\`
|
||||||
|
|
||||||
|
# Instructions for Step 1
|
||||||
|
* Extract product(s)/service(s), core features, differentiation, and relevant value propositions.
|
||||||
|
* Create a short summary (max. 4 bullet points) of the key findings.
|
||||||
|
* **Output:** Table with columns: *Product/Solution | Description (1–2 sentences) | Core Features | Differentiation | Primary Source (URL)*.
|
||||||
|
* **Format Requirement:** Respond ONLY with the results for this single step. Your response must start with the heading "## Step 1: Offer (WHAT)" and include the short summary and the Markdown table. Do not provide any other explanations.`,
|
||||||
|
|
||||||
|
// Step 2: Target Groups
|
||||||
|
`# Task
|
||||||
|
Now perform **Step 2 – Target Groups (WHO – Companies)**.
|
||||||
|
|
||||||
|
# Context: Validated results from previous steps
|
||||||
|
{{previous_steps_data}}
|
||||||
|
|
||||||
|
# Instructions for Step 2
|
||||||
|
* Identify B2B target segments (industries/company types/sizes/regions) based on the given offer.
|
||||||
|
* **Output:** Table: *Target Industry/Segment | Typical Company Characteristics | Region(s) | Proof of Relevance (URL)*.
|
||||||
|
* **Format Requirement:** Respond ONLY with the results for this single step. Your response must start with the heading "## Step 2: Target Groups (Companies)" and include the Markdown table.`,
|
||||||
|
|
||||||
|
// Step 3: Personas
|
||||||
|
`# Task
|
||||||
|
Now perform **Step 3 – Personas/Roles (WHO – People)**.
|
||||||
|
|
||||||
|
# Context: Validated results from previous steps
|
||||||
|
{{previous_steps_data}}
|
||||||
|
|
||||||
|
# Instructions for Step 3
|
||||||
|
* For each target industry: at least 4 **specific** roles with their area of responsibility and involvement in purchasing (E, I, D, U based on RACI logic). Do **not** invent people; logically derive roles from problem/process context.
|
||||||
|
* **Output:** Table: *Role (precise) | Area of Responsibility | Why relevant for the product | Buying Involvement (E/I/D/U) | Source/Indication (URL or reference)*.
|
||||||
|
* **Format Requirement:** Respond ONLY with the results for this single step. Your response must start with the heading "## Step 3: Personas (Roles)".`,
|
||||||
|
|
||||||
|
// Step 4: Pain Points
|
||||||
|
`# Task
|
||||||
|
Now perform **Step 4 – Pain Points per Role (WHY)**.
|
||||||
|
|
||||||
|
# Context: Validated results from previous steps
|
||||||
|
{{previous_steps_data}}
|
||||||
|
|
||||||
|
# Instructions for Step 4
|
||||||
|
* For each role, formulate 3–5 specific pain points (observations, not features).
|
||||||
|
* Tag each pain point with a category: **Cost | Time | Risk | Compliance | Quality | Employee Retention.**
|
||||||
|
* Add an **Impact Estimate (€, h, %)** as a hypothesis.
|
||||||
|
* **Output:** Table: *Role | Pain Point (specific, measurable/operational) | Category | Impact (Cost, Risk, Time) | Impact Estimate (€, h, %) | Urgency (high/medium/low) | Source/Indication (URL)*.
|
||||||
|
* **Format Requirement:** Respond ONLY with the results for this single step. Your response must start with the heading "## Step 4: Pain Points per Role".`,
|
||||||
|
|
||||||
|
// Step 5: Gains
|
||||||
|
`# Task
|
||||||
|
Now perform **Step 5 – Gains & Benefits per Role (WHY switch)**.
|
||||||
|
|
||||||
|
# Context: Validated results from previous steps
|
||||||
|
{{previous_steps_data}}
|
||||||
|
|
||||||
|
# Instructions for Step 5
|
||||||
|
* Based on the identified pain points, formulate 2-3 concrete gains (advantages/benefits) for each role.
|
||||||
|
* Quantify the benefit as a hypothesis (e.g., savings in €, time gained in h, efficiency increase in %).
|
||||||
|
* **Output:** Table: *Role | Gain (specific benefit) | Quantification (Hypothesis in €, h, %) | Source/Indication (URL)*.
|
||||||
|
* **Format Requirement:** Respond ONLY with the results for this single step. Your response must start with the heading "## Step 5: Gains & Benefits per Role".`,
|
||||||
|
|
||||||
|
// Step 6: Messages
|
||||||
|
`# Task
|
||||||
|
Now perform **Step 6 – Marketing Message (HOW to speak)**.
|
||||||
|
|
||||||
|
# Context: Validated results from previous steps
|
||||||
|
{{previous_steps_data}}
|
||||||
|
|
||||||
|
# Inputs for this step
|
||||||
|
* **Desired channels for the message:** \`{{channels}}\`
|
||||||
|
|
||||||
|
# Instructions for Step 6: Chain-of-Thought Analysis & Copywriting
|
||||||
|
|
||||||
|
**Your goal is to create a SEPARATE, specific message for EACH role from Step 3 within EACH target industry from Step 2. The result MUST be a complete matrix (e.g., 3 target industries x 4 roles = 12 unique messages).**
|
||||||
|
|
||||||
|
For each combination of **[Target Industry/Segment]** and **[Role]**, perform the following thought process before writing the final message:
|
||||||
|
|
||||||
|
1. **Step 6.1 (Analysis): Product-Role Fit.**
|
||||||
|
* Which product/solution from the "Offer" table (Step 1) is most relevant to the **[Role]**?
|
||||||
|
* *Example thought:* "For the Customer Service Manager, the scheduling software is more relevant than the mobile app because they are responsible for dispatching."
|
||||||
|
|
||||||
|
2. **Step 6.2 (Analysis): Industry Use Case.**
|
||||||
|
* What are 1-2 typical use cases for the selected product in the **[Target Industry/Segment]**? What does the **[Role]** specifically do with it?
|
||||||
|
* *Example thought:* "A service technician in mechanical engineering typically uses the mobile app to access maintenance logs and create digital service reports directly on-site with the customer."
|
||||||
|
|
||||||
|
3. **Step 6.3 (Analysis): Benefit Quantification.**
|
||||||
|
* Look at the pain points (Step 4) and gains (Step 5) for the **[Role]**. Which pain point is most urgent, which gain most convincing?
|
||||||
|
* Derive a concrete KPI relevant to the **[Role]** (e.g., First-Contact Resolution Rate, Time-to-Resolution, plant downtime).
|
||||||
|
* Formulate the quantifiable benefit from Step 5 in relation to this KPI.
|
||||||
|
* *Example thought:* "The Maintenance Manager's pain point is 'unplanned plant downtime'. The gain is 'reducing downtime by 15%'. The relevant KPI is 'Overall Equipment Effectiveness (OEE)'. The benefit is increasing OEE through predictive maintenance planning."
|
||||||
|
|
||||||
|
4. **Step 6.4 (Synthesis): Formulate Message.**
|
||||||
|
* Synthesize the findings from 6.1-6.3 into a concise core message (2-3 sentences) following the structure: **Observation (Problem) → Low-threshold solution idea → Product bridge → Quantified benefit.**
|
||||||
|
* Create variations of this message for the channels: {{channels}}.
|
||||||
|
|
||||||
|
# Output Format
|
||||||
|
Create ONLY the final Markdown table. The thought process (6.1-6.3) is an internal instruction for you and should NOT appear in the output.
|
||||||
|
* **Table Columns:** *Target Industry/Segment | 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.`,
|
||||||
|
];
|
||||||
|
|
||||||
|
export const PROMPTS = {
|
||||||
|
de: {
|
||||||
|
SYSTEM_PROMPT: SYSTEM_PROMPT_DE,
|
||||||
|
STEP_PROMPTS: STEP_PROMPTS_DE,
|
||||||
|
},
|
||||||
|
en: {
|
||||||
|
SYSTEM_PROMPT: SYSTEM_PROMPT_EN,
|
||||||
|
STEP_PROMPTS: STEP_PROMPTS_EN,
|
||||||
|
}
|
||||||
|
};
|
||||||
59
b2b-marketing-assistant/index.html
Normal file
59
b2b-marketing-assistant/index.html
Normal file
@@ -0,0 +1,59 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8" />
|
||||||
|
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
|
<title>B2B Marketing Assistant powered by Gemini</title>
|
||||||
|
<script src="https://cdn.tailwindcss.com"></script>
|
||||||
|
<script type="importmap">
|
||||||
|
{
|
||||||
|
"imports": {
|
||||||
|
"react-dom/": "https://aistudiocdn.com/react-dom@^19.2.0/",
|
||||||
|
"@google/genai": "https://aistudiocdn.com/@google/genai@^1.28.0",
|
||||||
|
"react/": "https://aistudiocdn.com/react@^19.2.0/",
|
||||||
|
"react": "https://aistudiocdn.com/react@^19.2.0"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
<style>
|
||||||
|
@media print {
|
||||||
|
body {
|
||||||
|
-webkit-print-color-adjust: exact;
|
||||||
|
print-color-adjust: exact;
|
||||||
|
color-adjust: exact;
|
||||||
|
background-color: #fff;
|
||||||
|
color: #000;
|
||||||
|
}
|
||||||
|
main {
|
||||||
|
padding-top: 1rem !important;
|
||||||
|
padding-bottom: 1rem !important;
|
||||||
|
}
|
||||||
|
section {
|
||||||
|
page-break-inside: avoid;
|
||||||
|
}
|
||||||
|
table {
|
||||||
|
width: 100% !important;
|
||||||
|
table-layout: fixed;
|
||||||
|
}
|
||||||
|
td, th {
|
||||||
|
word-wrap: break-word;
|
||||||
|
}
|
||||||
|
textarea {
|
||||||
|
border: none !important;
|
||||||
|
resize: none !important;
|
||||||
|
box-shadow: none !important;
|
||||||
|
-webkit-box-shadow: none !important;
|
||||||
|
}
|
||||||
|
.overflow-x-auto {
|
||||||
|
overflow-x: visible !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
<link rel="stylesheet" href="/index.css">
|
||||||
|
</head>
|
||||||
|
<body class="bg-slate-50 dark:bg-slate-900">
|
||||||
|
<div id="root"></div>
|
||||||
|
<script type="module" src="/index.tsx"></script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
16
b2b-marketing-assistant/index.tsx
Normal file
16
b2b-marketing-assistant/index.tsx
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
|
||||||
|
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(
|
||||||
|
<React.StrictMode>
|
||||||
|
<App />
|
||||||
|
</React.StrictMode>
|
||||||
|
);
|
||||||
Reference in New Issue
Block a user