enhance Feedback component with referer tracking

This commit is contained in:
2025-06-30 15:11:44 +05:00
parent 08c23f24f7
commit f6271e62de
5 changed files with 63 additions and 20 deletions
+1 -1
View File
@@ -1,4 +1,4 @@
# NEXT_PUBLIC_API=http://192.168.1.250:3003/ # NEXT_PUBLIC_API=http://192.168.1.224:3003/
NEXT_PUBLIC_API=https://graff.estate/api/ NEXT_PUBLIC_API=https://graff.estate/api/
NEXT_PUBLIC_S3_BUCKET=https://storage.yandexcloud.net/dult-faib-knac-fint/ NEXT_PUBLIC_S3_BUCKET=https://storage.yandexcloud.net/dult-faib-knac-fint/
NEXT_PUBLIC_TINYMCE_API_KEY=2vf68779upg45y46o6g5gaxldy9gzr399eyaaqa0ki3mj2h2 NEXT_PUBLIC_TINYMCE_API_KEY=2vf68779upg45y46o6g5gaxldy9gzr399eyaaqa0ki3mj2h2
+13 -7
View File
@@ -1,5 +1,4 @@
"use client"; "use client";
import { api } from "@/api"; import { api } from "@/api";
import { projectsTags } from "@/consts/projectsTags"; import { projectsTags } from "@/consts/projectsTags";
import { Product } from "@/types/Product"; import { Product } from "@/types/Product";
@@ -9,15 +8,19 @@ import { getExampleNumber } from "libphonenumber-js";
import examples from "libphonenumber-js/mobile/examples"; import examples from "libphonenumber-js/mobile/examples";
import Link from "next/link"; import Link from "next/link";
import { usePathname } from "next/navigation"; import { usePathname } from "next/navigation";
import { ChangeEvent, useMemo, useState } from "react"; import { useMemo, useState } from "react";
import { FormProvider, useForm } from "react-hook-form"; import { FormProvider, useForm } from "react-hook-form";
import ReactInputMask from "react-input-mask"; import ReactInputMask from "react-input-mask";
import { Country } from "react-phone-number-input"; import { Country } from "react-phone-number-input";
import CheckIcon from "../../../public/icons/check.svg"; import CheckIcon from "../../../public/icons/check.svg";
import useAddReferer from "@/hooks/useAddReferer";
import { useRefererStore } from "@/stores/useRefererStore";
export function Feedback() { export function Feedback() {
const pathname = usePathname(); const pathname = usePathname();
useAddReferer();
return ( return (
<div <div
id="contacts" id="contacts"
@@ -40,6 +43,7 @@ interface IInput {
phone: string; phone: string;
email: string; email: string;
products: Product[]; products: Product[];
referer?: string | null;
} }
export function FeedbackForm() { export function FeedbackForm() {
@@ -47,6 +51,8 @@ export function FeedbackForm() {
[string, Country] [string, Country]
>(["+7", "RU"]); >(["+7", "RU"]);
const { referer } = useRefererStore();
const placeholder = useMemo( const placeholder = useMemo(
() => () =>
getExampleNumber(country, examples) getExampleNumber(country, examples)
@@ -64,7 +70,7 @@ export function FeedbackForm() {
const { register, handleSubmit, formState } = form; const { register, handleSubmit, formState } = form;
async function onSubmit(data: IInput) { async function onSubmit(data: IInput) {
await api.post("mail", { json: data }).json(); await api.post("mail", { json: { ...data, referer } }).json();
} }
return ( return (
@@ -77,7 +83,7 @@ export function FeedbackForm() {
onSubmit={handleSubmit(onSubmit)} onSubmit={handleSubmit(onSubmit)}
> >
<div className="lg:space-y-[1.111vw] space-y-4"> <div className="lg:space-y-[1.111vw] space-y-4">
<p className="font-medium heading2">Нам нужно</p> <p className="heading2 font-medium">Нам нужно</p>
<CheckboxesGroup name="products" options={projectsTags} /> <CheckboxesGroup name="products" options={projectsTags} />
</div> </div>
<input <input
@@ -126,7 +132,7 @@ export function FeedbackForm() {
maskChar={null} maskChar={null}
mask={"+7 " + (placeholder?.replace(/\d/g, "9") ?? "")} mask={"+7 " + (placeholder?.replace(/\d/g, "9") ?? "")}
placeholder={"+7 " + placeholder} placeholder={"+7 " + placeholder}
className="w-full h-full bg-transparent rounded-none transition-all outline-none placeholder:btnl placeholder:font-medium placeholder:select-none peer btnl" className="placeholder:btnl placeholder:font-medium placeholder:select-none peer btnl w-full h-full bg-transparent rounded-none transition-all outline-none"
/> />
<div className="bottom-0 absolute w-full border-b border-[#37393B] peer-focus:border-white -mb-2" /> <div className="bottom-0 absolute w-full border-b border-[#37393B] peer-focus:border-white -mb-2" />
</div> </div>
@@ -155,10 +161,10 @@ export function FeedbackForm() {
) : ( ) : (
<div className="bg-[#37393B99] aspect-[643/480] w-full rounded-2xl flex justify-center items-center"> <div className="bg-[#37393B99] aspect-[643/480] w-full rounded-2xl flex justify-center items-center">
<div className="flex gap-3 justify-center items-center"> <div className="flex gap-3 justify-center items-center">
<div className="p-3 rounded-full bg-gradient"> <div className="bg-gradient p-3 rounded-full">
<CheckIcon className="text-white lg:w-[1.667vw] lg:h-[1.667vw] w-6 h-6" /> <CheckIcon className="text-white lg:w-[1.667vw] lg:h-[1.667vw] w-6 h-6" />
</div> </div>
<p className="font-medium heading2"> <p className="heading2 font-medium">
Мы получили заявку Мы получили заявку
<br />и скоро свяжемся с вами! <br />и скоро свяжемся с вами!
</p> </p>
+14 -12
View File
@@ -1,17 +1,19 @@
import Link from 'next/link'; /* eslint-disable react-hooks/exhaustive-deps */
import { PropsWithChildren } from 'react'; "use client";
import ArrowMoreIcon from '../../../public/icons/arrow_more.svg'; import Link from "next/link";
import RutubeIcon from '../../../public/icons/rutube.svg'; import { PropsWithChildren, useEffect } from "react";
import TelegramIcon from '../../../public/icons/tg.svg'; import ArrowMoreIcon from "../../../public/icons/arrow_more.svg";
import VkIcon from '../../../public/icons/vk.svg'; import RutubeIcon from "../../../public/icons/rutube.svg";
import YoutubeIcon from '../../../public/icons/youtube.svg'; import TelegramIcon from "../../../public/icons/tg.svg";
import VkIcon from "../../../public/icons/vk.svg";
import YoutubeIcon from "../../../public/icons/youtube.svg";
export function Footer() { export function Footer() {
return ( return (
<footer className="lg:px-5 lg:pb-5 md:max-lg:px-4 md:max-lg:pb-4 px-[10px] pb-[10px] space-y-6 mb-0"> <footer className="lg:px-5 lg:pb-5 md:max-lg:px-4 md:max-lg:pb-4 px-[10px] pb-[10px] space-y-6 mb-0">
<div className="max-md:flex-col lg:gap-[0.833vw] md:max-lg:gap-[1.042vw] flex gap-[1.111vw]"> <div className="max-md:flex-col lg:gap-[0.833vw] md:max-lg:gap-[1.042vw] flex gap-[1.111vw]">
<Link <Link
href={'tel:' + '8 800 770 00 67'.replaceAll(' ', '')} href={"tel:" + "8 800 770 00 67".replaceAll(" ", "")}
className="lg:p-[1.667vw] p-6 flex flex-col justify-between max-md:gap-y-10 bg-[linear-gradient(to_top_left,#7A7A7A50,transparent)] lg:rounded-[1.111vw] rounded-2xl lg:aspect-[696/248] lg:w-[48.333vw] md:max-lg:w-[47.656vw] hover:bg-[#37393B99] bg-transparent transition-colors" className="lg:p-[1.667vw] p-6 flex flex-col justify-between max-md:gap-y-10 bg-[linear-gradient(to_top_left,#7A7A7A50,transparent)] lg:rounded-[1.111vw] rounded-2xl lg:aspect-[696/248] lg:w-[48.333vw] md:max-lg:w-[47.656vw] hover:bg-[#37393B99] bg-transparent transition-colors"
> >
<p className="text-[#7A7A7A] text1 font-medium">Позвонить</p> <p className="text-[#7A7A7A] text1 font-medium">Позвонить</p>
@@ -21,7 +23,7 @@ export function Footer() {
</div> </div>
</Link> </Link>
<Link <Link
href={'mailto:' + 'info@graff.tech'} href={"mailto:" + "info@graff.tech"}
className="lg:p-[1.667vw] p-6 flex flex-col justify-between max-md:gap-y-10 bg-[linear-gradient(to_top_left,#7A7A7A50,transparent)] lg:rounded-[1.111vw] rounded-2xl lg:aspect-[624/248] lg:w-[43.333vw] md:max-lg:w-[47.656vw] hover:bg-[#37393B99] bg-transparent transition-colors" className="lg:p-[1.667vw] p-6 flex flex-col justify-between max-md:gap-y-10 bg-[linear-gradient(to_top_left,#7A7A7A50,transparent)] lg:rounded-[1.111vw] rounded-2xl lg:aspect-[624/248] lg:w-[43.333vw] md:max-lg:w-[47.656vw] hover:bg-[#37393B99] bg-transparent transition-colors"
> >
<p className="text-[#7A7A7A] text1 font-medium">Написать</p> <p className="text-[#7A7A7A] text1 font-medium">Написать</p>
@@ -47,7 +49,7 @@ export function Footer() {
</div> </div>
<div className="lg:gap-x-[1.667vw] gap-y-2 lg:flex grid"> <div className="lg:gap-x-[1.667vw] gap-y-2 lg:flex grid">
<Link <Link
href={'/policy'} href={"/policy"}
className="text-[#37393B] text1 font-medium leading-[18.9px] w-fit" className="text-[#37393B] text1 font-medium leading-[18.9px] w-fit"
> >
Политика конфиденциальности и обработки персональных данных Политика конфиденциальности и обработки персональных данных
@@ -56,7 +58,7 @@ export function Footer() {
© 2025 GRAFF interactive. Все права защищены © 2025 GRAFF interactive. Все права защищены
</p> </p>
<Link <Link
href={'https://graff.tech'} href={"https://graff.tech"}
className="text-[#37393B] text1 font-medium leading-[18.9px] lg:w-fit md:col-start-2 md:text-right w-full" className="text-[#37393B] text1 font-medium leading-[18.9px] lg:w-fit md:col-start-2 md:text-right w-full"
> >
graff.tech graff.tech
@@ -69,7 +71,7 @@ export function Footer() {
export function ContactLink({ export function ContactLink({
children, children,
href, href,
className = '', className = "",
}: PropsWithChildren<{ href: string; className?: string }>) { }: PropsWithChildren<{ href: string; className?: string }>) {
return ( return (
<Link <Link
+18
View File
@@ -0,0 +1,18 @@
/* eslint-disable react-hooks/exhaustive-deps */
import { useRefererStore } from "@/stores/useRefererStore";
import { useEffect } from "react";
function useAddReferer() {
const { setReferer } = useRefererStore();
useEffect(() => {
const referer = new URLSearchParams(window.location.search).get("ref");
if (!referer) return;
setReferer(referer);
}, []);
return null;
}
export default useAddReferer;
+17
View File
@@ -0,0 +1,17 @@
import { create } from "zustand";
import { persist } from "zustand/middleware";
export const useRefererStore = create<{
referer: string | null;
setReferer: (referer: string | null) => void;
}>()(
persist(
(set) => ({
referer: null,
setReferer: (referer) => set({ referer }),
}),
{
name: "referer",
}
)
);