diff --git a/next.config.ts b/next.config.ts
index 347c61b..06d2ab2 100644
--- a/next.config.ts
+++ b/next.config.ts
@@ -7,6 +7,9 @@ const nextConfig: NextConfig = {
// eslint: {
// ignoreDuringBuilds: true,
// },
+ images: {
+ remotePatterns: [{ protocol: 'http', hostname: '**' }],
+ },
};
const withNextIntl = createNextIntlPlugin({
requestConfig: './src/shared/config/i18n/request.ts',
diff --git a/package-lock.json b/package-lock.json
index e996102..84fb777 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -12,6 +12,8 @@
"@dnd-kit/modifiers": "^9.0.0",
"@dnd-kit/sortable": "^10.0.0",
"@dnd-kit/utilities": "^3.2.2",
+ "@hookform/resolvers": "^5.2.2",
+ "@pbe/react-yandex-maps": "^1.2.5",
"@radix-ui/react-accordion": "^1.2.8",
"@radix-ui/react-aspect-ratio": "^1.1.8",
"@radix-ui/react-avatar": "^1.1.11",
@@ -44,12 +46,13 @@
"next-themes": "^0.4.6",
"react": "^19.1.1",
"react-dom": "^19.1.1",
+ "react-hook-form": "^7.68.0",
"recharts": "^2.15.3",
- "sonner": "^2.0.3",
+ "sonner": "^2.0.7",
"swiper": "^12.0.3",
"tailwind-merge": "^3.2.0",
"vaul": "^1.1.2",
- "zod": "^4.1.11",
+ "zod": "^4.2.1",
"zustand": "^5.0.9"
},
"devDependencies": {
@@ -434,6 +437,18 @@
"tslib": "2"
}
},
+ "node_modules/@hookform/resolvers": {
+ "version": "5.2.2",
+ "resolved": "https://registry.npmjs.org/@hookform/resolvers/-/resolvers-5.2.2.tgz",
+ "integrity": "sha512-A/IxlMLShx3KjV/HeTcTfaMxdwy690+L/ZADoeaTltLx+CVuzkeVIPuybK3jrRfw7YZnmdKsVVHAlEPIAEUNlA==",
+ "license": "MIT",
+ "dependencies": {
+ "@standard-schema/utils": "^0.3.0"
+ },
+ "peerDependencies": {
+ "react-hook-form": "^7.55.0"
+ }
+ },
"node_modules/@humanfs/core": {
"version": "0.19.1",
"resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.1.tgz",
@@ -1514,6 +1529,21 @@
"node": ">=0.10"
}
},
+ "node_modules/@pbe/react-yandex-maps": {
+ "version": "1.2.5",
+ "resolved": "https://registry.npmjs.org/@pbe/react-yandex-maps/-/react-yandex-maps-1.2.5.tgz",
+ "integrity": "sha512-cBojin5e1fPx9XVCAqHQJsCnHGMeBNsP0TrNfpWCrPFfxb30ye+JgcGr2mn767Gbr1d+RufBLRiUcX2kaiAwjQ==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/yandex-maps": "2.1.29"
+ },
+ "engines": {
+ "node": ">=16"
+ },
+ "peerDependencies": {
+ "react": "^16.x || ^17.x || ^18.x"
+ }
+ },
"node_modules/@pkgr/core": {
"version": "0.2.9",
"resolved": "https://registry.npmjs.org/@pkgr/core/-/core-0.2.9.tgz",
@@ -2923,6 +2953,12 @@
"integrity": "sha512-bXHSaW5jRTmke9Vd0h5P7BtWZG9Znqb8gSDxZnxaGSJnGwPLDPfS+3g0BKzeWqzgZPsIVZkM7m2tbo18cm5HBw==",
"license": "MIT"
},
+ "node_modules/@standard-schema/utils": {
+ "version": "0.3.0",
+ "resolved": "https://registry.npmjs.org/@standard-schema/utils/-/utils-0.3.0.tgz",
+ "integrity": "sha512-e7Mew686owMaPJVNNLs55PUvgz371nKgwsc4vxE49zsODpJEnxgxRo2y/OKrqueavXgZNMDVj3DdHFlaSAeU8g==",
+ "license": "MIT"
+ },
"node_modules/@swc/core": {
"version": "1.15.5",
"resolved": "https://registry.npmjs.org/@swc/core/-/core-1.15.5.tgz",
@@ -3626,6 +3662,12 @@
"@types/react": "^19.2.0"
}
},
+ "node_modules/@types/yandex-maps": {
+ "version": "2.1.29",
+ "resolved": "https://registry.npmjs.org/@types/yandex-maps/-/yandex-maps-2.1.29.tgz",
+ "integrity": "sha512-nuibRWj3RU/9KXlCzTrRtDE+n6V9l7NbT9JakicqZ5OXIdwyb6blvV2Uwn6lB58WYm3DSUDP2I2AWlnWMc8z2w==",
+ "license": "MIT"
+ },
"node_modules/@typescript-eslint/eslint-plugin": {
"version": "8.49.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.49.0.tgz",
@@ -8076,6 +8118,22 @@
"react": "^19.2.3"
}
},
+ "node_modules/react-hook-form": {
+ "version": "7.68.0",
+ "resolved": "https://registry.npmjs.org/react-hook-form/-/react-hook-form-7.68.0.tgz",
+ "integrity": "sha512-oNN3fjrZ/Xo40SWlHf1yCjlMK417JxoSJVUXQjGdvdRCU07NTFei1i1f8ApUAts+IVh14e4EdakeLEA+BEAs/Q==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=18.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/react-hook-form"
+ },
+ "peerDependencies": {
+ "react": "^16.8.0 || ^17 || ^18 || ^19"
+ }
+ },
"node_modules/react-is": {
"version": "16.13.1",
"resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz",
@@ -9582,9 +9640,9 @@
}
},
"node_modules/zod": {
- "version": "4.2.0",
- "resolved": "https://registry.npmjs.org/zod/-/zod-4.2.0.tgz",
- "integrity": "sha512-Bd5fw9wlIhtqCCxotZgdTOMwGm1a0u75wARVEY9HMs1X17trvA/lMi4+MGK5EUfYkXVTbX8UDiDKW4OgzHVUZw==",
+ "version": "4.2.1",
+ "resolved": "https://registry.npmjs.org/zod/-/zod-4.2.1.tgz",
+ "integrity": "sha512-0wZ1IRqGGhMP76gLqz8EyfBXKk0J2qo2+H3fi4mcUP/KtTocoX08nmIAHl1Z2kJIZbZee8KOpBCSNPRgauucjw==",
"license": "MIT",
"funding": {
"url": "https://github.com/sponsors/colinhacks"
diff --git a/package.json b/package.json
index 8f8af58..891c4b9 100644
--- a/package.json
+++ b/package.json
@@ -15,6 +15,8 @@
"@dnd-kit/modifiers": "^9.0.0",
"@dnd-kit/sortable": "^10.0.0",
"@dnd-kit/utilities": "^3.2.2",
+ "@hookform/resolvers": "^5.2.2",
+ "@pbe/react-yandex-maps": "^1.2.5",
"@radix-ui/react-accordion": "^1.2.8",
"@radix-ui/react-aspect-ratio": "^1.1.8",
"@radix-ui/react-avatar": "^1.1.11",
@@ -47,12 +49,13 @@
"next-themes": "^0.4.6",
"react": "^19.1.1",
"react-dom": "^19.1.1",
+ "react-hook-form": "^7.68.0",
"recharts": "^2.15.3",
- "sonner": "^2.0.3",
+ "sonner": "^2.0.7",
"swiper": "^12.0.3",
"tailwind-merge": "^3.2.0",
"vaul": "^1.1.2",
- "zod": "^4.1.11",
+ "zod": "^4.2.1",
"zustand": "^5.0.9"
},
"devDependencies": {
diff --git a/public/fine-dining-restaurant-plating.jpg b/public/fine-dining-restaurant-plating.jpg
new file mode 100644
index 0000000..2b3c3bd
Binary files /dev/null and b/public/fine-dining-restaurant-plating.jpg differ
diff --git a/public/fresh-ingredients-culinary-market.jpg b/public/fresh-ingredients-culinary-market.jpg
new file mode 100644
index 0000000..822a707
Binary files /dev/null and b/public/fresh-ingredients-culinary-market.jpg differ
diff --git a/public/generic-company-logo.png b/public/generic-company-logo.png
new file mode 100644
index 0000000..9d34545
Binary files /dev/null and b/public/generic-company-logo.png differ
diff --git a/public/gourmet-food-culinary-magazine-hero-image.jpg b/public/gourmet-food-culinary-magazine-hero-image.jpg
new file mode 100644
index 0000000..3b8d45e
Binary files /dev/null and b/public/gourmet-food-culinary-magazine-hero-image.jpg differ
diff --git a/public/professional-chef-cooking-gourmet-food.jpg b/public/professional-chef-cooking-gourmet-food.jpg
new file mode 100644
index 0000000..17a741f
Binary files /dev/null and b/public/professional-chef-cooking-gourmet-food.jpg differ
diff --git a/src/app/[locale]/about/page.tsx b/src/app/[locale]/about/page.tsx
new file mode 100644
index 0000000..1164e29
--- /dev/null
+++ b/src/app/[locale]/about/page.tsx
@@ -0,0 +1,15 @@
+import { AboutContent } from '@/features/about/ui/AboutContent';
+import { AboutHero } from '@/features/about/ui/AboutHero';
+import { PartnershipForm } from '@/features/about/ui/AboutPage';
+
+const page = () => {
+ return (
+
+ );
+};
+
+export default page;
diff --git a/src/app/[locale]/faq/page.tsx b/src/app/[locale]/faq/page.tsx
new file mode 100644
index 0000000..8aee4b4
--- /dev/null
+++ b/src/app/[locale]/faq/page.tsx
@@ -0,0 +1,11 @@
+import Faq from '@/features/faq/ui/Faq';
+
+const page = () => {
+ return (
+
+
+
+ );
+};
+
+export default page;
diff --git a/src/app/[locale]/layout.tsx b/src/app/[locale]/layout.tsx
index ba40a4b..0699b69 100644
--- a/src/app/[locale]/layout.tsx
+++ b/src/app/[locale]/layout.tsx
@@ -3,6 +3,7 @@ import { routing } from '@/shared/config/i18n/routing';
import QueryProvider from '@/shared/config/react-query/QueryProvider';
import { ThemeProvider } from '@/shared/config/theme-provider';
import { PRODUCT_INFO } from '@/shared/constants/data';
+import { Toaster } from '@/shared/ui/sonner';
import type { Metadata } from 'next';
import { hasLocale, Locale, NextIntlClientProvider } from 'next-intl';
import { setRequestLocale } from 'next-intl/server';
@@ -48,6 +49,7 @@ export default async function RootLayout({ children, params }: Props) {
>
{children}
+
diff --git a/src/app/[locale]/privacy-policy/page.tsx b/src/app/[locale]/privacy-policy/page.tsx
new file mode 100644
index 0000000..e966baf
--- /dev/null
+++ b/src/app/[locale]/privacy-policy/page.tsx
@@ -0,0 +1,11 @@
+import PrivacyPolicy from '@/features/privacy-policy/ui/PrivacyPlicy';
+
+const page = () => {
+ return (
+
+ );
+};
+
+export default page;
diff --git a/src/features/about/ui/AboutContent.tsx b/src/features/about/ui/AboutContent.tsx
new file mode 100644
index 0000000..8febd94
--- /dev/null
+++ b/src/features/about/ui/AboutContent.tsx
@@ -0,0 +1,110 @@
+import { Card } from '@/shared/ui/card';
+import Image from 'next/image';
+
+export function AboutContent() {
+ const features = [
+ {
+ number: '1',
+ title: 'Sifatli Kontent',
+ description:
+ "Jahon oshpazlik san'ati va zamonaviy gastronomiya tendentsiyalari haqida chuqur maqolalar va tahlillar",
+ },
+ {
+ number: '2',
+ title: 'Professional Jamoa',
+ description:
+ 'Tajribali kulinariya mutaxassislari va oshpazlar tomonidan tayyorlangan kontent',
+ },
+ {
+ number: '3',
+ title: 'Yangiliklar',
+ description:
+ "Gastronomiya sohasidagi so'nggi yangiliklar va eng yangi trendlar haqida xabarlar",
+ },
+ ];
+
+ const images = [
+ {
+ url: '/professional-chef-cooking-gourmet-food.jpg',
+ alt: 'Professional Oshpaz',
+ },
+ {
+ url: '/fine-dining-restaurant-plating.jpg',
+ alt: 'Fine Dining',
+ },
+ {
+ url: '/fresh-ingredients-culinary-market.jpg',
+ alt: 'Fresh Ingredients',
+ },
+ ];
+
+ return (
+
+
+ {/* Mission Section */}
+
+
+ Bizning maqsadimiz
+
+
+ {features.map((feature) => (
+
+
+ {feature.number}
+
+ {feature.title}
+
+ {feature.description}
+
+
+ ))}
+
+
+
+ {/* About Text */}
+
+
+ Innovatsiya, sifat va professionallik
+
+
+ {`Gastro Market – bu gastronomiya dunyosidagi eng so'nggi
+ yangiliklarni, retseptlarni va tendentsiyalarni taqdim etuvchi
+ onlayn platforma. Biz o'quvchilarimizga sifatli va qiziqarli kontent
+ taqdim etishga intilamiz.`}
+
+
+ {`Bizning jamoamiz tajribali kulinariya mutaxassislari, oshpazlar va
+ gastronomiya sohasidagi ekspertlardan iborat. Biz har bir maqolada
+ sifat va professionallikka e'tibor qaratamiz.`}
+
+
+
+ {/* Image Gallery */}
+
+
+ Bizning dunyo
+
+
+ {images.map((image, idx) => (
+
+
+
+ ))}
+
+
+
+
+ );
+}
diff --git a/src/features/about/ui/AboutHero.tsx b/src/features/about/ui/AboutHero.tsx
new file mode 100644
index 0000000..09333ef
--- /dev/null
+++ b/src/features/about/ui/AboutHero.tsx
@@ -0,0 +1,27 @@
+import Image from 'next/image';
+
+export function AboutHero() {
+ return (
+
+
+
+
+
+
+ Gastro Market
+
+
+ {
+ "Gastronomiya va kulinariya san'ati haqidagi yetakchi onlayn magazin"
+ }
+
+
+
+ );
+}
diff --git a/src/features/about/ui/AboutPage.tsx b/src/features/about/ui/AboutPage.tsx
new file mode 100644
index 0000000..b0ce1f9
--- /dev/null
+++ b/src/features/about/ui/AboutPage.tsx
@@ -0,0 +1,274 @@
+'use client';
+
+import formatPhone from '@/shared/lib/formatPhone';
+import { Button } from '@/shared/ui/button';
+import { Card } from '@/shared/ui/card';
+import {
+ Form,
+ FormControl,
+ FormDescription,
+ FormField,
+ FormItem,
+ FormLabel,
+ FormMessage,
+} from '@/shared/ui/form';
+import { Input } from '@/shared/ui/input';
+import { Label } from '@/shared/ui/label';
+import { zodResolver } from '@hookform/resolvers/zod';
+import { Upload } from 'lucide-react';
+import { useState } from 'react';
+import { useForm } from 'react-hook-form';
+import { toast } from 'sonner';
+import * as z from 'zod';
+
+const MAX_FILE_SIZE = 5 * 1024 * 1024; // 5MB
+const ACCEPTED_FILE_TYPES = [
+ 'application/pdf',
+ 'application/msword',
+ 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
+];
+
+const partnershipFormSchema = z.object({
+ companyName: z.string().min(2, {
+ message: "Kompaniya nomi kamida 2 ta belgidan iborat bo'lishi kerak",
+ }),
+ website: z
+ .string()
+ .url({ message: "To'g'ri website manzilini kiriting" })
+ .optional()
+ .or(z.literal('')),
+ contactPerson: z
+ .string()
+ .min(2, { message: "Ism kamida 2 ta belgidan iborat bo'lishi kerak" }),
+ email: z
+ .string()
+ .email({ message: "To'g'ri email manzilini kiriting" })
+ .optional(),
+ phone: z
+ .string()
+ .min(9, { message: "To'g'ri telefon raqamini kiriting" })
+ .regex(/^[\d\s+\-$$$$]+$/, {
+ message: "Telefon raqami faqat raqamlardan iborat bo'lishi kerak",
+ }),
+ companyFile: z
+ .custom()
+ .refine((files) => files?.length === 1, 'File yuklash majburiy')
+ .refine(
+ (files) => files?.[0]?.size <= MAX_FILE_SIZE,
+ 'File hajmi 5MB dan oshmasligi kerak',
+ )
+ .refine(
+ (files) => ACCEPTED_FILE_TYPES.includes(files?.[0]?.type),
+ 'Faqat PDF yoki Word formatidagi fayllar qabul qilinadi',
+ ),
+});
+
+type PartnershipFormValues = z.infer;
+
+export function PartnershipForm() {
+ const [isSubmitting, setIsSubmitting] = useState(false);
+
+ const form = useForm({
+ resolver: zodResolver(partnershipFormSchema),
+ defaultValues: {
+ companyName: '',
+ website: '',
+ contactPerson: '',
+ email: '',
+ phone: '+998',
+ },
+ });
+
+ async function onSubmit(data: PartnershipFormValues) {
+ console.log(data);
+
+ setIsSubmitting(true);
+
+ try {
+ await new Promise((resolve) => setTimeout(resolve, 2000));
+
+ toast.success("So'rov yuborildi!", {
+ richColors: true,
+ position: 'top-center',
+ });
+
+ form.reset();
+ } catch {
+ toast.error('Xatolik yuz berdi', {
+ richColors: true,
+ position: 'top-center',
+ });
+ } finally {
+ setIsSubmitting(false);
+ }
+ }
+
+ return (
+
+
+
+
+ {`Hamkor bo'ling`}
+
+
+ {`Gastro Market bilan hamkorlik qilishni xohlaysizmi? Quyidagi formani
+ to'ldiring va biz siz bilan tez orada bog'lanamiz.`}
+
+
+
+
+
+
+
+
+
+ );
+}
diff --git a/src/features/cart/lib/form.ts b/src/features/cart/lib/form.ts
new file mode 100644
index 0000000..9ee5d18
--- /dev/null
+++ b/src/features/cart/lib/form.ts
@@ -0,0 +1,11 @@
+import { z } from 'zod';
+
+export const orderForm = z.object({
+ firstName: z.string().min(1, { message: 'Majburiy maydon' }),
+ lastName: z.string().min(1, { message: 'Majburiy maydon' }),
+ phone: z.string().min(12, { message: 'Xato raqam kiritildi' }),
+ long: z.string().min(1, { message: 'Majburiy maydon' }),
+ lat: z.string().min(1, { message: 'Majburiy maydon' }),
+ city: z.string().optional(),
+});
+// 998901234567
diff --git a/src/features/cart/ui/OrderPage.tsx b/src/features/cart/ui/OrderPage.tsx
index e0b1eca..71dbc8c 100644
--- a/src/features/cart/ui/OrderPage.tsx
+++ b/src/features/cart/ui/OrderPage.tsx
@@ -1,14 +1,24 @@
'use client';
import formatPhone from '@/shared/lib/formatPhone';
+import { Button } from '@/shared/ui/button';
+import { Form, FormControl, FormField, FormItem } from '@/shared/ui/form';
import { Input } from '@/shared/ui/input';
import { Label } from '@/shared/ui/label';
-import { Textarea } from '@/shared/ui/textarea';
+import { zodResolver } from '@hookform/resolvers/zod';
+import {
+ Map,
+ Placemark,
+ Polygon,
+ YMaps,
+ ZoomControl,
+} from '@pbe/react-yandex-maps';
import {
Building2,
CheckCircle2,
Clock,
CreditCard,
+ LocateFixed,
MapPin,
Package,
Truck,
@@ -16,17 +26,27 @@ import {
Wallet,
} from 'lucide-react';
import Image from 'next/image';
-import { useState } from 'react';
+import { useEffect, useState } from 'react';
+import { useForm } from 'react-hook-form';
+import z from 'zod';
+import { orderForm } from '../lib/form';
+
+interface CoordsData {
+ lat: number;
+ lon: number;
+ polygon: [number, number][][];
+}
const OrderPage = () => {
- const [formData, setFormData] = useState({
- fullName: '',
- phone: '+998',
- email: '',
- city: '',
- address: '',
- postalCode: '',
- notes: '',
+ const form = useForm>({
+ resolver: zodResolver(orderForm),
+ defaultValues: {
+ firstName: '',
+ lastName: '',
+ lat: '',
+ long: '',
+ phone: '',
+ },
});
const [paymentMethod, setPaymentMethod] = useState('cash');
@@ -34,7 +54,6 @@ const OrderPage = () => {
const [isSubmitting, setIsSubmitting] = useState(false);
const [orderSuccess, setOrderSuccess] = useState(false);
- // Cart items from previous page (in real app, this would come from context/store)
const cartItems = [
{
id: 5,
@@ -67,25 +86,109 @@ const OrderPage = () => {
deliveryMethod === 'express' ? 25000 : subtotal > 50000 ? 0 : 15000;
const total = subtotal + deliveryFee;
- const handleInputChange = (
- e: React.ChangeEvent,
- ) => {
- setFormData({
- ...formData,
- [e.target.name]: e.target.value,
- });
+ const [coords, setCoords] = useState({
+ latitude: 41.311081,
+ longitude: 69.240562,
+ zoom: 12,
+ });
+
+ const [polygonCoords, setPolygonCoords] = useState<
+ [number, number][][] | null
+ >(null);
+
+ const getCoords = async (name: string): Promise => {
+ const res = await fetch(
+ `https://nominatim.openstreetmap.org/search?q=${encodeURIComponent(name)}&format=json&polygon_geojson=1&limit=1`,
+ );
+ const data = await res.json();
+
+ if (data.length > 0 && data[0].geojson) {
+ const lat = parseFloat(data[0].lat);
+ const lon = parseFloat(data[0].lon);
+
+ let polygon: [number, number][][] = [];
+
+ if (data[0].geojson.type === 'Polygon') {
+ polygon = data[0].geojson.coordinates.map((ring: [number, number][]) =>
+ ring.map((coord: [number, number]) => [coord[1], coord[0]]),
+ );
+ } else if (data[0].geojson.type === 'MultiPolygon') {
+ polygon = data[0].geojson.coordinates.map(
+ (poly: [number, number][][]) =>
+ poly[0].map((coord: [number, number]) => [coord[1], coord[0]]),
+ );
+ }
+
+ return { lat, lon, polygon };
+ }
+
+ return null;
};
- const handleSubmit = (e: React.FormEvent) => {
- e.preventDefault();
- setIsSubmitting(true);
+ const handleMapClick = (
+ e: ymaps.IEvent,
+ ) => {
+ const [lat, lon] = e.get('coords');
+
+ setCoords({ latitude: lat, longitude: lon, zoom: 14 });
+
+ form.setValue('lat', lat.toString(), { shouldDirty: true });
+ form.setValue('long', lon.toString(), { shouldDirty: true });
+ };
+
+ const handleShowMyLocation = () => {
+ if (!navigator.geolocation) {
+ alert('Sizning brauzeringiz geolokatsiyani qo‘llab-quvvatlamaydi');
+ return;
+ }
+ navigator.geolocation.getCurrentPosition(
+ (position) => {
+ const lat = position.coords.latitude;
+ const lon = position.coords.longitude;
+ setCoords({ latitude: lat, longitude: lon, zoom: 14 });
+ form.setValue('lat', lat.toString());
+ form.setValue('long', lon.toString());
+ },
+ (error) => {
+ alert('Joylashuv aniqlanmadi: ' + error.message);
+ },
+ );
+ };
+
+ const cityValue = form.watch('city');
+
+ useEffect(() => {
+ if (!cityValue || cityValue.length < 3) return;
+
+ const timeout = setTimeout(async () => {
+ const result = await getCoords(cityValue);
+
+ if (!result) return;
+
+ setCoords({
+ latitude: result.lat,
+ longitude: result.lon,
+ zoom: 12,
+ });
+
+ setPolygonCoords(result.polygon);
+
+ form.setValue('lat', result.lat.toString(), { shouldDirty: true });
+ form.setValue('long', result.lon.toString(), { shouldDirty: true });
+ }, 700); // debounce
+
+ return () => clearTimeout(timeout);
+ }, [cityValue]);
+
+ function onSubmit(value: z.infer) {
+ setIsSubmitting(true);
+ console.log(value);
- // Simulate API call
setTimeout(() => {
setIsSubmitting(false);
setOrderSuccess(true);
}, 2000);
- };
+ }
if (orderSuccess) {
return (
@@ -133,302 +236,374 @@ const OrderPage = () => {
{"Ma'lumotlaringizni to'ldiring"}
-
-