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
// Core
import { AxiosError, isAxiosError } from 'axios'
import { QueryClient } from '@tanstack/react-query'
// App
// import router from '@/router'
import { handleAdminUnauthorized } from '@/lib/auth/admin-auth'
// import useProfileStore from '@stores/profile'
import { QueryData } from '@/lib/types/base-api'
import { AxiosError, isAxiosError } from 'axios'
import { QueryClient } from '@tanstack/react-query'
// App
// import router from '@/router'
import { handleAdminUnauthorized } from '@/lib/auth/admin-auth'
// import useProfileStore from '@stores/profile'
import { QueryData } from '@/lib/types/base-api'
// import { BASE_PATHS } from '@/constants/path'
// Constants
const RETRY_COUNT = 3
const EXPIRED_TOKEN_ERROR = 401
const DENIED_PERMISSION_ERROR = 403
const INTERNAL_SERVER_ERROR = 500
const API_QUERY_STALE_TIME = 2 * 60 * 1000
const API_QUERY_GC_TIME = 10 * 60 * 1000
const RETRY_COUNT = 3
const EXPIRED_TOKEN_ERROR = 401
const DENIED_PERMISSION_ERROR = 403
const INTERNAL_SERVER_ERROR = 500
const API_QUERY_STALE_TIME = 2 * 60 * 1000
const API_QUERY_GC_TIME = 10 * 60 * 1000
// Utils
// Handle check base retry logical
......@@ -27,8 +27,10 @@ const handleCheckBaseRetryLogical = (failureCount: number, error: Error) => {
// Expired token error
if (error.response?.status === EXPIRED_TOKEN_ERROR) {
handleUnAuthorizationError()
return false
if (typeof window !== "undefined" && window.location.pathname.startsWith("/admin")) {
handleUnAuthorizationError();
}
return false;
}
// Denied permission error
......@@ -42,15 +44,15 @@ const handleCheckBaseRetryLogical = (failureCount: number, error: Error) => {
}
// Handle un authorization error
const handleUnAuthorizationError = () => {
void handleAdminUnauthorized()
// useProfileStore.getState().resetStore()
// const languageAwarePath = addLanguageToPath({
// path: BASE_PATHS.authSignIn
// })
// router.navigate('')
}
const handleUnAuthorizationError = () => {
void handleAdminUnauthorized()
// useProfileStore.getState().resetStore()
// const languageAwarePath = addLanguageToPath({
// path: BASE_PATHS.authSignIn
// })
// router.navigate('')
}
// Handle delay value
const handleDelayRetry = (failureCount: number) => failureCount * 1000 + Math.random() * 1000
......@@ -58,13 +60,13 @@ const handleDelayRetry = (failureCount: number) => failureCount * 1000 + Math.ra
// Query client
export const queryClient = new QueryClient({
defaultOptions: {
queries: {
staleTime: API_QUERY_STALE_TIME,
gcTime: API_QUERY_GC_TIME,
refetchOnWindowFocus: false,
refetchOnMount: false,
refetchOnReconnect: false,
placeholderData: (previousData: unknown) => previousData,
queries: {
staleTime: API_QUERY_STALE_TIME,
gcTime: API_QUERY_GC_TIME,
refetchOnWindowFocus: false,
refetchOnMount: false,
refetchOnReconnect: false,
placeholderData: (previousData: unknown) => previousData,
retry(failureCount, error) {
if (!handleCheckBaseRetryLogical(failureCount, error)) return false
......
'use client';
"use client";
import ImageNext from "@/components/shared/image-next";
import { Swiper, SwiperSlide } from "swiper/react";
......@@ -7,40 +7,98 @@ import { Swiper as SwiperType } from "swiper/types";
import { useRef } from "react";
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 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 (
<Swiper
modules={[Autoplay]}
autoplay={{ delay: 4000, disableOnInteraction: false }}
loop
loop={rows.length > 1}
slidesPerView={1}
onSwiper={(s) => (swiperRef.current = s)}
className="w-full overflow-hidden"
>
<SwiperSlide>
<ImageNext
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"
alt="Banner"
width={2560}
height={720}
sizes="100vw"
className="w-full h-[200px] sm:h-[300px] md:h-[400px] lg:h-[500px] object-cover"
/>
</SwiperSlide>
<SwiperSlide>
<ImageNext
src="https://vcci-hcm.org.vn/wp-content/uploads/2022/07/Landscape-HCM_3-01.png"
alt="Banner"
width={2560}
height={720}
sizes="100vw"
className="w-full h-[200px] sm:h-[300px] md:h-[400px] lg:h-[500px] object-cover"
/>
</SwiperSlide>
{rows.map((row: any) => (
<SwiperSlide key={row.id}>
{row.file_id ? (
<BannerSlideItem
fileId={row.file_id}
alt={row.banner_name || "Banner"}
/>
) : (
<ImageNext
src="/img-error.png"
alt={row.banner_name || "Banner"}
width={2560}
height={720}
sizes="100vw"
className="w-full h-[200px] sm:h-[300px] md:h-[400px] lg:h-[500px] object-cover"
/>
)}
</SwiperSlide>
))}
</Swiper>
);
}
};
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