Commit 91a2bc40 authored by Lê Đức Huy's avatar Lê Đức Huy

feat: implement header, admin views, sidebar, and core api query configurations

parent 1334e92b
...@@ -27,8 +27,10 @@ const handleCheckBaseRetryLogical = (failureCount: number, error: Error) => { ...@@ -27,8 +27,10 @@ const handleCheckBaseRetryLogical = (failureCount: number, error: Error) => {
// Expired token error // Expired token error
if (error.response?.status === EXPIRED_TOKEN_ERROR) { if (error.response?.status === EXPIRED_TOKEN_ERROR) {
handleUnAuthorizationError() if (typeof window !== "undefined" && window.location.pathname.startsWith("/admin")) {
return false handleUnAuthorizationError();
}
return false;
} }
// Denied permission error // Denied permission error
......
'use client'; "use client";
import ImageNext from "@/components/shared/image-next"; import ImageNext from "@/components/shared/image-next";
import { Swiper, SwiperSlide } from "swiper/react"; import { Swiper, SwiperSlide } from "swiper/react";
...@@ -7,40 +7,98 @@ import { Swiper as SwiperType } from "swiper/types"; ...@@ -7,40 +7,98 @@ import { Swiper as SwiperType } from "swiper/types";
import { useRef } from "react"; import { useRef } from "react";
import "swiper/css"; import "swiper/css";
import { useGetBanner } from "@/api/endpoints/banner";
import { useQuery } from "@tanstack/react-query";
import { fetchCmsFileById, resolveCmsFileUrl } from "@/lib/api/files";
import { Skeleton } from "@/components/ui/skeleton";
type ApiEnvelope<T> = {
responseData?: T;
data?: {
responseData?: T;
};
};
const getEnvelopeData = <T,>(payload?: ApiEnvelope<T>) =>
payload?.responseData ?? payload?.data?.responseData;
function BannerSlideItem({ fileId, alt }: { fileId: string; alt: string }) {
const { data: file, isPending } = useQuery({
queryKey: ["file", fileId],
queryFn: () => fetchCmsFileById(fileId),
enabled: !!fileId,
});
if (isPending) {
return (
<Skeleton className="w-full h-[200px] sm:h-[300px] md:h-[400px] lg:h-[500px]" />
);
}
const url = file ? resolveCmsFileUrl(file.path) : "/img-error.png";
return (
<ImageNext
src={url}
alt={alt}
width={2560}
height={720}
sizes="100vw"
className="w-full h-[200px] sm:h-[300px] md:h-[400px] lg:h-[500px] object-cover"
/>
);
}
const Banner = () => { const Banner = () => {
const swiperRef = useRef<SwiperType | null>(null); const swiperRef = useRef<SwiperType | null>(null);
const { data: bannerData } = useGetBanner({
filters: "status@=ACTIVE",
sortField: "display_order",
sortOrder: "asc",
});
const pageData = getEnvelopeData<{ rows?: any[] }>(bannerData);
const rows = pageData?.rows ?? [];
if (!rows || rows.length === 0) {
return (
<div className="w-full h-[200px] sm:h-[300px] md:h-[400px] lg:h-[500px] bg-slate-100 flex items-center justify-center">
<Skeleton className="w-full h-full" />
</div>
);
}
return ( return (
<Swiper <Swiper
modules={[Autoplay]} modules={[Autoplay]}
autoplay={{ delay: 4000, disableOnInteraction: false }} autoplay={{ delay: 4000, disableOnInteraction: false }}
loop loop={rows.length > 1}
slidesPerView={1} slidesPerView={1}
onSwiper={(s) => (swiperRef.current = s)} onSwiper={(s) => (swiperRef.current = s)}
className="w-full overflow-hidden" className="w-full overflow-hidden"
> >
<SwiperSlide> {rows.map((row: any) => (
<ImageNext <SwiperSlide key={row.id}>
src="https://vcci-hcm.org.vn/wp-content/uploads/2025/10/1.1.-Hero-Banner-CEO-2025-Bi-Sai-Nam-2025-Nhe-2560x720-Px.jpg.webp" {row.file_id ? (
alt="Banner" <BannerSlideItem
width={2560} fileId={row.file_id}
height={720} alt={row.banner_name || "Banner"}
sizes="100vw"
className="w-full h-[200px] sm:h-[300px] md:h-[400px] lg:h-[500px] object-cover"
/> />
</SwiperSlide> ) : (
<SwiperSlide>
<ImageNext <ImageNext
src="https://vcci-hcm.org.vn/wp-content/uploads/2022/07/Landscape-HCM_3-01.png" src="/img-error.png"
alt="Banner" alt={row.banner_name || "Banner"}
width={2560} width={2560}
height={720} height={720}
sizes="100vw" sizes="100vw"
className="w-full h-[200px] sm:h-[300px] md:h-[400px] lg:h-[500px] object-cover" className="w-full h-[200px] sm:h-[300px] md:h-[400px] lg:h-[500px] object-cover"
/> />
)}
</SwiperSlide> </SwiperSlide>
))}
</Swiper> </Swiper>
); );
} };
export default Banner; export default Banner;
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment