File: //proc/self/root/tmp/contextual_rate_page.mjs
import fs from 'node:fs';
const file = 'web/src/modules/photos/RatePage.jsx';
let source = fs.readFileSync(file, 'utf8');
if (!source.includes('const getRateContext =')) {
const helper = `
const getRateContext = (category = 'all') => {
const key = String(category || 'all').toLowerCase();
if (['dating', 'tinder', 'bumble'].includes(key)) {
return {
key: 'dating',
label: 'Dating-Foto',
heading: 'Wie wirkt dieses Foto im Dating-Kontext?',
subtitle: 'Bewerte die Foto-Wirkung: offen, sympathisch und natürlich. Es geht nicht um die Person als Ganzes.',
lowLabel: 'wirkt schwach',
highLabel: 'wirkt sehr stark',
chipA: 'Sympathie',
chipB: 'Natürlichkeit',
chipC: 'erste Einladung',
uploadEntry: 'rate_context_dating'
};
}
if (['business', 'linkedin', 'bewerbung', 'kompetenz'].includes(key)) {
return {
key: 'business',
label: 'Business-Foto',
heading: 'Wie wirkt dieses Foto beruflich?',
subtitle: 'Bewerte Kompetenz, Vertrauen und Nahbarkeit. Ein gutes Business-Foto wirkt professionell, aber nicht distanziert.',
lowLabel: 'wenig überzeugend',
highLabel: 'sehr überzeugend',
chipA: 'Kompetenz',
chipB: 'Vertrauen',
chipC: 'Nahbarkeit',
uploadEntry: 'rate_context_business'
};
}
if (['social', 'instagram', 'facebook', 'whatsapp', 'selfie'].includes(key)) {
return {
key: 'social',
label: 'Social-Profilbild',
heading: 'Wie wirkt dieses Foto als Social-Profilbild?',
subtitle: 'Bewerte Wiedererkennung, Sympathie und ob das Bild auch klein als Avatar funktioniert.',
lowLabel: 'geht unter',
highLabel: 'funktioniert stark',
chipA: 'Avatar-Wirkung',
chipB: 'Sympathie',
chipC: 'Klarheit',
uploadEntry: 'rate_context_social'
};
}
return {
key: 'all',
label: 'Foto-Wirkung',
heading: 'Welche erste Wirkung hat dieses Foto?',
subtitle: 'Bewerte das Foto respektvoll nach Wirkung, Klarheit und Passung. Es geht um Entscheidungshilfe, nicht um Beschämung.',
lowLabel: 'passt noch nicht',
highLabel: 'wirkt sehr stark',
chipA: 'Klarheit',
chipB: 'Sympathie',
chipC: 'Passung',
uploadEntry: 'rate_context_all'
};
};
`;
source = source.replace('\nexport const RatePage = () => {', `${helper}\nexport const RatePage = () => {`);
}
if (!source.includes('const rateContext = getRateContext(category);')) {
source = source.replace(
"\tconst canNativeShare = typeof navigator !== 'undefined' && typeof navigator.share === 'function';",
"\tconst canNativeShare = typeof navigator !== 'undefined' && typeof navigator.share === 'function';\n\tconst rateContext = getRateContext(category);"
);
}
source = source.replace(
`\t\t\t\ttext: 'Gib diesem Foto bitte ehrliches Feedback auf bewertemich.de.',`,
`\t\t\t\ttext: rateContext.heading + ' ' + rateContext.subtitle,`
);
source = source.replace(
`\t\tvoid trackEvent('vote_streak_progress', {\n\t\t\tphotoId: Number(id),\n\t\t\tsource: inviteAttribution.isInvite ? inviteAttribution.source : 'community',\n\t\t\tsurface: 'rate_page',\n\t\t\tentryPoint: category,\n\t\t\tmeta: { streak: next, score }\n\t\t});`,
`\t\tvoid trackEvent('vote_streak_progress', {\n\t\t\tphotoId: Number(id),\n\t\t\tsource: inviteAttribution.isInvite ? inviteAttribution.source : 'community',\n\t\t\tsurface: 'rate_page',\n\t\t\tentryPoint: category,\n\t\t\tmeta: { streak: next, score, context: rateContext.key }\n\t\t});`
);
source = source.replace(
`\t\t\t{showVoteToUpload ? (`,
`\t\t\t<section className="mb-5 rounded-3xl border border-slate-200 bg-white p-5 shadow-sm">\n\t\t\t\t<div className="flex flex-col gap-4 md:flex-row md:items-center md:justify-between">\n\t\t\t\t\t<div>\n\t\t\t\t\t\t<div className="text-xs font-bold uppercase tracking-[0.22em] text-coral-600">{rateContext.label}</div>\n\t\t\t\t\t\t<h1 className="mt-2 text-2xl font-black text-slate-950">{rateContext.heading}</h1>\n\t\t\t\t\t\t<p className="mt-2 max-w-2xl text-sm leading-6 text-slate-600">{rateContext.subtitle}</p>\n\t\t\t\t\t</div>\n\t\t\t\t\t<Link\n\t\t\t\t\t\tto={buildUploadPath(rateContext.uploadEntry)}\n\t\t\t\t\t\tclassName="btn border-0 bg-slate-950 text-white hover:bg-slate-800"\n\t\t\t\t\t\tonClick={() => trackEvent('rate_context_upload_click', { source: 'rate_page', surface: 'context_card', entryPoint: rateContext.key })}\n\t\t\t\t\t>\n\t\t\t\t\t\tEigenes Foto in diesem Kontext testen\n\t\t\t\t\t</Link>\n\t\t\t\t</div>\n\t\t\t\t<div className="mt-4 flex flex-wrap gap-2 text-sm">\n\t\t\t\t\t<span className="rounded-full bg-slate-100 px-3 py-1 text-slate-700">{rateContext.chipA}</span>\n\t\t\t\t\t<span className="rounded-full bg-slate-100 px-3 py-1 text-slate-700">{rateContext.chipB}</span>\n\t\t\t\t\t<span className="rounded-full bg-slate-100 px-3 py-1 text-slate-700">{rateContext.chipC}</span>\n\t\t\t\t</div>\n\t\t\t</section>\n\t\t\t{showVoteToUpload ? (`
);
source = source.replace(
`\t\t\t\t<div className="text-gray-700">Ø {stats.avg_score ? Number(stats.avg_score).toFixed(1) : '-'} · {stats.votes} Stimmen</div>\n\t\t\t\t<div className="flex flex-wrap gap-2">`,
`\t\t\t\t<div>\n\t\t\t\t\t<div className="text-gray-700">Ø {stats.avg_score ? Number(stats.avg_score).toFixed(1) : '-'} · {stats.votes} Stimmen</div>\n\t\t\t\t\t<div className="mt-1 text-xs text-gray-500">1 = {rateContext.lowLabel} · 10 = {rateContext.highLabel}</div>\n\t\t\t\t</div>\n\t\t\t\t<div className="flex flex-wrap gap-2" aria-label={rateContext.heading}>`
);
source = source.replace(
`\t\t\t\t\t\t<button key={n} className="btn btn-ghost border" onClick={() => vote(n)} disabled={hasVoted}>{n}</button>`,
`\t\t\t\t\t\t<button key={n} className="btn btn-ghost border" aria-label={rateContext.heading + ' Bewertung ' + n + ' von 10'} onClick={() => vote(n)} disabled={hasVoted}>{n}</button>`
);
source = source.replace(
`to={buildUploadPath('vote_streak_prompt')}`,
`to={buildUploadPath('vote_streak_prompt_' + rateContext.key)}`
);
fs.writeFileSync(file, source, 'utf8');
console.log('contextual rate page added');