enhance Feedback component with referer tracking
This commit is contained in:
@@ -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
|
||||||
@@ -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>
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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;
|
||||||
@@ -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",
|
||||||
|
}
|
||||||
|
)
|
||||||
|
);
|
||||||
Reference in New Issue
Block a user