vreadCrumb added
This commit is contained in:
@@ -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>
|
||||||
|
|||||||
@@ -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 */}
|
||||||
@@ -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>
|
||||||
);
|
);
|
||||||
@@ -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
195
components/breadCrumb.tsx
Normal 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>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -2,7 +2,6 @@
|
|||||||
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";
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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">
|
||||||
|
|||||||
@@ -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}>
|
||||||
|
|||||||
@@ -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"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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": "Техническое обслуживание"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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": "So‘rovingiz muvaffaqiyatli yuborildi!",
|
"success": "So‘rovingiz muvaffaqiyatli yuborildi!",
|
||||||
"error": "Xatolik yuz berdi. Iltimos, qayta urinib ko‘ring."
|
"error": "Xatolik yuz berdi. Iltimos, qayta urinib ko‘ring."
|
||||||
|
},
|
||||||
|
"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"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user