update
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
'use client';
|
||||
|
||||
import Link from 'next/link';
|
||||
import { FaHouse, FaAngleRight } from 'react-icons/fa6';
|
||||
import { FaAngleRight, FaHouse } from 'react-icons/fa6';
|
||||
|
||||
interface BreadcrumbItem {
|
||||
name: string | undefined;
|
||||
@@ -19,25 +20,27 @@ export const Breadcrumb = ({ items }: { items: BreadcrumbItem[] }) => {
|
||||
>
|
||||
<Link href="/" itemProp="item">
|
||||
<span itemProp="name" className="flex items-center gap-2">
|
||||
<span style={{ fontSize: 0 }}>Trang chủ</span> <FaHouse className="text-gray-700" />
|
||||
<span style={{ fontSize: 0 }}>Trang chủ</span>
|
||||
<FaHouse className="text-gray-700" />
|
||||
</span>
|
||||
</Link>{' '}
|
||||
</Link>
|
||||
<FaAngleRight className="text-gray-700" />
|
||||
<meta itemProp="position" content="1" />
|
||||
</li>
|
||||
{items.map((item, idx) => (
|
||||
|
||||
{items.map((item, index) => (
|
||||
<li
|
||||
key={idx}
|
||||
key={`${item.url}-${index}`}
|
||||
itemProp="itemListElement"
|
||||
itemScope
|
||||
itemType="http://schema.org/ListItem"
|
||||
className="flex items-center gap-2"
|
||||
>
|
||||
<Link href={item.url ?? '/'} itemProp="item">
|
||||
<span itemProp="name">{item?.name}</span>
|
||||
<span itemProp="name">{item.name}</span>
|
||||
</Link>
|
||||
{idx < items.length - 1 && <FaAngleRight className="text-gray-700" />}
|
||||
<meta itemProp="position" content={(idx + 1).toString()} />
|
||||
{index < items.length - 1 && <FaAngleRight className="text-gray-700" />}
|
||||
<meta itemProp="position" content={(index + 2).toString()} />
|
||||
</li>
|
||||
))}
|
||||
</ol>
|
||||
|
||||
@@ -1,14 +1,13 @@
|
||||
'use client';
|
||||
import { useEffect, useState } from 'react';
|
||||
import { MSWContext } from '@/contexts/MSWContext';
|
||||
|
||||
/**
|
||||
* Khởi động MSW browser worker trong development.
|
||||
* Block render cho đến khi worker ready để tránh race condition
|
||||
* (requests gửi trước khi MSW sẵn sàng sẽ bị passthrough → "Failed to fetch").
|
||||
* Cung cấp trạng thái ready qua MSWContext để các hook fetch
|
||||
* (useApiData) tự chờ MSW sẵn sàng trước khi gọi API.
|
||||
*/
|
||||
const MSWProvider = ({ children }: { children: React.ReactNode }) => {
|
||||
// Production: render ngay (NODE_ENV !== 'development' → ready = true)
|
||||
// Development: chờ worker.start() xong rồi mới render
|
||||
const [ready, setReady] = useState(process.env.NODE_ENV !== 'development');
|
||||
|
||||
useEffect(() => {
|
||||
@@ -23,12 +22,11 @@ const MSWProvider = ({ children }: { children: React.ReactNode }) => {
|
||||
.then(() => setReady(true))
|
||||
.catch((err) => {
|
||||
console.error('[MSW] Failed to start worker:', err);
|
||||
setReady(true); // vẫn render dù worker fail
|
||||
setReady(true);
|
||||
});
|
||||
}, []);
|
||||
|
||||
if (!ready) return null;
|
||||
return <>{children}</>;
|
||||
return <MSWContext.Provider value={ready}>{children}</MSWContext.Provider>;
|
||||
};
|
||||
|
||||
export default MSWProvider;
|
||||
|
||||
5
src/components/common/Skeleton/index.tsx
Normal file
5
src/components/common/Skeleton/index.tsx
Normal file
@@ -0,0 +1,5 @@
|
||||
const Skeleton = ({ className = '' }: { className?: string }) => (
|
||||
<div className={`animate-pulse rounded-lg bg-gray-200 ${className}`} />
|
||||
);
|
||||
|
||||
export default Skeleton;
|
||||
@@ -10,7 +10,6 @@ export const ErrorLink = () => {
|
||||
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 }}
|
||||
@@ -34,23 +33,22 @@ export const ErrorLink = () => {
|
||||
<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.
|
||||
Nội dung bạn truy cập không tồn tại hoặc đường dẫn đã được 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ủ
|
||||
Về trang chủ
|
||||
</Link>
|
||||
|
||||
<Link
|
||||
href="/products"
|
||||
href="/pc-gaming"
|
||||
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
|
||||
Xem danh mục PC Gaming
|
||||
</Link>
|
||||
</div>
|
||||
</motion.div>
|
||||
|
||||
Reference in New Issue
Block a user