Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
V
VCCI-News
Project
Project
Details
Activity
Releases
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
Văn Hoàng
VCCI-News
Commits
9f9abbe5
Commit
9f9abbe5
authored
Nov 25, 2025
by
Phạm Quang Bảo
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
update/change to components nextjs
parent
3d0df67f
Changes
8
Hide whitespace changes
Inline
Side-by-side
Showing
8 changed files
with
668 additions
and
616 deletions
+668
-616
next.config.ts
next.config.ts
+7
-1
index.tsx
src/app/(main)/(home)/components/card-event/index.tsx
+8
-8
index.tsx
src/app/(main)/(home)/components/card-news/index.tsx
+8
-8
page.tsx
src/app/(main)/(home)/page.tsx
+577
-599
index.tsx
src/components/base/image/index.tsx
+19
-0
memberImages.ts
src/constants/memberImages.ts
+10
-0
partnerImages.ts
src/constants/partnerImages.ts
+22
-0
stripImageAndHtml.ts
src/helpers/stripImageAndHtml.ts
+17
-0
No files found.
next.config.ts
View file @
9f9abbe5
...
@@ -2,7 +2,7 @@ import type { NextConfig } from "next";
...
@@ -2,7 +2,7 @@ import type { NextConfig } from "next";
import
links
from
"./src/links/index"
;
import
links
from
"./src/links/index"
;
const
nextConfig
:
NextConfig
=
{
const
nextConfig
:
NextConfig
=
{
/* config options here */
/* config options here */
images
:
{
images
:
{
remotePatterns
:
[
remotePatterns
:
[
{
{
protocol
:
"https"
,
protocol
:
"https"
,
...
@@ -10,6 +10,12 @@ const nextConfig: NextConfig = {
...
@@ -10,6 +10,12 @@ const nextConfig: NextConfig = {
port
:
""
,
port
:
""
,
pathname
:
"/vcci/images/**"
,
pathname
:
"/vcci/images/**"
,
},
},
{
protocol
:
"https"
,
hostname
:
"vcci-hcm.org.vn"
,
// WordPress / media host
port
:
""
,
pathname
:
"/wp-content/uploads/**"
,
},
],
],
},
},
};
};
...
...
src/app/(main)/(home)/components/card-event/index.tsx
View file @
9f9abbe5
...
@@ -2,21 +2,21 @@ import { EventItem } from '@/api/types/event'
...
@@ -2,21 +2,21 @@ import { EventItem } from '@/api/types/event'
import
BASE_URL
from
'@/links'
import
BASE_URL
from
'@/links'
import
dayjs
from
'dayjs'
;
import
dayjs
from
'dayjs'
;
import
AppEditorContent
from
'@/components/shared/editor-content'
;
import
AppEditorContent
from
'@/components/shared/editor-content'
;
import
Link
from
"next/link"
;
import
ImageNext
from
"@/components/base/image"
;
function
CardEvent
({
event
}:
{
event
:
EventItem
})
{
function
CardEvent
({
event
}:
{
event
:
EventItem
})
{
return
(
return
(
<
a
<
Link
href=
{
`hoat-dong/su-kien/${event.id}`
}
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'
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}`
}
src=
{
`${BASE_URL.imageEndpoint}${event.image}`
}
alt=
{
event
.
name
}
alt=
{
event
.
name
}
className=
'w-[100px] md:w-[130px] aspect-3/2 object-cover'
className=
'aspect-3/2 object-cover'
onError=
{
(
e
)
=>
{
width=
{
130
}
e
.
currentTarget
.
onerror
=
null
height=
{
86
}
e
.
currentTarget
.
src
=
"/img-error.png"
}
}
/>
/>
<
div
className=
'flex-1'
>
<
div
className=
'flex-1'
>
<
p
className=
'text-[#0056b3] font-bold text-sm line-clamp-2'
>
<
p
className=
'text-[#0056b3] font-bold text-sm line-clamp-2'
>
...
@@ -27,7 +27,7 @@ function CardEvent({ event }: { event: EventItem }) {
...
@@ -27,7 +27,7 @@ function CardEvent({ event }: { event: EventItem }) {
</
p
>
</
p
>
{
/* <AppEditorContent className='line-clamp-2' value={event.description} /> */
}
{
/* <AppEditorContent className='line-clamp-2' value={event.description} /> */
}
</
div
>
</
div
>
</
a
>
</
Link
>
);
);
}
}
...
...
src/app/(main)/(home)/components/card-news/index.tsx
View file @
9f9abbe5
...
@@ -2,21 +2,21 @@ import { NewsItem } from "@/api/types/news";
...
@@ -2,21 +2,21 @@ import { NewsItem } from "@/api/types/news";
import
BASE_URL
from
"@/links"
;
import
BASE_URL
from
"@/links"
;
import
dayjs
from
"dayjs"
;
import
dayjs
from
"dayjs"
;
import
AppEditorContent
from
"@/components/shared/editor-content"
;
import
AppEditorContent
from
"@/components/shared/editor-content"
;
import
Link
from
"next/link"
;
import
ImageNext
from
"@/components/base/image"
;
function
CardNews
({
news
}:
{
news
:
NewsItem
})
{
function
CardNews
({
news
}:
{
news
:
NewsItem
})
{
return
(
return
(
<
a
<
Link
href=
{
`${news.external_link}`
}
href=
{
`${news.external_link}`
}
className=
"flex flex-row gap-2 mb-2 sm:gap-3 sm:mb-3"
className=
"flex flex-row gap-2 mb-2 sm:gap-3 sm:mb-3"
>
>
<
img
<
ImageNext
src=
{
`${BASE_URL.imageEndpoint}${news.thumbnail}`
}
src=
{
`${BASE_URL.imageEndpoint}${news.thumbnail}`
}
alt=
{
news
.
title
}
alt=
{
news
.
title
}
className=
"w-[100px] md:w-[130px] aspect-3/2 object-cover"
className=
"aspect-3/2 object-cover"
onError=
{
(
e
)
=>
{
width=
{
130
}
e
.
currentTarget
.
onerror
=
null
height=
{
86
}
e
.
currentTarget
.
src
=
"/img-error.png"
}
}
/>
/>
<
div
className=
"flex-1"
>
<
div
className=
"flex-1"
>
<
p
className=
"text-[#363636] font-bold text-sm line-clamp-2"
>
<
p
className=
"text-[#363636] font-bold text-sm line-clamp-2"
>
...
@@ -27,7 +27,7 @@ function CardNews({ news }: { news: NewsItem }) {
...
@@ -27,7 +27,7 @@ function CardNews({ news }: { news: NewsItem }) {
</
p
>
</
p
>
{
/* <AppEditorContent className='line-clamp-2' value={news.description} /> */
}
{
/* <AppEditorContent className='line-clamp-2' value={news.description} /> */
}
</
div
>
</
div
>
</
a
>
</
Link
>
);
);
}
}
...
...
src/app/(main)/(home)/page.tsx
View file @
9f9abbe5
"use client"
;
"use client"
;
import
Image
from
"next/image"
;
// core
import
{
useEffect
,
useRef
,
useState
}
from
"react"
;
import
{
useRef
,
useState
}
from
"react"
;
import
Link
from
'next/link'
// app
import
{
Autoplay
,
Grid
}
from
"swiper/modules"
;
import
{
Autoplay
,
Grid
}
from
"swiper/modules"
;
import
{
Swiper
,
SwiperSlide
}
from
"swiper/react"
;
import
{
Swiper
,
SwiperSlide
}
from
"swiper/react"
;
import
{
Swiper
as
SwiperType
}
from
"swiper/types"
;
import
{
Swiper
as
SwiperType
}
from
"swiper/types"
;
...
@@ -9,42 +12,40 @@ import "swiper/css";
...
@@ -9,42 +12,40 @@ import "swiper/css";
import
"swiper/css/navigation"
;
import
"swiper/css/navigation"
;
import
"swiper/css/pagination"
;
import
"swiper/css/pagination"
;
import
BASE_URL
from
"@/links/index"
;
import
BASE_URL
from
"@/links/index"
;
import
dayjs
from
"dayjs"
;
import
ImageNext
from
'@/components/base/image'
;
import
{
Spinner
}
from
"@/components/ui"
;
import
{
Spinner
}
from
"@/components/ui"
;
import
CardNews
from
"./components/card-news"
;
import
CardNews
from
"./components/card-news"
;
import
CardEvent
from
"./components/card-event"
;
import
CardEvent
from
"./components/card-event"
;
import
EventCalendar
from
"@components/base/event-calendar"
;
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
// server
import
{
useGetEvents
}
from
"@/api/endpoints/event"
;
import
{
useGetEvents
}
from
"@/api/endpoints/event"
;
import
{
useGetCategory
}
from
"@/api/endpoints/category"
;
import
{
useGetNews
}
from
"@/api/endpoints/news"
;
import
{
useGetNews
}
from
"@/api/endpoints/news"
;
import
{
GetCategoryAdminResponseType
}
from
"@/api/types/category"
;
import
{
GetNewsResponseType
,
NewsItem
}
from
"@/api/types/news"
;
import
{
GetNewsResponseType
,
NewsItem
}
from
"@/api/types/news"
;
import
{
EventApiResponse
,
EventItem
}
from
"@/api/types/event"
;
import
{
EventApiResponse
,
EventItem
}
from
"@/api/types/event"
;
import
{
ChevronsRight
,
Link
}
from
"lucide-react"
;
import
{
ChevronsRight
}
from
"lucide-react"
;
import
{
useParams
}
from
"next/navigation"
;
const
Page
=
()
=>
{
const
Page
=
()
=>
{
// state
const
[
tab
,
setTab
]
=
useState
(
"all"
);
const
[
tab
,
setTab
]
=
useState
(
"all"
);
const
[
currentIndex
,
setCurrentIndex
]
=
useState
(
0
);
const
swiperRef
=
useRef
<
SwiperType
|
null
>
(
null
);
const
swiperRef
=
useRef
<
SwiperType
|
null
>
(
null
);
// query
// query
const
{
data
:
news
Data
,
isLoading
:
isLoadingNews
}
=
useGetNews
<
GetNewsResponseType
>
(
const
{
data
:
news
All
}
=
useGetNews
<
GetNewsResponseType
>
(
{
{
pageSize
:
'5'
,
pageSize
:
'10'
,
filters
:
tab
===
"all"
?
``
:
`page_config.code @=
${
tab
}
`
,
}
}
);
);
const
{
data
:
newsData
}
=
useGetNews
<
GetNewsResponseType
>
(
const
{
data
:
newsAll
,
isLoading
:
isLoadingNewsAll
}
=
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
>
(
const
{
data
:
businessOpportunities
,
isLoading
:
isLoadingBusinessOpportunities
}
=
useGetNews
<
GetNewsResponseType
>
(
{
{
pageSize
:
'5'
,
pageSize
:
'5'
,
...
@@ -58,642 +59,619 @@ const Page = () => {
...
@@ -58,642 +59,619 @@ const Page = () => {
}
}
);
);
const
{
data
:
eventData
,
isLoading
:
isLoadingEvent
}
=
useGetEvents
<
EventApiResponse
>
();
// template
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"
,
];
return
(
return
(
(
isLoadingBusinessOpportunities
||
isLoadingPolicyAndLegalInformation
||
isLoadingEvent
)
?
(
<>
<
div
className=
"container w-full h-[80vh] flex justify-center items-center"
>
{
/* Banner */
}
<
Spinner
/>
<
Swiper
</
div
>
modules=
{
[
Autoplay
]
}
)
:
(
autoplay=
{
{
delay
:
4000
,
disableOnInteraction
:
false
}
}
<>
loop
{
/* Banner */
}
slidesPerView=
{
1
}
<
Swiper
onSwiper=
{
(
s
)
=>
(
swiperRef
.
current
=
s
)
}
modules=
{
[
Autoplay
]
}
>
autoplay=
{
{
delay
:
4000
,
disableOnInteraction
:
false
}
}
<
SwiperSlide
>
loop
<
ImageNext
slidesPerView=
{
1
}
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"
onSwiper=
{
(
s
)
=>
(
swiperRef
.
current
=
s
)
}
alt=
"Banner"
onSlideChange=
{
(
s
)
=>
width=
{
2560
}
setCurrentIndex
(
height=
{
720
}
typeof
s
.
realIndex
===
"number"
?
s
.
realIndex
:
s
.
activeIndex
priority
)
sizes=
"100vw"
}
className=
"w-full h-[200px] sm:h-[300px] md:h-[400px] lg:h-[500px] object-cover"
>
/>
<
SwiperSlide
>
</
SwiperSlide
>
<
img
<
SwiperSlide
>
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
alt=
"Banner"
src=
"https://vcci-hcm.org.vn/wp-content/uploads/2022/07/Landscape-HCM_3-01.png"
className=
"w-full h-[200px] sm:h-[300px] md:h-[400px] lg:h-[500px] object-cover"
alt=
"Banner"
/>
width=
{
2560
}
</
SwiperSlide
>
height=
{
720
}
<
SwiperSlide
>
sizes=
"100vw"
<
img
className=
"w-full h-[200px] sm:h-[300px] md:h-[400px] lg:h-[500px] object-cover"
src=
"https://vcci-hcm.org.vn/wp-content/uploads/2022/07/Landscape-HCM_3-01.png"
/>
alt=
"Banner"
</
SwiperSlide
>
className=
"w-full h-[200px] sm:h-[300px] md:h-[400px] lg:h-[500px] object-cover"
</
Swiper
>
/>
</
SwiperSlide
>
<
div
className=
"container mx-auto px-3 sm:px-6 lg:px-10 space-y-12"
>
</
Swiper
>
{
/* Featured News */
}
<
section
>
<
div
className=
"container mx-auto px-3 sm:px-6 lg:px-10 space-y-12"
>
<
div
className=
"flex items-center justify-center py-8 px-4"
>
{
/* Featured News */
}
<
div
className=
"flex items-center w-full max-w-4xl"
>
<
section
>
<
div
className=
"flex-1 h-px bg-linear-to-r from-transparent via-gray-300 to-gray-400"
></
div
>
<
div
className=
"flex items-center justify-center py-8 px-4"
>
<
h1
className=
"px-6 text-[20px] sm:text-[24px] md:text-[28px] uppercase font-bold text-[#063e8e] whitespace-nowrap"
>
<
div
className=
"flex items-center w-full max-w-4xl"
>
Tin Nổi Bật
<
div
className=
"flex-1 h-px bg-linear-to-r from-transparent via-gray-300 to-gray-400"
></
div
>
</
h1
>
<
h1
className=
"px-6 text-[20px] sm:text-[24px] md:text-[28px] uppercase font-bold text-[#063e8e] whitespace-nowrap"
>
<
div
className=
"flex-1 h-px bg-linear-to-l from-transparent via-gray-300 to-gray-400"
></
div
>
Tin Nổi Bật
</
h1
>
<
div
className=
"flex-1 h-px bg-linear-to-l from-transparent via-gray-300 to-gray-400"
></
div
>
</
div
>
</
div
>
</
div
>
</
div
>
<
Swiper
<
Swiper
modules=
{
[
Autoplay
]
}
modules=
{
[
Autoplay
]
}
autoplay=
{
{
delay
:
4000
,
disableOnInteraction
:
false
}
}
autoplay=
{
{
delay
:
4000
,
disableOnInteraction
:
false
}
}
loop
loop
breakpoints=
{
{
breakpoints=
{
{
0
:
{
slidesPerView
:
1.1
,
spaceBetween
:
10
},
0
:
{
slidesPerView
:
1.1
,
spaceBetween
:
10
},
640
:
{
slidesPerView
:
2
,
spaceBetween
:
16
},
640
:
{
slidesPerView
:
2
,
spaceBetween
:
16
},
1024
:
{
slidesPerView
:
3
,
spaceBetween
:
24
},
1024
:
{
slidesPerView
:
3
,
spaceBetween
:
24
},
}
}
}
}
className=
"pb-5"
className=
"pb-5"
>
>
{
newsAll
?.
responseData
?.
rows
.
map
((
news
)
=>
(
{
newsAll
?.
responseData
?.
rows
.
map
((
news
)
=>
(
<
SwiperSlide
key=
{
news
.
id
}
>
<
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"
>
<
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"
/>
<
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
>
</
Link
>
</
SwiperSlide
>
))
}
</
Swiper
>
</
section
>
<
div
>
<
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 */
}
<
section
className=
"flex flex-col lg:flex-row gap-5 pb-10 mb-0"
>
{
/* Left */
}
<
div
className=
"flex-1"
>
<
div
className=
"flex justify-between items-center"
>
<
Link
href=
"/thong-tin-truyen-thong/tin-vcci/"
className=
"text-[18px] sm:text-[20px] font-semibold uppercase text-[#063e8e]"
>
Tin tức
</
Link
>
<
Link
href=
"/thong-tin-truyen-thong/tin-vcci/"
className=
"text-[#063e8e] text-sm sm:text-base"
>
<
ChevronsRight
/>
</
Link
>
</
div
>
<
hr
className=
"border-[#063e8e] mb-4"
/>
<
div
className=
"flex flex-col md:flex-row gap-5"
>
{
newsAll
?.
responseData
.
rows
.
slice
(
0
,
1
)
.
map
((
news
:
NewsItem
)
=>
(
<
Link
key=
{
news
.
id
}
href=
{
`${news.external_link}`
}
href=
{
`${news.external_link}`
}
className=
"
relative block bg-white shadow-md overflow-hidden hover:shadow-lg transition-shadow duration-300
"
className=
"
flex flex-col w-full md:w-1/2 min-h-[180px] sm:min-h-[220px] gap-3 mb-3 bg-white
"
>
>
<
img
<
div
className=
"w-full aspect-3/2 overflow-hidden"
>
src=
{
`${BASE_URL.imageEndpoint}${news.thumbnail}`
}
<
ImageNext
alt=
{
news
.
title
}
src=
{
`${BASE_URL.imageEndpoint}${news.thumbnail}`
}
className=
"w-full aspect-3/2 sm:h-56 md:h-64 object-cover"
alt=
{
news
.
title
}
onError=
{
(
e
)
=>
{
width=
{
600
}
e
.
currentTarget
.
onerror
=
null
height=
{
400
}
e
.
currentTarget
.
src
=
"/img-error.png"
sizes=
"(max-width:768px) 100vw,50vw"
}
}
className=
"w-full h-full object-cover"
/>
/>
<
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"
>
</
div
>
<
p
className=
"text-white text-center font-semibold line-clamp-2 text-sm sm:text-base leading-snug"
>
<
div
className=
"flex-1 p-5 pt-1"
>
<
p
className=
"text-[#063E8E] font-bold pb-2 text-xl line-clamp-2"
>
{
news
.
title
}
{
news
.
title
}
</
p
>
</
p
>
<
p
className=
"line-clamp-4 text-justify"
>
{
stripImagesAndHtml
(
news
.
description
)
}
</
p
>
</
div
>
</
div
>
</
a
>
</
Link
>
</
SwiperSlide
>
))
}
))
}
</
Swiper
>
</
section
>
<
div
>
<
div
className=
"w-full md:w-1/2"
>
<
a
href=
"https://hardwaretools.com.vn/"
>
<
div
className=
"flex flex-wrap gap-2 sm:gap-3 mb-5"
>
<
img
src=
"/home/Standard-Banner-1-2024.png.webp"
alt=
"banner"
/>
<
button
</
a
>
className=
{
`flex-1 py-[3px] text-sm transition-colors cursor-pointer ${tab === "all"
? " bg-[#d3d3d3] text-[#063e8e] font-semibold"
: "border-gray-300 text-[#363636] bg-[#e8e8e8] hover:bg-[#e8e8e8] hover:text-[#063e8e] font-semibold"
}`
}
onClick=
{
()
=>
setTab
(
"all"
)
}
>
Tất cả
</
button
>
<
button
className=
{
`flex-1 py-[3px] text-[14px] transition-colors cursor-pointer ${`
tin
-
vcci
` === tab
? "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"
)
}
>
Tin VCCI
</
button
>
<
button
className=
{
`flex-1 py-[3px] text-[14px] transition-colors cursor-pointer ${`
tin
-
kinh
-
te
` === tab
? "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"
)
}
>
Tin Kinh Tế
</
button
>
<
button
className=
{
`flex-1 py-[3px] text-[14px] transition-colors cursor-pointer ${`
chuyen
-
de
` === tab
? "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"
)
}
>
Chuyên Đề
</
button
>
</
div
>
{
newsData
?.
responseData
?.
rows
.
slice
(
0
,
4
).
map
((
news
)
=>
(
<
CardNews
key=
{
news
.
id
}
news=
{
news
}
/>
))
}
</
div
>
</
div
>
</
div
>
</
div
>
{
/* Tin tức + Liên kết nhanh */
}
{
/* Right */
}
<
section
className=
"flex flex-col lg:flex-row gap-5 pb-10 mb-0"
>
<
aside
className=
"w-full lg:w-[30%]"
>
{
/* Left */
}
<
div
className=
"flex justify-between items-center"
>
<
div
className=
"flex-1"
>
<
h2
className=
"text-[18px] sm:text-[20px] font-semibold uppercase text-[#063e8e]"
>
<
div
className=
"flex justify-between items-center"
>
Liên kết nhanh
<
a
</
h2
>
href=
"/thong-tin-truyen-thong/tin-vcci/"
</
div
>
className=
"text-[18px] sm:text-[20px] font-semibold uppercase text-[#063e8e]"
<
hr
className=
"border-[#063e8e] mb-4"
/>
<
div
className=
"space-y-2 text-[#063e8e] text-sm md:text-base pb-10"
>
<
div
>
<
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/"
>
>
Tin tức
🔗 Cẩm nang hướng dẫn đầu tư kinh doanh tại Việt Nam
</
a
>
</
Link
>
<
a
</
div
>
href=
"/thong-tin-truyen-thong/tin-vcci/"
<
div
>
className=
"text-[#063e8e] text-sm sm:text-base"
<
Link
className=
"text-[#363636]"
href=
"https://vcci-hcm.org.vn/lien-ket-nhanh/doanh-nghiep-kien-nghi-ve-chinh-sach-va-phap-luat/"
>
>
<
ChevronsRight
/>
🔗 Doanh nghiệp kiến nghị về chính sách và pháp luật
</
a
>
</
Link
>
</
div
>
</
div
>
<
hr
className=
"border-[#063e8e] mb-4"
/>
</
div
>
<
div
>
<
Link
href=
"https://hardwaretools.com.vn/"
>
<
ImageNext
src=
"/home/20-2048x1365.webp"
alt=
"banner"
width=
{
2048
}
height=
{
1365
}
/>
</
Link
>
</
div
>
</
aside
>
</
section
>
{
/* Sự kiện */
}
<
section
className=
"flex flex-col lg:flex-row gap-5 pb-10 mb-0"
>
<
div
className=
"flex-1 bg-[#063e8e] p-5"
>
<
div
className=
"flex justify-between items-center"
>
<
h2
className=
"text-[18px] sm:text-[20px] font-bold uppercase text-[#e8c518]"
>
Sự kiện sắp diễn ra
</
h2
>
<
Link
href=
"/hoat-dong/su-kien"
className=
"text-[#e8c518] text-sm sm:text-base"
>
<
ChevronsRight
/>
</
Link
>
</
div
>
<
hr
className=
"border-[#e8c518] mb-4"
/>
<
div
className=
"flex flex-col md:flex-row gap-5"
>
<
div
className=
"flex flex-col md:flex-row gap-5"
>
{
newsAll
?.
responseData
.
rows
{
isLoadingEvent
?
(
.
slice
(
0
,
1
)
<
div
className=
"container w-full h-[80vh] flex justify-center items-center"
>
.
map
((
news
:
NewsItem
)
=>
(
<
Spinner
/>
<
a
</
div
>
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"
{
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"
>
<
div
className=
"w-full aspect-3/2 overflow-hidden"
>
<
img
<
ImageNext
src=
{
`${BASE_URL.imageEndpoint}${news.thumbnail}`
}
src=
{
`${BASE_URL.imageEndpoint}${event.image}`
}
alt=
{
news
.
title
}
alt=
{
event
.
name
}
width=
{
600
}
height=
{
400
}
sizes=
"(max-width:768px) 100vw,50vw"
className=
"w-full h-full object-cover"
className=
"w-full h-full object-cover"
onError=
{
(
e
)
=>
{
e
.
currentTarget
.
onerror
=
null
e
.
currentTarget
.
src
=
"/img-error.png"
}
}
/>
/>
</
div
>
</
div
>
<
div
className=
"flex-1
p-5 pt-1
"
>
<
div
className=
"flex-1"
>
<
p
className=
"text-[#0
63E8E] font-bold pb-2
text-xl line-clamp-2"
>
<
p
className=
"text-[#0
056b3] font-bold
text-xl line-clamp-2"
>
{
news
.
titl
e
}
{
event
.
nam
e
}
</
p
>
</
p
>
<
p
className=
"line-clamp-4 text-justify"
>
{
stripImagesAndHtml
(
news
.
description
)
}
</
p
>
<
p
className=
"text-gray-500 text-sm my-1"
>
{
dayjs
(
event
.
start_time
).
format
(
"DD/MM/YYYY"
)
}
</
p
>
<
p
className=
"line-clamp-3 text-justify"
>
{
stripImagesAndHtml
(
event
.
description
)
}
</
p
>
</
div
>
</
div
>
</
a
>
</
Link
>
))
}
))
}
<
div
className=
"w-full md:w-1/2"
>
<
div
className=
"w-full md:w-1/2"
>
{
eventData
?.
responseData
.
rows
.
slice
(
0
,
4
).
map
((
event
)
=>
(
<
div
className=
"flex flex-wrap gap-2 sm:gap-3 mb-5"
>
<
CardEvent
key=
{
event
.
id
}
event=
{
event
}
/>
<
button
))
}
className=
{
`flex-1 py-[3px] text-sm transition-colors cursor-pointer ${tab === "all"
? " bg-[#d3d3d3] text-[#063e8e] font-semibold"
: "border-gray-300 text-[#363636] bg-[#e8e8e8] hover:bg-[#e8e8e8] hover:text-[#063e8e] font-semibold"
}`
}
onClick=
{
()
=>
setTab
(
"all"
)
}
>
Tất cả
</
button
>
<
button
className=
{
`flex-1 py-[3px] text-[14px] transition-colors cursor-pointer ${`
tin
-
vcci
` === tab
? "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`
)
}
>
Tin VCCI
</
button
>
<
button
className=
{
`flex-1 py-[3px] text-[14px] transition-colors cursor-pointer ${`
tin
-
kinh
-
te
` === tab
? "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`
)
}
>
Tin Kinh Tế
</
button
>
<
button
className=
{
`flex-1 py-[3px] text-[14px] transition-colors cursor-pointer ${`
chuyen
-
de
` === tab
? "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`
)
}
>
Chuyên Đề
</
button
>
</
div
>
</
div
>
</>
{
newsData
?.
responseData
?.
rows
.
slice
(
0
,
4
).
map
((
news
)
=>
(
)
}
<
CardNews
key=
{
news
.
id
}
news=
{
news
}
/>
))
}
</
div
>
</
div
>
</
div
>
</
div
>
</
div
>
{
/* Right */
}
<
div
className=
"bg-[#063e8e] w-full lg:w-[30%] p-5"
>
<
aside
className=
"w-full lg:w-[30%]"
>
<
aside
>
<
div
className=
"flex justify-between items-center"
>
<
h2
className=
"text-[18px] sm:text-[20px] font-semibold uppercase text-[#063e8e]"
>
Liên kết nhanh
</
h2
>
</
div
>
<
hr
className=
"border-[#063e8e] mb-4"
/>
<
div
className=
"space-y-2 text-[#063e8e] text-sm md:text-base pb-10"
>
<
div
>
<
a
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
>
</
div
>
<
div
>
<
a
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
>
</
div
>
</
div
>
<
div
>
<
a
href=
"https://hardwaretools.com.vn/"
>
<
img
src=
"/home/20-2048x1365.webp"
alt=
"banner"
/>
</
a
>
</
div
>
</
aside
>
</
section
>
{
/* Sự kiện */
}
<
section
className=
"flex flex-col lg:flex-row gap-5 pb-10 mb-0"
>
<
div
className=
"flex-1 bg-[#063e8e] p-5"
>
<
div
className=
"flex justify-between items-center"
>
<
div
className=
"flex justify-between items-center"
>
<
h2
className=
"text-[18px] sm:text-[20px] font-bold uppercase text-[#e8c518]"
>
<
h2
className=
"text-[18px] sm:text-[20px] font-bold uppercase text-[#e8c518]"
>
Sự kiện sắp diễn ra
Lịch sự kiện
</
h2
>
</
h2
>
<
a
href=
"/hoat-dong/su-kien"
className=
"text-[#e8c518] text-sm sm:text-base"
>
<
Link
href=
"/hoat-dong/su-kien"
className=
"text-[#e8c518] hover:underline text-sm sm:text-base"
>
<
ChevronsRight
/>
<
ChevronsRight
/>
</
a
>
</
Link
>
</
div
>
</
div
>
<
hr
className=
"border-[#e8c518] mb-4"
/>
<
hr
className=
"border-[#e8c518] mb-4"
/>
<
EventCalendar
/>
<
div
className=
"flex flex-col md:flex-row gap-5"
>
</
aside
>
{
eventDataFiltered
?.
slice
(
0
,
1
).
map
((
event
:
EventItem
)
=>
(
</
div
>
<
a
</
section
>
key=
{
event
.
id
}
href=
{
`hoat-dong/su-kien/${event.id}`
}
{
/* Cơ hội kinh doanh + Chính sách */
}
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=
"flex flex-col lg:flex-row gap-5 pb-10 mb-0"
>
<
div
className=
"flex flex-col flex-1"
>
<
div
>
<
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"
>
<
Link
href=
"/xuc-tien-thuong-mai/co-hoi/"
className=
"text-[18px] sm:text-[20px] font-bold uppercase text-[#063e8e]"
>
>
<
div
className=
"w-full aspect-3/2 overflow-hidden"
>
Cơ hội kinh doanh
<
img
</
Link
>
src=
{
`${BASE_URL.imageEndpoint}${event.image}`
}
<
Link
alt=
{
event
.
name
}
href=
"/xuc-tien-thuong-mai/co-hoi/"
className=
"w-full h-full object-cover"
className=
"text-[#063e8e] text-sm sm:text-base"
onError=
{
(
e
)
=>
{
>
(
e
.
target
as
HTMLImageElement
).
src
=
<
ChevronsRight
/>
"/img-error.png"
;
</
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
>
</
div
>
)
:
(
<>
{
businessOpportunities
?.
responseData
.
rows
.
slice
(
0
,
1
)
.
map
((
news
:
NewsItem
)
=>
(
<
Link
key=
{
news
.
id
}
href=
{
`${news.external_link}`
}
>
<
div
className=
"w-full aspect-3/2 relative overflow-hidden mb-5"
>
<
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"
/>
<
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"
>
{
news
.
title
}
</
p
>
</
div
>
</
div
>
</
Link
>
))
}
<
div
className=
"flex-1"
>
{
businessOpportunities
?.
responseData
.
rows
.
slice
(
0
,
3
).
map
((
news
)
=>
(
<
p
className=
"text-[#0056b3] font-bold text-xl line-clamp-2"
>
<
CardNews
key=
{
news
.
id
}
news=
{
news
}
/>
{
event
.
name
}
))
}
</
p
>
</>
<
p
className=
"text-gray-500 text-sm my-1"
>
)
}
{
dayjs
(
event
.
start_time
).
format
(
"DD/MM/YYYY"
)
}
</
p
>
<
p
className=
"line-clamp-3 text-justify"
>
{
stripImagesAndHtml
(
event
.
description
)
}
</
p
>
</
div
>
</
a
>
))
}
<
div
className=
"w-full md:w-1/2"
>
{
eventDataFiltered
?.
slice
(
0
,
4
).
map
((
event
)
=>
(
<
CardEvent
key=
{
event
.
id
}
event=
{
event
}
/>
))
}
</
div
>
</
div
>
</
div
>
</
div
>
</
div
>
<
div
className=
"flex-1"
>
<
div
className=
"bg-[#063e8e] w-full lg:w-[30%] p-5"
>
<
aside
>
<
div
className=
"flex justify-between items-center"
>
<
div
className=
"flex justify-between items-center"
>
<
h2
className=
"text-[18px] sm:text-[20px] font-bold uppercase text-[#e8c518]"
>
<
Link
Lịch sự kiện
href=
"/thong-tin-truyen-thong/phap-luat"
</
h2
>
className=
"text-[18px] sm:text-[20px] font-bold uppercase text-[#063e8e]"
<
a
>
href=
"/hoat-dong/su-kien"
Chính sách
&
pháp luật
className=
"text-[#e8c518] hover:underline text-sm sm:text-base"
</
Link
>
<
Link
href=
"/thong-tin-truyen-thong/phap-luat"
className=
"text-[#063e8e] text-sm sm:text-base"
>
>
<
ChevronsRight
/>
<
ChevronsRight
/>
</
a
>
</
Link
>
</
div
>
</
div
>
<
hr
className=
"border-[#e8c518] mb-4"
/>
<
hr
className=
"border-[#063e8e] mb-4"
/>
<
EventCalendar
/>
<
div
className=
"pt-2"
>
</
aside
>
{
isLoadingPolicyAndLegalInformation
?
(
</
div
>
<
div
className=
"container w-full h-[80vh] flex justify-center items-center"
>
</
section
>
<
Spinner
/>
</
div
>
{
/* Cơ hội kinh doanh + Chính sách */
}
)
:
(
<
div
className=
"flex flex-col lg:flex-row gap-5 pb-10 mb-0"
>
<>
<
div
className=
"flex flex-col flex-1"
>
{
policyAndLegalInformation
?.
responseData
.
rows
<
div
>
.
slice
(
0
,
1
)
<
a
href=
"https://vcci-hcm.org.vn/wp-content/uploads/2022/11/MEDIA-KIT_VCCI-HCM-2022-Final.pdf"
>
.
map
((
news
:
NewsItem
)
=>
(
<
img
src=
"/home/Standard-Banner-1-2024.png.webp"
alt=
"banner"
/>
<
Link
key=
{
news
.
id
}
href=
{
`${news.external_link}`
}
>
</
a
>
<
div
className=
"w-full aspect-3/2 relative overflow-hidden mb-5"
>
</
div
>
<
ImageNext
<
section
className=
"flex flex-col md:flex-row gap-5 pt-8"
>
src=
{
`${BASE_URL.imageEndpoint}${news.thumbnail}`
}
<
div
className=
"flex-1"
>
alt=
{
news
.
title
}
<
div
className=
"flex justify-between items-center"
>
width=
{
600
}
<
a
height=
{
400
}
href=
"/xuc-tien-thuong-mai/co-hoi/"
sizes=
"(max-width:768px) 100vw,50vw"
className=
"text-[18px] sm:text-[20px] font-bold uppercase text-[#063e8e]"
className=
"w-full h-full object-cover"
>
/>
Cơ hội kinh doanh
<
div
className=
"absolute bg-white opacity-80 bottom-5 left-5 right-5 p-5"
>
</
a
>
<
p
className=
"text-[#063e8e] font-semibold text-sm sm:text-base z-10 line-clamp-3"
>
<
a
{
news
.
title
}
href=
"/xuc-tien-thuong-mai/co-hoi/"
</
p
>
className=
"text-[#063e8e] text-sm sm:text-base"
</
div
>
>
<
ChevronsRight
/>
</
a
>
</
div
>
<
hr
className=
"border-[#063e8e] mb-4"
/>
<
div
className=
"pt-2"
>
{
businessOpportunities
?.
responseData
.
rows
.
slice
(
0
,
1
)
.
map
((
news
:
NewsItem
)
=>
(
<
a
key=
{
news
.
id
}
href=
{
`${news.external_link}`
}
>
<
div
className=
"w-full aspect-3/2 relative overflow-hidden mb-5"
>
<
img
src=
{
`${BASE_URL.imageEndpoint}${news.thumbnail}`
}
alt=
{
news
.
title
}
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"
>
{
news
.
title
}
</
p
>
</
div
>
</
div
>
</
div
>
</
Link
>
</
a
>
))
}
{
policyAndLegalInformation
?.
responseData
.
rows
.
slice
(
0
,
3
).
map
((
news
)
=>
(
<
CardNews
key=
{
news
.
id
}
news=
{
news
}
/>
))
}
))
}
</>
{
businessOpportunities
?.
responseData
.
rows
.
slice
(
0
,
3
).
map
((
news
)
=>
(
)
}
<
CardNews
key=
{
news
.
id
}
news=
{
news
}
/>
))
}
</
div
>
</
div
>
</
div
>
<
div
className=
"flex-1"
>
</
div
>
<
div
className=
"flex justify-between items-center"
>
</
section
>
<
a
</
div
>
href=
"/thong-tin-truyen-thong/phap-luat"
<
div
className=
"w-full lg:w-[30%] justify-center items-start flex"
>
className=
"text-[18px] sm:text-[20px] font-bold uppercase text-[#063e8e]"
<
Link
href=
"https://smartgara.ecaraid.com/"
>
>
<
ImageNext
Chính sách
&
pháp luật
src=
"/home/eCarAid_web_banner_600x400.webp"
</
a
>
alt=
"banner"
<
a
width=
{
600
}
href=
"/thong-tin-truyen-thong/phap-luat"
height=
{
400
}
className=
"text-[#063e8e] text-sm sm:text-base"
/>
>
</
Link
>
<
ChevronsRight
/>
</
div
>
</
a
>
</
div
>
</
div
>
<
hr
className=
"border-[#063e8e] mb-4"
/>
<
div
className=
"pt-2"
>
{
policyAndLegalInformation
?.
responseData
.
rows
.
slice
(
0
,
1
)
.
map
((
news
:
NewsItem
)
=>
(
<
a
key=
{
news
.
id
}
href=
{
`${news.external_link}`
}
>
<
div
className=
"w-full aspect-3/2 relative overflow-hidden mb-5"
>
<
img
src=
{
`${BASE_URL.imageEndpoint}${news.thumbnail}`
}
alt=
{
news
.
title
}
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"
>
{
news
.
title
}
</
p
>
</
div
>
</
div
>
</
a
>
))
}
{
policyAndLegalInformation
?.
responseData
.
rows
.
slice
(
0
,
3
).
map
((
news
)
=>
(
{
/* Hội viên tiêu biểu */
}
<
CardNews
key=
{
news
.
id
}
news=
{
news
}
/>
<
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
>
<
Link
href=
"/danh-ba-hoi-vien"
className=
"text-[#063e8e] hover:underline text-sm font-medium"
>
<
ChevronsRight
/>
</
Link
>
</
div
>
<
hr
className=
"border-[#063e8e] mb-5"
/>
<
div
>
<
Swiper
modules=
{
[
Autoplay
,
Grid
]
}
autoplay=
{
{
delay
:
4000
,
disableOnInteraction
:
false
}
}
loop
grid=
{
{
rows
:
1
,
fill
:
"row"
}
}
slidesPerGroup=
{
3
}
breakpoints=
{
{
0
:
{
slidesPerView
:
2
,
spaceBetween
:
10
},
640
:
{
slidesPerView
:
3
,
spaceBetween
:
16
},
1024
:
{
slidesPerView
:
3
,
spaceBetween
:
24
},
}
}
className=
"partner-swiper"
>
{
memberImages
.
map
((
src
,
i
)
=>
(
<
SwiperSlide
key=
{
i
}
>
<
div
className=
"aspect-square flex justify-center items-center bg-white rounded-lg shadow"
>
<
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
>
</
SwiperSlide
>
))
}
</
Swiper
>
</
div
>
</
aside
>
{
/* right */
}
<
aside
className=
"w-full lg:w-[30%] py-5"
>
<
div
className=
"flex justify-between items-center mb-3"
>
<
h2
className=
"text-xl font-bold uppercase text-[#063e8e]"
>
Kết nối hội viên
</
h2
>
</
div
>
<
hr
className=
"border-[#063e8e] mb-5"
/>
<
div
className=
"pb-10"
>
<
Swiper
modules=
{
[
Autoplay
,
Grid
]
}
autoplay=
{
{
delay
:
4000
,
disableOnInteraction
:
false
}
}
loop
grid=
{
{
rows
:
2
,
fill
:
"row"
}
}
slidesPerGroup=
{
3
}
breakpoints=
{
{
0
:
{
slidesPerView
:
2
,
spaceBetween
:
10
},
640
:
{
slidesPerView
:
3
,
spaceBetween
:
16
},
1024
:
{
slidesPerView
:
3
,
spaceBetween
:
24
},
}
}
className=
"partner-swiper"
>
{
partnerImages
.
map
((
src
,
i
)
=>
(
<
SwiperSlide
key=
{
i
}
>
<
div
className=
"aspect-square flex justify-center items-center bg-white rounded-lg shadow"
>
<
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
>
</
SwiperSlide
>
))
}
</
Swiper
>
</
div
>
</
aside
>
</
section
>
{
/* Video + đối tác */
}
<
section
className=
"flex flex-col lg:flex-row gap-5 pb-10"
>
{
/* left */
}
<
div
className=
"flex flex-col flex-1"
>
<
div
className=
"flex justify-between items-center mb-3"
>
<
h2
className=
"text-xl font-bold uppercase text-[#063e8e]"
>
Video
</
h2
>
<
Link
href=
"/video"
className=
"text-[#063e8e] hover:underline text-sm font-medium"
>
<
ChevronsRight
/>
</
Link
>
</
div
>
<
hr
className=
"border-[#063e8e] mb-5"
/>
<
div
className=
"flex flex-col md:flex-row gap-4 md:gap-6"
>
{
[
{
src
:
"https://www.youtube.com/embed/J0Iz0iGuAXY"
,
title
:
"VCCI-HCM 2024 IN REVIEW (ENGLISH VERSION)"
,
},
{
src
:
"https://www.youtube.com/embed/_OnnGWv2ehM"
,
title
:
"Hội nghị Hội viên VCCI - Gala Mừng Xuân Ất Tỵ 2025"
,
},
].
map
((
video
,
i
)
=>
(
<
div
key=
{
i
}
className=
"w-full md:w-1/2"
>
<
div
className=
"aspect-video rounded-lg overflow-hidden shadow"
>
<
iframe
className=
"w-full h-full font-bold"
src=
{
video
.
src
}
title=
{
video
.
title
}
frameBorder=
"0"
allow=
"accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share"
referrerPolicy=
"strict-origin-when-cross-origin"
allowFullScreen
/>
</
div
>
</
div
>
<
p
className=
"mt-2 text-sm text-gray-700 font-medium"
>
{
video
.
title
}
</
p
>
</
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
>
</
div
>
</
div
>
</
div
>
</
div
>
{
/* Hội viên tiêu biểu */
}
{
/* right */
}
<
section
className=
"flex flex-col lg:flex-row gap-5 pb-10 mb-0"
>
<
aside
className=
"w-full lg:w-[30%]"
>
{
/* left */
}
<
div
className=
"flex justify-between items-center mb-3"
>
<
aside
className=
"w-full lg:w-1/3 flex-1 bg-[#e8c518] p-5"
>
<
h2
className=
"text-xl font-bold uppercase text-[#063e8e]"
>
<
div
className=
"flex justify-between items-center mb-3"
>
Đối tác
<
h2
className=
"text-xl font-bold uppercase text-[#063e8e]"
>
</
h2
>
Hội viên tiêu biểu
</
div
>
</
h2
>
<
hr
className=
"border-[#063e8e] mb-5"
/>
<
a
<
div
className=
"pb-10"
>
href=
"/danh-ba-hoi-vien"
<
Swiper
className=
"text-[#063e8e] hover:underline text-sm font-medium"
modules=
{
[
Autoplay
,
Grid
]
}
>
autoplay=
{
{
delay
:
4000
,
disableOnInteraction
:
false
}
}
<
ChevronsRight
/>
loop
</
a
>
grid=
{
{
rows
:
2
,
fill
:
"row"
}
}
</
div
>
slidesPerGroup=
{
3
}
<
hr
className=
"border-[#063e8e] mb-5"
/>
breakpoints=
{
{
<
div
>
0
:
{
slidesPerView
:
2
,
spaceBetween
:
10
},
<
Swiper
640
:
{
slidesPerView
:
3
,
spaceBetween
:
16
},
modules=
{
[
Autoplay
,
Grid
]
}
1024
:
{
slidesPerView
:
3
,
spaceBetween
:
24
},
autoplay=
{
{
delay
:
4000
,
disableOnInteraction
:
false
}
}
}
}
loop
className=
"partner-swiper"
grid=
{
{
rows
:
1
,
fill
:
"row"
}
}
>
slidesPerGroup=
{
3
}
{
partnerImages
.
map
((
src
,
i
)
=>
(
breakpoints=
{
{
<
SwiperSlide
key=
{
i
}
>
0
:
{
slidesPerView
:
2
,
spaceBetween
:
10
},
<
div
className=
"aspect-square flex justify-center items-center bg-white rounded-lg shadow"
>
640
:
{
slidesPerView
:
3
,
spaceBetween
:
16
},
<
ImageNext
1024
:
{
slidesPerView
:
3
,
spaceBetween
:
24
},
src=
{
src
}
}
}
alt=
{
`partner-${i}`
}
className=
"partner-swiper"
width=
{
160
}
>
height=
{
160
}
{
hoivien
.
map
((
src
,
i
)
=>
(
sizes=
"(max-width:640px) 25vw,(max-width:1024px) 15vw,10vw"
<
SwiperSlide
key=
{
i
}
>
className=
"w-3/4 h-3/4 object-contain"
<
div
className=
"aspect-square flex justify-center items-center bg-white rounded-lg shadow"
>
<
img
src=
{
src
}
alt=
{
`partner-${i}`
}
className=
"w-3/4 h-3/4 object-contain"
/>
</
div
>
</
SwiperSlide
>
))
}
</
Swiper
>
</
div
>
</
aside
>
{
/* right */
}
<
aside
className=
"w-full lg:w-[30%] py-5"
>
<
div
className=
"flex justify-between items-center mb-3"
>
<
h2
className=
"text-xl font-bold uppercase text-[#063e8e]"
>
Kết nối hội viên
</
h2
>
</
div
>
<
hr
className=
"border-[#063e8e] mb-5"
/>
<
div
className=
"pb-10"
>
<
Swiper
modules=
{
[
Autoplay
,
Grid
]
}
autoplay=
{
{
delay
:
4000
,
disableOnInteraction
:
false
}
}
loop
grid=
{
{
rows
:
2
,
fill
:
"row"
}
}
slidesPerGroup=
{
3
}
breakpoints=
{
{
0
:
{
slidesPerView
:
2
,
spaceBetween
:
10
},
640
:
{
slidesPerView
:
3
,
spaceBetween
:
16
},
1024
:
{
slidesPerView
:
3
,
spaceBetween
:
24
},
}
}
className=
"partner-swiper"
>
{
images
.
map
((
src
,
i
)
=>
(
<
SwiperSlide
key=
{
i
}
>
<
div
className=
"aspect-square flex justify-center items-center bg-white rounded-lg shadow"
>
<
img
src=
{
src
}
alt=
{
`partner-${i}`
}
className=
"w-3/4 h-3/4 object-contain"
/>
</
div
>
</
SwiperSlide
>
))
}
</
Swiper
>
</
div
>
</
aside
>
</
section
>
{
/* Video + đối tác */
}
<
section
className=
"flex flex-col lg:flex-row gap-5 pb-10"
>
{
/* left */
}
<
div
className=
"flex flex-col flex-1"
>
<
div
className=
"flex justify-between items-center mb-3"
>
<
h2
className=
"text-xl font-bold uppercase text-[#063e8e]"
>
Video
</
h2
>
<
a
href=
"/video"
className=
"text-[#063e8e] hover:underline text-sm font-medium"
>
<
ChevronsRight
/>
</
a
>
</
div
>
<
hr
className=
"border-[#063e8e] mb-5"
/>
<
div
className=
"flex flex-col md:flex-row gap-4 md:gap-6"
>
{
[
{
src
:
"https://www.youtube.com/embed/J0Iz0iGuAXY"
,
title
:
"VCCI-HCM 2024 IN REVIEW (ENGLISH VERSION)"
,
},
{
src
:
"https://www.youtube.com/embed/_OnnGWv2ehM"
,
title
:
"Hội nghị Hội viên VCCI - Gala Mừng Xuân Ất Tỵ 2025"
,
},
].
map
((
video
,
i
)
=>
(
<
div
key=
{
i
}
className=
"w-full md:w-1/2"
>
<
div
className=
"aspect-video rounded-lg overflow-hidden shadow"
>
<
iframe
className=
"w-full h-full font-bold"
src=
{
video
.
src
}
title=
{
video
.
title
}
frameBorder=
"0"
allow=
"accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share"
referrerPolicy=
"strict-origin-when-cross-origin"
allowFullScreen
/>
/>
</
div
>
</
div
>
<
p
className=
"mt-2 text-sm text-gray-700 font-medium"
>
</
SwiperSlide
>
{
video
.
title
}
</
p
>
</
div
>
))
}
))
}
</
div
>
</
Swiper
>
</
div
>
</
div
>
</
aside
>
{
/* right */
}
</
section
>
<
aside
className=
"w-full lg:w-[30%]"
>
</
div
>
<
div
className=
"flex justify-between items-center mb-3"
>
</>
<
h2
className=
"text-xl font-bold uppercase text-[#063e8e]"
>
Đối tác
</
h2
>
</
div
>
<
hr
className=
"border-[#063e8e] mb-5"
/>
<
div
className=
"pb-10"
>
<
Swiper
modules=
{
[
Autoplay
,
Grid
]
}
autoplay=
{
{
delay
:
4000
,
disableOnInteraction
:
false
}
}
loop
grid=
{
{
rows
:
2
,
fill
:
"row"
}
}
slidesPerGroup=
{
3
}
breakpoints=
{
{
0
:
{
slidesPerView
:
2
,
spaceBetween
:
10
},
640
:
{
slidesPerView
:
3
,
spaceBetween
:
16
},
1024
:
{
slidesPerView
:
3
,
spaceBetween
:
24
},
}
}
className=
"partner-swiper"
>
{
images
.
map
((
src
,
i
)
=>
(
<
SwiperSlide
key=
{
i
}
>
<
div
className=
"aspect-square flex justify-center items-center bg-white rounded-lg shadow"
>
<
img
src=
{
src
}
alt=
{
`partner-${i}`
}
className=
"w-3/4 h-3/4 object-contain"
/>
</
div
>
</
SwiperSlide
>
))
}
</
Swiper
>
</
div
>
</
aside
>
</
section
>
</
div
>
</>
)
);
);
};
};
...
...
src/components/base/image/index.tsx
0 → 100644
View file @
9f9abbe5
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
;
src/constants/memberImages.ts
0 → 100644
View file @
9f9abbe5
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
src/constants/partnerImages.ts
0 → 100644
View file @
9f9abbe5
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
src/helpers/stripImageAndHtml.ts
0 → 100644
View file @
9f9abbe5
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
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment