Commit 9f9abbe5 authored by Phạm Quang Bảo's avatar Phạm Quang Bảo

update/change to components nextjs

parent 3d0df67f
......@@ -10,6 +10,12 @@ const nextConfig: NextConfig = {
port: "",
pathname: "/vcci/images/**",
},
{
protocol: "https",
hostname: "vcci-hcm.org.vn", // WordPress / media host
port: "",
pathname: "/wp-content/uploads/**",
},
],
},
};
......
......@@ -2,21 +2,21 @@ import { EventItem } from '@/api/types/event'
import BASE_URL from '@/links'
import dayjs from 'dayjs';
import AppEditorContent from '@/components/shared/editor-content';
import Link from "next/link";
import ImageNext from "@/components/base/image";
function CardEvent({ event }: { event: EventItem }) {
return (
<a
<Link
href={`hoat-dong/su-kien/${event.id}`}
className='flex flex-row gap-2 mb-2 sm:gap-3 sm:mb-3 p-2 sm:p-3 border border-gray-200 bg-white rounded-md'
>
<img
<ImageNext
src={`${BASE_URL.imageEndpoint}${event.image}`}
alt={event.name}
className='w-[100px] md:w-[130px] aspect-3/2 object-cover'
onError={(e) => {
e.currentTarget.onerror = null
e.currentTarget.src = "/img-error.png"
}}
className='aspect-3/2 object-cover'
width={130}
height={86}
/>
<div className='flex-1'>
<p className='text-[#0056b3] font-bold text-sm line-clamp-2'>
......@@ -27,7 +27,7 @@ function CardEvent({ event }: { event: EventItem }) {
</p>
{/* <AppEditorContent className='line-clamp-2' value={event.description} /> */}
</div>
</a>
</Link>
);
}
......
......@@ -2,21 +2,21 @@ import { NewsItem } from "@/api/types/news";
import BASE_URL from "@/links";
import dayjs from "dayjs";
import AppEditorContent from "@/components/shared/editor-content";
import Link from "next/link";
import ImageNext from "@/components/base/image";
function CardNews({ news }: { news: NewsItem }) {
return (
<a
<Link
href={`${news.external_link}`}
className="flex flex-row gap-2 mb-2 sm:gap-3 sm:mb-3"
>
<img
<ImageNext
src={`${BASE_URL.imageEndpoint}${news.thumbnail}`}
alt={news.title}
className="w-[100px] md:w-[130px] aspect-3/2 object-cover"
onError={(e) => {
e.currentTarget.onerror = null
e.currentTarget.src = "/img-error.png"
}}
className="aspect-3/2 object-cover"
width={130}
height={86}
/>
<div className="flex-1">
<p className="text-[#363636] font-bold text-sm line-clamp-2">
......@@ -27,7 +27,7 @@ function CardNews({ news }: { news: NewsItem }) {
</p>
{/* <AppEditorContent className='line-clamp-2' value={news.description} /> */}
</div>
</a>
</Link>
);
}
......
"use client";
import Image from "next/image";
import { useEffect, useRef, useState } from "react";
// core
import { useRef, useState } from "react";
import Link from 'next/link'
// app
import { Autoplay, Grid } from "swiper/modules";
import { Swiper, SwiperSlide } from "swiper/react";
import { Swiper as SwiperType } from "swiper/types";
......@@ -9,42 +12,40 @@ import "swiper/css";
import "swiper/css/navigation";
import "swiper/css/pagination";
import BASE_URL from "@/links/index";
import dayjs from "dayjs";
import ImageNext from '@/components/base/image';
import { Spinner } from "@/components/ui";
import CardNews from "./components/card-news";
import CardEvent from "./components/card-event";
import EventCalendar from "@components/base/event-calendar";
import dayjs from "dayjs";
import stripImagesAndHtml from '@/helpers/stripImageAndHtml';
import partnerImages from '@/constants/partnerImages';
import memberImages from '@/constants/memberImages';
// server
import { useGetEvents } from "@/api/endpoints/event";
import { useGetCategory } from "@/api/endpoints/category";
import { useGetNews } from "@/api/endpoints/news";
import { GetCategoryAdminResponseType } from "@/api/types/category";
import { GetNewsResponseType, NewsItem } from "@/api/types/news";
import { EventApiResponse, EventItem } from "@/api/types/event";
import { ChevronsRight, Link } from "lucide-react";
import { useParams } from "next/navigation";
import { ChevronsRight } from "lucide-react";
const Page = () => {
// state
const [tab, setTab] = useState("all");
const [currentIndex, setCurrentIndex] = useState(0);
const swiperRef = useRef<SwiperType | null>(null);
// query
const { data: newsData, isLoading: isLoadingNews } = useGetNews<GetNewsResponseType>(
const { data: newsAll } = useGetNews<GetNewsResponseType>(
{
pageSize: '5',
filters: tab === "all" ? `` : `page_config.code @=${tab}`,
pageSize: '10',
}
);
const { data: newsAll, isLoading: isLoadingNewsAll } = useGetNews<GetNewsResponseType>(
const { data: newsData } = useGetNews<GetNewsResponseType>(
{
pageSize: '10',
pageSize: '5',
filters: tab === "all" ? `` : `page_config.code @=${tab}`,
}
);
const { data: eventData, isLoading: isLoadingEvent } = useGetEvents<EventApiResponse>();
const { data: businessOpportunities, isLoading: isLoadingBusinessOpportunities } = useGetNews<GetNewsResponseType>(
{
pageSize: '5',
......@@ -58,68 +59,8 @@ const Page = () => {
}
);
const { data: eventData, isLoading: isLoadingEvent } = useGetEvents<EventApiResponse>();
const now = new Date();
const eventDataFiltered = eventData?.responseData.rows.filter(event => {
const eventTime = new Date(event.start_time);
return eventTime > now;
});
// helpers
const stripImagesAndHtml = (html?: string) => {
if (!html) return ''
// remove img tags first
const withoutImgs = html.replace(/<img[^>]*>/gi, '')
// use DOMParser on client for robust extraction
if (typeof window !== 'undefined' && typeof DOMParser !== 'undefined') {
try {
const doc = new DOMParser().parseFromString(withoutImgs, 'text/html')
return doc.body.textContent || ''
} catch {
// fallback to regex
}
}
return withoutImgs.replace(/<[^>]*>/g, '')
}
const images = [
"/home/doi-tac/AMFORI-1.png.webp",
"/home/doi-tac/AUS4SKILLS-1.png.webp",
"/home/doi-tac/BetterWork-1.png.webp",
"/home/doi-tac/BOI-LOGO.jpg.webp",
"/home/doi-tac/DNV-logo-1-1.png.webp",
"/home/doi-tac/GERMAN-COOPERATION-1.png.webp",
"/home/doi-tac/GIZ.png.webp",
"/home/doi-tac/HBA.png.webp",
"/home/doi-tac/ILO-1.png.webp",
"/home/doi-tac/InvestHK.png.webp",
"/home/doi-tac/IOM-1.png.webp",
"/home/doi-tac/JICA-134x100-1-1.jpg.webp",
"/home/doi-tac/KIRBY.png.webp",
"/home/doi-tac/NHO-1.png.webp",
"/home/doi-tac/OXFAM-1.png.webp",
"/home/doi-tac/RECOTVET-1.png.webp",
"/home/doi-tac/SC-1.png.webp",
"/home/doi-tac/UNDP.png.webp",
];
const hoivien = [
"/home/hoi-vien-tieu-bieu/logo-GTD-768x768.png.webp",
"/home/hoi-vien-tieu-bieu/Nhua-Long-Thanh_Logo.jpg.webp",
"/home/hoi-vien-tieu-bieu/Nova_Group_logo-1.png.webp",
"/home/hoi-vien-tieu-bieu/samngoclinh-1-768x768.png.webp",
"/home/hoi-vien-tieu-bieu/Screenshot-2022-12-26-144136-768x768.png.webp",
"/home/hoi-vien-tieu-bieu/UOB-logo_Vuong.jpeg.webp",
];
// template
return (
(isLoadingBusinessOpportunities || isLoadingPolicyAndLegalInformation || isLoadingEvent) ? (
<div className="container w-full h-[80vh] flex justify-center items-center">
<Spinner />
</div>
) : (
<>
{/* Banner */}
<Swiper
......@@ -128,23 +69,25 @@ const Page = () => {
loop
slidesPerView={1}
onSwiper={(s) => (swiperRef.current = s)}
onSlideChange={(s) =>
setCurrentIndex(
typeof s.realIndex === "number" ? s.realIndex : s.activeIndex
)
}
>
<SwiperSlide>
<img
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"
<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}
priority
sizes="100vw"
className="w-full h-[200px] sm:h-[300px] md:h-[400px] lg:h-[500px] object-cover"
/>
</SwiperSlide>
<SwiperSlide>
<img
<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>
......@@ -176,34 +119,38 @@ const Page = () => {
>
{newsAll?.responseData?.rows.map((news) => (
<SwiperSlide key={news.id}>
<a
<Link
href={`${news.external_link}`}
className="relative block bg-white shadow-md overflow-hidden hover:shadow-lg transition-shadow duration-300"
>
<img
<ImageNext
src={`${BASE_URL.imageEndpoint}${news.thumbnail}`}
alt={news.title}
width={600}
height={400}
sizes="(max-width:640px) 100vw,(max-width:1024px) 50vw,33vw"
className="w-full aspect-3/2 sm:h-56 md:h-64 object-cover"
onError={(e) => {
e.currentTarget.onerror = null
e.currentTarget.src = "/img-error.png"
}}
/>
<div className="absolute bottom-0 left-0 right-0 h-20 md:h-24 bg-linear-to-t from-black/80 to-transparent flex items-center justify-center p-3">
<p className="text-white text-center font-semibold line-clamp-2 text-sm sm:text-base leading-snug">
{news.title}
</p>
</div>
</a>
</Link>
</SwiperSlide>
))}
</Swiper>
</section>
<div>
<a href="https://hardwaretools.com.vn/">
<img src="/home/Standard-Banner-1-2024.png.webp" alt="banner" />
</a>
<Link href="https://hardwaretools.com.vn/">
<ImageNext
src="/home/Standard-Banner-1-2024.png.webp"
alt="banner"
width={2560}
height={720}
/>
</Link>
</div>
{/* Tin tức + Liên kết nhanh */}
......@@ -211,18 +158,18 @@ const Page = () => {
{/* Left */}
<div className="flex-1">
<div className="flex justify-between items-center">
<a
<Link
href="/thong-tin-truyen-thong/tin-vcci/"
className="text-[18px] sm:text-[20px] font-semibold uppercase text-[#063e8e]"
>
Tin tức
</a>
<a
</Link>
<Link
href="/thong-tin-truyen-thong/tin-vcci/"
className="text-[#063e8e] text-sm sm:text-base"
>
<ChevronsRight />
</a>
</Link>
</div>
<hr className="border-[#063e8e] mb-4" />
......@@ -230,20 +177,19 @@ const Page = () => {
{newsAll?.responseData.rows
.slice(0, 1)
.map((news: NewsItem) => (
<a
<Link
key={news.id}
href={`${news.external_link}`}
className="flex flex-col w-full md:w-1/2 min-h-[180px] sm:min-h-[220px] gap-3 mb-3 bg-white"
>
<div className="w-full aspect-3/2 overflow-hidden">
<img
<ImageNext
src={`${BASE_URL.imageEndpoint}${news.thumbnail}`}
alt={news.title}
width={600}
height={400}
sizes="(max-width:768px) 100vw,50vw"
className="w-full h-full object-cover"
onError={(e) => {
e.currentTarget.onerror = null
e.currentTarget.src = "/img-error.png"
}}
/>
</div>
......@@ -253,7 +199,7 @@ const Page = () => {
</p>
<p className="line-clamp-4 text-justify">{stripImagesAndHtml(news.description)}</p>
</div>
</a>
</Link>
))}
<div className="w-full md:w-1/2">
......@@ -263,8 +209,7 @@ const Page = () => {
? " bg-[#d3d3d3] text-[#063e8e] font-semibold"
: "border-gray-300 text-[#363636] bg-[#e8e8e8] hover:bg-[#e8e8e8] hover:text-[#063e8e] font-semibold"
}`}
onClick={() => setTab("all")}
>
onClick={() => setTab("all")}>
Tất cả
</button>
<button
......@@ -272,7 +217,7 @@ const Page = () => {
? "bg-[#d3d3d3] text-[#063e8e] font-semibold"
: "border-gray-300 text-[#363636] bg-[#e8e8e8] hover:bg-[#e8e8e8] hover:text-[#063e8e] font-semibold"
}`}
onClick={() => setTab(`tin-vcci`)}
onClick={() => setTab("tin-vcci")}
>
Tin VCCI
</button>
......@@ -281,7 +226,7 @@ const Page = () => {
? "bg-[#d3d3d3] text-[#063e8e] font-semibold"
: "border-gray-300 text-[#363636] bg-[#e8e8e8] hover:bg-[#e8e8e8] hover:text-[#063e8e] font-semibold"
}`}
onClick={() => setTab(`tin-kinh-te`)}
onClick={() => setTab("tin-kinh-te")}
>
Tin Kinh Tế
</button>
......@@ -290,7 +235,7 @@ const Page = () => {
? "bg-[#d3d3d3] text-[#063e8e] font-semibold"
: "border-gray-300 text-[#363636] bg-[#e8e8e8] hover:bg-[#e8e8e8] hover:text-[#063e8e] font-semibold"
}`}
onClick={() => setTab(`chuyen-de`)}
onClick={() => setTab("chuyen-de")}
>
Chuyên Đề
</button>
......@@ -313,36 +258,31 @@ const Page = () => {
<hr className="border-[#063e8e] mb-4" />
<div className="space-y-2 text-[#063e8e] text-sm md:text-base pb-10">
<div>
<a
<Link
className="text-[#363636]"
href="https://vcci-hcm.org.vn/lien-ket-nhanh/cam-nang-huong-dan-dau-tu-kinh-doanh-tai-viet-nam-2023/"
>
<Link
size={16}
className="inline mr-2 font-semibold text-[#063e8e]"
style={{ verticalAlign: "middle", marginTop: "-2px" }}
/>
Cẩm nang hướng dẫn đầu tư kinh doanh tại Việt Nam
</a>
🔗 Cẩm nang hướng dẫn đầu tư kinh doanh tại Việt Nam
</Link>
</div>
<div>
<a
<Link
className="text-[#363636]"
href="https://vcci-hcm.org.vn/lien-ket-nhanh/doanh-nghiep-kien-nghi-ve-chinh-sach-va-phap-luat/"
>
<Link
size={16}
className="inline mr-2 font-semibold text-[#063e8e]"
style={{ verticalAlign: "middle", marginTop: "-2px" }}
/>
Doanh nghiệp kiến nghị về chính sách và pháp luật
</a>
🔗 Doanh nghiệp kiến nghị về chính sách và pháp luật
</Link>
</div>
</div>
<div>
<a href="https://hardwaretools.com.vn/">
<img src="/home/20-2048x1365.webp" alt="banner" />
</a>
<Link href="https://hardwaretools.com.vn/">
<ImageNext
src="/home/20-2048x1365.webp"
alt="banner"
width={2048}
height={1365}
/>
</Link>
</div>
</aside>
</section >
......@@ -354,28 +294,33 @@ const Page = () => {
<h2 className="text-[18px] sm:text-[20px] font-bold uppercase text-[#e8c518]">
Sự kiện sắp diễn ra
</h2>
<a href="/hoat-dong/su-kien" className="text-[#e8c518] text-sm sm:text-base">
<Link href="/hoat-dong/su-kien" className="text-[#e8c518] text-sm sm:text-base">
<ChevronsRight />
</a>
</Link>
</div>
<hr className="border-[#e8c518] mb-4" />
<div className="flex flex-col md:flex-row gap-5">
{eventDataFiltered?.slice(0, 1).map((event: EventItem) => (
<a
{isLoadingEvent ? (
<div className="container w-full h-[80vh] flex justify-center items-center">
<Spinner />
</div>
) : (
<>
{eventData?.responseData.rows.slice(0, 1).map((event: EventItem) => (
<Link
key={event.id}
href={`hoat-dong/su-kien/${event.id}`}
className="flex flex-col w-full md:w-1/2 min-h-[180px] sm:min-h-[220px] gap-3 mb-3 border border-gray-200 bg-white rounded-md p-3"
>
<div className="w-full aspect-3/2 overflow-hidden">
<img
<ImageNext
src={`${BASE_URL.imageEndpoint}${event.image}`}
alt={event.name}
width={600}
height={400}
sizes="(max-width:768px) 100vw,50vw"
className="w-full h-full object-cover"
onError={(e) => {
(e.target as HTMLImageElement).src =
"/img-error.png";
}}
/>
</div>
......@@ -388,13 +333,15 @@ const Page = () => {
</p>
<p className="line-clamp-3 text-justify">{stripImagesAndHtml(event.description)}</p>
</div>
</a>
</Link>
))}
<div className="w-full md:w-1/2">
{eventDataFiltered?.slice(0, 4).map((event) => (
{eventData?.responseData.rows.slice(0, 4).map((event) => (
<CardEvent key={event.id} event={event} />
))}
</div>
</>
)}
</div>
</div>
<div className="bg-[#063e8e] w-full lg:w-[30%] p-5">
......@@ -403,12 +350,12 @@ const Page = () => {
<h2 className="text-[18px] sm:text-[20px] font-bold uppercase text-[#e8c518]">
Lịch sự kiện
</h2>
<a
<Link
href="/hoat-dong/su-kien"
className="text-[#e8c518] hover:underline text-sm sm:text-base"
>
<ChevronsRight />
</a>
</Link>
</div>
<hr className="border-[#e8c518] mb-4" />
<EventCalendar />
......@@ -420,41 +367,51 @@ const Page = () => {
< div className="flex flex-col lg:flex-row gap-5 pb-10 mb-0" >
<div className="flex flex-col flex-1">
<div>
<a href="https://vcci-hcm.org.vn/wp-content/uploads/2022/11/MEDIA-KIT_VCCI-HCM-2022-Final.pdf">
<img src="/home/Standard-Banner-1-2024.png.webp" alt="banner" />
</a>
<Link href="https://vcci-hcm.org.vn/wp-content/uploads/2022/11/MEDIA-KIT_VCCI-HCM-2022-Final.pdf">
<ImageNext
src="/home/Standard-Banner-1-2024.png.webp"
alt="banner"
width={2560}
height={720}
/>
</Link>
</div>
<section className="flex flex-col md:flex-row gap-5 pt-8">
<div className="flex-1">
<div className="flex justify-between items-center">
<a
<Link
href="/xuc-tien-thuong-mai/co-hoi/"
className="text-[18px] sm:text-[20px] font-bold uppercase text-[#063e8e]"
>
Cơ hội kinh doanh
</a>
<a
</Link>
<Link
href="/xuc-tien-thuong-mai/co-hoi/"
className="text-[#063e8e] text-sm sm:text-base"
>
<ChevronsRight />
</a>
</Link>
</div>
<hr className="border-[#063e8e] mb-4" />
<div className="pt-2">
{isLoadingBusinessOpportunities ? (
<div className="container w-full h-[80vh] flex justify-center items-center">
<Spinner />
</div>
) : (
<>
{businessOpportunities?.responseData.rows
.slice(0, 1)
.map((news: NewsItem) => (
<a key={news.id} href={`${news.external_link}`}>
<Link key={news.id} href={`${news.external_link}`}>
<div className="w-full aspect-3/2 relative overflow-hidden mb-5">
<img
<ImageNext
src={`${BASE_URL.imageEndpoint}${news.thumbnail}`}
alt={news.title}
width={600}
height={400}
sizes="(max-width:768px) 100vw,50vw"
className="w-full h-full object-cover"
onError={(e) => {
e.currentTarget.onerror = null
e.currentTarget.src = "/img-error.png"
}}
/>
<div className="absolute bg-white opacity-80 bottom-5 left-5 right-5 p-5">
<p className="text-[#063e8e] font-semibold text-sm sm:text-base z-10 line-clamp-3">
......@@ -462,44 +419,51 @@ const Page = () => {
</p>
</div>
</div>
</a>
</Link>
))}
{businessOpportunities?.responseData.rows.slice(0, 3).map((news) => (
<CardNews key={news.id} news={news} />
))}
</>
)}
</div>
</div>
<div className="flex-1">
<div className="flex justify-between items-center">
<a
<Link
href="/thong-tin-truyen-thong/phap-luat"
className="text-[18px] sm:text-[20px] font-bold uppercase text-[#063e8e]"
>
Chính sách & pháp luật
</a>
<a
</Link>
<Link
href="/thong-tin-truyen-thong/phap-luat"
className="text-[#063e8e] text-sm sm:text-base"
>
<ChevronsRight />
</a>
</Link>
</div>
<hr className="border-[#063e8e] mb-4" />
<div className="pt-2">
{isLoadingPolicyAndLegalInformation ? (
<div className="container w-full h-[80vh] flex justify-center items-center">
<Spinner />
</div>
) : (
<>
{policyAndLegalInformation?.responseData.rows
.slice(0, 1)
.map((news: NewsItem) => (
<a key={news.id} href={`${news.external_link}`}>
<Link key={news.id} href={`${news.external_link}`}>
<div className="w-full aspect-3/2 relative overflow-hidden mb-5">
<img
<ImageNext
src={`${BASE_URL.imageEndpoint}${news.thumbnail}`}
alt={news.title}
width={600}
height={400}
sizes="(max-width:768px) 100vw,50vw"
className="w-full h-full object-cover"
onError={(e) => {
e.currentTarget.onerror = null
e.currentTarget.src = "/img-error.png"
}}
/>
<div className="absolute bg-white opacity-80 bottom-5 left-5 right-5 p-5">
<p className="text-[#063e8e] font-semibold text-sm sm:text-base z-10 line-clamp-3">
......@@ -507,37 +471,43 @@ const Page = () => {
</p>
</div>
</div>
</a>
</Link>
))}
{policyAndLegalInformation?.responseData.rows.slice(0, 3).map((news) => (
<CardNews key={news.id} news={news} />
))}
</>
)}
</div>
</div>
</section>
</div>
<div className="w-full lg:w-[30%] justify-center items-start flex">
<a href="https://smartgara.ecaraid.com/">
<img src="/home/eCarAid_web_banner_600x400.webp" alt="banner" />
</a>
<Link href="https://smartgara.ecaraid.com/">
<ImageNext
src="/home/eCarAid_web_banner_600x400.webp"
alt="banner"
width={600}
height={400}
/>
</Link>
</div>
</div >
{/* Hội viên tiêu biểu */}
< section className="flex flex-col lg:flex-row gap-5 pb-10 mb-0" >
<section className="flex flex-col lg:flex-row gap-5 pb-10 mb-0" >
{/* left */}
< aside className="w-full lg:w-1/3 flex-1 bg-[#e8c518] p-5" >
<div className="flex justify-between items-center mb-3">
<h2 className="text-xl font-bold uppercase text-[#063e8e]">
Hội viên tiêu biểu
</h2>
<a
<Link
href="/danh-ba-hoi-vien"
className="text-[#063e8e] hover:underline text-sm font-medium"
>
<ChevronsRight />
</a>
</Link>
</div>
<hr className="border-[#063e8e] mb-5" />
<div>
......@@ -554,12 +524,15 @@ const Page = () => {
}}
className="partner-swiper"
>
{hoivien.map((src, i) => (
{memberImages.map((src, i) => (
<SwiperSlide key={i}>
<div className="aspect-square flex justify-center items-center bg-white rounded-lg shadow">
<img
<ImageNext
src={src}
alt={`partner-${i}`}
width={160}
height={160}
sizes="(max-width:640px) 25vw,(max-width:1024px) 15vw,10vw"
className="w-3/4 h-3/4 object-contain"
/>
</div>
......@@ -591,12 +564,15 @@ const Page = () => {
}}
className="partner-swiper"
>
{images.map((src, i) => (
{partnerImages.map((src, i) => (
<SwiperSlide key={i}>
<div className="aspect-square flex justify-center items-center bg-white rounded-lg shadow">
<img
<ImageNext
src={src}
alt={`partner-${i}`}
width={160}
height={160}
sizes="(max-width:640px) 25vw,(max-width:1024px) 15vw,10vw"
className="w-3/4 h-3/4 object-contain"
/>
</div>
......@@ -615,12 +591,12 @@ const Page = () => {
<h2 className="text-xl font-bold uppercase text-[#063e8e]">
Video
</h2>
<a
<Link
href="/video"
className="text-[#063e8e] hover:underline text-sm font-medium"
>
<ChevronsRight />
</a>
</Link>
</div>
<hr className="border-[#063e8e] mb-5" />
<div className="flex flex-col md:flex-row gap-4 md:gap-6">
......@@ -676,12 +652,15 @@ const Page = () => {
}}
className="partner-swiper"
>
{images.map((src, i) => (
{partnerImages.map((src, i) => (
<SwiperSlide key={i}>
<div className="aspect-square flex justify-center items-center bg-white rounded-lg shadow">
<img
<ImageNext
src={src}
alt={`partner-${i}`}
width={160}
height={160}
sizes="(max-width:640px) 25vw,(max-width:1024px) 15vw,10vw"
className="w-3/4 h-3/4 object-contain"
/>
</div>
......@@ -689,11 +668,10 @@ const Page = () => {
))}
</Swiper>
</div>
</aside >
</section >
</div >
</aside>
</section>
</div>
</>
)
);
};
......
import Image from "next/image";
import { useState } from "react";
const ImageNext = ({ src, alt, width, height, className, onError }: any) => {
const [imgSrc, setImgSrc] = useState(src);
return (
<Image
src={imgSrc}
alt={alt}
width={width}
height={height}
className={className}
onError={() => setImgSrc(onError || "/img-error.png")}
/>
);
};
export default ImageNext;
const memberImages = [
"/home/hoi-vien-tieu-bieu/logo-GTD-768x768.png.webp",
"/home/hoi-vien-tieu-bieu/Nhua-Long-Thanh_Logo.jpg.webp",
"/home/hoi-vien-tieu-bieu/Nova_Group_logo-1.png.webp",
"/home/hoi-vien-tieu-bieu/samngoclinh-1-768x768.png.webp",
"/home/hoi-vien-tieu-bieu/Screenshot-2022-12-26-144136-768x768.png.webp",
"/home/hoi-vien-tieu-bieu/UOB-logo_Vuong.jpeg.webp",
];
export default memberImages;
\ No newline at end of file
const partnerImages = [
"/home/doi-tac/AMFORI-1.png.webp",
"/home/doi-tac/AUS4SKILLS-1.png.webp",
"/home/doi-tac/BetterWork-1.png.webp",
"/home/doi-tac/BOI-LOGO.jpg.webp",
"/home/doi-tac/DNV-logo-1-1.png.webp",
"/home/doi-tac/GERMAN-COOPERATION-1.png.webp",
"/home/doi-tac/GIZ.png.webp",
"/home/doi-tac/HBA.png.webp",
"/home/doi-tac/ILO-1.png.webp",
"/home/doi-tac/InvestHK.png.webp",
"/home/doi-tac/IOM-1.png.webp",
"/home/doi-tac/JICA-134x100-1-1.jpg.webp",
"/home/doi-tac/KIRBY.png.webp",
"/home/doi-tac/NHO-1.png.webp",
"/home/doi-tac/OXFAM-1.png.webp",
"/home/doi-tac/RECOTVET-1.png.webp",
"/home/doi-tac/SC-1.png.webp",
"/home/doi-tac/UNDP.png.webp",
];
export default partnerImages;
\ No newline at end of file
const stripImagesAndHtml = (html?: string) => {
if (!html) return ''
// remove img tags first
const withoutImgs = html.replace(/<img[^>]*>/gi, '')
// use DOMParser on client for robust extraction
if (typeof window !== 'undefined' && typeof DOMParser !== 'undefined') {
try {
const doc = new DOMParser().parseFromString(withoutImgs, 'text/html')
return doc.body.textContent || ''
} catch {
// fallback to regex
}
}
return withoutImgs.replace(/<[^>]*>/g, '')
}
export default stripImagesAndHtml
\ No newline at end of file
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