// ─── CASE STUDY PAGE ───────────────────────────────────────────────────────
// Three hero variants, togglable via Tweaks panel.
// Shared structure: Hero → Diensten (chips) → Player → Foto-galerij
const CASE = (window.SPRAAKMAKER_CASES || [])[0]; // fallback
// Load cases from static data file
async function loadCases() {
return window.SPRAAKMAKER_CASES || [];
}
// Read URL query param
function getQueryParam(key) {
const p = new URLSearchParams(window.location.search);
return p.get(key);
}
// ── Shared building blocks ──────────────────────────────────────────────
function ServicesChips({ services, dark = false }) {
return (
{services.map((slug, i) => (
{getServiceLabel(slug)}
))}
);
}
function SpotifyPlayer({ showId, compact = false }) {
if (!showId) {
return (
Geen Spotify-link beschikbaar
);
}
// Use /video variant — richer embed (video + audio) per user preference
const height = compact ? 232 : 351;
return (
);
}
function DescriptionBlock({ text, accent = false }) {
const paras = text.split('\n\n');
return (
{paras.map((p, i) => (
{p}
))}
);
}
function PhotoGallery({ photos, variant = 'mosaic' }) {
const isMobile = useIsMobile();
if (!photos || photos.length === 0) return null;
if (variant === 'strip') {
// horizontal strip
return (
{photos.slice(0, isMobile ? 3 : 3).map((src, i) => (
))}
);
}
// mosaic: 1 big + small grid
const [big, ...rest] = photos;
return (
{rest.slice(0, 3).map((src, i) => (
))}
);
}
// ── VARIANT A: Split hero (classic editorial) ───────────────────────────
function HeroSplit({ c }) {
const isMobile = useIsMobile(900);
return (
);
}
// ── VARIANT B: Massive typography hero ──────────────────────────────────
function HeroTypo({ c }) {
const isMobile = useIsMobile(900);
return (
);
}
// ── VARIANT C: Magazine hero (image-led, blush band) ─────────────────────
function HeroMagazine({ c }) {
const isMobile = useIsMobile(900);
const firstPhoto = c.photos?.[0];
return (
{/* Top bar with breadcrumb */}
{c.client}
{c.title}
{c.summary}
{firstPhoto && (
)}
);
}
// ── Body sections (shared across variants) ─────────────────────────────
function ServicesSection({ c, dark = true }) {
const isMobile = useIsMobile();
return (
);
}
function StorySection({ c }) {
const isMobile = useIsMobile();
return (
);
}
function PlayerSection({ c }) {
const isMobile = useIsMobile();
return (
);
}
function GallerySection({ c, variant = 'mosaic' }) {
const isMobile = useIsMobile();
if (!c.photos || c.photos.length === 0) return null;
return (
In de studio
Achter de schermen
);
}
// ── Related cases teaser ─────────────────────────────────────────────────
function RelatedCases({ currentId, allCases }) {
const isMobile = useIsMobile();
const others = (allCases || []).filter(c => c.id !== currentId).slice(0, 4);
return (
);
}
// ── TWEAKS PANEL ─────────────────────────────────────────────────────────
const TWEAK_DEFAULTS = /*EDITMODE-BEGIN*/{
"variant": "B",
"caseId": "7people-the-7people-podcast"
}/*EDITMODE-END*/;
function TweaksPanel({ editActive, state, setState, allCases }) {
if (!editActive) return null;
const variants = [
{ k: 'A', label: 'Split hero', desc: 'Tegel + titel naast elkaar' },
{ k: 'B', label: 'Typo hero', desc: 'Massive titel, kleine tegel' },
{ k: 'C', label: 'Magazine', desc: 'Foto-led, zachte achtergrond' },
];
return (
Tweaks
Vergelijk hero-varianten & case
Hero-variant
{variants.map(v => (
))}
Case
);
}
// ── PAGE ──────────────────────────────────────────────────────────────────
function CaseStudyPage() {
const [scrolled, setScrolled] = React.useState(false);
const [state, setState] = React.useState(TWEAK_DEFAULTS);
const [editActive, setEditActive] = React.useState(false);
const [allCases, setAllCases] = React.useState(null);
React.useEffect(() => {
loadCases().then(list => {
setAllCases(list);
const urlId = getQueryParam('id');
if (urlId && list.find(c => c.id === urlId)) {
setState(prev => ({ ...prev, caseId: urlId }));
} else if (!list.find(c => c.id === TWEAK_DEFAULTS.caseId) && list.length > 0) {
setState(prev => ({ ...prev, caseId: list[0].id }));
}
});
}, []);
React.useEffect(() => {
const onScroll = () => setScrolled(window.scrollY > 40);
window.addEventListener('scroll', onScroll, { passive: true });
return () => window.removeEventListener('scroll', onScroll);
}, []);
// Tweaks wiring
React.useEffect(() => {
const handler = (e) => {
if (e.data?.type === '__activate_edit_mode') setEditActive(true);
if (e.data?.type === '__deactivate_edit_mode') setEditActive(false);
};
window.addEventListener('message', handler);
window.parent.postMessage({ type: '__edit_mode_available' }, '*');
return () => window.removeEventListener('message', handler);
}, []);
React.useEffect(() => {
window.parent.postMessage({ type: '__edit_mode_set_keys', edits: state }, '*');
}, [state]);
if (!allCases) {
return Laden…
;
}
if (allCases.length === 0) {
return Nog geen cases gepubliceerd.
;
}
const c = allCases.find(c => c.id === state.caseId) || allCases[0];
let Hero;
if (state.variant === 'B') Hero = HeroTypo;
else if (state.variant === 'C') Hero = HeroMagazine;
else Hero = HeroSplit;
return (
<>
>
);
}
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render();