update
This commit is contained in:
59
src/components/common/error/index.tsx
Normal file
59
src/components/common/error/index.tsx
Normal file
@@ -0,0 +1,59 @@
|
||||
import Link from 'next/link';
|
||||
import { motion } from 'framer-motion';
|
||||
|
||||
export const ErrorLink = () => {
|
||||
return (
|
||||
<div className="flex min-h-screen items-center justify-center bg-gradient-to-br from-slate-50 via-white to-blue-100 px-4">
|
||||
<motion.div
|
||||
initial={{ opacity: 0, scale: 0.95 }}
|
||||
animate={{ opacity: 1, scale: 1 }}
|
||||
transition={{ duration: 0.4 }}
|
||||
className="w-full max-w-md rounded-3xl bg-white p-8 text-center shadow-xl"
|
||||
>
|
||||
{/* Icon lỗi link */}
|
||||
<motion.div
|
||||
animate={{ y: [0, -4, 0] }}
|
||||
transition={{ repeat: Infinity, duration: 1.8 }}
|
||||
className="mx-auto mb-6 flex h-20 w-20 items-center justify-center rounded-full bg-orange-100"
|
||||
>
|
||||
<svg
|
||||
className="h-10 w-10 text-orange-500"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
strokeWidth="2"
|
||||
viewBox="0 0 24 24"
|
||||
>
|
||||
<path
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
d="M13.828 10.172a4 4 0 00-5.656 5.656M7 7a7 7 0 019.9 9.9M12 12l.01.01"
|
||||
/>
|
||||
</svg>
|
||||
</motion.div>
|
||||
|
||||
<h1 className="text-2xl font-bold text-gray-800">Đường dẫn không hợp lệ</h1>
|
||||
|
||||
<p className="mt-3 text-sm text-gray-600">
|
||||
Bạn truy cập không tồn tại hoặc đường dẫn đã bị thay đổi.
|
||||
</p>
|
||||
|
||||
{/* CTA */}
|
||||
<div className="mt-8 flex flex-col gap-3">
|
||||
<Link
|
||||
href="/"
|
||||
className="rounded-xl bg-blue-600 px-6 py-3 text-sm font-semibold text-white transition hover:bg-blue-700"
|
||||
>
|
||||
← Về trang chủ
|
||||
</Link>
|
||||
|
||||
<Link
|
||||
href="/products"
|
||||
className="rounded-xl border border-gray-300 px-6 py-3 text-sm font-medium text-gray-700 transition hover:bg-gray-100"
|
||||
>
|
||||
Xem tất cả sản phẩm
|
||||
</Link>
|
||||
</div>
|
||||
</motion.div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
@@ -8,7 +8,7 @@ import ItemProduct from '@/components/common/ItemProduct';
|
||||
|
||||
import { InfoCategory } from '@/types';
|
||||
|
||||
import { menuData } from '@/components/layout/other/Header/menuData';
|
||||
import { menuData } from '@/components/other/Header/menuData';
|
||||
import { productData } from './productData';
|
||||
|
||||
const BoxListCategory: React.FC = () => {
|
||||
@@ -1,11 +1,11 @@
|
||||
import React from 'react';
|
||||
import SliderHome from './SliderHome';
|
||||
import BoxProductDeal from './BoxDeal';
|
||||
import BoxProductDeal from './Deal';
|
||||
import CategoryFeature from './CategoryFeature';
|
||||
import BoxListCategory from './BoxCategory';
|
||||
import BoxArticle from './BoxArticle';
|
||||
import BoxArticleVideo from './BoxArticleVideo';
|
||||
import BoxReviewCustomer from './BoxReviewCustomer';
|
||||
import BoxListCategory from './Category';
|
||||
import BoxArticle from './Article';
|
||||
import BoxArticleVideo from './ArticleVideo';
|
||||
import BoxReviewCustomer from './ReviewCustomer';
|
||||
|
||||
const Home = () => {
|
||||
return (
|
||||
@@ -13,7 +13,7 @@ const ItemCategoryChild: React.FC<BoxCategoryChildProps> = ({ item }) => {
|
||||
? item.big_image
|
||||
: item.thumnail
|
||||
? item.thumnail
|
||||
: '/static/assets/nguyencong_2023/images/favicon.png';
|
||||
: 'https://nguyencongpc.vn/static/assets/nguyencong_2023/images/favicon.png';
|
||||
|
||||
return (
|
||||
<li>
|
||||
@@ -3,7 +3,7 @@ import React from 'react';
|
||||
import Link from 'next/link';
|
||||
import type { CategoryData } from '@/types';
|
||||
import { productCategoryData } from '@/data/product/category';
|
||||
import { findCategoryBySlug } from '@/lib/category';
|
||||
import { findCategoryBySlug } from '@/lib/product/category';
|
||||
|
||||
// box
|
||||
import { Breadcrumb } from '@components/common/Breadcrumb';
|
||||
@@ -18,7 +18,7 @@ interface CategoryPageProps {
|
||||
}
|
||||
|
||||
const CategoryPage: React.FC<CategoryPageProps> = ({ slug }) => {
|
||||
// Ép kiểu dữ liệu từ index.ts về CategoryData[] nếu cần
|
||||
// Ép kiểu dữ liệu từ index.ts về CategoryData[]
|
||||
const categories = productCategoryData as unknown as CategoryData[];
|
||||
const currentCategory = findCategoryBySlug(slug, categories);
|
||||
|
||||
77
src/components/product/ProductDetail/ImageProduct/index.tsx
Normal file
77
src/components/product/ProductDetail/ImageProduct/index.tsx
Normal file
@@ -0,0 +1,77 @@
|
||||
import { useState, useEffect } from 'react';
|
||||
import { Swiper, SwiperSlide } from 'swiper/react';
|
||||
import { Autoplay, Navigation, Pagination, Thumbs } from 'swiper/modules';
|
||||
import Image from 'next/image';
|
||||
import Link from 'next/link';
|
||||
import { ProductImageGallery } from '@/types';
|
||||
import type { Swiper as SwiperType } from 'swiper';
|
||||
import '@fancyapps/ui/dist/fancybox/fancybox.css';
|
||||
import { Fancybox, type FancyboxOptions } from '@fancyapps/ui';
|
||||
|
||||
interface ImageProps {
|
||||
ItemImage: ProductImageGallery[];
|
||||
}
|
||||
|
||||
export const ImageProduct: React.FC<ImageProps> = ({ ItemImage }) => {
|
||||
const [thumbsSwiper, setThumbsSwiper] = useState<SwiperType | null>(null);
|
||||
|
||||
useEffect(() => {
|
||||
if (typeof window === 'undefined') return;
|
||||
|
||||
Fancybox.bind("[data-fancybox='gallery']", {
|
||||
dragToClose: true,
|
||||
Toolbar: {
|
||||
display: {
|
||||
left: [],
|
||||
middle: ['counter'],
|
||||
right: ['zoom', 'close'],
|
||||
},
|
||||
},
|
||||
Thumbs: { autoStart: false },
|
||||
} as any);
|
||||
|
||||
return () => {
|
||||
Fancybox.unbind("[data-fancybox='gallery']");
|
||||
Fancybox.close();
|
||||
};
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<div className="product-images-show">
|
||||
<div className="gallery-top product-info-image">
|
||||
<Swiper
|
||||
modules={[Autoplay, Navigation, Pagination, Thumbs]}
|
||||
spaceBetween={12}
|
||||
slidesPerView={1}
|
||||
loop={true}
|
||||
thumbs={{ swiper: thumbsSwiper }}
|
||||
>
|
||||
{ItemImage?.map((item, index) => (
|
||||
<SwiperSlide key={index}>
|
||||
<Link href={item.size.original} data-fancybox="gallery" className="bigImage">
|
||||
<Image src={item.size.original} alt={''} width="595" height="595" />
|
||||
</Link>
|
||||
</SwiperSlide>
|
||||
))}
|
||||
</Swiper>
|
||||
</div>
|
||||
<div className="gallery-thumbs product-images-slider mt-2">
|
||||
<Swiper
|
||||
modules={[Autoplay, Navigation, Pagination, Thumbs]}
|
||||
spaceBetween={12}
|
||||
slidesPerView={6}
|
||||
loop={true}
|
||||
onSwiper={setThumbsSwiper}
|
||||
>
|
||||
{ItemImage?.map((item, index) => (
|
||||
<SwiperSlide key={index}>
|
||||
<div className="smallImage">
|
||||
<Image src={item.size.original} alt={''} width="90" height="60" />
|
||||
</div>
|
||||
</SwiperSlide>
|
||||
))}
|
||||
</Swiper>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
51
src/components/product/ProductDetail/index.tsx
Normal file
51
src/components/product/ProductDetail/index.tsx
Normal file
@@ -0,0 +1,51 @@
|
||||
'use client';
|
||||
import React from 'react';
|
||||
import Link from 'next/link';
|
||||
import type { ProductDetailData } from '@/types';
|
||||
import { productDetailData } from '@/data/product/detail';
|
||||
import { findProductDetailBySlug } from '@/lib/product/productdetail';
|
||||
import { ErrorLink } from '@components/common/error';
|
||||
|
||||
import { Breadcrumb } from '@components/common/Breadcrumb';
|
||||
import { ImageProduct } from './ImageProduct';
|
||||
|
||||
interface ProductDetailPageProps {
|
||||
slug: string;
|
||||
}
|
||||
|
||||
const ProductDetailPage: React.FC<ProductDetailPageProps> = ({ slug }) => {
|
||||
const productDetails = productDetailData as unknown as ProductDetailData[];
|
||||
const Products = findProductDetailBySlug(slug, productDetails);
|
||||
|
||||
const breadcrumbItems = Products?.product_info?.productPath?.[0]?.path.map((item) => ({
|
||||
name: item.name,
|
||||
url: item.url,
|
||||
})) ?? [{ name: 'Trang chủ', url: '/' }];
|
||||
|
||||
// Trường hợp không tìm thấy chi tiết sản phẩm
|
||||
// Không tìm thấy chi tiết sản phẩm
|
||||
if (!Products) {
|
||||
return <ErrorLink />;
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className="container">
|
||||
<Breadcrumb items={breadcrumbItems} />
|
||||
</div>
|
||||
<section className="page-product-detail mt-2 bg-white">
|
||||
<div className="container">
|
||||
<div className="box-content-product-detail flex justify-between gap-5">
|
||||
<div className="box-left">
|
||||
{/* image product */}
|
||||
<ImageProduct ItemImage={Products.product_info.productImageGallery} />
|
||||
</div>
|
||||
<div className="box-right"></div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default ProductDetailPage;
|
||||
Reference in New Issue
Block a user