connetcted to backend: form request
This commit is contained in:
@@ -10,6 +10,9 @@ import BackAnimatsiya from "@/components/backAnimatsiya/backAnimatsiya";
|
||||
import { PriceModal } from "@/components/priceContact";
|
||||
import PageTransition from "@/components/pageTransition/pageTransition";
|
||||
import { InitialLoading } from "@/components/initialLoading/initialLoading";
|
||||
import { Providers } from "@/components/provider";
|
||||
|
||||
("info@ignum-tech.com");
|
||||
|
||||
const geistSans = Geist({
|
||||
variable: "--font-geist-sans",
|
||||
@@ -61,12 +64,7 @@ export default async function RootLayout({
|
||||
>
|
||||
<InitialLoading />
|
||||
<NextIntlClientProvider messages={messages} locale={locale}>
|
||||
<BackAnimatsiya />
|
||||
<Navbar />
|
||||
{children}
|
||||
<Footer />
|
||||
<PriceModal />
|
||||
<Analytics />
|
||||
<Providers>{children}</Providers>
|
||||
</NextIntlClientProvider>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -7,6 +7,11 @@ import { Mail, Phone, MapPin } from "lucide-react";
|
||||
import { useLocale, useTranslations } from "next-intl";
|
||||
import Image from "next/image";
|
||||
import Link from "next/link";
|
||||
import { useMutation } from "@tanstack/react-query";
|
||||
import { toast } from "react-toastify";
|
||||
import axios from "axios";
|
||||
import httpClient from "@/request/api";
|
||||
import { endPoints } from "@/request/links";
|
||||
|
||||
export function Footer() {
|
||||
const locale = useLocale();
|
||||
@@ -14,12 +19,25 @@ export function Footer() {
|
||||
const [email, setEmail] = useState("");
|
||||
const [subscribed, setSubscribed] = useState(false);
|
||||
|
||||
const handleSubscribe = (e: React.FormEvent) => {
|
||||
e.preventDefault();
|
||||
if (email) {
|
||||
const formRequest = useMutation({
|
||||
mutationKey: [],
|
||||
mutationFn: (data: any) => httpClient.post(endPoints.post.sendNumber, data),
|
||||
onSuccess: () => {
|
||||
toast.success(t("succes"));
|
||||
setSubscribed(true);
|
||||
setEmail("");
|
||||
setTimeout(() => setSubscribed(false), 3000);
|
||||
},
|
||||
onError: (error) => {
|
||||
console.log("error: ", error);
|
||||
toast.error(t("error"));
|
||||
},
|
||||
});
|
||||
|
||||
const handleSubscribe = (e: React.FormEvent) => {
|
||||
e.preventDefault();
|
||||
if (email) {
|
||||
formRequest.mutate({ number: email });
|
||||
}
|
||||
};
|
||||
|
||||
@@ -49,9 +67,11 @@ export function Footer() {
|
||||
className="flex sm:flex-row flex-col w-full gap-2 md:w-auto"
|
||||
>
|
||||
<input
|
||||
type="email"
|
||||
type="text"
|
||||
placeholder={t("enterPhone")}
|
||||
value={email}
|
||||
minLength={9}
|
||||
maxLength={13}
|
||||
onChange={(e) => setEmail(e.target.value)}
|
||||
className="font-almarai flex-1 rounded-full bg-white px-6 py-3 text-gray-800 placeholder-gray-400 focus:outline-none md:w-64"
|
||||
required
|
||||
@@ -179,9 +199,7 @@ export function Footer() {
|
||||
<div className="border-t border-gray-800 px-4 py-8">
|
||||
<div className="mx-auto max-w-6xl">
|
||||
<div className="font-almarai flex flex-col justify-between gap-4 text-sm text-gray-400 md:flex-row md:items-center">
|
||||
<div>
|
||||
Copyright © 2025 Ignum Company.
|
||||
</div>
|
||||
<div>Copyright © 2025 Ignum Company.</div>
|
||||
<div className="flex gap-6">
|
||||
<a href="#terms" className="hover:text-white">
|
||||
Terms & Conditions
|
||||
|
||||
@@ -3,21 +3,26 @@
|
||||
import { Check } from "lucide-react";
|
||||
import { useTranslations } from "next-intl";
|
||||
import { useState } from "react";
|
||||
import { useMutation } from "@tanstack/react-query";
|
||||
import axios from "axios";
|
||||
import { toast } from "react-toastify";
|
||||
import httpClient from "@/request/api";
|
||||
import { endPoints } from "@/request/links";
|
||||
|
||||
interface FormData {
|
||||
firstName: string;
|
||||
lastName: string;
|
||||
email: string;
|
||||
subject: string;
|
||||
name: string;
|
||||
surname: string;
|
||||
address: string;
|
||||
theme: string;
|
||||
message: string;
|
||||
agreeToPolicy: boolean;
|
||||
}
|
||||
|
||||
interface FormErrors {
|
||||
firstName?: string;
|
||||
lastName?: string;
|
||||
email?: string;
|
||||
subject?: string;
|
||||
name?: string;
|
||||
surname?: string;
|
||||
address?: string;
|
||||
theme?: string;
|
||||
message?: string;
|
||||
agreeToPolicy?: string;
|
||||
}
|
||||
@@ -25,10 +30,10 @@ interface FormErrors {
|
||||
export default function Form() {
|
||||
const t = useTranslations();
|
||||
const [formData, setFormData] = useState<FormData>({
|
||||
firstName: "",
|
||||
lastName: "",
|
||||
email: "",
|
||||
subject: "",
|
||||
name: "",
|
||||
surname: "",
|
||||
address: "",
|
||||
theme: "",
|
||||
message: "",
|
||||
agreeToPolicy: false,
|
||||
});
|
||||
@@ -38,22 +43,48 @@ export default function Form() {
|
||||
"idle" | "success" | "error"
|
||||
>("idle");
|
||||
|
||||
const formRequest = useMutation({
|
||||
mutationKey: [],
|
||||
mutationFn: (data: FormData) => httpClient.post(endPoints.post.contact, data),
|
||||
onSuccess: () => {
|
||||
setSubmitStatus("success");
|
||||
setFormData({
|
||||
name: "",
|
||||
surname: "",
|
||||
address: "",
|
||||
theme: "",
|
||||
message: "",
|
||||
agreeToPolicy: false,
|
||||
});
|
||||
setIsSubmitting(false);
|
||||
toast.success(t("succes"));
|
||||
setTimeout(() => setSubmitStatus("idle"), 3000);
|
||||
},
|
||||
onError: (error) => {
|
||||
console.log("error: ", error);
|
||||
setIsSubmitting(false);
|
||||
setSubmitStatus("error");
|
||||
toast.error(t("error"));
|
||||
setTimeout(() => setSubmitStatus("idle"), 3000);
|
||||
},
|
||||
});
|
||||
|
||||
const validateForm = (): boolean => {
|
||||
const newErrors: FormErrors = {};
|
||||
|
||||
if (!formData.firstName.trim()) {
|
||||
newErrors.firstName = "First name is required";
|
||||
if (!formData.name.trim()) {
|
||||
newErrors.name = "First name is required";
|
||||
}
|
||||
if (!formData.lastName.trim()) {
|
||||
newErrors.lastName = "Last name is required";
|
||||
if (!formData.surname.trim()) {
|
||||
newErrors.surname = "Last name is required";
|
||||
}
|
||||
if (!formData.email.trim()) {
|
||||
newErrors.email = "Email is required";
|
||||
} else if (!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(formData.email)) {
|
||||
newErrors.email = "Please enter a valid email";
|
||||
if (!formData.address.trim()) {
|
||||
newErrors.address = "address is required";
|
||||
} else if (!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(formData.address)) {
|
||||
newErrors.address = "Please enter a valid address";
|
||||
}
|
||||
if (!formData.subject.trim()) {
|
||||
newErrors.subject = "Subject is required";
|
||||
if (!formData.theme.trim()) {
|
||||
newErrors.theme = "theme is required";
|
||||
}
|
||||
if (!formData.message.trim()) {
|
||||
newErrors.message = "Message is required";
|
||||
@@ -90,32 +121,7 @@ export default function Form() {
|
||||
|
||||
setIsSubmitting(true);
|
||||
setSubmitStatus("idle");
|
||||
|
||||
try {
|
||||
const response = await fetch("/api/contact", {
|
||||
method: "POST",
|
||||
headers: { "Content-Type": "application/json" },
|
||||
body: JSON.stringify(formData),
|
||||
});
|
||||
|
||||
if (response.ok) {
|
||||
setSubmitStatus("success");
|
||||
setFormData({
|
||||
firstName: "",
|
||||
lastName: "",
|
||||
email: "",
|
||||
subject: "",
|
||||
message: "",
|
||||
agreeToPolicy: false,
|
||||
});
|
||||
} else {
|
||||
setSubmitStatus("error");
|
||||
}
|
||||
} catch {
|
||||
setSubmitStatus("error");
|
||||
} finally {
|
||||
setIsSubmitting(false);
|
||||
}
|
||||
formRequest.mutate(formData);
|
||||
};
|
||||
|
||||
return (
|
||||
@@ -125,66 +131,74 @@ export default function Form() {
|
||||
<div>
|
||||
<input
|
||||
type="text"
|
||||
name="firstName"
|
||||
name="name"
|
||||
placeholder={t("contact.form.placeholders.firstName")}
|
||||
value={formData.firstName}
|
||||
value={formData.name}
|
||||
onChange={handleChange}
|
||||
className={`font-almarai w-full rounded-3xl border bg-white px-4 py-3 text-sm
|
||||
text-gray-700 placeholder-gray-400 outline-none transition focus:ring-2 focus:ring-red-500 ${
|
||||
errors.firstName ? "border-red-500" : "border-transparent"
|
||||
}`}
|
||||
errors.name ? "border-red-500" : "border-transparent"
|
||||
}`}
|
||||
/>
|
||||
{errors.firstName && (
|
||||
<p className="font-almarai mt-1 text-xs text-red-500">{errors.firstName}</p>
|
||||
{errors.name && (
|
||||
<p className="font-almarai mt-1 text-xs text-red-500">
|
||||
{errors.name}
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
<div>
|
||||
<input
|
||||
type="text"
|
||||
name="lastName"
|
||||
name="surname"
|
||||
placeholder={t("contact.form.placeholders.lastName")}
|
||||
value={formData.lastName}
|
||||
value={formData.surname}
|
||||
onChange={handleChange}
|
||||
className={`font-almarai w-full rounded-3xl border bg-white px-4 py-3 text-sm text-gray-700 placeholder-gray-400 outline-none transition focus:ring-2 focus:ring-red-500 ${
|
||||
errors.lastName ? "border-red-500" : "border-transparent"
|
||||
errors.surname ? "border-red-500" : "border-transparent"
|
||||
}`}
|
||||
/>
|
||||
{errors.lastName && (
|
||||
<p className="font-almarai mt-1 text-xs text-red-500">{errors.lastName}</p>
|
||||
{errors.surname && (
|
||||
<p className="font-almarai mt-1 text-xs text-red-500">
|
||||
{errors.surname}
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Second Row - Email & Subject */}
|
||||
{/* Second Row - address & theme */}
|
||||
<div className="grid grid-cols-1 gap-4 md:grid-cols-2">
|
||||
<div>
|
||||
<input
|
||||
type="email"
|
||||
name="email"
|
||||
type="address"
|
||||
name="address"
|
||||
placeholder={t("contact.form.placeholders.email")}
|
||||
value={formData.email}
|
||||
value={formData.address}
|
||||
onChange={handleChange}
|
||||
className={`font-almarai w-full rounded-3xl border bg-white px-4 py-3 text-sm text-gray-700 placeholder-gray-400 outline-none transition focus:ring-2 focus:ring-red-500 ${
|
||||
errors.email ? "border-red-500" : "border-transparent"
|
||||
errors.address ? "border-red-500" : "border-transparent"
|
||||
}`}
|
||||
/>
|
||||
{errors.email && (
|
||||
<p className="font-almarai mt-1 text-xs text-red-500">{errors.email}</p>
|
||||
{errors.address && (
|
||||
<p className="font-almarai mt-1 text-xs text-red-500">
|
||||
{errors.address}
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
<div>
|
||||
<input
|
||||
type="text"
|
||||
name="subject"
|
||||
name="theme"
|
||||
placeholder={t("contact.form.placeholders.subject")}
|
||||
value={formData.subject}
|
||||
value={formData.theme}
|
||||
onChange={handleChange}
|
||||
className={`font-almarai w-full rounded-3xl border bg-white px-4 py-3 text-sm text-gray-700 placeholder-gray-400 outline-none transition focus:ring-2 focus:ring-red-500 ${
|
||||
errors.subject ? "border-red-500" : "border-transparent"
|
||||
errors.theme ? "border-red-500" : "border-transparent"
|
||||
}`}
|
||||
/>
|
||||
{errors.subject && (
|
||||
<p className="font-almarai mt-1 text-xs text-red-500">{errors.subject}</p>
|
||||
{errors.theme && (
|
||||
<p className="font-almarai mt-1 text-xs text-red-500">
|
||||
{errors.theme}
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
@@ -202,7 +216,9 @@ export default function Form() {
|
||||
}`}
|
||||
/>
|
||||
{errors.message && (
|
||||
<p className="font-almarai mt-1 text-xs text-red-500">{errors.message}</p>
|
||||
<p className="font-almarai mt-1 text-xs text-red-500">
|
||||
{errors.message}
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
|
||||
@@ -236,7 +252,9 @@ export default function Form() {
|
||||
</button>
|
||||
</div>
|
||||
{errors.agreeToPolicy && (
|
||||
<p className="font-almarai text-xs text-red-500">{errors.agreeToPolicy}</p>
|
||||
<p className="font-almarai text-xs text-red-500">
|
||||
{errors.agreeToPolicy}
|
||||
</p>
|
||||
)}
|
||||
|
||||
{/* Status Messages */}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import Image from "next/image";
|
||||
import { Mail, MapPin, Phone, Check } from "lucide-react";
|
||||
import { Mail, MapPin, Phone } from "lucide-react";
|
||||
import ContactHeader from "./contactHeader";
|
||||
import Form from "./form";
|
||||
import { useTranslations } from "next-intl";
|
||||
@@ -25,7 +25,7 @@ export function Contact() {
|
||||
];
|
||||
|
||||
return (
|
||||
<section className="relative min-h-175 w-full py-16 md:py-40">
|
||||
<section className="relative min-h-175 w-full py-30 md:py-40">
|
||||
{/* Background Image */}
|
||||
<div className="absolute inset-0 z-0">
|
||||
<Image
|
||||
|
||||
27
components/provider/index.tsx
Normal file
27
components/provider/index.tsx
Normal file
@@ -0,0 +1,27 @@
|
||||
"use client";
|
||||
|
||||
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
|
||||
import BackAnimatsiya from "../backAnimatsiya/backAnimatsiya";
|
||||
import { Footer, Navbar } from "../layout";
|
||||
import { PriceModal } from "../priceContact";
|
||||
import { Analytics } from "@vercel/analytics/next";
|
||||
import { ReactNode } from "react";
|
||||
import { ToastContainer } from 'react-toastify';
|
||||
|
||||
const queryClient = new QueryClient();
|
||||
|
||||
export function Providers({ children }: { children: ReactNode }) {
|
||||
return (
|
||||
<div>
|
||||
<QueryClientProvider client={queryClient}>
|
||||
<BackAnimatsiya />
|
||||
<Navbar />
|
||||
{children}
|
||||
<Footer />
|
||||
<PriceModal />
|
||||
<Analytics />
|
||||
<ToastContainer/>
|
||||
</QueryClientProvider>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -228,7 +228,9 @@
|
||||
"contactTitle": "Send us your phone number",
|
||||
"contactSubTitle": "Our staff will contact you",
|
||||
"enterPhone": "Enter your phone number",
|
||||
"send": "Send",
|
||||
"send": "Sent",
|
||||
"error":"Error!",
|
||||
"succes":"sent!",
|
||||
"priceModal": {
|
||||
"title": "Get Price",
|
||||
"product": {
|
||||
|
||||
@@ -229,6 +229,8 @@
|
||||
"contactSubTitle": "Наши сотрудники свяжутся с вами",
|
||||
"enterPhone": "Введите ваш номер телефона",
|
||||
"send": "Отправить",
|
||||
"error": "Ошибка!",
|
||||
"succes": "Отправлено!",
|
||||
"priceModal": {
|
||||
"title": "Узнать цену",
|
||||
"product": {
|
||||
|
||||
@@ -229,7 +229,8 @@
|
||||
"contactSubTitle": "Xodimlarimiz siz bilan bog'lanishadi",
|
||||
"enterPhone": "Telefon raqamingiz kiriting",
|
||||
"send": "Yuborish",
|
||||
|
||||
"error":"Xatolik!",
|
||||
"succes":"Yuborildi!",
|
||||
"priceModal": {
|
||||
"title": "Narxni bilish",
|
||||
"product": {
|
||||
|
||||
@@ -38,8 +38,10 @@
|
||||
"@radix-ui/react-toggle": "1.1.1",
|
||||
"@radix-ui/react-toggle-group": "1.1.1",
|
||||
"@radix-ui/react-tooltip": "1.1.6",
|
||||
"@tanstack/react-query": "^5.90.20",
|
||||
"@vercel/analytics": "1.3.1",
|
||||
"autoprefixer": "^10.4.20",
|
||||
"axios": "^1.13.4",
|
||||
"class-variance-authority": "^0.7.1",
|
||||
"clsx": "^2.1.1",
|
||||
"cmdk": "1.0.4",
|
||||
@@ -58,6 +60,7 @@
|
||||
"react-fast-marquee": "^1.6.5",
|
||||
"react-hook-form": "^7.60.0",
|
||||
"react-resizable-panels": "^2.1.7",
|
||||
"react-toastify": "^11.0.5",
|
||||
"recharts": "2.15.4",
|
||||
"sonner": "^1.7.4",
|
||||
"swiper": "^12.0.3",
|
||||
|
||||
223
pnpm-lock.yaml
generated
223
pnpm-lock.yaml
generated
@@ -95,12 +95,18 @@ importers:
|
||||
'@radix-ui/react-tooltip':
|
||||
specifier: 1.1.6
|
||||
version: 1.1.6(@types/react-dom@19.2.3(@types/react@19.2.9))(@types/react@19.2.9)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)
|
||||
'@tanstack/react-query':
|
||||
specifier: ^5.90.20
|
||||
version: 5.90.20(react@19.2.0)
|
||||
'@vercel/analytics':
|
||||
specifier: 1.3.1
|
||||
version: 1.3.1(next@16.0.10(react-dom@19.2.0(react@19.2.0))(react@19.2.0))(react@19.2.0)
|
||||
autoprefixer:
|
||||
specifier: ^10.4.20
|
||||
version: 10.4.23(postcss@8.5.6)
|
||||
axios:
|
||||
specifier: ^1.13.4
|
||||
version: 1.13.4
|
||||
class-variance-authority:
|
||||
specifier: ^0.7.1
|
||||
version: 0.7.1
|
||||
@@ -155,6 +161,9 @@ importers:
|
||||
react-resizable-panels:
|
||||
specifier: ^2.1.7
|
||||
version: 2.1.9(react-dom@19.2.0(react@19.2.0))(react@19.2.0)
|
||||
react-toastify:
|
||||
specifier: ^11.0.5
|
||||
version: 11.0.5(react-dom@19.2.0(react@19.2.0))(react@19.2.0)
|
||||
recharts:
|
||||
specifier: 2.15.4
|
||||
version: 2.15.4(react-dom@19.2.0(react@19.2.0))(react@19.2.0)
|
||||
@@ -1379,6 +1388,14 @@ packages:
|
||||
'@tailwindcss/postcss@4.1.18':
|
||||
resolution: {integrity: sha512-Ce0GFnzAOuPyfV5SxjXGn0CubwGcuDB0zcdaPuCSzAa/2vII24JTkH+I6jcbXLb1ctjZMZZI6OjDaLPJQL1S0g==}
|
||||
|
||||
'@tanstack/query-core@5.90.20':
|
||||
resolution: {integrity: sha512-OMD2HLpNouXEfZJWcKeVKUgQ5n+n3A2JFmBaScpNDUqSrQSjiveC7dKMe53uJUg1nDG16ttFPz2xfilz6i2uVg==}
|
||||
|
||||
'@tanstack/react-query@5.90.20':
|
||||
resolution: {integrity: sha512-vXBxa+qeyveVO7OA0jX1z+DeyCA4JKnThKv411jd5SORpBKgkcVnYKCiBgECvADvniBX7tobwBmg01qq9JmMJw==}
|
||||
peerDependencies:
|
||||
react: ^18 || ^19
|
||||
|
||||
'@types/d3-array@3.2.2':
|
||||
resolution: {integrity: sha512-hOLWVbm7uRza0BYXpIIW5pxfrKe0W+D5lrFiAEYR+pb6w3N2SwSMaJbXdUfSEv+dT4MfHBLtn5js0LAWaO6otw==}
|
||||
|
||||
@@ -1435,6 +1452,9 @@ packages:
|
||||
resolution: {integrity: sha512-ik3ZgC9dY/lYVVM++OISsaYDeg1tb0VtP5uL3ouh1koGOaUMDPpbFIei4JkFimWUFPn90sbMNMXQAIVOlnYKJA==}
|
||||
engines: {node: '>=10'}
|
||||
|
||||
asynckit@0.4.0:
|
||||
resolution: {integrity: sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==}
|
||||
|
||||
autoprefixer@10.4.23:
|
||||
resolution: {integrity: sha512-YYTXSFulfwytnjAPlw8QHncHJmlvFKtczb8InXaAx9Q0LbfDnfEYDE55omerIJKihhmU61Ft+cAOSzQVaBUmeA==}
|
||||
engines: {node: ^10 || ^12 || >=14}
|
||||
@@ -1442,6 +1462,9 @@ packages:
|
||||
peerDependencies:
|
||||
postcss: ^8.1.0
|
||||
|
||||
axios@1.13.4:
|
||||
resolution: {integrity: sha512-1wVkUaAO6WyaYtCkcYCOx12ZgpGf9Zif+qXa4n+oYzK558YryKqiL6UWwd5DqiH3VRW0GYhTZQ/vlgJrCoNQlg==}
|
||||
|
||||
baseline-browser-mapping@2.9.17:
|
||||
resolution: {integrity: sha512-agD0MgJFUP/4nvjqzIB29zRPUuCF7Ge6mEv9s8dHrtYD7QWXRcx75rOADE/d5ah1NI+0vkDl0yorDd5U852IQQ==}
|
||||
hasBin: true
|
||||
@@ -1451,6 +1474,10 @@ packages:
|
||||
engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7}
|
||||
hasBin: true
|
||||
|
||||
call-bind-apply-helpers@1.0.2:
|
||||
resolution: {integrity: sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==}
|
||||
engines: {node: '>= 0.4'}
|
||||
|
||||
caniuse-lite@1.0.30001765:
|
||||
resolution: {integrity: sha512-LWcNtSyZrakjECqmpP4qdg0MMGdN368D7X8XvvAqOcqMv0RxnlqVKZl2V6/mBR68oYMxOZPLw/gO7DuisMHUvQ==}
|
||||
|
||||
@@ -1470,6 +1497,10 @@ packages:
|
||||
react: ^18 || ^19 || ^19.0.0-rc
|
||||
react-dom: ^18 || ^19 || ^19.0.0-rc
|
||||
|
||||
combined-stream@1.0.8:
|
||||
resolution: {integrity: sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==}
|
||||
engines: {node: '>= 0.8'}
|
||||
|
||||
csstype@3.2.3:
|
||||
resolution: {integrity: sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==}
|
||||
|
||||
@@ -1529,6 +1560,10 @@ packages:
|
||||
decimal.js@10.6.0:
|
||||
resolution: {integrity: sha512-YpgQiITW3JXGntzdUmyUR1V812Hn8T1YVXhCu+wO3OpS4eU9l4YdD3qjyiKdV6mvV29zapkMeD390UVEf2lkUg==}
|
||||
|
||||
delayed-stream@1.0.0:
|
||||
resolution: {integrity: sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==}
|
||||
engines: {node: '>=0.4.0'}
|
||||
|
||||
detect-libc@2.1.2:
|
||||
resolution: {integrity: sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==}
|
||||
engines: {node: '>=8'}
|
||||
@@ -1539,6 +1574,10 @@ packages:
|
||||
dom-helpers@5.2.1:
|
||||
resolution: {integrity: sha512-nRCa7CK3VTrM2NmGkIy4cbK7IZlgBE/PYMn55rrXefr5xXDP0LdtfPnblFDoVdcAfslJ7or6iqAUnx0CCGIWQA==}
|
||||
|
||||
dunder-proto@1.0.1:
|
||||
resolution: {integrity: sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==}
|
||||
engines: {node: '>= 0.4'}
|
||||
|
||||
electron-to-chromium@1.5.267:
|
||||
resolution: {integrity: sha512-0Drusm6MVRXSOJpGbaSVgcQsuB4hEkMpHXaVstcPmhu5LIedxs1xNK/nIxmQIU/RPC0+1/o0AVZfBTkTNJOdUw==}
|
||||
|
||||
@@ -1559,6 +1598,22 @@ packages:
|
||||
resolution: {integrity: sha512-LgQMM4WXU3QI+SYgEc2liRgznaD5ojbmY3sb8LxyguVkIg5FxdpTkvk72te2R38/TGKxH634oLxXRGY6d7AP+Q==}
|
||||
engines: {node: '>=10.13.0'}
|
||||
|
||||
es-define-property@1.0.1:
|
||||
resolution: {integrity: sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==}
|
||||
engines: {node: '>= 0.4'}
|
||||
|
||||
es-errors@1.3.0:
|
||||
resolution: {integrity: sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==}
|
||||
engines: {node: '>= 0.4'}
|
||||
|
||||
es-object-atoms@1.1.1:
|
||||
resolution: {integrity: sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==}
|
||||
engines: {node: '>= 0.4'}
|
||||
|
||||
es-set-tostringtag@2.1.0:
|
||||
resolution: {integrity: sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==}
|
||||
engines: {node: '>= 0.4'}
|
||||
|
||||
escalade@3.2.0:
|
||||
resolution: {integrity: sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==}
|
||||
engines: {node: '>=6'}
|
||||
@@ -1570,6 +1625,19 @@ packages:
|
||||
resolution: {integrity: sha512-jt2DW/aNFNwke7AUd+Z+e6pz39KO5rzdbbFCg2sGafS4mk13MI7Z8O5z9cADNn5lhGODIgLwug6TZO2ctf7kcw==}
|
||||
engines: {node: '>=6.0.0'}
|
||||
|
||||
follow-redirects@1.15.11:
|
||||
resolution: {integrity: sha512-deG2P0JfjrTxl50XGCDyfI97ZGVCxIpfKYmfyrQ54n5FO/0gfIES8C/Psl6kWVDolizcaaxZJnTS0QSMxvnsBQ==}
|
||||
engines: {node: '>=4.0'}
|
||||
peerDependencies:
|
||||
debug: '*'
|
||||
peerDependenciesMeta:
|
||||
debug:
|
||||
optional: true
|
||||
|
||||
form-data@4.0.5:
|
||||
resolution: {integrity: sha512-8RipRLol37bNs2bhoV67fiTEvdTrbMUYcFTiy3+wuuOnUog2QBHCZWXDRijWQfAkhBj2Uf5UnVaiWwA5vdd82w==}
|
||||
engines: {node: '>= 6'}
|
||||
|
||||
fraction.js@5.3.4:
|
||||
resolution: {integrity: sha512-1X1NTtiJphryn/uLQz3whtY6jK3fTqoE3ohKs0tT+Ujr1W59oopxmoEh7Lu5p6vBaPbgoM0bzveAW4Qi5RyWDQ==}
|
||||
|
||||
@@ -1587,13 +1655,40 @@ packages:
|
||||
react-dom:
|
||||
optional: true
|
||||
|
||||
function-bind@1.1.2:
|
||||
resolution: {integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==}
|
||||
|
||||
get-intrinsic@1.3.0:
|
||||
resolution: {integrity: sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==}
|
||||
engines: {node: '>= 0.4'}
|
||||
|
||||
get-nonce@1.0.1:
|
||||
resolution: {integrity: sha512-FJhYRoDaiatfEkUK8HKlicmu/3SGFD51q3itKDGoSTysQJBnfOcxU5GxnhE1E6soB76MbT0MBtnKJuXyAx+96Q==}
|
||||
engines: {node: '>=6'}
|
||||
|
||||
get-proto@1.0.1:
|
||||
resolution: {integrity: sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==}
|
||||
engines: {node: '>= 0.4'}
|
||||
|
||||
gopd@1.2.0:
|
||||
resolution: {integrity: sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==}
|
||||
engines: {node: '>= 0.4'}
|
||||
|
||||
graceful-fs@4.2.11:
|
||||
resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==}
|
||||
|
||||
has-symbols@1.1.0:
|
||||
resolution: {integrity: sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==}
|
||||
engines: {node: '>= 0.4'}
|
||||
|
||||
has-tostringtag@1.0.2:
|
||||
resolution: {integrity: sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==}
|
||||
engines: {node: '>= 0.4'}
|
||||
|
||||
hasown@2.0.2:
|
||||
resolution: {integrity: sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==}
|
||||
engines: {node: '>= 0.4'}
|
||||
|
||||
input-otp@1.4.1:
|
||||
resolution: {integrity: sha512-+yvpmKYKHi9jIGngxagY9oWiiblPB7+nEO75F2l2o4vs+6vpPZZmUl4tBNYuTCvQjhvEIbdNeJu70bhfYP2nbw==}
|
||||
peerDependencies:
|
||||
@@ -1707,6 +1802,18 @@ packages:
|
||||
magic-string@0.30.21:
|
||||
resolution: {integrity: sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==}
|
||||
|
||||
math-intrinsics@1.1.0:
|
||||
resolution: {integrity: sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==}
|
||||
engines: {node: '>= 0.4'}
|
||||
|
||||
mime-db@1.52.0:
|
||||
resolution: {integrity: sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==}
|
||||
engines: {node: '>= 0.6'}
|
||||
|
||||
mime-types@2.1.35:
|
||||
resolution: {integrity: sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==}
|
||||
engines: {node: '>= 0.6'}
|
||||
|
||||
motion-dom@12.29.2:
|
||||
resolution: {integrity: sha512-/k+NuycVV8pykxyiTCoFzIVLA95Nb1BFIVvfSu9L50/6K6qNeAYtkxXILy/LRutt7AzaYDc2myj0wkCVVYAPPA==}
|
||||
|
||||
@@ -1796,6 +1903,9 @@ packages:
|
||||
prop-types@15.8.1:
|
||||
resolution: {integrity: sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==}
|
||||
|
||||
proxy-from-env@1.1.0:
|
||||
resolution: {integrity: sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==}
|
||||
|
||||
react-day-picker@9.8.0:
|
||||
resolution: {integrity: sha512-E0yhhg7R+pdgbl/2toTb0xBhsEAtmAx1l7qjIWYfcxOy8w4rTSVfbtBoSzVVhPwKP/5E9iL38LivzoE3AQDhCQ==}
|
||||
engines: {node: '>=18'}
|
||||
@@ -1867,6 +1977,12 @@ packages:
|
||||
'@types/react':
|
||||
optional: true
|
||||
|
||||
react-toastify@11.0.5:
|
||||
resolution: {integrity: sha512-EpqHBGvnSTtHYhCPLxML05NLY2ZX0JURbAdNYa6BUkk+amz4wbKBQvoKQAB0ardvSarUBuY4Q4s1sluAzZwkmA==}
|
||||
peerDependencies:
|
||||
react: ^18 || ^19
|
||||
react-dom: ^18 || ^19
|
||||
|
||||
react-transition-group@4.4.5:
|
||||
resolution: {integrity: sha512-pZcd1MCJoiKiBR2NRxeCRg13uCXbydPnmB4EOeRrY7480qNWO8IIgQG6zlDkm6uRMsURXPuKq0GWtiM59a5Q6g==}
|
||||
peerDependencies:
|
||||
@@ -3121,6 +3237,13 @@ snapshots:
|
||||
postcss: 8.5.6
|
||||
tailwindcss: 4.1.18
|
||||
|
||||
'@tanstack/query-core@5.90.20': {}
|
||||
|
||||
'@tanstack/react-query@5.90.20(react@19.2.0)':
|
||||
dependencies:
|
||||
'@tanstack/query-core': 5.90.20
|
||||
react: 19.2.0
|
||||
|
||||
'@types/d3-array@3.2.2': {}
|
||||
|
||||
'@types/d3-color@3.1.3': {}
|
||||
@@ -3170,6 +3293,8 @@ snapshots:
|
||||
dependencies:
|
||||
tslib: 2.8.1
|
||||
|
||||
asynckit@0.4.0: {}
|
||||
|
||||
autoprefixer@10.4.23(postcss@8.5.6):
|
||||
dependencies:
|
||||
browserslist: 4.28.1
|
||||
@@ -3179,6 +3304,14 @@ snapshots:
|
||||
postcss: 8.5.6
|
||||
postcss-value-parser: 4.2.0
|
||||
|
||||
axios@1.13.4:
|
||||
dependencies:
|
||||
follow-redirects: 1.15.11
|
||||
form-data: 4.0.5
|
||||
proxy-from-env: 1.1.0
|
||||
transitivePeerDependencies:
|
||||
- debug
|
||||
|
||||
baseline-browser-mapping@2.9.17: {}
|
||||
|
||||
browserslist@4.28.1:
|
||||
@@ -3189,6 +3322,11 @@ snapshots:
|
||||
node-releases: 2.0.27
|
||||
update-browserslist-db: 1.2.3(browserslist@4.28.1)
|
||||
|
||||
call-bind-apply-helpers@1.0.2:
|
||||
dependencies:
|
||||
es-errors: 1.3.0
|
||||
function-bind: 1.1.2
|
||||
|
||||
caniuse-lite@1.0.30001765: {}
|
||||
|
||||
class-variance-authority@0.7.1:
|
||||
@@ -3211,6 +3349,10 @@ snapshots:
|
||||
- '@types/react'
|
||||
- '@types/react-dom'
|
||||
|
||||
combined-stream@1.0.8:
|
||||
dependencies:
|
||||
delayed-stream: 1.0.0
|
||||
|
||||
csstype@3.2.3: {}
|
||||
|
||||
d3-array@3.2.4:
|
||||
@@ -3259,6 +3401,8 @@ snapshots:
|
||||
|
||||
decimal.js@10.6.0: {}
|
||||
|
||||
delayed-stream@1.0.0: {}
|
||||
|
||||
detect-libc@2.1.2: {}
|
||||
|
||||
detect-node-es@1.1.0: {}
|
||||
@@ -3268,6 +3412,12 @@ snapshots:
|
||||
'@babel/runtime': 7.28.6
|
||||
csstype: 3.2.3
|
||||
|
||||
dunder-proto@1.0.1:
|
||||
dependencies:
|
||||
call-bind-apply-helpers: 1.0.2
|
||||
es-errors: 1.3.0
|
||||
gopd: 1.2.0
|
||||
|
||||
electron-to-chromium@1.5.267: {}
|
||||
|
||||
embla-carousel-react@8.5.1(react@19.2.0):
|
||||
@@ -3287,12 +3437,37 @@ snapshots:
|
||||
graceful-fs: 4.2.11
|
||||
tapable: 2.3.0
|
||||
|
||||
es-define-property@1.0.1: {}
|
||||
|
||||
es-errors@1.3.0: {}
|
||||
|
||||
es-object-atoms@1.1.1:
|
||||
dependencies:
|
||||
es-errors: 1.3.0
|
||||
|
||||
es-set-tostringtag@2.1.0:
|
||||
dependencies:
|
||||
es-errors: 1.3.0
|
||||
get-intrinsic: 1.3.0
|
||||
has-tostringtag: 1.0.2
|
||||
hasown: 2.0.2
|
||||
|
||||
escalade@3.2.0: {}
|
||||
|
||||
eventemitter3@4.0.7: {}
|
||||
|
||||
fast-equals@5.4.0: {}
|
||||
|
||||
follow-redirects@1.15.11: {}
|
||||
|
||||
form-data@4.0.5:
|
||||
dependencies:
|
||||
asynckit: 0.4.0
|
||||
combined-stream: 1.0.8
|
||||
es-set-tostringtag: 2.1.0
|
||||
hasown: 2.0.2
|
||||
mime-types: 2.1.35
|
||||
|
||||
fraction.js@5.3.4: {}
|
||||
|
||||
framer-motion@12.29.2(react-dom@19.2.0(react@19.2.0))(react@19.2.0):
|
||||
@@ -3304,10 +3479,42 @@ snapshots:
|
||||
react: 19.2.0
|
||||
react-dom: 19.2.0(react@19.2.0)
|
||||
|
||||
function-bind@1.1.2: {}
|
||||
|
||||
get-intrinsic@1.3.0:
|
||||
dependencies:
|
||||
call-bind-apply-helpers: 1.0.2
|
||||
es-define-property: 1.0.1
|
||||
es-errors: 1.3.0
|
||||
es-object-atoms: 1.1.1
|
||||
function-bind: 1.1.2
|
||||
get-proto: 1.0.1
|
||||
gopd: 1.2.0
|
||||
has-symbols: 1.1.0
|
||||
hasown: 2.0.2
|
||||
math-intrinsics: 1.1.0
|
||||
|
||||
get-nonce@1.0.1: {}
|
||||
|
||||
get-proto@1.0.1:
|
||||
dependencies:
|
||||
dunder-proto: 1.0.1
|
||||
es-object-atoms: 1.1.1
|
||||
|
||||
gopd@1.2.0: {}
|
||||
|
||||
graceful-fs@4.2.11: {}
|
||||
|
||||
has-symbols@1.1.0: {}
|
||||
|
||||
has-tostringtag@1.0.2:
|
||||
dependencies:
|
||||
has-symbols: 1.1.0
|
||||
|
||||
hasown@2.0.2:
|
||||
dependencies:
|
||||
function-bind: 1.1.2
|
||||
|
||||
input-otp@1.4.1(react-dom@19.2.0(react@19.2.0))(react@19.2.0):
|
||||
dependencies:
|
||||
react: 19.2.0
|
||||
@@ -3395,6 +3602,14 @@ snapshots:
|
||||
dependencies:
|
||||
'@jridgewell/sourcemap-codec': 1.5.5
|
||||
|
||||
math-intrinsics@1.1.0: {}
|
||||
|
||||
mime-db@1.52.0: {}
|
||||
|
||||
mime-types@2.1.35:
|
||||
dependencies:
|
||||
mime-db: 1.52.0
|
||||
|
||||
motion-dom@12.29.2:
|
||||
dependencies:
|
||||
motion-utils: 12.29.2
|
||||
@@ -3483,6 +3698,8 @@ snapshots:
|
||||
object-assign: 4.1.1
|
||||
react-is: 16.13.1
|
||||
|
||||
proxy-from-env@1.1.0: {}
|
||||
|
||||
react-day-picker@9.8.0(react@19.2.0):
|
||||
dependencies:
|
||||
'@date-fns/tz': 1.2.0
|
||||
@@ -3548,6 +3765,12 @@ snapshots:
|
||||
optionalDependencies:
|
||||
'@types/react': 19.2.9
|
||||
|
||||
react-toastify@11.0.5(react-dom@19.2.0(react@19.2.0))(react@19.2.0):
|
||||
dependencies:
|
||||
clsx: 2.1.1
|
||||
react: 19.2.0
|
||||
react-dom: 19.2.0(react@19.2.0)
|
||||
|
||||
react-transition-group@4.4.5(react-dom@19.2.0(react@19.2.0))(react@19.2.0):
|
||||
dependencies:
|
||||
'@babel/runtime': 7.28.6
|
||||
|
||||
35
request/api.ts
Normal file
35
request/api.ts
Normal file
@@ -0,0 +1,35 @@
|
||||
import axios, { InternalAxiosRequestConfig } from "axios";
|
||||
import { getRouteLang } from "./getLang";
|
||||
|
||||
const baseUrl = process.env.NEXT_PUBLIC_BASE_URL;
|
||||
|
||||
const httpClient = axios.create({
|
||||
baseURL: baseUrl,
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
}
|
||||
});
|
||||
|
||||
// Request interceptor
|
||||
httpClient.interceptors.request.use(
|
||||
(config: InternalAxiosRequestConfig) => {
|
||||
const language = getRouteLang();
|
||||
config.headers["Accept-Language"] = language;
|
||||
return config;
|
||||
},
|
||||
(error) => {
|
||||
return Promise.reject(error);
|
||||
}
|
||||
);
|
||||
|
||||
// Response interceptor (xatoliklarni boshqarish uchun)
|
||||
httpClient.interceptors.response.use(
|
||||
(response) => response,
|
||||
(error) => {
|
||||
// Xatolikni formatlash
|
||||
const message = error.response?.data?.message || error.message;
|
||||
return Promise.reject(message);
|
||||
}
|
||||
);
|
||||
|
||||
export default httpClient;
|
||||
18
request/getLang.ts
Normal file
18
request/getLang.ts
Normal file
@@ -0,0 +1,18 @@
|
||||
// utils/getRouteLang.ts
|
||||
import { locales, defaultLocale } from "@/i18n/config";
|
||||
|
||||
export function getRouteLang(): string {
|
||||
if (typeof window === "undefined") return defaultLocale;
|
||||
|
||||
// 1)Fall back to the first non-empty pathname segment
|
||||
const rawSegments = window.location.pathname.split("/"); // e.g. ['', 'uz', 'path', ...]
|
||||
const segments = rawSegments.filter(Boolean); // removes empty strings
|
||||
if (segments.length > 0) {
|
||||
const candidate = segments[0]; // first segment after root
|
||||
if (locales.includes(candidate as (typeof locales)[number]))
|
||||
return candidate;
|
||||
}
|
||||
|
||||
// 2) final fallback
|
||||
return defaultLocale;
|
||||
}
|
||||
23
request/links.ts
Normal file
23
request/links.ts
Normal file
@@ -0,0 +1,23 @@
|
||||
export const endPoints = {
|
||||
category: {
|
||||
all: "category/",
|
||||
},
|
||||
subCategory: {
|
||||
byId: (id: number) => `subCategory/?category=${id}`,
|
||||
},
|
||||
product: {
|
||||
byCategory: (categoryId: number) => `product/?category=${categoryId}`,
|
||||
bySubCategory: (subCategoryId: number) =>
|
||||
`product/?subCategory=${subCategoryId}`,
|
||||
detail: (id: number) => `product/${id}/`,
|
||||
},
|
||||
faq: "faq/",
|
||||
gallery: "gallery/",
|
||||
contact: "contact/",
|
||||
statistics: "statistics/",
|
||||
post: {
|
||||
sendNumber: "callBack/",
|
||||
productContact: "customer/",
|
||||
contact: "question/",
|
||||
},
|
||||
};
|
||||
Reference in New Issue
Block a user