update
This commit is contained in:
3
.vscode/settings.json
vendored
Normal file
3
.vscode/settings.json
vendored
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
{
|
||||||
|
"prettier.useTabs": false
|
||||||
|
}
|
||||||
33
package-lock.json
generated
33
package-lock.json
generated
@@ -8,6 +8,7 @@
|
|||||||
"name": "nguyencongpc",
|
"name": "nguyencongpc",
|
||||||
"version": "0.1.0",
|
"version": "0.1.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"@tippyjs/react": "^4.2.6",
|
||||||
"next": "16.0.10",
|
"next": "16.0.10",
|
||||||
"postcss": "^8.5.6",
|
"postcss": "^8.5.6",
|
||||||
"react": "19.2.1",
|
"react": "19.2.1",
|
||||||
@@ -1234,6 +1235,16 @@
|
|||||||
"node": ">=12.4.0"
|
"node": ">=12.4.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@popperjs/core": {
|
||||||
|
"version": "2.11.8",
|
||||||
|
"resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.11.8.tgz",
|
||||||
|
"integrity": "sha512-P1st0aksCrn9sGZhp8GMYwBnQsbvAWsZAX44oXNNvLHGqAOcoVxmjZiohstwQ7SqKnbR47akdNi+uleWD8+g6A==",
|
||||||
|
"license": "MIT",
|
||||||
|
"funding": {
|
||||||
|
"type": "opencollective",
|
||||||
|
"url": "https://opencollective.com/popperjs"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@rtsao/scc": {
|
"node_modules/@rtsao/scc": {
|
||||||
"version": "1.1.0",
|
"version": "1.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/@rtsao/scc/-/scc-1.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/@rtsao/scc/-/scc-1.1.0.tgz",
|
||||||
@@ -1521,6 +1532,19 @@
|
|||||||
"tailwindcss": "4.1.18"
|
"tailwindcss": "4.1.18"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@tippyjs/react": {
|
||||||
|
"version": "4.2.6",
|
||||||
|
"resolved": "https://registry.npmjs.org/@tippyjs/react/-/react-4.2.6.tgz",
|
||||||
|
"integrity": "sha512-91RicDR+H7oDSyPycI13q3b7o4O60wa2oRbjlz2fyRLmHImc4vyDwuUP8NtZaN0VARJY5hybvDYrFzhY9+Lbyw==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"tippy.js": "^6.3.1"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"react": ">=16.8",
|
||||||
|
"react-dom": ">=16.8"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@tybys/wasm-util": {
|
"node_modules/@tybys/wasm-util": {
|
||||||
"version": "0.10.1",
|
"version": "0.10.1",
|
||||||
"resolved": "https://registry.npmjs.org/@tybys/wasm-util/-/wasm-util-0.10.1.tgz",
|
"resolved": "https://registry.npmjs.org/@tybys/wasm-util/-/wasm-util-0.10.1.tgz",
|
||||||
@@ -6253,6 +6277,15 @@
|
|||||||
"url": "https://github.com/sponsors/jonschlinkert"
|
"url": "https://github.com/sponsors/jonschlinkert"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/tippy.js": {
|
||||||
|
"version": "6.3.7",
|
||||||
|
"resolved": "https://registry.npmjs.org/tippy.js/-/tippy.js-6.3.7.tgz",
|
||||||
|
"integrity": "sha512-E1d3oP2emgJ9dRQZdf3Kkn0qJgI6ZLpyS5z6ZkY1DF3kaQaBsGZsndEpHwx+eC+tYM41HaSNvNtLx8tU57FzTQ==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@popperjs/core": "^2.9.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/to-regex-range": {
|
"node_modules/to-regex-range": {
|
||||||
"version": "5.0.1",
|
"version": "5.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz",
|
||||||
|
|||||||
@@ -9,6 +9,7 @@
|
|||||||
"lint": "eslint"
|
"lint": "eslint"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"@tippyjs/react": "^4.2.6",
|
||||||
"next": "16.0.10",
|
"next": "16.0.10",
|
||||||
"postcss": "^8.5.6",
|
"postcss": "^8.5.6",
|
||||||
"react": "19.2.1",
|
"react": "19.2.1",
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ import 'swiper/css/pagination';
|
|||||||
import '@styles/globals.css';
|
import '@styles/globals.css';
|
||||||
import Header from '@components/layout/Header';
|
import Header from '@components/layout/Header';
|
||||||
|
|
||||||
import PreLoader from '@components/Common/PreLoader';
|
import PreLoader from '@components/common/PreLoader';
|
||||||
|
|
||||||
export default function RootLayout({
|
export default function RootLayout({
|
||||||
children,
|
children,
|
||||||
|
|||||||
@@ -1,38 +1,46 @@
|
|||||||
|
|
||||||
|
|
||||||
const BoxShowroom: React.FC = () => {
|
const BoxShowroom: React.FC = () => {
|
||||||
return(
|
return (
|
||||||
<>
|
<>
|
||||||
<dialog id="boxShowroom" className="modal">
|
<dialog id="boxShowroom" className="modal">
|
||||||
<div className="modal-box">
|
<div className="modal-box">
|
||||||
<form method="dialog">
|
<form method="dialog">
|
||||||
<button className="btn btn-sm btn-circle btn-ghost absolute right-2 top-2">✕</button>
|
<button className="btn btn-sm btn-circle btn-ghost absolute top-2 right-2">✕</button>
|
||||||
</form>
|
</form>
|
||||||
<div className="popup-showrom-container d-block">
|
<div className="popup-showrom-container d-block">
|
||||||
<p className="group-title">HỆ THỐNG SHOWROOM</p>
|
<p className="group-title">HỆ THỐNG SHOWROOM</p>
|
||||||
<div className="flex flex-wrap justify-between">
|
<div className="flex flex-wrap justify-between">
|
||||||
|
<div className="item">
|
||||||
|
<p className="item-title">1. Hà Nội</p>
|
||||||
|
<p>17 Hà Kế Tấn, Phường Phương Liệt, Hà Nội.</p>
|
||||||
|
<p>
|
||||||
|
Giờ làm việc: <b>08:30 - 20:30</b>
|
||||||
|
</p>
|
||||||
|
<div
|
||||||
|
className="map-holder js-map-holder"
|
||||||
|
data-src="https://www.google.com/maps/embed?pb=!1m18!1m12!1m3!1d3132.222076725264!2d105.83522224518104!3d20.998217116862435!2m3!1f0!2f0!3f0!3m2!1i1024!2i768!4f13.1!3m3!1m2!1s0x3135ac7b37915991%3A0xe20876d091ded6bc!2zMTcgUC4gSMOgIEvhur8gVOG6pW4sIFBoxrDGoW5nIExp4buHdCwgVGhhbmggWHXDom4sIEjDoCBO4buZaSwgVmnhu4d0IE5hbQ!5e0!3m2!1svi!2s!4v1720509407173!5m2!1svi!2s"
|
||||||
|
></div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div className="item">
|
<div className="item">
|
||||||
<p className="item-title">1. Hà Nội</p>
|
<p className="item-title">2. Hồ Chí Minh</p>
|
||||||
<p>17 Hà Kế Tấn, Phường Phương Liệt, Hà Nội.</p>
|
<p>249 Lý Thường Kiệt, Phường Phú Thọ, TP. Hồ Chí Minh</p>
|
||||||
<p>Giờ làm việc: <b>08:30 - 20:30</b></p>
|
<p>
|
||||||
<div className="map-holder js-map-holder" data-src="https://www.google.com/maps/embed?pb=!1m18!1m12!1m3!1d3132.222076725264!2d105.83522224518104!3d20.998217116862435!2m3!1f0!2f0!3f0!3m2!1i1024!2i768!4f13.1!3m3!1m2!1s0x3135ac7b37915991%3A0xe20876d091ded6bc!2zMTcgUC4gSMOgIEvhur8gVOG6pW4sIFBoxrDGoW5nIExp4buHdCwgVGhhbmggWHXDom4sIEjDoCBO4buZaSwgVmnhu4d0IE5hbQ!5e0!3m2!1svi!2s!4v1720509407173!5m2!1svi!2s"></div>
|
Giờ làm việc: <b>08:30 - 20:30</b>
|
||||||
</div>
|
</p>
|
||||||
|
<div
|
||||||
|
className="map-holder js-map-holder"
|
||||||
|
data-src="https://www.google.com/maps/embed?pb=!1m14!1m8!1m3!1d15678.56730501209!2d106.66439700000001!3d10.762063!3m2!1i1024!2i768!4f13.1!3m3!1m2!1s0x0%3A0x85a7fc3a74bcd7fd!2zTcOheSBUw61uaCBOZ3V54buFbiBDw7RuZyAxNzYgVMOibiBQaMaw4bubYw!5e0!3m2!1svi!2sus!4v1658936898247!5m2!1svi!2sus"
|
||||||
|
></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<label className="modal-backdrop" htmlFor="my_modal_7">
|
||||||
|
Close
|
||||||
|
</label>
|
||||||
|
</dialog>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
<div className="item">
|
export default BoxShowroom;
|
||||||
<p className="item-title">2. Hồ Chí Minh</p>
|
|
||||||
<p>249 Lý Thường Kiệt, Phường Phú Thọ, TP. Hồ Chí Minh</p>
|
|
||||||
<p>Giờ làm việc: <b>08:30 - 20:30</b></p>
|
|
||||||
<div className="map-holder js-map-holder" data-src="https://www.google.com/maps/embed?pb=!1m14!1m8!1m3!1d15678.56730501209!2d106.66439700000001!3d10.762063!3m2!1i1024!2i768!4f13.1!3m3!1m2!1s0x0%3A0x85a7fc3a74bcd7fd!2zTcOheSBUw61uaCBOZ3V54buFbiBDw7RuZyAxNzYgVMOibiBQaMaw4bubYw!5e0!3m2!1svi!2sus!4v1658936898247!5m2!1svi!2sus"></div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<label className="modal-backdrop" htmlFor="my_modal_7">Close</label>
|
|
||||||
</dialog>
|
|
||||||
</>
|
|
||||||
)
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
export default BoxShowroom;
|
|
||||||
|
|||||||
@@ -703,7 +703,7 @@ export const menuData: MenuTypes = [
|
|||||||
"big_image": "https://nguyencongpc.vn\/media\/category\/cat_big_3638_1696996039.png",
|
"big_image": "https://nguyencongpc.vn\/media\/category\/cat_big_3638_1696996039.png",
|
||||||
"isParent": "0",
|
"isParent": "0",
|
||||||
"url": "\/rtx-4070",
|
"url": "\/rtx-4070",
|
||||||
"is_featured": "1",
|
"is_featured": "0",
|
||||||
"summary": "<p> <\/p>\r\n<div id=\"eJOY__extension_root\" class=\"eJOY__extension_root_class\" style=\"all: unset;\"> <\/div>",
|
"summary": "<p> <\/p>\r\n<div id=\"eJOY__extension_root\" class=\"eJOY__extension_root_class\" style=\"all: unset;\"> <\/div>",
|
||||||
"children": []
|
"children": []
|
||||||
},
|
},
|
||||||
|
|||||||
0
src/components/layout/home/BoxArticle/index.tsx
Normal file
0
src/components/layout/home/BoxArticle/index.tsx
Normal file
83
src/components/layout/home/BoxCategory/ItemProduct.tsx
Normal file
83
src/components/layout/home/BoxCategory/ItemProduct.tsx
Normal file
@@ -0,0 +1,83 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import Tippy from '@tippyjs/react';
|
||||||
|
import 'tippy.js/dist/tippy.css';
|
||||||
|
import { Product } from '@/types/TypeListProduct';
|
||||||
|
import Image from 'next/image';
|
||||||
|
import Link from 'next/link';
|
||||||
|
|
||||||
|
type ProductItemProps = {
|
||||||
|
item: Product;
|
||||||
|
};
|
||||||
|
|
||||||
|
const formatCurrency = (value: number | string) => {
|
||||||
|
const num = typeof value === 'string' ? parseInt(value) : value;
|
||||||
|
return num.toLocaleString('vi-VN');
|
||||||
|
};
|
||||||
|
|
||||||
|
const ItemProduct: React.FC<ProductItemProps> = ({ item }) => {
|
||||||
|
const offers = item.specialOffer?.all ?? [];
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="product-item js-p-item">
|
||||||
|
<a href={item.productUrl} className="product-image relative">
|
||||||
|
{item.productImage.large ? (
|
||||||
|
<Image src={item.productImage.large} width="203" height="203" alt={item.productName} />
|
||||||
|
) : (
|
||||||
|
<Image src="https://nguyencongpc.vn/static/assets/nguyencong_2023/images/not-image.png" alt={item.productName} />
|
||||||
|
)}
|
||||||
|
|
||||||
|
<span className="p-type-holder">
|
||||||
|
{item.productType.isHot === 1 && (
|
||||||
|
<i className="p-icon-type p-icon-hot"></i>
|
||||||
|
)}
|
||||||
|
{item.productType.isNew === 1 && (
|
||||||
|
<i className="p-icon-type p-icon-new"></i>
|
||||||
|
)}
|
||||||
|
</span>
|
||||||
|
|
||||||
|
<span className="p-type-holder p-type-holder-2">
|
||||||
|
{item.productType.isBestSale === 1 && (
|
||||||
|
<i className="p-icon-type p-icon-best-sale"></i>
|
||||||
|
)}
|
||||||
|
</span>
|
||||||
|
</a>
|
||||||
|
<div className="product-info">
|
||||||
|
<Link href={item.productUrl}>
|
||||||
|
<h3 className="product-title line-clamp-3">{item.productName}</h3>
|
||||||
|
</Link>
|
||||||
|
{item.marketPrice > 0 ? (
|
||||||
|
<div className="product-martket-main flex items-center">
|
||||||
|
<p className="product-market-price">
|
||||||
|
{item.marketPrice.toLocaleString()}<u>đ</u>
|
||||||
|
</p>
|
||||||
|
<div className="product-percent-price">-{Math.round(item.price_off)} %</div>
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
<div className="product-martket-main flex items-center">
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
<div className="product-price-main font-[600]">
|
||||||
|
{item.price > '0'
|
||||||
|
? `${formatCurrency(item.price)}đ`
|
||||||
|
: 'Liên hệ'}
|
||||||
|
</div>
|
||||||
|
{item.specialOffer?.all?.length ? (
|
||||||
|
<div className="product-offer line-clamp-2"
|
||||||
|
dangerouslySetInnerHTML={{
|
||||||
|
__html: item.specialOffer!.all![0].title,
|
||||||
|
}} />
|
||||||
|
) : (
|
||||||
|
<div className="product-offer line-clamp-2"></div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{item.extend?.buy_count ? (
|
||||||
|
<div style={{ height: 18 }}> <b>Đã bán: </b> <span>{item.extend.buy_count}</span> </div>
|
||||||
|
) : (
|
||||||
|
<div style={{ height: 18, display: 'block' }}> </div> )}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default ItemProduct;
|
||||||
56
src/components/layout/home/BoxCategory/index.tsx
Normal file
56
src/components/layout/home/BoxCategory/index.tsx
Normal file
@@ -0,0 +1,56 @@
|
|||||||
|
'use client';
|
||||||
|
import React from 'react';
|
||||||
|
import Link from 'next/link';
|
||||||
|
import { FaCaretDown } from 'react-icons/fa';
|
||||||
|
import { Swiper, SwiperSlide } from 'swiper/react';
|
||||||
|
import { Autoplay, Navigation, Pagination } from 'swiper/modules';
|
||||||
|
import ItemProduct from './ItemProduct'
|
||||||
|
|
||||||
|
import { Category } from '../../../../types/Menu';
|
||||||
|
|
||||||
|
|
||||||
|
import { menuData } from '../../Header/menuData';
|
||||||
|
import {productData} from './productData'
|
||||||
|
|
||||||
|
const BoxListCategory: React.FC = () => {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
{menuData[0].product.all_category.map((item,index) => (
|
||||||
|
<div className="box-product-category boder-radius-10" key={index}>
|
||||||
|
<div className="flex items-center justify-between">
|
||||||
|
<div className="title">
|
||||||
|
<h2 className="title-box font-[600]">{item.title}</h2>
|
||||||
|
</div>
|
||||||
|
<div className="list-category-child flex items-center justify-end flex-1">
|
||||||
|
|
||||||
|
{item.children.slice(0, 4).map((item2,index2) => (
|
||||||
|
<Link key={index2} href={item2.url} className="title-category">{item2.title}</Link>
|
||||||
|
))}
|
||||||
|
|
||||||
|
<Link href={item.url} className="title-all-category flex items-center gap-2">
|
||||||
|
<span>Xem tất cả </span>
|
||||||
|
<FaCaretDown size={16} /></Link>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="box-list-item-category swiper-product-category">
|
||||||
|
<Swiper
|
||||||
|
modules={[Autoplay, Navigation, Pagination]}
|
||||||
|
spaceBetween={12}
|
||||||
|
slidesPerView={5}
|
||||||
|
loop={true}
|
||||||
|
navigation={true}
|
||||||
|
>
|
||||||
|
{productData.map((item,index) => (
|
||||||
|
<SwiperSlide key={index}>
|
||||||
|
<ItemProduct item={item} />
|
||||||
|
</SwiperSlide>
|
||||||
|
))}
|
||||||
|
</Swiper>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default BoxListCategory
|
||||||
2161
src/components/layout/home/BoxCategory/productData.ts
Normal file
2161
src/components/layout/home/BoxCategory/productData.ts
Normal file
File diff suppressed because it is too large
Load Diff
@@ -1 +1,101 @@
|
|||||||
const ProductItem = ({ item }: { item: Product }) => {};
|
import React from 'react';
|
||||||
|
import Tippy from '@tippyjs/react';
|
||||||
|
import 'tippy.js/dist/tippy.css';
|
||||||
|
import { DealType } from '@/types/TypeListProductDeal';
|
||||||
|
|
||||||
|
type ProductItemProps = {
|
||||||
|
item: DealType;
|
||||||
|
};
|
||||||
|
|
||||||
|
const formatCurrency = (value: number | string) => {
|
||||||
|
const num = typeof value === 'string' ? parseInt(value) : value;
|
||||||
|
return num.toLocaleString('vi-VN');
|
||||||
|
};
|
||||||
|
|
||||||
|
const ProductItem: React.FC<ProductItemProps> = ({ item }) => {
|
||||||
|
const { product_info } = item;
|
||||||
|
const offers = product_info.specialOffer?.all ?? [];
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
className="product-item"
|
||||||
|
>
|
||||||
|
<a
|
||||||
|
href={product_info.productUrl}
|
||||||
|
className="product-image relative"
|
||||||
|
>
|
||||||
|
{product_info.productImage.large ? (
|
||||||
|
<img
|
||||||
|
src={product_info.productImage.large}
|
||||||
|
width="164"
|
||||||
|
height="164"
|
||||||
|
alt={product_info.productName}
|
||||||
|
className="lazy"
|
||||||
|
/>
|
||||||
|
) : (
|
||||||
|
<img
|
||||||
|
src="/static/assets/nguyencong_2023/images/not-image.png"
|
||||||
|
width="164"
|
||||||
|
height="164"
|
||||||
|
alt={product_info.productName}
|
||||||
|
className="lazy"
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
<span className="p-type-holder">
|
||||||
|
{product_info.productType.isHot === 1 && (
|
||||||
|
<i className="p-icon-type p-icon-hot"></i>
|
||||||
|
)}
|
||||||
|
{product_info.productType.isNew === 1 && (
|
||||||
|
<i className="p-icon-type p-icon-new"></i>
|
||||||
|
)}
|
||||||
|
</span>
|
||||||
|
</a>
|
||||||
|
<div className="product-info">
|
||||||
|
<a href={product_info.productUrl}>
|
||||||
|
<h3 className="product-title line-clamp-3">
|
||||||
|
{product_info.productName}
|
||||||
|
</h3>
|
||||||
|
</a>
|
||||||
|
<div className="product-martket-main flex items-center">
|
||||||
|
{product_info.marketPrice > 0 ? (
|
||||||
|
<p className="product-market-price">
|
||||||
|
{product_info.marketPrice.toLocaleString()} ₫
|
||||||
|
</p>
|
||||||
|
) : (
|
||||||
|
<p className="product-market-price">
|
||||||
|
{product_info.sale_rules.normal_price.toLocaleString()} ₫
|
||||||
|
</p>
|
||||||
|
)}
|
||||||
|
<div className="product-percent-price">
|
||||||
|
-{product_info.price_off || 0}%
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="product-price-main font-bold">
|
||||||
|
{item.price > '0'
|
||||||
|
? `${formatCurrency(product_info.price)}đ`
|
||||||
|
: 'Liên hệ'}
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
className="p-quantity-sale"
|
||||||
|
>
|
||||||
|
<i className="sprite sprite-fire-deal"></i>
|
||||||
|
<div className="bg-gradient"></div>
|
||||||
|
<p className="js-line-deal-left"></p>
|
||||||
|
<span>
|
||||||
|
Còn {Number(item.quantity) - Number(item.sale_quantity)}/{Number(item.quantity)} sản phẩm
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
{offers.length > 0 && (
|
||||||
|
<div
|
||||||
|
className="product-offer line-clamp-2"
|
||||||
|
dangerouslySetInnerHTML={{
|
||||||
|
__html: product_info.specialOffer!.all![0].title,
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default ProductItem;
|
||||||
|
|||||||
@@ -4,6 +4,8 @@ import { Swiper, SwiperSlide } from 'swiper/react';
|
|||||||
import { Autoplay, Navigation, Pagination } from 'swiper/modules';
|
import { Autoplay, Navigation, Pagination } from 'swiper/modules';
|
||||||
import { FaCaretRight } from 'react-icons/fa';
|
import { FaCaretRight } from 'react-icons/fa';
|
||||||
import CounDown from './CounDown';
|
import CounDown from './CounDown';
|
||||||
|
import ProductItem from './ProductItem'
|
||||||
|
import { productDealData } from './productDealData';
|
||||||
|
|
||||||
const BoxProductDeal: React.FC = () => {
|
const BoxProductDeal: React.FC = () => {
|
||||||
return (
|
return (
|
||||||
@@ -28,7 +30,13 @@ const BoxProductDeal: React.FC = () => {
|
|||||||
slidesPerView={6}
|
slidesPerView={6}
|
||||||
loop={true}
|
loop={true}
|
||||||
navigation={true}
|
navigation={true}
|
||||||
></Swiper>
|
>
|
||||||
|
{productDealData.map((item,index) => (
|
||||||
|
<SwiperSlide key={index}>
|
||||||
|
<ProductItem item={item} />
|
||||||
|
</SwiperSlide>
|
||||||
|
))}
|
||||||
|
</Swiper>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -36,7 +36,7 @@ export const productDealData: TypeListProductDeal = [
|
|||||||
"priceUnit": "chiếc",
|
"priceUnit": "chiếc",
|
||||||
"marketPrice": 0,
|
"marketPrice": 0,
|
||||||
"price": "11690000",
|
"price": "11690000",
|
||||||
"price_off": "",
|
"price_off": 2,
|
||||||
"currency": "vnd",
|
"currency": "vnd",
|
||||||
"sale_rules": {
|
"sale_rules": {
|
||||||
"price": "11690000",
|
"price": "11690000",
|
||||||
@@ -1422,7 +1422,7 @@ export const productDealData: TypeListProductDeal = [
|
|||||||
},
|
},
|
||||||
"comment": {
|
"comment": {
|
||||||
"rate": 5,
|
"rate": 5,
|
||||||
"total": 3
|
"total": 3,
|
||||||
},
|
},
|
||||||
"quantity": 1,
|
"quantity": 1,
|
||||||
"productSKU": "",
|
"productSKU": "",
|
||||||
|
|||||||
29
src/components/layout/home/CategoryFeature/index.tsx
Normal file
29
src/components/layout/home/CategoryFeature/index.tsx
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
'use client';
|
||||||
|
import React from 'react';
|
||||||
|
import { menuData } from '../../Header/menuData';
|
||||||
|
import ItemCategory from './itemCategory';
|
||||||
|
import { Category } from '../../../../types/Menu';
|
||||||
|
|
||||||
|
const renderFeaturedCategories = (categories: Category[]) => {
|
||||||
|
return categories.map((cat, idx) => (
|
||||||
|
<React.Fragment key={idx}>
|
||||||
|
{cat.is_featured == '1' && <ItemCategory item={cat} />}
|
||||||
|
{cat.children && renderFeaturedCategories(cat.children)}
|
||||||
|
</React.Fragment>
|
||||||
|
));
|
||||||
|
};
|
||||||
|
|
||||||
|
const CategoryFeature = () => {
|
||||||
|
return (
|
||||||
|
<div className="box-category-outstanding boder-radius-10">
|
||||||
|
<div className="title-box">
|
||||||
|
<h2 className="title title-box font-[600]">Danh mục nổi bật</h2>
|
||||||
|
</div>
|
||||||
|
<div className="list-category-outstanding grid grid-cols-10 gap-3">
|
||||||
|
{renderFeaturedCategories(menuData[0].product.all_category)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default CategoryFeature;
|
||||||
34
src/components/layout/home/CategoryFeature/itemCategory.tsx
Normal file
34
src/components/layout/home/CategoryFeature/itemCategory.tsx
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
'use client';
|
||||||
|
import React from 'react';
|
||||||
|
import Image from 'next/image';
|
||||||
|
import Link from 'next/link';
|
||||||
|
import { Category } from '@types/Menu';
|
||||||
|
|
||||||
|
const ItemCategory: React.FC<{ item: Category }> = ({ item }) => {
|
||||||
|
return (
|
||||||
|
<Link href={item.url} className="item-category flex flex-col items-center">
|
||||||
|
<p className="item-category-img">
|
||||||
|
{item.thumnail ? (
|
||||||
|
<Image
|
||||||
|
src={item.thumnail}
|
||||||
|
width={50}
|
||||||
|
height={50}
|
||||||
|
alt={item.title}
|
||||||
|
className="swiper-lazy lazy"
|
||||||
|
/>
|
||||||
|
) : (
|
||||||
|
<Image
|
||||||
|
src="https://nguyencongpc.vn/static/assets/nguyencong_2023/images/not-image.png"
|
||||||
|
width={50}
|
||||||
|
height={50}
|
||||||
|
alt={item.title}
|
||||||
|
className="border-radius-10"
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</p>
|
||||||
|
<p className="title line-clamp-2">{item.title}</p>
|
||||||
|
</Link>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default ItemCategory;
|
||||||
@@ -1,6 +1,9 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import SliderHome from './SliderHome';
|
import SliderHome from './SliderHome';
|
||||||
import BoxProductDeal from './BoxDeal';
|
import BoxProductDeal from './BoxDeal';
|
||||||
|
import CategoryFeature from './CategoryFeature';
|
||||||
|
import BoxListCategory from './BoxCategory';
|
||||||
|
|
||||||
const Home = () => {
|
const Home = () => {
|
||||||
return (
|
return (
|
||||||
<div className="page-hompage mt-4">
|
<div className="page-hompage mt-4">
|
||||||
@@ -9,6 +12,11 @@ const Home = () => {
|
|||||||
<SliderHome />
|
<SliderHome />
|
||||||
{/* deal */}
|
{/* deal */}
|
||||||
<BoxProductDeal />
|
<BoxProductDeal />
|
||||||
|
{/* box danh mục nổi bật */}
|
||||||
|
<CategoryFeature />
|
||||||
|
|
||||||
|
{/* DANH SÁCH DANH MỤC */}
|
||||||
|
<BoxListCategory />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -2167,7 +2167,7 @@ textarea::placeholder {
|
|||||||
.page-hompage .box-banner-under-slider {
|
.page-hompage .box-banner-under-slider {
|
||||||
margin-top: -50px;
|
margin-top: -50px;
|
||||||
}
|
}
|
||||||
.page-hompage .box-banner-under-slider svg {
|
.swiper .swiper-button-prev svg,.swiper .swiper-button-next svg {
|
||||||
width: 8px !important;
|
width: 8px !important;
|
||||||
color: #000;
|
color: #000;
|
||||||
}
|
}
|
||||||
@@ -2438,7 +2438,7 @@ textarea::placeholder {
|
|||||||
.page-hompage .box-product-category .title {
|
.page-hompage .box-product-category .title {
|
||||||
width: 30%;
|
width: 30%;
|
||||||
}
|
}
|
||||||
.page-hompage .box-product-category .box-list-item-category {
|
.page-hompage .box-product-category .swiper {
|
||||||
padding: 2px;
|
padding: 2px;
|
||||||
min-height: 300px;
|
min-height: 300px;
|
||||||
}
|
}
|
||||||
|
|||||||
135
src/types/TypeListProduct.ts
Normal file
135
src/types/TypeListProduct.ts
Normal file
@@ -0,0 +1,135 @@
|
|||||||
|
export interface ProductImage {
|
||||||
|
small: string;
|
||||||
|
large: string;
|
||||||
|
original: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ImageCollectionItem {
|
||||||
|
image: ProductImage;
|
||||||
|
alt: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface Brand {
|
||||||
|
id: number;
|
||||||
|
brand_index: string;
|
||||||
|
name: string;
|
||||||
|
image: string;
|
||||||
|
url: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface SaleRules {
|
||||||
|
price: string | number;
|
||||||
|
normal_price: number;
|
||||||
|
min_purchase: string | number;
|
||||||
|
max_purchase: string | number;
|
||||||
|
remain_quantity: number;
|
||||||
|
from_time: string | number;
|
||||||
|
to_time: string | number;
|
||||||
|
type: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface Deal {
|
||||||
|
id: string;
|
||||||
|
pro_id: string;
|
||||||
|
title: string;
|
||||||
|
price: string;
|
||||||
|
quantity: string;
|
||||||
|
min_purchase: string;
|
||||||
|
max_purchase: string;
|
||||||
|
is_featured: string;
|
||||||
|
from_time: string;
|
||||||
|
to_time: string;
|
||||||
|
is_started: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface PricingTrace {
|
||||||
|
price: string;
|
||||||
|
type: string;
|
||||||
|
type_id: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface Category {
|
||||||
|
id: string;
|
||||||
|
catPath: string;
|
||||||
|
name: string;
|
||||||
|
url: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface Extend {
|
||||||
|
pixel_code?: string;
|
||||||
|
review_count?: string;
|
||||||
|
review_score?: string;
|
||||||
|
buy_count?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface SpecialOfferItem {
|
||||||
|
id: number;
|
||||||
|
title: string;
|
||||||
|
type: string;
|
||||||
|
thumbnail: string;
|
||||||
|
cash_value: number;
|
||||||
|
quantity: number;
|
||||||
|
from_time: string;
|
||||||
|
to_time: string;
|
||||||
|
url: string;
|
||||||
|
description:
|
||||||
|
string;
|
||||||
|
status: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface Product {
|
||||||
|
id: number;
|
||||||
|
productId: number;
|
||||||
|
priceUnit: string;
|
||||||
|
marketPrice: number;
|
||||||
|
price: string | number;
|
||||||
|
price_off: number;
|
||||||
|
currency: string;
|
||||||
|
sale_rules: SaleRules;
|
||||||
|
lastUpdate: string;
|
||||||
|
warranty: string;
|
||||||
|
productName: string;
|
||||||
|
productSummary: string;
|
||||||
|
package_accessory: string;
|
||||||
|
productImage: ProductImage;
|
||||||
|
imageCollection: ImageCollectionItem[];
|
||||||
|
productUrl: string;
|
||||||
|
brand: Brand;
|
||||||
|
visit: number;
|
||||||
|
rating: number;
|
||||||
|
reviewCount: number;
|
||||||
|
review: { rate: number; total: number };
|
||||||
|
comment: { rate: number; total: number };
|
||||||
|
quantity: number;
|
||||||
|
productSKU: string;
|
||||||
|
productModel: string;
|
||||||
|
hasVAT: number;
|
||||||
|
condition: string;
|
||||||
|
config_count: number;
|
||||||
|
configurable: number;
|
||||||
|
component_count: number;
|
||||||
|
specialOffer: { other?: SpecialOfferItem[], all?: SpecialOfferItem[] };
|
||||||
|
specialOfferGroup: [];
|
||||||
|
productType: {
|
||||||
|
isNew: number;
|
||||||
|
isHot: number;
|
||||||
|
isBestSale: number;
|
||||||
|
isSaleOff: number;
|
||||||
|
'online-only': number;
|
||||||
|
};
|
||||||
|
bulk_price: [];
|
||||||
|
thum_poster: string;
|
||||||
|
thum_poster_type: string;
|
||||||
|
addon: [];
|
||||||
|
variants: [];
|
||||||
|
variant_option: [];
|
||||||
|
extend: Extend;
|
||||||
|
weight: number;
|
||||||
|
promotion_price: number | null;
|
||||||
|
deal_list: Deal[];
|
||||||
|
pricing_traces: PricingTrace[];
|
||||||
|
categories: Category[];
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
export type TypeListProduct = Product[];
|
||||||
@@ -100,8 +100,8 @@ interface ProductInfo {
|
|||||||
productId: number;
|
productId: number;
|
||||||
priceUnit: string;
|
priceUnit: string;
|
||||||
marketPrice: number;
|
marketPrice: number;
|
||||||
price: string;
|
price: string | number;
|
||||||
price_off: number | string;
|
price_off: number;
|
||||||
currency: string;
|
currency: string;
|
||||||
sale_rules: SaleRules;
|
sale_rules: SaleRules;
|
||||||
lastUpdate: string;
|
lastUpdate: string;
|
||||||
@@ -118,7 +118,7 @@ interface ProductInfo {
|
|||||||
reviewCount: number;
|
reviewCount: number;
|
||||||
review: ReviewInfo;
|
review: ReviewInfo;
|
||||||
comment: ReviewInfo;
|
comment: ReviewInfo;
|
||||||
quantity: number;
|
quantity: string | number;
|
||||||
productSKU: string;
|
productSKU: string;
|
||||||
productModel: string;
|
productModel: string;
|
||||||
hasVAT: number;
|
hasVAT: number;
|
||||||
@@ -143,7 +143,7 @@ interface ProductInfo {
|
|||||||
categories: Category[];
|
categories: Category[];
|
||||||
}
|
}
|
||||||
|
|
||||||
interface DealType {
|
export interface DealType {
|
||||||
id: string;
|
id: string;
|
||||||
pro_id: string;
|
pro_id: string;
|
||||||
title: string;
|
title: string;
|
||||||
|
|||||||
Reference in New Issue
Block a user