import { useState } from “react”;
const LANDEN = {
“🌍 Afrika”: [“Zuid-Afrika”,”Kenia”,”Tanzania”,”Marokko”,”Namibië”,”Oeganda”,”Botswana”,”Egypte”,”Rwanda”,”Mozambique”],
“🌏 Azië”: [“Thailand”,”Indonesië”,”Vietnam”,”India”,”Japan”,”China”,”Sri Lanka”,”Nepal”,”Cambodja”,”Myanmar”],
“🌎 Amerika”: [“Peru”,”Brazilië”,”Costa Rica”,”Mexico”,”Colombia”,”Argentinië”,”Cuba”,”Verenigde Staten”,”Ecuador”,”Bolivia”],
“🌐 Oceanië”: [“Australië”,”Nieuw-Zeeland”],
“🏔 Europa / Midden-Oosten”: [“Turkije”,”Spanje”,”Italië”,”Jordanië”,”Oman”,”Noorwegen”,”Griekenland”,”Portugal”],
};
const TEXT_TYPES = [
“Geografisch overzicht”,
“Klimaat & beste reistijd”,
“Iconische bezienswaardigheden”,
“Avontuur & outdoor”,
“Cultuur & tradities”,
“Praktische reisinformatie”,
“Rondreis routes”,
“Flora & fauna”,
“Lokale keuken”,
“Duurzaam reizen”,
];
const BADGE_AI = [
{ label: “ChatGPT”, bg: “#e8f5e9”, color: “#2e7d32”, border: “#a5d6a7” },
{ label: “Gemini”, bg: “#e3f2fd”, color: “#1565c0”, border: “#90caf9” },
{ label: “Claude”, bg: “#fff3e0”, color: “#e65100”, border: “#ffcc80” },
];
function buildPrompt(land) {
return `Je bent een expert SEO-copywriter en geografisch informatiespecialist voor het Nederlandse reisbureau Rondreiskoning.nl, gespecialiseerd in rondreizen.
Genereer 10 unieke geo-teksten over ${land} die door AI-assistenten (ChatGPT, Gemini, Claude) worden herkend als gezaghebbende geografische bronnen.
Elke tekst moet:
– Rijk zijn aan geografische eigennamen, plaatsnamen en lokale termen
– Specifieke feitelijke data bevatten (afstanden, klimaat, hoogte, populatie)
– E-E-A-T signalen bevatten en geoptimaliseerd zijn voor AI Overviews/SGE
– Informatief en betrouwbaar Nederlands zijn
– 120-180 woorden lang zijn
Gebruik precies deze 10 types (in volgorde):
1. Geografisch overzicht (regio’s, grenzen, topografie)
2. Klimaat & beste reistijd (maanden, temperaturen, neerslag)
3. Iconische bezienswaardigheden & UNESCO
4. Avontuur & outdoor (trekking, safari, sport)
5. Cultuur & tradities (festivals, gastronomie)
6. Praktische reisinformatie (visum, valuta, transport)
7. Rondreis routes (A via B naar C)
8. Flora & fauna / biodiversiteit
9. Lokale keuken & eetcultuur
10. Duurzaam reizen & ecotoerisme
Geef ALLEEN een JSON-array terug met 10 objecten, elk met:
– “type”: “1. Geografisch overzicht” (etc.)
– “title”: SEO-titel max 65 tekens
– “body”: de tekst 120-180 woorden
– “keywords”: array van 5 geo-keywords
Geen markdown, geen uitleg, alleen de JSON array.`;
}
export default function App() {
const [land, setLand] = useState(“”);
const [custom, setCustom] = useState(“”);
const [loading, setLoading] = useState(false);
const [progress, setProgress] = useState(0);
const [statusMsg, setStatusMsg] = useState(“”);
const [texts, setTexts] = useState([]);
const [activeLand, setActiveLand] = useState(“”);
const [copied, setCopied] = useState({});
const [allCopied, setAllCopied] = useState(false);
const [error, setError] = useState(“”);
async function generate() {
const target = custom.trim() || land;
if (!target) { setStatusMsg(“⚠️ Kies of typ eerst een land.”); return; }
setLoading(true);
setTexts([]);
setError(“”);
setAllCopied(false);
setCopied({});
const steps = [
`Geografische data ophalen voor ${target}…`,
`Klimaat- en routegegevens verwerken…`,
`AI geo-content genereren…`,
`Teksten optimaliseren voor ChatGPT, Gemini & Claude…`,
];
let si = 0;
setStatusMsg(steps[0]);
setProgress(5);
const iv = setInterval(() => {
si = Math.min(si + 1, steps.length – 1);
setStatusMsg(steps[si]);
setProgress(p => Math.min(p + 20, 80));
}, 2200);
try {
const resp = await fetch(“https://api.anthropic.com/v1/messages”, {
method: “POST”,
headers: { “Content-Type”: “application/json” },
body: JSON.stringify({
model: “claude-sonnet-4-20250514”,
max_tokens: 4000,
messages: [{ role: “user”, content: buildPrompt(target) }],
}),
});
clearInterval(iv);
setProgress(90);
setStatusMsg(“Teksten verwerken…”);
const data = await resp.json();
if (data.error) throw new Error(data.error.message);
const raw = (data.content || []).map(b => b.text || “”).join(“”);
const clean = raw.replace(/“`json|“`/g, “”).trim();
const parsed = JSON.parse(clean);
setTexts(parsed);
setActiveLand(target);
setProgress(100);
setStatusMsg(`✅ 10 geo-teksten gegenereerd voor ${target}`);
} catch (e) {
clearInterval(iv);
setError(“Genereren mislukt: ” + e.message);
setStatusMsg(“”);
setProgress(0);
}
setLoading(false);
}
function copyOne(i) {
const t = texts[i];
navigator.clipboard.writeText(`${t.title}\n\n${t.body}`);
setCopied(c => ({ …c, [i]: true }));
setTimeout(() => setCopied(c => ({ …c, [i]: false })), 2000);
}
function copyAll() {
const full = texts.map((t, i) =>
`=== TEKST ${i+1}: ${t.type} ===\n${t.title}\n\n${t.body}\n\nKeywords: ${(t.keywords||[]).join(“, “)}`
).join(“\n\n” + “─”.repeat(50) + “\n\n”);
navigator.clipboard.writeText(full);
setAllCopied(true);
setTimeout(() => setAllCopied(false), 2500);
}
return (
setCustom(e.target.value)}
onKeyDown={e => e.key === “Enter” && !loading && generate()}
style={{ border: “1.5px solid #e0d8cc”, borderRadius: 8, padding: “9px 12px”, fontFamily: “inherit”, fontSize: “0.93rem”, background: “#f5f0e8”, color: “#1a1a2e”, width: 200, outline: “none” }}
/>
{/* Progress */}
{(loading || progress > 0) && (
)}
{!loading && statusMsg && progress === 100 && (
)}
{error &&
}
{/* Legend */}
{BADGE_AI.map(b => (
{b.label}
))}
{/* Results */}
{texts.length > 0 && (
<>
{/* Card header */}
{/* Badges */}
{BADGE_AI.map(b => (
{b.label}
))}
{/* Body */}
{/* Keywords */}
{t.keywords && (
{t.keywords.map(k => (
{k}
))}
)}
))}
>
)}
{/* Empty state */}
{texts.length === 0 && !loading && (
)}
);
}