fix(tradein): hide lead CTA when contact email unset (no garbage mailto fallback)

Review #689: the CTA fallback was a throwaway address (settings.contact_email,
a Nominatim User-Agent contact — NOT a lead inbox). Unset env on first deploy →
client's заявка with PII drafts to a stranger, lead silently lost.
Fix: CONTACT_EMAIL = env only (trimmed, no fallback); render the «Оставить заявку»
CTA ONLY when leadEnabled (email configured). PDF + transparency unaffected.
tsc clean.
This commit is contained in:
lekss361 2026-05-30 11:13:00 +03:00
parent 2769fe6daf
commit 16a6d5a97b

View file

@ -35,11 +35,11 @@ const CONF_BAND: Record<string, string> = {
};
/**
* Контакт для lead-стаба. Настраивается через NEXT_PUBLIC_TRADEIN_CONTACT_EMAIL;
* fallback совпадает с backend `settings.contact_email`.
* Лид-инбокс. Задаётся ТОЛЬКО через env NEXT_PUBLIC_TRADEIN_CONTACT_EMAIL.
* БЕЗ fallback: если не задан CTA «оставить заявку» скрывается (leadEnabled),
* чтобы заявка с PII не ушла на чужой/тестовый адрес (review PR #689).
*/
const CONTACT_EMAIL =
process.env.NEXT_PUBLIC_TRADEIN_CONTACT_EMAIL ?? "erginrajpopxbe@outlook.com";
const CONTACT_EMAIL = (process.env.NEXT_PUBLIC_TRADEIN_CONTACT_EMAIL ?? "").trim();
/**
* Эвристика уровня сопоставимых объектов из confidence_explanation.
@ -88,6 +88,8 @@ export function HeroTransparency({ estimate, brandSlug, brandName }: Props) {
const band = CONF_BAND[estimate.confidence] ?? CONF_BAND.low;
const tier = compsTier(estimate.confidence_explanation);
const fresh = freshnessMoment(estimate);
// CTA показываем только при сконфигурированном лид-инбоксе (см. CONTACT_EMAIL) — review #689.
const leadEnabled = CONTACT_EMAIL.length > 0;
// PDF endpoint (api/v1/trade_in.py) уже honor'ит ?brand= для white-label —
// тот же паттерн, что в OfferCard, поэтому консистентно.
@ -110,16 +112,19 @@ export function HeroTransparency({ estimate, brandSlug, brandName }: Props) {
return (
<div className="hero-trust">
<div className="hero-trust-actions">
{/* CTA — prominent primary (конверсия = преимущество над «мёртвым» PDF) */}
{/* TODO(lead-flow): заменить mailto-стаб на POST /api/v1/trade-in/lead, когда появится backend. */}
<a className="btn btn-accent hero-trust-cta" href={leadHref}>
<svg width="15" height="15" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
<path d="M4 4h16v12H5.17L4 17.17V4z" />
<line x1="8" y1="9" x2="16" y2="9" />
<line x1="8" y1="12.5" x2="13" y2="12.5" />
</svg>
Оставить заявку на трейд-ин
</a>
{/* CTA — prominent primary (конверсия = преимущество над «мёртвым» PDF). */}
{/* Рендерим ТОЛЬКО при сконфигурированном лид-инбоксе (review #689) — иначе */}
{/* заявка с PII ушла бы на чужой/тестовый адрес. TODO(lead-flow): POST /lead. */}
{leadEnabled && (
<a className="btn btn-accent hero-trust-cta" href={leadHref}>
<svg width="15" height="15" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
<path d="M4 4h16v12H5.17L4 17.17V4z" />
<line x1="8" y1="9" x2="16" y2="9" />
<line x1="8" y1="12.5" x2="13" y2="12.5" />
</svg>
Оставить заявку на трейд-ин
</a>
)}
<a className="btn btn-ghost hero-trust-pdf" href={pdfHref} target="_blank" rel="noopener noreferrer">
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
<path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z" />