analytics: count a successful form as the FORM_SUCCESS Metrika goal

The counter was in place but nothing reached it: src/lib/lead.ts only pushed
a custom dataLayer event, which Metrika's ecommerce: 'dataLayer' ignores, so
form conversions were not counted at all.

All five forms — including the fitnes callback modal — funnel through the one
success branch in submitLead(), so a single ym(112352796, 'reachGoal',
'FORM_SUCCESS') per landing covers every one of them. The counter id is
hardcoded to match the inline snippet in index.html; two sources for it would
drift. The call is optional-chained and wrapped in try/catch, because losing
analytics must never cost a lead.

The honeypot is the one success that is not a conversion: the server answers
bots with 200 {ok:true, leadId:0} so they learn nothing, and leadId === 0 is
what keeps that answer out of the goal.

The goal itself still has to be created in the counter's settings as a
JavaScript event named FORM_SUCCESS — noted in deploy/README.md, replacing
the entry that said conversions were unwired.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
Yuriy Panov
2026-09-09 01:30:47 +06:00
co-authored by Claude Opus 5
parent e13854e918
commit 8af2e463b3
5 changed files with 86 additions and 4 deletions
+20
View File
@@ -9,6 +9,9 @@ import {
const ENDPOINT = '/api/leads/medical-centers-existing-physio'
const DRAFT_KEY = 'exo_medcenter_lead_draft'
/** Тот же счётчик, что и в инлайн-снипете index.html — один на все четыре лендинга. */
const METRIKA_ID = 112352796
const FALLBACK_ERROR =
'Не удалось отправить форму автоматически. Данные сохранены в браузере — свяжитесь с нами по +7 927 789-60-71.'
@@ -62,6 +65,8 @@ export async function submitLead(
window.dataLayer = window.dataLayer ?? []
window.dataLayer.push({ event: 'medcenter_lead_sent', form })
// leadId === 0 — ответ-обманка ханипота: сделки в amoCRM нет, конверсии тоже.
if (body.leadId !== 0) reachGoal('FORM_SUCCESS')
localStorage.removeItem(DRAFT_KEY)
return { ok: true }
} catch (error) {
@@ -79,6 +84,20 @@ function saveDraft(payload: LeadInput) {
}
}
/**
* Цель Метрики. `ym` объявляется синхронно инлайн-снипетом и до загрузки tag.js
* копит вызовы в очереди, так что ждать загрузки счётчика не нужно; опциональный
* вызов — страховка на случай блокировщика, вырезавшего снипет целиком.
*/
function reachGoal(goal: string) {
try {
window.ym?.(METRIKA_ID, 'reachGoal', goal)
} catch (error) {
// Аналитика не имеет права ломать отправку формы.
console.warn('Metrika reachGoal failed', error)
}
}
/* -------------------------------------------------------------------------- */
/* Клик по кнопке звонка */
/* -------------------------------------------------------------------------- */
@@ -152,5 +171,6 @@ export function reportCallClick(): void {
declare global {
interface Window {
dataLayer?: Record<string, unknown>[]
ym?: (counterId: number, action: string, ...args: unknown[]) => void
}
}