vreadCrumb added

This commit is contained in:
nabijonovdavronbek619@gmail.com
2026-02-10 16:41:40 +05:00
parent 071685b52c
commit 6fbe23109c
12 changed files with 270 additions and 19 deletions

View File

@@ -1,3 +1,4 @@
import { Breadcrumb } from "@/components/breadCrumb";
import Catalog from "@/components/pages/home/blog/catalog"; import Catalog from "@/components/pages/home/blog/catalog";
import { ProductBanner } from "@/components/pages/products"; import { ProductBanner } from "@/components/pages/products";
import { MainSubCategory } from "@/components/pages/subCategory"; import { MainSubCategory } from "@/components/pages/subCategory";
@@ -7,6 +8,7 @@ export default function Page() {
<div className="bg-[#1e1d1c] pb-30"> <div className="bg-[#1e1d1c] pb-30">
<ProductBanner /> <ProductBanner />
<div className="max-w-300 mx-auto w-full pt-20"> <div className="max-w-300 mx-auto w-full pt-20">
<Breadcrumb />
<Catalog /> <Catalog />
</div> </div>
</div> </div>

View File

@@ -9,6 +9,7 @@ import { AlertCircle } from "lucide-react";
import { LoadingSkeleton } from "@/components/pages/products/slug/loading"; import { LoadingSkeleton } from "@/components/pages/products/slug/loading";
import { EmptyState } from "@/components/pages/products/slug/empty"; import { EmptyState } from "@/components/pages/products/slug/empty";
import { useEffect } from "react"; import { useEffect } from "react";
import { Breadcrumb } from "@/components/breadCrumb";
// Types // Types
interface ProductImage { interface ProductImage {
@@ -41,7 +42,7 @@ export default function SlugPage() {
enabled: !!productZustand.id, enabled: !!productZustand.id,
}); });
useEffect(()=>console.log("product detail: ",product)) useEffect(() => console.log("product detail: ", product));
// Loading State // Loading State
if (isLoading) { if (isLoading) {
@@ -55,12 +56,16 @@ export default function SlugPage() {
// Extract images // Extract images
const productImages = product.images?.map((img) => img.image) || []; const productImages = product.images?.map((img) => img.image) || [];
const mainImage = product.images?.find((img) => img.is_main)?.image || productImages[0] || ""; const mainImage =
const features = product.features.map((item:any)=>item.name) product.images?.find((img) => img.is_main)?.image || productImages[0] || "";
const features = product.features.map((item: any) => item.name);
return ( return (
<div className="min-h-screen bg-[#1e1d1c] py-32 lg:py-40 px-4 md:px-8"> <div className="min-h-screen bg-[#1e1d1c] px-4 md:px-8">
<div className="max-w-7xl mx-auto"> <div className="max-w-7xl mx-auto">
<div className="pt-30 pb-10">
<Breadcrumb />
</div>
{/* Main Product Section */} {/* Main Product Section */}
<div className="grid grid-cols-1 lg:grid-cols-2 gap-8 lg:gap-12 mb-12"> <div className="grid grid-cols-1 lg:grid-cols-2 gap-8 lg:gap-12 mb-12">
{/* Left - Image Slider */} {/* Left - Image Slider */}

View File

@@ -1,11 +1,18 @@
"use client";
import { Breadcrumb } from "@/components/breadCrumb";
import { ProductBanner, Products } from "@/components/pages/products"; import { ProductBanner, Products } from "@/components/pages/products";
import FilterCatalog from "@/components/pages/products/filter/catalog/filterCatalog"; import FilterCatalog from "@/components/pages/products/filter/catalog/filterCatalog";
import { useSubCategory } from "@/store/useSubCategory";
export default function Page() { export default function Page() {
const subCategory = useSubCategory((state) => state.subCategory);
return ( return (
<div className="bg-[#1e1d1c] pb-30"> <div className="bg-[#1e1d1c] pb-30">
<ProductBanner /> <ProductBanner />
{/* <FilterCatalog /> */} {/* <FilterCatalog /> */}
<div className="max-w-300 w-full mx-auto pt-15">
<Breadcrumb customLabels={{ subCategory: subCategory.name}} />
</div>
<Products /> <Products />
</div> </div>
); );

View File

@@ -1,3 +1,4 @@
import { Breadcrumb } from "@/components/breadCrumb";
import { ProductBanner } from "@/components/pages/products"; import { ProductBanner } from "@/components/pages/products";
import { MainSubCategory } from "@/components/pages/subCategory"; import { MainSubCategory } from "@/components/pages/subCategory";
@@ -5,7 +6,10 @@ export default function Page() {
return ( return (
<div className="bg-[#1e1d1c] pb-30"> <div className="bg-[#1e1d1c] pb-30">
<ProductBanner /> <ProductBanner />
<div className="py-20"> <div className="pb-20">
<div className="max-w-350 mx-auto w-full py-10">
<Breadcrumb />
</div>
<MainSubCategory /> <MainSubCategory />
</div> </div>
</div> </div>

195
components/breadCrumb.tsx Normal file
View File

@@ -0,0 +1,195 @@
"use client";
import Link from "next/link";
import { usePathname } from "next/navigation";
import { ChevronRight, Home } from "lucide-react";
import { useTranslations } from "next-intl";
import { useCategory } from "@/store/useCategory";
import { useSubCategory } from "@/store/useSubCategory";
import { useProductPageInfo } from "@/store/useProduct";
interface BreadcrumbProps {
customLabels?: Record<string, string>;
className?: string;
}
export function Breadcrumb({
customLabels = {},
className = "",
}: BreadcrumbProps) {
const pathname = usePathname();
const t = useTranslations();
const category = useCategory((state) => state.category);
const subCategory = useSubCategory((state) => state.subCategory);
const product = useProductPageInfo((state) => state.product);
console.log("sub category: ", subCategory);
// Pathdan segments olish
const segments = pathname.split("/").filter((segment) => segment !== "");
// Agar locale bo'lsa, uni olib tashlash (uz, en, ru)
const locales = ["uz", "en", "ru"];
const filteredSegments = segments.filter(
(segment) => !locales.includes(segment),
);
// Agar faqat home page bo'lsa, breadcrumb ko'rsatmaslik
if (filteredSegments.length === 0) {
return null;
}
// Breadcrumb items yaratish
const breadcrumbItems: Array<{
label: string;
href: string;
isLast: boolean;
}> = [];
// Home qo'shish (har doim birinchi)
breadcrumbItems.push({
label: t("breadcrumb.home") || "Home",
href: "/",
isLast: false,
});
// Locale olish
const locale = segments.find((seg) => locales.includes(seg)) || "";
const localePrefix = locale ? `/${locale}` : "";
// Segmentlarni tahlil qilish
filteredSegments.forEach((segment, index) => {
const isLast = index === filteredSegments.length - 1;
if (segment === "catalog_page") {
// Catalog_page - asosiy kategoriyalar sahifasi
breadcrumbItems.push({
label: t("breadcrumb.catalog_page") || "Katalog",
href: `${localePrefix}/catalog_page`,
isLast: isLast,
});
} else if (segment === "subCategory") {
// SubCategory - kategoriya nomi ko'rsatiladi
if (category?.name) {
breadcrumbItems.push({
label: category.name,
href: `${localePrefix}/catalog_page/subCategory`,
isLast: isLast,
});
}
} else if (segment === "products") {
if (subCategory?.name) {
// Agar subCategory orqali kelgan bo'lsa
// 1. Kategoriya
if (category?.name) {
const categoryInBreadcrumb = breadcrumbItems.find(
(item) => item.label === category.name,
);
if (!categoryInBreadcrumb) {
breadcrumbItems.push({
label: category.name,
href: `${localePrefix}/catalog_page/subCategory`,
isLast: false,
});
}
}
// 2. SubKategoriya
breadcrumbItems.push({
label: subCategory.name,
href: `${localePrefix}/catalog_page/subCategory`,
isLast: false,
});
} else if (category?.name) {
// To'g'ridan-to'g'ri kategoriyadan products ga kelgan
breadcrumbItems.push({
label: category.name,
href: `${localePrefix}/catalog_page`,
isLast: false,
});
}
} else if (segment.startsWith("[") && segment.endsWith("]")) {
// Dynamic route (masalan, [slug])
// Custom label yoki default
const slugValue = segment.replace(/\[|\]/g, "");
const label = customLabels[slugValue] || slugValue;
breadcrumbItems.push({
label: label,
href: `${localePrefix}/${filteredSegments.slice(0, index + 1).join("/")}`,
isLast: isLast,
});
} else {
// Boshqa segmentlar
const label = getLabel(segment);
breadcrumbItems.push({
label: label,
href: `${localePrefix}/${filteredSegments.slice(0, index + 1).join("/")}`,
isLast: isLast,
});
}
});
// Default label translator
function getLabel(segment: string): string {
// Agar custom label berilgan bo'lsa
if (customLabels[segment]) {
return customLabels[segment];
}
// Agar translation mavjud bo'lsa
try {
if (segment === "special_product") {
return product.name;
}
return t(`breadcrumb.${segment}`);
} catch {
// Aks holda, segment nomini formatlash
return segment
.replace(/-/g, " ")
.replace(/_/g, " ")
.split(" ")
.map((word) => word.charAt(0).toUpperCase() + word.slice(1))
.join(" ");
}
}
return (
<nav aria-label="Breadcrumb" className={`py-4 ${className}`}>
<ol className="flex items-center flex-wrap gap-2 text-sm">
{breadcrumbItems.map((item, index) => (
<li
key={`${item.label}-${index}`}
className="flex items-center gap-2"
>
{index > 0 && (
<ChevronRight className="w-4 h-4 text-gray-400 dark:text-gray-600" />
)}
{index === 0 ? (
// Home link with icon
<Link
href={item.href}
className="flex items-center gap-1.5 text-gray-600 hover:text-red-600 dark:text-gray-400 dark:hover:text-red-500 transition-colors duration-200"
>
<Home className="w-4 h-4" />
<span className="hidden sm:inline">{item.label}</span>
</Link>
) : item.isLast ? (
// Last item (current page)
<span className="text-gray-900 dark:text-white font-medium line-clamp-1">
{item.label}
</span>
) : (
// Regular link
<Link
href={item.href}
className="text-gray-600 hover:text-red-600 dark:text-gray-400 dark:hover:text-red-500 transition-colors duration-200 line-clamp-1"
>
{item.label}
</Link>
)}
</li>
))}
</ol>
</nav>
);
}

View File

@@ -2,14 +2,13 @@
import { useQuery } from "@tanstack/react-query"; import { useQuery } from "@tanstack/react-query";
import httpClient from "@/request/api"; import httpClient from "@/request/api";
import { endPoints } from "@/request/links"; import { endPoints } from "@/request/links";
import { useEffect } from "react";
import CatalogCard from "../../products/catalog"; import CatalogCard from "../../products/catalog";
import CatalogCardSkeleton from "@/components/loadingSkleton"; import CatalogCardSkeleton from "@/components/loadingSkleton";
import EmptyData from "@/components/EmptyData"; import EmptyData from "@/components/EmptyData";
import { getRouteLang } from "@/request/getLang"; import { getRouteLang } from "@/request/getLang";
import { CategoryType } from "@/lib/types"; import { CategoryType } from "@/lib/types";
export default function Catalog() { export default function Catalog() {
const language = getRouteLang(); const language = getRouteLang();
const { data, isLoading } = useQuery({ const { data, isLoading } = useQuery({
queryKey: ["category", language], queryKey: ["category", language],

View File

@@ -84,8 +84,8 @@ export default function CatalogCard({
}; };
const navigateLink = have_sub_category const navigateLink = have_sub_category
? `/${locale}/subCategory?category=${id}` ? `/${locale}/catalog_page/subCategory?category=${id}`
: `/${locale}/products?category=${id}`; : `/${locale}/catalog_page/products?category=${id}`;
return ( return (
<Link <Link

View File

@@ -18,7 +18,7 @@ export default function ProductCard({
const locale = useLocale(); const locale = useLocale();
return ( return (
<Link href={`/${locale}/products/${slug}`} onClick={getProduct}> <Link href={`/${locale}/catalog_page/products/${slug}`} onClick={getProduct}>
<article className="group transition-all duration-300 hover:cursor-pointer max-sm:max-w-100 max-sm:mx-auto max-sm:w-full"> <article className="group transition-all duration-300 hover:cursor-pointer max-sm:max-w-100 max-sm:mx-auto max-sm:w-full">
{/* Image Container */} {/* Image Container */}
<div className="relative rounded-2xl h-45 sm:h-55 md:h-65 lg:w-[95%] w-[90%] mx-auto overflow-hidden"> <div className="relative rounded-2xl h-45 sm:h-55 md:h-65 lg:w-[95%] w-[90%] mx-auto overflow-hidden">

View File

@@ -32,7 +32,7 @@ export default function Card({
image, image,
category, category,
}); });
router.push(`/${locale}/products`); router.push(`/${locale}/catalog_page/products`);
}; };
return ( return (
<Link href="#" onClick={handleClick}> <Link href="#" onClick={handleClick}>

View File

@@ -222,9 +222,8 @@
"contact": "Contact", "contact": "Contact",
"help": "Help" "help": "Help"
}, },
"address":"Tashkent city, Yunusabad district, 3rd dead-end of Niyozbek Yoli street, house 39", "address": "Tashkent city, Yunusabad district, 3rd dead-end of Niyozbek Yoli street, house 39",
"create":"Created by {name}" "create": "Created by {name}"
}, },
"rasmlar": "Images", "rasmlar": "Images",
"fotogalereya": "Photo Gallery", "fotogalereya": "Photo Gallery",
@@ -232,8 +231,8 @@
"contactSubTitle": "Our staff will contact you", "contactSubTitle": "Our staff will contact you",
"enterPhone": "Enter your phone number", "enterPhone": "Enter your phone number",
"send": "Sent", "send": "Sent",
"error":"Error!", "error": "Error!",
"succes":"sent!", "succes": "sent!",
"priceModal": { "priceModal": {
"title": "Get Price", "title": "Get Price",
"product": { "product": {
@@ -257,5 +256,18 @@
}, },
"success": "Your request has been sent successfully!", "success": "Your request has been sent successfully!",
"error": "An error occurred. Please try again." "error": "An error occurred. Please try again."
},
"breadcrumb": {
"home": "Home",
"about": "About Us",
"services": "Services",
"products": "Products",
"contact": "Contact",
"blog": "Blog",
"fire-safety": "Fire Safety",
"fire-alarm": "Fire Alarm",
"fire-suppression": "Fire Suppression",
"installation": "Installation",
"maintenance": "Maintenance"
} }
} }

View File

@@ -222,8 +222,8 @@
"contact": "Контакты", "contact": "Контакты",
"help": "Помощь" "help": "Помощь"
}, },
"address":"г. Ташкент, Юнусабадский район, 3-й тупик улицы Ниязбек йўли, дом 39", "address": "г. Ташкент, Юнусабадский район, 3-й тупик улицы Ниязбек йўли, дом 39",
"create":"Разработано {name}" "create": "Разработано {name}"
}, },
"rasmlar": "Изображения", "rasmlar": "Изображения",
"fotogalereya": "Фотогалерея", "fotogalereya": "Фотогалерея",
@@ -256,5 +256,18 @@
}, },
"success": "Ваш запрос успешно отправлен!", "success": "Ваш запрос успешно отправлен!",
"error": "Произошла ошибка. Попробуйте снова." "error": "Произошла ошибка. Попробуйте снова."
},
"breadcrumb": {
"home": "Главная",
"about": "О нас",
"services": "Услуги",
"products": "Продукция",
"contact": "Контакты",
"blog": "Блог",
"fire-safety": "Пожарная безопасность",
"fire-alarm": "Пожарная сигнализация",
"fire-suppression": "Пожаротушение",
"installation": "Монтаж",
"maintenance": "Техническое обслуживание"
} }
} }

View File

@@ -223,7 +223,7 @@
"help": "Yordam" "help": "Yordam"
}, },
"address": "Toshkent shahri , Yunusabod tumani , Niyozbek yo'li 3 tor ko'chasi , 39 uy", "address": "Toshkent shahri , Yunusabod tumani , Niyozbek yo'li 3 tor ko'chasi , 39 uy",
"create":"{name} - Jamoasi tomonidan ishlab chiqilgan" "create": "{name} - Jamoasi tomonidan ishlab chiqilgan"
}, },
"rasmlar": "Rasmlar", "rasmlar": "Rasmlar",
"fotogalereya": "Fotogalereya", "fotogalereya": "Fotogalereya",
@@ -256,5 +256,19 @@
}, },
"success": "Sorovingiz muvaffaqiyatli yuborildi!", "success": "Sorovingiz muvaffaqiyatli yuborildi!",
"error": "Xatolik yuz berdi. Iltimos, qayta urinib koring." "error": "Xatolik yuz berdi. Iltimos, qayta urinib koring."
},
"breadcrumb": {
"home": "Bosh sahifa",
"about": "Biz haqimizda",
"services": "Xizmatlar",
"catalog_page": "Mahsulotlar",
"subCategory":"{subCategory}",
"contact": "Bog'lanish",
"blog": "Blog",
"fire-safety": "Yong'in xavfsizligi",
"fire-alarm": "Yong'in signalizatsiyasi",
"fire-suppression": "Yong'in o'chirish",
"installation": "O'rnatish",
"maintenance": "Texnik xizmat"
} }
} }