main
/payment/success metrics
This commit is contained in:
parent
b1c34bd5cd
commit
a201f449b5
@ -6,7 +6,8 @@
|
|||||||
"dev": "next dev --turbopack",
|
"dev": "next dev --turbopack",
|
||||||
"build": "next build",
|
"build": "next build",
|
||||||
"start": "next start -p 3001",
|
"start": "next start -p 3001",
|
||||||
"lint": "next lint"
|
"lint": "next lint",
|
||||||
|
"lint:fix": "next lint --fix"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@lottiefiles/dotlottie-react": "^0.14.1",
|
"@lottiefiles/dotlottie-react": "^0.14.1",
|
||||||
@ -34,4 +35,4 @@
|
|||||||
"prettier": "^3.5.3",
|
"prettier": "^3.5.3",
|
||||||
"typescript": "^5"
|
"typescript": "^5"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
24
public/metrics-scripts/ym-script.js
Normal file
24
public/metrics-scripts/ym-script.js
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
(function (m, e, t, r, i, k, a) {
|
||||||
|
m[i] =
|
||||||
|
m[i] ||
|
||||||
|
function () {
|
||||||
|
(m[i].a = m[i].a || []).push(arguments);
|
||||||
|
};
|
||||||
|
m[i].l = 1 * new Date();
|
||||||
|
for (var j = 0; j < document.scripts.length; j++) {
|
||||||
|
if (document.scripts[j].src === r) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
(k = e.createElement(t)),
|
||||||
|
(a = e.getElementsByTagName(t)[0]),
|
||||||
|
(k.async = 1),
|
||||||
|
(k.src = r),
|
||||||
|
a.parentNode.insertBefore(k, a);
|
||||||
|
})(
|
||||||
|
window,
|
||||||
|
document,
|
||||||
|
"script",
|
||||||
|
"https://cdn.jsdelivr.net/npm/yandex-metrica-watch/tag.js",
|
||||||
|
"ym"
|
||||||
|
);
|
||||||
23
src/app/[locale]/(core)/payment/success/Metrics.module.scss
Normal file
23
src/app/[locale]/(core)/payment/success/Metrics.module.scss
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
.button {
|
||||||
|
position: fixed;
|
||||||
|
bottom: calc(0dvh + 64px);
|
||||||
|
left: 50%;
|
||||||
|
transform: translate(-50%, 0);
|
||||||
|
opacity: 0;
|
||||||
|
pointer-events: none;
|
||||||
|
max-width: 400px;
|
||||||
|
width: calc(100dvw - 32px);
|
||||||
|
animation: fadeIn 0.5s ease-in-out 2s forwards;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes fadeIn {
|
||||||
|
from {
|
||||||
|
opacity: 0;
|
||||||
|
pointer-events: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
to {
|
||||||
|
opacity: 1;
|
||||||
|
pointer-events: auto;
|
||||||
|
}
|
||||||
|
}
|
||||||
185
src/app/[locale]/(core)/payment/success/Metrics.tsx
Normal file
185
src/app/[locale]/(core)/payment/success/Metrics.tsx
Normal file
@ -0,0 +1,185 @@
|
|||||||
|
"use client";
|
||||||
|
|
||||||
|
import { useEffect, useState } from "react";
|
||||||
|
import Script from "next/script";
|
||||||
|
import { useTranslations } from "next-intl";
|
||||||
|
|
||||||
|
import { Button, Typography } from "@/components/ui";
|
||||||
|
import { ROUTES } from "@/shared/constants/client-routes";
|
||||||
|
|
||||||
|
import styles from "./Metrics.module.scss";
|
||||||
|
|
||||||
|
interface MetricsProps {
|
||||||
|
fbPixels: string[];
|
||||||
|
productPrice: string;
|
||||||
|
currency: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function Metrics({ fbPixels, productPrice, currency }: MetricsProps) {
|
||||||
|
const t = useTranslations("Payment.Success");
|
||||||
|
|
||||||
|
const [isButtonVisible, setIsButtonVisible] = useState(false);
|
||||||
|
|
||||||
|
const navigateToHome = () => {
|
||||||
|
window.location.href = ROUTES.home()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Yandex Metrica
|
||||||
|
useEffect(() => {
|
||||||
|
const interval = setInterval(() => {
|
||||||
|
if (typeof window.ym === 'function' && typeof window.klaviyo === 'object' && typeof window.gtag === 'function') {
|
||||||
|
try {
|
||||||
|
window.gtag('event', 'PaymentSuccess')
|
||||||
|
window.klaviyo.push(['track', "PaymentSuccess"]);
|
||||||
|
|
||||||
|
window.ym(95799066, "init", {
|
||||||
|
clickmap: true,
|
||||||
|
trackLinks: true,
|
||||||
|
accurateTrackBounce: true,
|
||||||
|
webvisor: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
window.ym(95799066, 'reachGoal', "PaymentSuccess", {}, () => {
|
||||||
|
console.log("Запрос отправлен");
|
||||||
|
// deleteYm()
|
||||||
|
setIsButtonVisible(true);
|
||||||
|
})
|
||||||
|
|
||||||
|
} catch (e) {
|
||||||
|
console.error('YM error:', e)
|
||||||
|
} finally {
|
||||||
|
clearInterval(interval);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, 200);
|
||||||
|
|
||||||
|
return () => clearInterval(interval)
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
return <>
|
||||||
|
|
||||||
|
{/* Klaviyo */}
|
||||||
|
{/* <Script src="https://static.klaviyo.com/onsite/js/klaviyo.js?company_id=RM7w5r" /> */}
|
||||||
|
<Script id="klaviyo-script">
|
||||||
|
{`const script = document.createElement("script");
|
||||||
|
script.src = "https://static.klaviyo.com/onsite/js/klaviyo.js?company_id=RM7w5r";
|
||||||
|
script.type = "text/javascript";
|
||||||
|
script.async = "";
|
||||||
|
document.head.appendChild(script);`}
|
||||||
|
</Script>
|
||||||
|
<Script id="klaviyo-script-proxy">
|
||||||
|
{`
|
||||||
|
!(function () {
|
||||||
|
if (!window.klaviyo) {
|
||||||
|
window._klOnsite = window._klOnsite || [];
|
||||||
|
try {
|
||||||
|
window.klaviyo = new Proxy(
|
||||||
|
{},
|
||||||
|
{
|
||||||
|
get: function (n, i) {
|
||||||
|
return "push" === i
|
||||||
|
? function () {
|
||||||
|
var n;
|
||||||
|
(n = window._klOnsite).push.apply(n, arguments);
|
||||||
|
}
|
||||||
|
: function () {
|
||||||
|
for (
|
||||||
|
var n = arguments.length, o = new Array(n), w = 0;
|
||||||
|
w < n;
|
||||||
|
w++
|
||||||
|
)
|
||||||
|
o[w] = arguments[w];
|
||||||
|
var t =
|
||||||
|
"function" == typeof o[o.length - 1]
|
||||||
|
? o.pop()
|
||||||
|
: void 0,
|
||||||
|
e = new Promise(function (n) {
|
||||||
|
window._klOnsite.push(
|
||||||
|
[i].concat(o, [
|
||||||
|
function (i) {
|
||||||
|
t && t(i), n(i);
|
||||||
|
},
|
||||||
|
])
|
||||||
|
);
|
||||||
|
});
|
||||||
|
return e;
|
||||||
|
};
|
||||||
|
},
|
||||||
|
}
|
||||||
|
);
|
||||||
|
} catch (n) {
|
||||||
|
(window.klaviyo = window.klaviyo || []),
|
||||||
|
(window.klaviyo.push = function () {
|
||||||
|
var n;
|
||||||
|
(n = window._klOnsite).push.apply(n, arguments);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})();
|
||||||
|
`}
|
||||||
|
</Script>
|
||||||
|
|
||||||
|
{/* Yandex Metrica */}
|
||||||
|
<Script id="yandex-metrica-script">
|
||||||
|
{`(function (m, e, t, r, i, k, a) {
|
||||||
|
m[i] =
|
||||||
|
m[i] ||
|
||||||
|
function () {
|
||||||
|
(m[i].a = m[i].a || []).push(arguments);
|
||||||
|
};
|
||||||
|
m[i].l = 1 * new Date();
|
||||||
|
for (var j = 0; j < document.scripts.length; j++) {
|
||||||
|
if (document.scripts[j].src === r) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
(k = e.createElement(t)),
|
||||||
|
(a = e.getElementsByTagName(t)[0]),
|
||||||
|
(k.async = 1),
|
||||||
|
(k.src = r),
|
||||||
|
a.parentNode.insertBefore(k, a);
|
||||||
|
})(
|
||||||
|
window,
|
||||||
|
document,
|
||||||
|
"script",
|
||||||
|
"https://cdn.jsdelivr.net/npm/yandex-metrica-watch/tag.js",
|
||||||
|
"ym"
|
||||||
|
);`}
|
||||||
|
</Script>
|
||||||
|
|
||||||
|
{/* Google Analytics */}
|
||||||
|
<Script id="google-analytics-script" async src="https://www.googletagmanager.com/gtag/js?id=G-4N17LL3BB5" />
|
||||||
|
<Script id="google-analytics-script-config">
|
||||||
|
{`window.dataLayer = window.dataLayer || [];
|
||||||
|
function gtag(){dataLayer.push(arguments);}
|
||||||
|
gtag('js', new Date());
|
||||||
|
|
||||||
|
gtag('config','G-4N17LL3BB5');`}
|
||||||
|
</Script>
|
||||||
|
|
||||||
|
{/* Facebook Pixel */}
|
||||||
|
{fbPixels.map((pixel) => (
|
||||||
|
<Script id={`facebook-pixel-${pixel}`} key={pixel}>
|
||||||
|
{`!function(f,b,e,v,n,t,s)
|
||||||
|
{if(f.fbq)return;n=f.fbq=function(){n.callMethod?
|
||||||
|
n.callMethod.apply(n,arguments):n.queue.push(arguments)};
|
||||||
|
if(!f._fbq)f._fbq=n;n.push=n;n.loaded=!0;n.version='2.0';
|
||||||
|
n.queue=[];t=b.createElement(e);t.async=!0;
|
||||||
|
t.src=v;s=b.getElementsByTagName(e)[0];
|
||||||
|
s.parentNode.insertBefore(t,s)}(window, document,'script',
|
||||||
|
'https://connect.facebook.net/en_US/fbevents.js');
|
||||||
|
fbq('init', '${pixel}');
|
||||||
|
fbq('track', 'PageView');
|
||||||
|
fbq('track', 'Purchase', { value: ${productPrice}, currency: "${currency}" });`}
|
||||||
|
</Script>
|
||||||
|
))}
|
||||||
|
|
||||||
|
{isButtonVisible &&
|
||||||
|
<Button onClick={navigateToHome} className={styles.button}>
|
||||||
|
<Typography color="white">
|
||||||
|
{t("button")}
|
||||||
|
</Typography>
|
||||||
|
</Button>
|
||||||
|
}
|
||||||
|
</>;
|
||||||
|
}
|
||||||
@ -1,20 +1,34 @@
|
|||||||
import { getTranslations } from "next-intl/server";
|
import { getTranslations } from "next-intl/server";
|
||||||
|
|
||||||
import { AnimatedInfoScreen, LottieAnimation } from "@/components/widgets";
|
import { AnimatedInfoScreen, LottieAnimation } from "@/components/widgets";
|
||||||
import { ROUTES } from "@/shared/constants/client-routes";
|
|
||||||
import { ELottieKeys } from "@/shared/constants/lottie";
|
import { ELottieKeys } from "@/shared/constants/lottie";
|
||||||
|
|
||||||
export default async function PaymentSuccess() {
|
import Metrics from "./Metrics";
|
||||||
|
|
||||||
|
export default async function PaymentSuccess({ searchParams }: {
|
||||||
|
searchParams: Promise<{
|
||||||
|
[key: string]: string | undefined
|
||||||
|
}>;
|
||||||
|
}) {
|
||||||
|
const params = await searchParams;
|
||||||
|
|
||||||
|
const fbPixels = params?.fb_pixels?.split(",") || [];
|
||||||
|
const productPrice = params?.price || "0";
|
||||||
|
const currency = params?.currency || "USD";
|
||||||
|
|
||||||
const t = await getTranslations("Payment.Success");
|
const t = await getTranslations("Payment.Success");
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<AnimatedInfoScreen
|
<>
|
||||||
lottieAnimation={<LottieAnimation loadKey={ELottieKeys.loaderCheckMark} />}
|
<AnimatedInfoScreen
|
||||||
title={t("title")}
|
lottieAnimation={<LottieAnimation loadKey={ELottieKeys.loaderCheckMark} />}
|
||||||
animationTime={0}
|
title={t("title")}
|
||||||
animationTexts={[]}
|
/>
|
||||||
buttonText={t("button")}
|
<Metrics
|
||||||
nextRoute={ROUTES.home()}
|
fbPixels={fbPixels}
|
||||||
/>
|
productPrice={productPrice}
|
||||||
|
currency={currency}
|
||||||
|
/>
|
||||||
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -8,10 +8,10 @@ import styles from "./AnimatedInfoScreen.module.scss";
|
|||||||
interface AnimatedInfoScreenProps {
|
interface AnimatedInfoScreenProps {
|
||||||
lottieAnimation: React.ReactNode;
|
lottieAnimation: React.ReactNode;
|
||||||
title: string;
|
title: string;
|
||||||
animationTime: number;
|
animationTime?: number;
|
||||||
animationTexts?: string[];
|
animationTexts?: string[];
|
||||||
buttonText: string;
|
buttonText?: string;
|
||||||
nextRoute: string;
|
nextRoute?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export default async function AnimatedInfoScreen({
|
export default async function AnimatedInfoScreen({
|
||||||
@ -29,15 +29,15 @@ export default async function AnimatedInfoScreen({
|
|||||||
<Typography as="h1" weight="bold" className={styles.title}>
|
<Typography as="h1" weight="bold" className={styles.title}>
|
||||||
{title}
|
{title}
|
||||||
</Typography>
|
</Typography>
|
||||||
{!!animationTexts?.length && <GPTAnimationText
|
{!!animationTexts?.length && animationTime && <GPTAnimationText
|
||||||
points={animationTexts}
|
points={animationTexts}
|
||||||
totalAnimationTime={animationTime}
|
totalAnimationTime={animationTime}
|
||||||
/>}
|
/>}
|
||||||
<Link className={styles.link} style={{ animationDelay: `${animationTime}ms` }} href={nextRoute}>
|
{nextRoute && buttonText && <Link className={styles.link} style={{ animationDelay: `${animationTime}ms` }} href={nextRoute}>
|
||||||
<RetainingButton className={styles.button} active={true}>
|
<RetainingButton className={styles.button} active={true}>
|
||||||
{buttonText}
|
{buttonText}
|
||||||
</RetainingButton>
|
</RetainingButton>
|
||||||
</Link>
|
</Link>}
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@ -20,4 +20,21 @@ export enum ERetainingFunnel {
|
|||||||
Green = "green",
|
Green = "green",
|
||||||
Purple = "purple",
|
Purple = "purple",
|
||||||
Stay50 = "stay50",
|
Stay50 = "stay50",
|
||||||
|
}
|
||||||
|
|
||||||
|
declare global {
|
||||||
|
interface Window {
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
|
ym: any;
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
|
ymab: any;
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
|
klaviyo: any;
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
|
fbq: any;
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
|
CollectJS: any;
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
|
gtag: any;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Loading…
Reference in New Issue
Block a user