From ebc08eff8f54a882a2809269c4e5922d4a8bdabf Mon Sep 17 00:00:00 2001 From: "nabijonovdavronbek619@gmail.com" Date: Fri, 17 Apr 2026 11:04:34 +0500 Subject: [PATCH] detail page updated , add subcategory zustand --- app/[lang]/[carType]/[carDeatil]/page.tsx | 166 +++++++++++++--------- components/cards/innerProductcard.tsx | 2 +- components/errorState.tsx | 43 ++++++ data/minimimValues.ts | 10 ++ public/locales/ru/common.json | 2 + public/locales/uz/common.json | 2 + vercel.json | 13 ++ 7 files changed, 173 insertions(+), 65 deletions(-) create mode 100644 components/errorState.tsx create mode 100644 data/minimimValues.ts create mode 100644 vercel.json diff --git a/app/[lang]/[carType]/[carDeatil]/page.tsx b/app/[lang]/[carType]/[carDeatil]/page.tsx index 9f7861b..d916f4d 100644 --- a/app/[lang]/[carType]/[carDeatil]/page.tsx +++ b/app/[lang]/[carType]/[carDeatil]/page.tsx @@ -1,98 +1,134 @@ "use client"; -import { useCarDetail } from "@/components/lib_components/carDetailProvider"; import Text from "@/components/lib_components/text"; import Image from "next/image"; import React, { useEffect, useState } from "react"; import CarRentalModal from "@/components/lib_components/carRentalModal"; -import { useTranslation } from "react-i18next"; -import { useCarType } from "@/store/carType"; -import { usePathname } from "next/navigation"; +import { usePathname, useParams } from "next/navigation"; import { logoImg } from "@/assets"; +import { useSubCategory } from "@/store/subCategory"; +import { minimumValues } from "@/data/minimimValues"; +import { LoadingSkeleton } from "@/components/loadingProduct"; +import { EmptyState } from "@/components/emptyState"; +import { ErrorState } from "@/components/errorState"; const baseUrl = "https://api.spes-texnika.uz/api/v1/products/"; +type Lang = "uz" | "ru" | "en"; +const validLangs: Lang[] = ["uz", "ru", "en"]; + +function checkCategory(categoryName: string | undefined, lang: string) { + if (!categoryName || !validLangs.includes(lang as Lang)) { + return { isMinimum: false, text: "" }; + } + const validLang = lang as Lang; + const matched = minimumValues[validLang].find( + (item: string) => item === categoryName, + ); + if (matched) + return { isMinimum: true, text: minimumValues.values[validLang] }; + return { isMinimum: false, text: "" }; +} + export default function CarDetailPage() { const [modalOpen, setModalOpen] = useState(false); - const { t } = useTranslation(); - // tools of request - const initialCar = useCarType((state) => state.initialCar); + const initialSubCategory = useSubCategory( + (state) => state.initialSubCategory, + ); const [loading, setLoading] = useState(true); const [error, setError] = useState(null); const pathname = usePathname(); + const params = useParams(); const lang = pathname.split("/")[1]; + const carId = params.carDeatil as string; const [cars, setCars] = useState([]); - console.log("car type id: ", initialCar.id); - console.log("request URL: ", `${baseUrl}${initialCar.id}/`); - - useEffect(() => { - // Agar ID bo'lmasa, fetchni ishga tushirma - if (!initialCar.id) { + const fetchProducts = async () => { + if (!carId) { setLoading(false); return; } - const fetchProducts = async () => { - try { - setLoading(true); - setError(null); + try { + setLoading(true); + setError(null); - const response = await fetch(`${baseUrl}${initialCar.id}/`, { - headers: { - "Accept-Language": lang, - }, - }); + const response = await fetch(`${baseUrl}${carId}/`, { + headers: { + "Accept-Language": lang, + }, + }); - if (!response.ok) { - throw new Error("Server xatosi"); - } - - const result = await response.json(); - console.log("backend full response: ", result); - console.log("backend Data: ", result?.data); - - // Data array ekanligini tekshirish - if (result?.data) { - if (Array.isArray(result.data)) { - setCars(result.data); - } else { - // Agar object bo'lsa, uni array ichiga o'rab qo'yamiz - setCars([result.data]); - } - } else { - setCars([]); - } - } catch (error) { - console.log("Xatolik: ", error); - setError(error instanceof Error ? error.message : "Noma'lum xatolik"); - setCars([]); - } finally { - setLoading(false); + if (!response.ok) { + throw new Error("Server xatosi"); } - }; + const result = await response.json(); + console.log("backend full response: ", result); + console.log("backend Data: ", result?.data); + + if (result?.data) { + if (Array.isArray(result.data)) { + setCars(result.data); + } else { + setCars([result.data]); + } + } else { + setCars([]); + } + } catch (err) { + console.log("Xatolik: ", err); + setError(err instanceof Error ? err.message : "Noma'lum xatolik"); + setCars([]); + } finally { + setLoading(false); + } + }; + + useEffect(() => { fetchProducts(); - }, [lang]); // initialCar.id ham dependency ga qo'shildi + }, [carId, lang]); - const firstData = cars ? cars[0] : undefined; + if (loading) { + return ( +
+ +
+ ); + } + + if (error) { + return ( +
+ +
+ ); + } + + const firstData = cars[0]; if (!firstData) { - return <>Maluot topilmadi; + return ( +
+ +
+ ); } + const { isMinimum, text } = checkCategory(initialSubCategory.name, lang); + return (
- {/* 1️⃣ Mashina nomi */} + {/* Mashina nomi */}
- {/* 2️⃣ Rasmi + asosiy narx ma’lumotlari */} + {/* Rasmi + asosiy narx ma'lumotlari */}
{/* Mashina rasmi */}
@@ -105,22 +141,24 @@ export default function CarDetailPage() { />
- {/* Asosiy ma’lumotlar */} + {/* Asosiy ma'lumotlar */}
- + {isMinimum ?

{text}

: } : {firstData.price?.toLocaleString("uz-UZ")}
-
- - - {firstData.minimal_order} - - -
+ {!isMinimum && ( +
+ + + {firstData.minimal_order} + + +
+ )} {/* Izoh */}
@@ -141,7 +179,7 @@ export default function CarDetailPage() {
- {/* 3️⃣ Texnik xususiyatlar (faqat mavjudlari) */} + {/* Texnik xususiyatlar */}

Texnik xususiyatlari @@ -159,7 +197,7 @@ export default function CarDetailPage() {

- {/* 4️⃣ Ijara modal */} + {/* Ijara modal */} { setDetail(data); setInitialCar(carInfo); diff --git a/components/errorState.tsx b/components/errorState.tsx new file mode 100644 index 0000000..1c167f7 --- /dev/null +++ b/components/errorState.tsx @@ -0,0 +1,43 @@ +import Text from "./lib_components/text"; + +interface ErrorStateProps { + message?: string; + onRetry?: () => void; +} + +export const ErrorState = ({ message, onRetry }: ErrorStateProps) => { + return ( +
+
+ + + +
+

+ +

+ {message && ( +

{message}

+ )} + {onRetry && ( + + )} +
+ ); +}; diff --git a/data/minimimValues.ts b/data/minimimValues.ts new file mode 100644 index 0000000..b8c6703 --- /dev/null +++ b/data/minimimValues.ts @@ -0,0 +1,10 @@ +export const minimumValues = { + uz: ["Shalanda va Traller", "Evakuator", "Samosval"], + ru: ["Шаланда и трейлер", "Эвакуатор", "Самосвал"], + en: ["Shalanda and Trailer", "Evakuator", "Samosval"], + values: { + uz: "1 ta reys uchun narx", + ru: "Цена за 1 рейс", + en: "Price per trip", + }, +}; diff --git a/public/locales/ru/common.json b/public/locales/ru/common.json index 4737a56..980427e 100644 --- a/public/locales/ru/common.json +++ b/public/locales/ru/common.json @@ -184,6 +184,8 @@ "manipulyator": "Манипулятор", "Avtovishka": "Автовышка", "Avtolift": "Автолифт", + "errorTitle": "Произошла ошибка", + "retry": "Повторить", "downloadError": "Ошибка при загрузке данных", "noData": "Специальная техника не найдена", "noDataDesc": "На данный момент товары отсутствуют. Пожалуйста, попробуйте позже." diff --git a/public/locales/uz/common.json b/public/locales/uz/common.json index 476d656..408485a 100644 --- a/public/locales/uz/common.json +++ b/public/locales/uz/common.json @@ -184,6 +184,8 @@ "manipulyator": "Manipulyator", "Avtovishka": "Avtovishka", "Avtolift": "Avtolift", + "errorTitle": "Xatolik yuz berdi", + "retry": "Qayta urinish", "downloadError": "Ma'lumotlarni yuklashda xatolik", "noData": "Mahsus texnikalar topilmadi", "noDataDesc": "Hozircha mahsulotlar mavjud emas. Iltimos, keyinroq qayta urinib ko'ring." diff --git a/vercel.json b/vercel.json new file mode 100644 index 0000000..90f6e27 --- /dev/null +++ b/vercel.json @@ -0,0 +1,13 @@ +{ + "framework": "nextjs", + "headers": [ + { + "source": "/(.*)", + "headers": [ + { "key": "X-Content-Type-Options", "value": "nosniff" }, + { "key": "X-Frame-Options", "value": "DENY" }, + { "key": "X-XSS-Protection", "value": "1; mode=block" } + ] + } + ] +}