<!DOCTYPE html>
<html lang="nl">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Fitness Blog & SEO Creator</title>
<script src="https://cdn.tailwindcss.com"></script>
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap" rel="stylesheet">
<style>
body {
font-family: 'Inter', sans-serif;
}
.spinner {
border-top-color: #3498db;
animation: spin 1s linear infinite;
}
@keyframes spin {
to { transform: rotate(360deg); }
}
.custom-scrollbar::-webkit-scrollbar {
width: 8px;
}
.custom-scrollbar::-webkit-scrollbar-track {
background: #1f2937;
}
.custom-scrollbar::-webkit-scrollbar-thumb {
background: #4b5563;
border-radius: 4px;
}
.custom-scrollbar::-webkit-scrollbar-thumb:hover {
background: #6b7280;
}
.suggestion-item {
cursor: pointer;
transition: background-color 0.2s;
}
.suggestion-item:hover {
background-color: #374151; /* bg-gray-700 */
}
</style>
</head>
<body class="bg-gray-900 text-white antialiased">
<div class="container mx-auto p-4 sm:p-6 lg:p-8 max-w-7xl">
<header class="text-center mb-8">
<h1 class="text-4xl sm:text-5xl font-bold text-transparent bg-clip-text bg-gradient-to-r from-blue-400 to-teal-400">Fitness Blog & SEO Creator</h1>
<p class="text-gray-400 mt-2 text-lg">Jouw AI-partner voor moeiteloze content- en SEO-creatie.</p>
</header>
<div class="grid grid-cols-1 lg:grid-cols-3 gap-8">
<!-- Controls Column -->
<div class="lg:col-span-1 space-y-6">
<div class="bg-gray-800 p-6 rounded-2xl shadow-lg">
<h2 class="text-2xl font-semibold mb-4 border-b border-gray-700 pb-2">Instellingen</h2>
<div>
<label for="blogTopic" class="block text-sm font-medium text-gray-300 mb-2">Waar moet de blogpost over gaan?</label>
<div class="flex items-center space-x-2">
<input type="text" id="blogTopic" class="w-full bg-gray-700 border border-gray-600 rounded-lg px-4 py-2 focus:ring-2 focus:ring-blue-500 focus:outline-none transition" placeholder="bv. 'Beste oefeningen voor buikspieren'">
<button id="generateTitlesBtn" title="Genereer titelideeën" class="p-2 bg-blue-600 hover:bg-blue-700 rounded-lg transition-colors">✨</button>
</div>
<div id="titleSuggestions" class="mt-2 text-sm"></div>
</div>
<div class="mt-4">
<label class="block text-sm font-medium text-gray-300 mb-2">Taal</label>
<div class="flex space-x-4">
<label class="flex items-center"><input type="radio" name="language" value="Nederlands" class="form-radio h-4 w-4 text-blue-500 bg-gray-700 border-gray-600" checked><span class="ml-2 text-gray-200">Nederlands</span></label>
<label class="flex items-center"><input type="radio" name="language" value="American English" class="form-radio h-4 w-4 text-blue-500 bg-gray-700 border-gray-600"><span class="ml-2 text-gray-200">Engels (VS)</span></label>
</div>
</div>
<div class="mt-4">
<label for="productPromotion" class="block text-sm font-medium text-gray-300 mb-2">Promoot een product</label>
<select id="productPromotion" class="w-full bg-gray-700 border border-gray-600 rounded-lg px-4 py-2 focus:ring-2 focus:ring-blue-500 focus:outline-none transition">
<option value="geen">Geen promotie</option>
<option value="tripwire">Tripwire: In 4-Weken Fit Challenge (€27)</option>
<option value="app">Coaching App Toegang (vanaf €25 p/m)</option>
<option value="shredder">The Fat Shredder (€597)</option>
<option value="coaching">1:1 Online Coaching (€1997+)</option>
<option value="bootcamp">Warrior Dad Bootcamp (€7997)</option>
</select>
</div>
<div class="mt-4">
<label for="blogStyle" class="block text-sm font-medium text-gray-300 mb-2">Schrijfstijl</label>
<select id="blogStyle" class="w-full bg-gray-700 border border-gray-600 rounded-lg px-4 py-2 focus:ring-2 focus:ring-blue-500 focus:outline-none transition">
<option value="normaal">Normaal</option>
<option value="informatief">Informatief</option>
<option value="grappig">Grappig / Met humor</option>
</select>
</div>
<div class="mt-6">
<button id="generateBtn" class="w-full bg-gradient-to-r from-blue-500 to-teal-500 hover:from-blue-600 hover:to-teal-600 text-white font-bold py-3 px-4 rounded-lg shadow-md transition-transform transform hover:scale-105 flex items-center justify-center">
<svg id="generateIcon" xmlns="http://www.w3.org/2000/svg" class="h-5 w-5 mr-2" viewBox="0 0 20 20" fill="currentColor"><path d="M17.414 2.586a2 2 0 00-2.828 0L7 10.172V13h2.828l7.586-7.586a2 2 0 000-2.828z" /><path fill-rule="evenodd" d="M2 6a2 2 0 012-2h4a1 1 0 010 2H4v10h10v-4a1 1 0 112 0v4a2 2 0 01-2 2H4a2 2 0 01-2-2V6z" clip-rule="evenodd" /></svg>
<span id="generateBtnText">Genereer Content</span>
<div id="spinner" class="spinner h-5 w-5 rounded-full border-2 border-white hidden ml-2"></div>
</button>
</div>
</div>
</div>
<!-- Output Column -->
<div class="lg:col-span-2 space-y-6">
<!-- Blog Post Output -->
<div class="bg-gray-800 p-6 rounded-2xl shadow-lg h-full flex flex-col min-h-[400px]">
<div class="flex justify-between items-center mb-4 border-b border-gray-700 pb-2">
<h2 class="text-2xl font-semibold">Resultaat: Blogpost</h2>
<button id="copyBlogBtn" class="bg-gray-700 hover:bg-gray-600 text-gray-300 font-semibold py-2 px-4 rounded-lg transition-colors flex items-center disabled:opacity-50 disabled:cursor-not-allowed">
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5 mr-2" viewBox="0 0 20 20" fill="currentColor"><path d="M8 3a1 1 0 011-1h2a1 1 0 110 2H9a1 1 0 01-1-1z" /><path d="M6 3a2 2 0 00-2 2v11a2 2 0 002 2h8a2 2 0 002-2V5a2 2 0 00-2-2 3 3 0 01-3 3H9a3 3 0 01-3-3z" /></svg>
<span id="copyBlogBtnText">Kopieer Tekst</span>
</button>
</div>
<div id="blogOutput" class="prose prose-invert max-w-none text-gray-300 flex-grow overflow-y-auto custom-scrollbar pr-2">
<p>Hier verschijnt je gegenereerde blogpost. Pas de instellingen aan en klik op 'Genereer Content' om te beginnen.</p>
</div>
</div>
<!-- SEO Tools -->
<div class="bg-gray-800 p-6 rounded-2xl shadow-lg">
<h2 class="text-2xl font-semibold mb-4 border-b border-gray-700 pb-2">SEO Tools 🚀</h2>
<div class="space-y-4">
<!-- SEO Description -->
<div>
<div class="flex justify-between items-center mb-1">
<h3 class="text-lg font-semibold text-gray-200">SEO Omschrijving (Meta)</h3>
<button id="copySeoBtn" class="text-sm bg-gray-700 hover:bg-gray-600 text-gray-300 font-semibold py-1 px-3 rounded-lg transition-colors flex items-center disabled:opacity-50" disabled>Kopieer</button>
</div>
<div id="seoDescriptionOutput" class="text-sm text-gray-400 bg-gray-900 p-3 rounded-md min-h-[60px]"></div>
</div>
<!-- SEO Keywords -->
<div>
<div class="flex justify-between items-center mb-1">
<h3 class="text-lg font-semibold text-gray-200">Keywords & Key zinnen</h3>
<button id="copyKeywordsBtn" class="text-sm bg-gray-700 hover:bg-gray-600 text-gray-300 font-semibold py-1 px-3 rounded-lg transition-colors flex items-center disabled:opacity-50" disabled>Kopieer</button>
</div>
<div id="seoKeywordsOutput" class="text-sm text-gray-400 bg-gray-900 p-3 rounded-md min-h-[60px]"></div>
</div>
</div>
</div>
<!-- Extra Content Tools -->
<div class="bg-gray-800 p-6 rounded-2xl shadow-lg">
<h2 class="text-2xl font-semibold mb-4 border-b border-gray-700 pb-2">Extra Content Tools ✨</h2>
<div class="grid grid-cols-1 md:grid-cols-3 gap-4">
<div>
<button id="generateSocialBtn" class="w-full bg-blue-600 hover:bg-blue-700 text-white font-semibold py-2 px-4 rounded-lg transition-colors flex items-center justify-center disabled:opacity-50" disabled>Genereer Social Post</button>
<div id="socialOutput" class="mt-2 text-sm text-gray-400 custom-scrollbar overflow-y-auto h-32 bg-gray-900 p-2 rounded-md"></div>
</div>
<div>
<button id="generateEmailBtn" class="w-full bg-green-600 hover:bg-green-700 text-white font-semibold py-2 px-4 rounded-lg transition-colors flex items-center justify-center disabled:opacity-50" disabled>Genereer E-mail</button>
<div id="emailOutput" class="mt-2 text-sm text-gray-400 custom-scrollbar overflow-y-auto h-32 bg-gray-900 p-2 rounded-md"></div>
</div>
<div>
<button id="generateVisualsBtn" class="w-full bg-purple-600 hover:bg-purple-700 text-white font-semibold py-2 px-4 rounded-lg transition-colors flex items-center justify-center disabled:opacity-50" disabled>Genereer Visuele Ideeën</button>
<div id="visualsOutput" class="mt-2 text-sm text-gray-400 custom-scrollbar overflow-y-auto h-32 bg-gray-900 p-2 rounded-md"></div>
</div>
</div>
</div>
</div>
</div>
</div>
<script>
// --- DOM Elementen ---
const blogTopicInput = document.getElementById('blogTopic');
const generateTitlesBtn = document.getElementById('generateTitlesBtn');
const titleSuggestions = document.getElementById('titleSuggestions');
const productPromotionSelect = document.getElementById('productPromotion');
const blogStyleSelect = document.getElementById('blogStyle');
const generateBtn = document.getElementById('generateBtn');
const generateBtnText = document.getElementById('generateBtnText');
const spinner = document.getElementById('spinner');
const blogOutput = document.getElementById('blogOutput');
const copyBlogBtn = document.getElementById('copyBlogBtn');
const copyBlogBtnText = document.getElementById('copyBlogBtnText');
const generateSocialBtn = document.getElementById('generateSocialBtn');
const socialOutput = document.getElementById('socialOutput');
const generateEmailBtn = document.getElementById('generateEmailBtn');
const emailOutput = document.getElementById('emailOutput');
const generateVisualsBtn = document.getElementById('generateVisualsBtn');
const visualsOutput = document.getElementById('visualsOutput');
const seoDescriptionOutput = document.getElementById('seoDescriptionOutput');
const copySeoBtn = document.getElementById('copySeoBtn');
const seoKeywordsOutput = document.getElementById('seoKeywordsOutput');
const copyKeywordsBtn = document.getElementById('copyKeywordsBtn');
// --- State ---
let generatedBlogContent = '';
let generatedSeoDescription = '';
let generatedSeoKeywords = '';
// --- Productinformatie ---
const products = {
tripwire: { name: "The In 4-Weken Fit Challenge", description: "Een laagdrempelige challenge van 4 weken om snel fit te worden voor slechts €27." },
app: { name: "On-Demand Coaching App", description: "Krijg on-demand toegang tot diverse trainings- en voedingsprogramma's via mijn coaching app, vanaf €25 per maand." },
shredder: { name: "The Fat Shredder", description: "Een intensief 12-weken programma gericht op maximaal vetverlies met behoud van spiermassa, voor €597." },
coaching: { name: "1:1 Online Coaching", description: "Een exclusief 1-op-1 coachingstraject van minimaal 6 maanden voor totale transformatie. Startinvestering is €1997, daarna €249 per maand." },
bootcamp: { name: "Warrior Dad Bootcamp", description: "Een all-inclusive 5-dagen bootcamp voor vaders die een held willen worden voor hun gezin, inclusief verblijf en unieke protocollen voor €7997." }
};
// --- Functies ---
function setButtonLoading(button, isLoading, originalText = '') {
button.disabled = isLoading;
if (isLoading) {
button.innerHTML = `<div class="spinner h-5 w-5 rounded-full border-2 border-white mx-auto"></div>`;
} else {
button.innerHTML = originalText;
}
}
function buildPrompt(type) {
const topic = blogTopicInput.value || "algemene fitnesstips";
const language = document.querySelector('input[name="language"]:checked').value;
const style = blogStyleSelect.value;
const productKey = productPromotionSelect.value;
switch(type) {
case 'titles':
return `Je bent een SEO- en copywriting-expert. Geef 5 pakkende, SEO-vriendelijke blogtitels in het ${language} voor het volgende onderwerp: "${topic}". Geef alleen de titels, genummerd (1., 2., etc.). Geen extra tekst of introductie.`;
case 'blog':
let blogPrompt = `Je bent een expert fitness copywriter en blogger. Schrijf een boeiende en motiverende blogpost in ${language}. Onderwerp: ${topic}. Schrijfstijl: ${style}. Zorg voor een pakkende titel, een duidelijke inleiding, een gestructureerde body met subkopjes (gebruik markdown H2 of H3), en een krachtige conclusie.`;
if (productKey !== 'geen') {
const product = products[productKey];
blogPrompt += `\n**Promotie:** Verwerk op een natuurlijke en overtuigende manier, tegen het einde van de blogpost, een promotie voor het volgende product: "${product.name}". Leg kort uit wat het is en voor wie het is, gebaseerd op deze beschrijving: "${product.description}". Sluit af met een duidelijke call-to-action die lezers aanmoedigt om meer te weten te komen of zich aan te melden.`;
} else {
blogPrompt += `\nSluit af met een algemene motiverende boodschap en moedig lezers aan om te reageren of de post te delen.`;
}
return blogPrompt + "\n\nFormatteer de output in Markdown.";
case 'seo_description':
return `Je bent een SEO-expert. Schrijf een pakkende meta-omschrijving in ${language} van maximaal 155 karakters voor de volgende blogpost. De omschrijving moet de lezer nieuwsgierig maken om door te klikken. Geef alleen de omschrijving, geen extra tekst.\n\n--- BLOGPOST CONTEXT ---\n${generatedBlogContent}\n--- EINDE CONTEXT ---`;
case 'seo_keywords':
return `Je bent een SEO-expert. Analyseer de volgende blogpost. Extraheer de 5-10 belangrijkste keywords en key zinnen in ${language}. Geef het resultaat als één enkele, door komma's gescheiden lijst (bijvoorbeeld: keyword1, key phrase 2, keyword3). Geef alleen de lijst, geen extra tekst.\n\n--- BLOGPOST CONTEXT ---\n${generatedBlogContent}\n--- EINDE CONTEXT ---`;
case 'social':
let callToAction = "";
if (productKey !== 'geen') {
const product = products[productKey];
callToAction = `De post moet een duidelijke call-to-action bevatten die verwijst naar mijn product: ${product.name}.`;
}
return `Je bent een social media expert gespecialiseerd in fitness. Maak een korte, pakkende social media post (voor Instagram/Facebook) in ${language} gebaseerd op de volgende blogpost. Gebruik relevante emoji's en 3-5 relevante hashtags. ${callToAction}\n\n--- BLOGPOST CONTEXT ---\n${generatedBlogContent}\n--- EINDE CONTEXT ---`;
case 'email':
return `Je bent een e-mailmarketingexpert. Schrijf een korte, enthousiaste e-mailnieuwsbrief in het ${language} om de volgende nieuwe blogpost aan te kondigen bij abonnees. Begin met een persoonlijke aanhef (bv. 'Hey [Naam],'), vat de essentie van de blog kort samen, en eindig met een duidelijke call-to-action om de volledige post te lezen. Gebruik de volgende blogpost als context:\n\n--- BLOGPOST CONTEXT ---\n${generatedBlogContent}\n--- EINDE CONTEXT ---`;
case 'visuals':
return `Je bent een creative director. Geef 3 concrete en creatieve ideeën voor visuele content die deze blogpost kan versterken. Denk aan een hoofdafbeelding, een infographic, en een idee voor een korte social media video (zoals een Reel). Beschrijf elk idee kort en krachtig in ${language}. Context:\n\n--- BLOGPOST CONTEXT ---\n${generatedBlogContent}\n--- EINDE CONTEXT ---`;
}
}
async function callGeminiAPI(prompt) {
const apiKey = ""; // API key wordt door de omgeving verstrekt
const apiUrl = `https://generativelanguage.googleapis.com/v1beta/models/gemini-2.5-flash-preview-05-20:generateContent?key=${apiKey}`;
const payload = { contents: [{ parts: [{ text: prompt }] }], generationConfig: { temperature: 0.7, topP: 0.95 } };
try {
const response = await fetch(apiUrl, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(payload) });
if (!response.ok) throw new Error(`API verzoek mislukt met status: ${response.status}`);
const result = await response.json();
if (result.candidates && result.candidates[0].content && result.candidates[0].content.parts[0]) {
return result.candidates[0].content.parts[0].text.trim();
}
return "Kon geen geldige content genereren.";
} catch (error) {
console.error('Fout bij het aanroepen van de Gemini API:', error);
return `Er is een fout opgetreden: ${error.message}.`;
}
}
function renderMarkdown(text) {
return text
.replace(/^### (.*$)/gim, '<h3 class="text-xl font-semibold mt-4 mb-2">$1</h3>')
.replace(/^## (.*$)/gim, '<h2 class="text-2xl font-bold mt-6 mb-3">$1</h2>')
.replace(/^# (.*$)/gim, '<h1 class="text-3xl font-bold mt-6 mb-4">$1</h1>')
.replace(/\*\*(.*?)\*\*/g, '<strong>$1</strong>')
.replace(/\*(.*?)\*/g, '<em>$1</em>')
.replace(/^\* (.*$)/gim, '<ul class="list-disc list-inside ml-4"><li>$1</li></ul>')
.replace(/\n/g, '<br>');
}
function copyToClipboard(text, button, originalText) {
if (!text) return;
navigator.clipboard.writeText(text).then(() => {
button.textContent = 'Gekopieerd!';
setTimeout(() => { button.textContent = originalText; }, 2000);
}).catch(err => {
console.error('Kopiëren mislukt', err);
button.textContent = 'Fout!';
});
}
// --- Event Listeners ---
generateTitlesBtn.addEventListener('click', async () => {
if (!blogTopicInput.value) {
titleSuggestions.innerHTML = `<p class="text-red-400">Vul eerst een onderwerp in.</p>`;
return;
}
setButtonLoading(generateTitlesBtn, true);
titleSuggestions.innerHTML = `<p class="text-gray-400">Titels worden bedacht...</p>`;
const prompt = buildPrompt('titles');
const result = await callGeminiAPI(prompt);
titleSuggestions.innerHTML = '';
const titles = result.split('\n').filter(t => t.match(/^\d+\./));
titles.forEach(title => {
const cleanTitle = title.replace(/^\d+\.\s*/, '');
const p = document.createElement('p');
p.textContent = cleanTitle;
p.className = 'p-2 rounded-md suggestion-item';
p.onclick = () => {
blogTopicInput.value = cleanTitle;
titleSuggestions.innerHTML = '';
};
titleSuggestions.appendChild(p);
});
setButtonLoading(generateTitlesBtn, false, '✨');
});
generateBtn.addEventListener('click', async () => {
generateBtn.disabled = true;
spinner.classList.remove('hidden');
generateBtnText.classList.add('hidden');
// Reset all outputs and disable buttons
const allOutputs = [blogOutput, seoDescriptionOutput, seoKeywordsOutput, socialOutput, emailOutput, visualsOutput];
allOutputs.forEach(el => el.innerHTML = '<p>Wordt gegenereerd...</p>');
const allButtons = [copyBlogBtn, copySeoBtn, copyKeywordsBtn, generateSocialBtn, generateEmailBtn, generateVisualsBtn];
allButtons.forEach(btn => btn.disabled = true);
blogOutput.innerHTML = '<p>Blogpost wordt gegenereerd, een moment geduld...</p>';
// 1. Generate Blog Post
const blogPrompt = buildPrompt('blog');
generatedBlogContent = await callGeminiAPI(blogPrompt);
blogOutput.innerHTML = renderMarkdown(generatedBlogContent);
copyBlogBtn.disabled = false;
// 2. Generate SEO content concurrently
const seoDescriptionPrompt = buildPrompt('seo_description');
const seoKeywordsPrompt = buildPrompt('seo_keywords');
const [seoDesc, seoKeywords] = await Promise.all([
callGeminiAPI(seoDescriptionPrompt),
callGeminiAPI(seoKeywordsPrompt)
]);
generatedSeoDescription = seoDesc;
seoDescriptionOutput.textContent = generatedSeoDescription;
copySeoBtn.disabled = false;
generatedSeoKeywords = seoKeywords;
seoKeywordsOutput.textContent = generatedSeoKeywords;
copyKeywordsBtn.disabled = false;
// 3. Enable extra tools and reset their output
const extraOutputs = [socialOutput, emailOutput, visualsOutput];
extraOutputs.forEach(el => el.innerHTML = '');
const extraButtons = [generateSocialBtn, generateEmailBtn, generateVisualsBtn];
extraButtons.forEach(btn => btn.disabled = false);
// 4. Reset main button
generateBtn.disabled = false;
spinner.classList.add('hidden');
generateBtnText.classList.remove('hidden');
});
async function handleExtraContentGeneration(button, outputElement, originalText, type) {
setButtonLoading(button, true, originalText);
outputElement.innerHTML = 'Content wordt gegenereerd...';
const prompt = buildPrompt(type);
const result = await callGeminiAPI(prompt);
outputElement.innerHTML = renderMarkdown(result);
setButtonLoading(button, false, originalText);
}
generateSocialBtn.addEventListener('click', () => handleExtraContentGeneration(generateSocialBtn, socialOutput, 'Genereer Social Post', 'social'));
generateEmailBtn.addEventListener('click', () => handleExtraContentGeneration(generateEmailBtn, emailOutput, 'Genereer E-mail', 'email'));
generateVisualsBtn.addEventListener('click', () => handleExtraContentGeneration(generateVisualsBtn, visualsOutput, 'Genereer Visuele Ideeën', 'visuals'));
copyBlogBtn.addEventListener('click', () => copyToClipboard(generatedBlogContent, copyBlogBtnText, 'Kopieer Tekst'));
copySeoBtn.addEventListener('click', () => copyToClipboard(generatedSeoDescription, copySeoBtn, 'Kopieer'));
copyKeywordsBtn.addEventListener('click', () => copyToClipboard(generatedSeoKeywords, copyKeywordsBtn, 'Kopieer'));
</script>
</body>
</html>
Populaire blogberichten
Over de Schrijver
Ilker bey heeft zijn passie op het fitness leefstijl gevonden in 2016. En helpt sinds dien mensen om hun gezonde leefstijl te verkrijgen door zich te richten op de ware waarheid achter het vet verlies en bouwen van meer spiermassa.
Om dit te bereiken heeft Ilker verschillende programma's ontwikkeld en meerdere boeken geschreven welke gebaseerd zijn op onderzoek en studies.
Ilker zal altijd zo veel mogelijk bronnen en referenties opgeven om onderbouwd verhaal te verspreiden.
©️ 2019+ Ilker bey™️ | Alle Rechten Voorbehouden | Coaching 📲 Powered bij Trainerize
Ben jij een Personal Trainer of wil jij vanuit je fitness passie ondernemen? Stuur mij dan een email 📨
Website Build With Lover For Iron 🏋️ With systeme.io