update 30/12
This commit is contained in:
@@ -1,43 +1,41 @@
|
|||||||
|
// src/app/[slug]/page.tsx
|
||||||
import { notFound } from "next/navigation";
|
import { notFound } from "next/navigation";
|
||||||
import { findBySlug } from "@/lib/slugMap";
|
import { findBySlug } from "@/lib/slugMap";
|
||||||
|
|
||||||
import ProductCategory from "@/components/product/Category";
|
import ProductCategory from "@/components/product/Category";
|
||||||
import ProductDetail from "@/components/product/ProductDetail";
|
import ProductDetail from "@/components/product/ProductDetail";
|
||||||
import ArticleHome from "@/components/article/Home";
|
|
||||||
import ArticleCategory from "@/components/article/Category";
|
import ArticleCategory from "@/components/article/Category";
|
||||||
import ArticleDetail from "@/components/article/ArticleDetail";
|
import ArticleDetail from "@/components/article/ArticleDetail";
|
||||||
|
import ArticleHome from "@/components/article/Home";
|
||||||
|
|
||||||
export default async function SlugPage({
|
export default async function SlugPage({
|
||||||
params,
|
params,
|
||||||
}: {
|
}: {
|
||||||
params: Promise<{ slug: string }>;
|
params: { slug: string };
|
||||||
}) {
|
}) {
|
||||||
const { slug } = await params;
|
const { slug } = await params;
|
||||||
|
|
||||||
if (!slug) return notFound();
|
if (!slug) return notFound();
|
||||||
|
|
||||||
const result = findBySlug(slug);
|
const result = findBySlug(slug);
|
||||||
|
|
||||||
if (!result) return notFound();
|
if (!result) return notFound();
|
||||||
|
|
||||||
switch (result.type) {
|
switch (result.type) {
|
||||||
case "product_category":
|
case "product_category":
|
||||||
return <ProductCategory slug={slug} />;
|
return <ProductCategory data={result.data} />;
|
||||||
|
|
||||||
case "product_detail":
|
case "product_detail":
|
||||||
return <ProductDetail slug={slug} />;
|
return <ProductDetail data={result.data} />;
|
||||||
|
|
||||||
case "article_home":
|
case "article_home":
|
||||||
return <ArticleHome slug={slug} />;
|
return <ArticleHome slug={slug} />;
|
||||||
|
|
||||||
case "article_category":
|
case "article_category":
|
||||||
return <ArticleCategory slug={slug} />;
|
return <ArticleCategory data={result.data} />;
|
||||||
|
|
||||||
case "article_detail":
|
case "article_detail":
|
||||||
return <ArticleDetail slug={slug} />;
|
return <ArticleDetail slug={result.data.slug} />;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
return notFound();
|
return notFound();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
8
src/app/cart/page.tsx
Normal file
8
src/app/cart/page.tsx
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
|
||||||
|
import CartHome from "@/components/cart/Home";
|
||||||
|
|
||||||
|
export default function Home() {
|
||||||
|
return (
|
||||||
|
<CartHome />
|
||||||
|
)
|
||||||
|
}
|
||||||
8
src/app/send-cart/page.tsx
Normal file
8
src/app/send-cart/page.tsx
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
|
||||||
|
import SendResult from "@/components/cart/Send";
|
||||||
|
|
||||||
|
export default function SendCartPage() {
|
||||||
|
return (
|
||||||
|
<SendResult />
|
||||||
|
)
|
||||||
|
}
|
||||||
8
src/app/tin-tuc/page.tsx
Normal file
8
src/app/tin-tuc/page.tsx
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
|
||||||
|
import ArticleHome from "@/components/article/Home";
|
||||||
|
|
||||||
|
export default function Home() {
|
||||||
|
return (
|
||||||
|
<ArticleHome slug="" />
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
export default function SendCart() {
|
export default function Home() {
|
||||||
return(
|
return(
|
||||||
<div className="cart-page container">
|
<div className="cart-page container">
|
||||||
<div className="text-center px-3">
|
<div className="text-center px-3">
|
||||||
|
|||||||
@@ -3,11 +3,11 @@ export default function SendCart() {
|
|||||||
<div className="max-w-[656px] m-auto my-10">
|
<div className="max-w-[656px] m-auto my-10">
|
||||||
<div>
|
<div>
|
||||||
<img
|
<img
|
||||||
src="images/send-cart-image.png"
|
src="images/send-cart-image.png"
|
||||||
alt="send-cart"
|
alt="send-cart"
|
||||||
width={1}
|
width={1}
|
||||||
height={1}
|
height={1}
|
||||||
className="block m-auto w-auto h-auto"
|
className="block m-auto w-auto h-auto"
|
||||||
/>
|
/>
|
||||||
<h1 className="mt-10 mb-5 font-600 text-center text-[40px] text-[#004BA4] leading-12">
|
<h1 className="mt-10 mb-5 font-600 text-center text-[40px] text-[#004BA4] leading-12">
|
||||||
{" "}
|
{" "}
|
||||||
|
|||||||
19
src/components/product/Category/Banner/index.tsx
Normal file
19
src/components/product/Category/Banner/index.tsx
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
export default function Banner() {
|
||||||
|
return (
|
||||||
|
<div className="swiper custom-dots overflow-hidden relative mb-6" id="js-category-banner">
|
||||||
|
<div className="swiper-wrapper">
|
||||||
|
<div className="swiper-slide">
|
||||||
|
<a href="">
|
||||||
|
<img
|
||||||
|
src="images/category/slide-category.png"
|
||||||
|
alt=""
|
||||||
|
width=""
|
||||||
|
height=""
|
||||||
|
className="block w-full lazy rounded-[24px]"
|
||||||
|
/>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
35
src/components/product/Category/FAQ/index.tsx
Normal file
35
src/components/product/Category/FAQ/index.tsx
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
export default function FAQ() {
|
||||||
|
return (
|
||||||
|
<div className="global-faq-container py-8 lg:py-12 text-18 leading-6">
|
||||||
|
<div className="text-center mb-8">
|
||||||
|
<p className="text-[#004BA4] text-20 lg:text-[40px] leading-5 lg:leading-[48px] mb-2 font-600">
|
||||||
|
Các câu hỏi thường gặp
|
||||||
|
</p>
|
||||||
|
<p className="max-w-[620px] m-auto text-16 leading-[21px] lg:text-[18px] lg:leading-6">
|
||||||
|
Nếu quý khách còn có bất kì câu hỏi nào cần hỗ trợ, vui lòng liên hệ với
|
||||||
|
chúng tôi qua các số hotline để được tư vấn và giải đáp nhanh chóng
|
||||||
|
nhất.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="faq-item js-faq-item relative">
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
className="bx bx-plus w-10 h-10 rounded-full bg-[#EAF1FF] border border-[#FBFBFB] text-24 absolute right-5 top-4"
|
||||||
|
aria-label="xem thêm"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<p className="m-0 text-20 font-600">
|
||||||
|
Có thể kiểm tra tính tương thích của linh kiện trước khi đặt không?
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<div className="faq-answer mt-4">
|
||||||
|
<p> Có. Chúng tôi cung cấp công cụ "Build PC" để bạn dễ dàng chọn và kiểm
|
||||||
|
tra tính tương thích giữa CPU, mainboard, RAM, GPU, và các linh kiện
|
||||||
|
khác trước khi mua hàng.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
195
src/components/product/Category/Filter/index.tsx
Normal file
195
src/components/product/Category/Filter/index.tsx
Normal file
@@ -0,0 +1,195 @@
|
|||||||
|
export default function ProductFilter() {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<p className="uppercase font-500 text-center border text-[#0678DB] leading-10 border-[#114CDD] rounded-[8px] mb-6">
|
||||||
|
Lọc sản phẩm
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<div className="product-filter-group">
|
||||||
|
<div className="filter-category-group text-18 leading-6 mb-6">
|
||||||
|
<p className="font-600 mb-3"> Danh mục </p>
|
||||||
|
<p className="leading-9 bg-[#F0F5FF] font-500 relative pl-6 mb-1">
|
||||||
|
<i className="bg-[#0678DB] w-1 absolute top-0 left-0 bottom-0" />
|
||||||
|
<span> Tất cả </span>
|
||||||
|
</p>
|
||||||
|
<div className="flex flex-col gap-1">
|
||||||
|
<a
|
||||||
|
href="/pc-photo-editing"
|
||||||
|
className="inline-flex items-start gap-1 py-[6px] hover:text-[#0678DB]"
|
||||||
|
>
|
||||||
|
{" "}
|
||||||
|
<i className="bxr bx-chevrons-right leading-[inherit]" />{" "}
|
||||||
|
<h2 className="inherit"> Photo Editing </h2>
|
||||||
|
</a>
|
||||||
|
<a
|
||||||
|
href="/pc-edit-render-video"
|
||||||
|
className="inline-flex items-start gap-1 py-[6px] hover:text-[#0678DB]"
|
||||||
|
>
|
||||||
|
{" "}
|
||||||
|
<i className="bxr bx-chevrons-right leading-[inherit]" />{" "}
|
||||||
|
<h2 className="inherit"> PC Video Editing </h2>
|
||||||
|
</a>
|
||||||
|
<a
|
||||||
|
href="/hhpc-3d"
|
||||||
|
className="inline-flex items-start gap-1 py-[6px] hover:text-[#0678DB]"
|
||||||
|
>
|
||||||
|
{" "}
|
||||||
|
<i className="bxr bx-chevrons-right leading-[inherit]" />{" "}
|
||||||
|
<h2 className="inherit"> PC 3D Design, Animation </h2>
|
||||||
|
</a>
|
||||||
|
<a
|
||||||
|
href="/hhpc-3d-render"
|
||||||
|
className="inline-flex items-start gap-1 py-[6px] hover:text-[#0678DB]"
|
||||||
|
>
|
||||||
|
{" "}
|
||||||
|
<i className="bxr bx-chevrons-right leading-[inherit]" />{" "}
|
||||||
|
<h2 className="inherit"> PC Rendering </h2>
|
||||||
|
</a>
|
||||||
|
<a
|
||||||
|
href="/pc-visualization"
|
||||||
|
className="inline-flex items-start gap-1 py-[6px] hover:text-[#0678DB]"
|
||||||
|
>
|
||||||
|
{" "}
|
||||||
|
<i className="bxr bx-chevrons-right leading-[inherit]" />{" "}
|
||||||
|
<h2 className="inherit"> PC Visualization </h2>
|
||||||
|
</a>
|
||||||
|
<a
|
||||||
|
href="/pc-architecture-cad"
|
||||||
|
className="inline-flex items-start gap-1 py-[6px] hover:text-[#0678DB]"
|
||||||
|
>
|
||||||
|
{" "}
|
||||||
|
<i className="bxr bx-chevrons-right leading-[inherit]" />{" "}
|
||||||
|
<h2 className="inherit"> PC Architecture & CAD </h2>
|
||||||
|
</a>
|
||||||
|
<a
|
||||||
|
href="/machine-learning-ai-tensorflow"
|
||||||
|
className="inline-flex items-start gap-1 py-[6px] hover:text-[#0678DB]"
|
||||||
|
>
|
||||||
|
{" "}
|
||||||
|
<i className="bxr bx-chevrons-right leading-[inherit]" />{" "}
|
||||||
|
<h2 className="inherit"> PC Machine Learning / AI </h2>
|
||||||
|
</a>
|
||||||
|
<a
|
||||||
|
href="/server-may-ao-gia-lap"
|
||||||
|
className="inline-flex items-start gap-1 py-[6px] hover:text-[#0678DB]"
|
||||||
|
>
|
||||||
|
{" "}
|
||||||
|
<i className="bxr bx-chevrons-right leading-[inherit]" />{" "}
|
||||||
|
<h2 className="inherit"> Server, Máy Ảo, Giả Lập </h2>
|
||||||
|
</a>
|
||||||
|
<a
|
||||||
|
href="/pc-dep"
|
||||||
|
className="inline-flex items-start gap-1 py-[6px] hover:text-[#0678DB]"
|
||||||
|
>
|
||||||
|
{" "}
|
||||||
|
<i className="bxr bx-chevrons-right leading-[inherit]" />{" "}
|
||||||
|
<h2 className="inherit"> PC Đẹp </h2>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="filter-item-group leading-[22px] text-16 mb-6">
|
||||||
|
<p className="font-600 text-18 mb-3"> Khoảng giá </p>
|
||||||
|
<div className="filter-list">
|
||||||
|
<a href="https://hoanghapc.vn/pc-workstation?max=10000000">
|
||||||
|
<span> Dưới 10 triệu </span>
|
||||||
|
<span> (3) </span>
|
||||||
|
</a>
|
||||||
|
<a href="https://hoanghapc.vn/pc-workstation?max=20000000&min=15000000">
|
||||||
|
<span> 15 triệu - 20 triệu </span>
|
||||||
|
<span> (3) </span>
|
||||||
|
</a>
|
||||||
|
<a href="https://hoanghapc.vn/pc-workstation?max=25000000&min=20000000">
|
||||||
|
<span> 20 triệu - 25 triệu </span>
|
||||||
|
<span> (1) </span>
|
||||||
|
</a>
|
||||||
|
<a href="https://hoanghapc.vn/pc-workstation?max=30000000&min=25000000">
|
||||||
|
<span> 25 triệu - 30 triệu </span>
|
||||||
|
<span> (2) </span>
|
||||||
|
</a>
|
||||||
|
<a href="https://hoanghapc.vn/pc-workstation?max=35000000&min=30000000">
|
||||||
|
<span> 30 triệu - 35 triệu </span>
|
||||||
|
<span> (2) </span>
|
||||||
|
</a>
|
||||||
|
<a href="https://hoanghapc.vn/pc-workstation?max=40000000&min=35000000">
|
||||||
|
<span> 35 triệu - 40 triệu </span>
|
||||||
|
<span> (3) </span>
|
||||||
|
</a>
|
||||||
|
<a href="https://hoanghapc.vn/pc-workstation?max=45000000&min=40000000">
|
||||||
|
<span> 40 triệu - 45 triệu </span>
|
||||||
|
<span> (13) </span>
|
||||||
|
</a>
|
||||||
|
<a href="https://hoanghapc.vn/pc-workstation?max=50000000&min=45000000">
|
||||||
|
<span> 45 triệu - 50 triệu </span>
|
||||||
|
<span> (13) </span>
|
||||||
|
</a>
|
||||||
|
<a href="https://hoanghapc.vn/pc-workstation?min=50000000">
|
||||||
|
<span> Trên 50 triệu </span>
|
||||||
|
<span> (85) </span>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="filter-item-group leading-[22px] text-16 mb-6">
|
||||||
|
<p className="font-600 text-18 mb-3"> Thương hiệu </p>
|
||||||
|
<div className="filter-list">
|
||||||
|
<a href="https://hoanghapc.vn/pc-workstation?brand=6">
|
||||||
|
<span> GIGABYTE </span>
|
||||||
|
<span> (2) </span>
|
||||||
|
</a>
|
||||||
|
<a href="https://hoanghapc.vn/pc-workstation?brand=19">
|
||||||
|
<span> HP </span>
|
||||||
|
<span> (1) </span>
|
||||||
|
</a>
|
||||||
|
<a href="https://hoanghapc.vn/pc-workstation?brand=7">
|
||||||
|
<span> MSI </span>
|
||||||
|
<span> (1) </span>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="filter-item-group leading-[22px] text-16 mb-6">
|
||||||
|
<p className="font-600 text-18 mb-3"> CPU </p>
|
||||||
|
<div className="filter-list">
|
||||||
|
<a href="https://hoanghapc.vn/pc-workstation?filter=397">
|
||||||
|
<span> Intel Core i7 </span>
|
||||||
|
<span> (13) </span>
|
||||||
|
</a>
|
||||||
|
<a href="https://hoanghapc.vn/pc-workstation?filter=398">
|
||||||
|
<span> Intel Core i9 </span>
|
||||||
|
<span> (20) </span>
|
||||||
|
</a>
|
||||||
|
<a href="https://hoanghapc.vn/pc-workstation?filter=399">
|
||||||
|
<span> Intel Xeon </span>
|
||||||
|
<span> (16) </span>
|
||||||
|
</a>
|
||||||
|
<a href="https://hoanghapc.vn/pc-workstation?filter=402">
|
||||||
|
<span> AMD Ryzen 7 </span>
|
||||||
|
<span> (5) </span>
|
||||||
|
</a>
|
||||||
|
<a href="https://hoanghapc.vn/pc-workstation?filter=403">
|
||||||
|
<span> AMD Ryzen 9 </span>
|
||||||
|
<span> (21) </span>
|
||||||
|
</a>
|
||||||
|
<a href="https://hoanghapc.vn/pc-workstation?filter=404">
|
||||||
|
<span> AMD Ryzen Threadripper </span>
|
||||||
|
<span> (8) </span>
|
||||||
|
</a>
|
||||||
|
<a href="https://hoanghapc.vn/pc-workstation?filter=637">
|
||||||
|
<span> Core Ultra 9 </span>
|
||||||
|
<span> (18) </span>
|
||||||
|
</a>
|
||||||
|
<a href="https://hoanghapc.vn/pc-workstation?filter=636">
|
||||||
|
<span> Core Ultra 7 </span>
|
||||||
|
<span> (10) </span>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<a href="/pc-workstation" className="js-partName block text-center uppercase bg-[linear-gradient(165.29deg,#259AFF_8.53%,#114CDD_93.19%)] text-white rounded-[30px] leading-5 text-16 font-500 p-[10px] mt-8">
|
||||||
|
Bỏ bộ lọc (1)
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
||||||
17
src/components/product/Category/Paging/index.tsx
Normal file
17
src/components/product/Category/Paging/index.tsx
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
export default function Paging() {
|
||||||
|
return (
|
||||||
|
<div className="text-center mt-12">
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
className="mb-3 bg-btn text-white rounded-[30px] h-10 font-500 text-16 table max-w-[240px] w-full m-auto mb-3"
|
||||||
|
aria-label="Xem thêm"
|
||||||
|
>
|
||||||
|
TẢI THÊM
|
||||||
|
</button>
|
||||||
|
<p className="text-14 leading-[18px] m-0">
|
||||||
|
{" "}
|
||||||
|
Hiển thị 1 - 24 trên tổng số 124 sản phẩm{" "}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
136
src/components/product/Category/Product/index.tsx
Normal file
136
src/components/product/Category/Product/index.tsx
Normal file
File diff suppressed because one or more lines are too long
43
src/components/product/Category/Sort/index.tsx
Normal file
43
src/components/product/Category/Sort/index.tsx
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
export default function SortByCollection() {
|
||||||
|
return (
|
||||||
|
<div className="sort-group flex flex-wrap items-center justify-between gap-4 text-16 leading-[22px] border-b border-[#DEE4EC] pb-3 mb-5">
|
||||||
|
<p className="m-0"> Tổng 124 sản phẩm </p>
|
||||||
|
|
||||||
|
<div className="flex items-center gap-3">
|
||||||
|
<p className="m-0"> Lọc theo: </p>
|
||||||
|
<div className="group relative border border-[#D6DAE1] whitespace-nowrap leading-[38px] rounded-[30px] px-4 min-w-[170px]">
|
||||||
|
<p className="m-0 flex items-center justify-between cursor-pointer">
|
||||||
|
<span> Lựa chọn </span>{" "}
|
||||||
|
<i className="bx bx-chevron-down text-[#A0A5AC] text-18 transition-all group-hover:rotate-[-180deg]" />
|
||||||
|
</p>
|
||||||
|
<div className="absolute shadow border bg-white opacity-0 z-[-1] right-0 top-[100%] whitespace-nowrap transition group-hover:opacity-100 group-hover:z-[5] leading-[22px] p-1 border border-[#D6DAE1] rounded-[12px] w-full">
|
||||||
|
<a
|
||||||
|
href=""
|
||||||
|
className="bg-[#F2F2F2] block p-[6px_8px] rounded-[8px] mb-[1px] hover:bg-[#F2F2F2]"
|
||||||
|
>
|
||||||
|
Mức độ phổ biến
|
||||||
|
</a>
|
||||||
|
<a
|
||||||
|
href=""
|
||||||
|
className="block p-[6px_8px] rounded-[8px] mb-[1px] hover:bg-[#F2F2F2]"
|
||||||
|
>
|
||||||
|
Giá tăng dần
|
||||||
|
</a>
|
||||||
|
<a
|
||||||
|
href=""
|
||||||
|
className="block p-[6px_8px] rounded-[8px] mb-[1px] hover:bg-[#F2F2F2]"
|
||||||
|
>
|
||||||
|
Giá giảm dần
|
||||||
|
</a>
|
||||||
|
<a
|
||||||
|
href=""
|
||||||
|
className="block p-[6px_8px] rounded-[8px] mb-[1px] hover:bg-[#F2F2F2]"
|
||||||
|
>
|
||||||
|
% Giảm giá nhiều
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
28
src/components/product/Category/Static/index.tsx
Normal file
28
src/components/product/Category/Static/index.tsx
Normal file
File diff suppressed because one or more lines are too long
File diff suppressed because it is too large
Load Diff
@@ -1,4 +1,9 @@
|
|||||||
export default function ProductDetail() {
|
type Props = {
|
||||||
|
slug: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
export default async function ProductDetail({ slug }: Props) {
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<div className="product-detail-page container">
|
<div className="product-detail-page container">
|
||||||
|
|||||||
33
src/lib/resolveArticlePage.ts
Normal file
33
src/lib/resolveArticlePage.ts
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
// src/lib/articlePage.ts
|
||||||
|
import { categories } from "../data/categories";
|
||||||
|
|
||||||
|
export type ArticleResult =
|
||||||
|
| { type: "article_home"; data: any }
|
||||||
|
| { type: "article_category"; data: any }
|
||||||
|
| { type: "article_detail"; data: { slug: string } };
|
||||||
|
|
||||||
|
export function resolveArticlePage(slug: string): ArticleResult | null {
|
||||||
|
const url = "/" + slug;
|
||||||
|
|
||||||
|
// HOME
|
||||||
|
if (url === "/tin-tuc") {
|
||||||
|
return { type: "article_home", data: null };
|
||||||
|
}
|
||||||
|
|
||||||
|
// CATEGORY
|
||||||
|
const cats = categories.article.all_category.article;
|
||||||
|
for (const parent of cats) {
|
||||||
|
if (parent.url === url) {
|
||||||
|
return { type: "article_category", data: parent };
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const child of parent.children ?? []) {
|
||||||
|
if (child.url === url) {
|
||||||
|
return { type: "article_category", data: child };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// DETAIL
|
||||||
|
return { type: "article_detail", data: { slug } };
|
||||||
|
}
|
||||||
32
src/lib/resolveProductPage.ts
Normal file
32
src/lib/resolveProductPage.ts
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
// hoanghapc/src/lib/productPage.ts
|
||||||
|
import { categories } from "../data/categories";
|
||||||
|
import { productList } from "../data/product-list";
|
||||||
|
|
||||||
|
export type ProductResult =
|
||||||
|
| { type: "product_category"; data: any }
|
||||||
|
| { type: "product_detail"; data: any };
|
||||||
|
|
||||||
|
export function resolveProductPage(slug: string): ProductResult | null {
|
||||||
|
const url = "/" + slug;
|
||||||
|
|
||||||
|
// CATEGORY
|
||||||
|
for (const parent of categories.product.all_category) {
|
||||||
|
if (parent.url === url) {
|
||||||
|
return { type: "product_category", data: parent };
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const child of parent.children ?? []) {
|
||||||
|
if (child.url === url) {
|
||||||
|
return { type: "product_category", data: child };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// DETAIL
|
||||||
|
const product = productList.find(p => p.productUrl === url);
|
||||||
|
if (product) {
|
||||||
|
return { type: "product_detail", data: product };
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
@@ -1,77 +1,17 @@
|
|||||||
// src/lib/slugMap.ts
|
import { resolveArticlePage } from "./resolveArticlePage";
|
||||||
import { categories } from "@/data/categories";
|
import { resolveProductPage } from "./resolveProductPage";
|
||||||
|
|
||||||
export type PageType =
|
|
||||||
| "article_home"
|
|
||||||
| "article_category"
|
|
||||||
| "article_detail"
|
|
||||||
| "product_category"
|
|
||||||
| "product_detail";
|
|
||||||
|
|
||||||
export type SlugResult =
|
export type SlugResult =
|
||||||
| { type: "article_home" }
|
| ReturnType<typeof resolveArticlePage>
|
||||||
| { type: "article_category"; slug: string }
|
| ReturnType<typeof resolveProductPage>;
|
||||||
| { type: "article_detail"; slug: string }
|
|
||||||
| { type: "product_category"; slug: string }
|
|
||||||
| { type: "product_detail"; slug: string };
|
|
||||||
|
|
||||||
export function findBySlug(rawSlug: string): SlugResult {
|
export function findBySlug(slug?: string): SlugResult | null {
|
||||||
const slug = normalizeSlug(rawSlug);
|
if (!slug) return null;
|
||||||
const url = "/" + slug;
|
|
||||||
|
|
||||||
/* 1. ARTICLE HOME */
|
// PRODUCT
|
||||||
if (url === "/tin-tuc") {
|
const product = resolveProductPage(slug);
|
||||||
return { type: "article_home" };
|
if (product) return product;
|
||||||
}
|
|
||||||
|
|
||||||
/* 2. ARTICLE CATEGORY */
|
// ARTICLE
|
||||||
const articleCats = categories.article.all_category.article;
|
return resolveArticlePage(slug);
|
||||||
for (const parent of articleCats) {
|
|
||||||
if (parent.url === url) {
|
|
||||||
return { type: "article_category", slug };
|
|
||||||
}
|
|
||||||
|
|
||||||
for (const child of parent.children ?? []) {
|
|
||||||
if (child.url === url) {
|
|
||||||
return { type: "article_category", slug };
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 3. PRODUCT CATEGORY */
|
|
||||||
const productCats = categories.product.all_category;
|
|
||||||
for (const cat of productCats) {
|
|
||||||
if (cat.url === url) {
|
|
||||||
return { type: "product_category", slug };
|
|
||||||
}
|
|
||||||
|
|
||||||
for (const child of cat.children ?? []) {
|
|
||||||
if (child.url === url) {
|
|
||||||
return { type: "product_category", slug };
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 4. PRODUCT DETAIL */
|
|
||||||
if (isProductDetailSlug(slug)) {
|
|
||||||
return { type: "product_detail", slug };
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 5. ARTICLE DETAIL (fallback) */
|
|
||||||
return { type: "article_detail", slug };
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ===============================
|
|
||||||
* HELPERS
|
|
||||||
* =============================== */
|
|
||||||
function normalizeSlug(slug: string) {
|
|
||||||
return slug.replace(/^\/+/, "").trim();
|
|
||||||
}
|
|
||||||
|
|
||||||
function isProductDetailSlug(slug: string) {
|
|
||||||
return (
|
|
||||||
slug.startsWith("hhpc-") ||
|
|
||||||
slug.startsWith("pc-") ||
|
|
||||||
slug.startsWith("man-hinh-")
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user