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
c03ea16b
Commit
c03ea16b
authored
May 14, 2026
by
Lê Bảo Hồng Đức
☄
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
re-design ui home client
parent
051d8a8b
Changes
13
Expand all
Show whitespace changes
Inline
Side-by-side
Showing
13 changed files
with
1912 additions
and
691 deletions
+1912
-691
index.tsx
src/app/(main)/(home)/components/banner/index.tsx
+3
-1
index.tsx
...(main)/(home)/components/business-opportunities/index.tsx
+68
-57
index.tsx
src/app/(main)/(home)/components/events-calendar/index.tsx
+173
-19
index.tsx
src/app/(main)/(home)/components/events/index.tsx
+90
-59
index.tsx
src/app/(main)/(home)/components/featured-news/index.tsx
+153
-58
index.tsx
src/app/(main)/(home)/components/members/index.tsx
+71
-87
index.tsx
src/app/(main)/(home)/components/news/index.tsx
+139
-106
index.tsx
src/app/(main)/(home)/components/policies-and-laws/index.tsx
+65
-59
index.tsx
src/app/(main)/(home)/components/quick-links/index.tsx
+39
-26
index.tsx
src/app/(main)/(home)/components/video-and-patners/index.tsx
+83
-79
page.tsx
src/app/(main)/(home)/page.tsx
+9
-11
footer.tsx
src/app/(main)/_lib/layout/footer.tsx
+107
-127
admin-news.ts
src/mockdata/admin-news.ts
+912
-2
No files found.
src/app/(main)/(home)/components/banner/index.tsx
View file @
c03ea16b
...
...
@@ -5,6 +5,7 @@ import { Swiper, SwiperSlide } from "swiper/react";
import
{
Autoplay
}
from
"swiper/modules"
;
import
{
Swiper
as
SwiperType
}
from
"swiper/types"
;
import
{
useRef
}
from
"react"
;
import
"swiper/css"
;
const
Banner
=
()
=>
{
const
swiperRef
=
useRef
<
SwiperType
|
null
>
(
null
);
...
...
@@ -16,6 +17,7 @@ const Banner = () => {
loop
slidesPerView=
{
1
}
onSwiper=
{
(
s
)
=>
(
swiperRef
.
current
=
s
)
}
className=
"w-full overflow-hidden"
>
<
SwiperSlide
>
<
ImageNext
...
...
src/app/(main)/(home)/components/business-opportunities/index.tsx
View file @
c03ea16b
import
{
useGetNews
}
from
"@/api/endpoints/news"
;
import
{
GetNewsResponseType
,
NewsItem
}
from
"@/api/types/news"
;
import
ImageNext
from
"@/components/shared/image-next"
;
import
{
Spinner
}
from
"@/components/ui/spinner"
;
import
{
ChevronsRight
}
from
"lucide-react"
;
'use client'
;
import
{
type
AdminNewsItem
,
getAdminNewsSeed
,
}
from
"@/mockdata/admin-news"
;
import
dayjs
from
"dayjs"
;
import
{
ChevronRight
}
from
"lucide-react"
;
import
Link
from
"next/link"
;
import
BASE_URL
from
"@/links/index"
;
import
CardNews
from
"./components/card-news"
;
const
businessOpportunities
=
()
=>
{
const
{
data
,
isLoading
}
=
useGetNews
<
GetNewsResponseType
>
(
{
pageSize
:
'5'
,
filters
:
`page_config.code @=co-hoi-kinh-doanh`
,
}
const
businessItems
=
getAdminNewsSeed
()
.
filter
(
(
item
)
=>
item
.
type
===
"tintuc"
&&
!
item
.
is_hidden
&&
(
item
.
category_ids
.
includes
(
"cat-business-opportunity"
)
||
item
.
tagsearch_values
.
some
((
tag
)
=>
tag
.
toLowerCase
().
includes
(
"cơ hội kinh doanh"
))),
)
.
sort
(
(
left
,
right
)
=>
new
Date
(
right
.
published_at
||
right
.
created_at
).
getTime
()
-
new
Date
(
left
.
published_at
||
left
.
created_at
).
getTime
(),
);
function
formatPublishDate
(
item
:
AdminNewsItem
)
{
return
dayjs
(
item
.
published_at
||
item
.
created_at
).
format
(
"DD/MM/YYYY"
);
}
function
BusinessOpportunities
()
{
const
[
featuredItem
,
...
listItems
]
=
businessItems
;
if
(
!
featuredItem
)
return
null
;
return
(
<
div
className=
"flex-1"
>
<
div
className=
"flex justify-between items-center"
>
<
section
className=
"flex-1"
>
<
div
className=
"mb-4 flex items-center justify-between gap-3"
>
<
div
>
<
h2
className=
"text-[28px] font-extrabold uppercase tracking-tight text-[#24469c] md:text-[34px]"
>
Cơ hội kinh doanh
</
h2
>
<
div
className=
"mt-2.5 h-[4px] w-[40px] rounded-full bg-[#f7b500]"
/>
</
div
>
<
Link
href=
"/xuc-tien-thuong-mai/co-hoi/"
className=
"text-[
18px] sm:text-[20px] font-bold uppercase text-[#063e8e
]"
className=
"text-[
#24469c] transition-colors hover:text-[#1b55a1
]"
>
Cơ hội kinh doanh
<
ChevronRight
className=
"h-5 w-5"
/>
</
Link
>
</
div
>
<
div
className=
"space-y-3"
>
<
Link
href=
"/xuc-tien-thuong-mai/co-hoi/"
className=
"
text-[#063e8e] text-sm sm:text-base
"
className=
"
block rounded-[18px] bg-[#f5f7fb] px-4 py-3.5 transition-colors hover:bg-[#eef3fb]
"
>
<
ChevronsRight
/>
<
h3
className=
"line-clamp-2 text-[16px] font-bold leading-[1.45] text-[#264798] md:text-[17px]"
>
{
featuredItem
.
title
}
</
h3
>
<
p
className=
"mt-2 text-[13px] text-[#9aa8c1]"
>
{
formatPublishDate
(
featuredItem
)
}
</
p
>
</
Link
>
</
div
>
<
hr
className=
"border-[#063e8e] mb-4"
/>
<
div
className=
"pt-2"
>
{
isLoading
?
(
<
div
className=
"container w-full h-[80vh] flex justify-center items-center"
>
<
Spinner
/>
</
div
>
)
:
(
<>
{
data
?.
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
className=
"space-y-2.5"
>
{
listItems
.
slice
(
0
,
3
).
map
((
item
)
=>
(
<
Link
key=
{
item
.
id
}
href=
"/xuc-tien-thuong-mai/co-hoi/"
className=
"flex gap-3 rounded-[14px] px-0.5 py-1 transition-colors hover:bg-[#f8fafe]"
>
<
span
className=
"mt-1 h-[40px] w-[2px] shrink-0 rounded-full bg-[#f7b500]"
/>
<
div
className=
"min-w-0"
>
<
h4
className=
"line-clamp-2 text-[15px] leading-[1.45] text-[#264798]"
>
{
item
.
title
}
</
h4
>
<
p
className=
"mt-1.5 text-[13px] text-[#9aa8c1]"
>
{
formatPublishDate
(
item
)
}
</
p
>
</
div
>
</
Link
>
))
}
{
data
?.
responseData
.
rows
.
slice
(
0
,
3
).
map
((
news
)
=>
(
<
CardNews
key=
{
news
.
id
}
news=
{
news
}
/>
))
}
</>
)
}
</
div
>
</
div
>
</
section
>
);
}
;
}
export
default
b
usinessOpportunities
;
export
default
B
usinessOpportunities
;
src/app/(main)/(home)/components/events-calendar/index.tsx
View file @
c03ea16b
import
EventCalendar
from
"@/components/base/event-calendar"
import
{
ChevronsRight
}
from
"lucide-react"
import
Link
from
"next/link"
'use client'
;
import
{
type
AdminNewsItem
,
getAdminNewsSeed
,
}
from
"@/mockdata/admin-news"
;
import
{
addMonths
,
format
,
getDay
,
startOfMonth
,
subMonths
}
from
"date-fns"
;
import
dayjs
from
"dayjs"
;
import
{
ChevronLeft
,
ChevronRight
}
from
"lucide-react"
;
import
{
useMemo
,
useState
}
from
"react"
;
const
weekDays
=
[
"CN"
,
"T2"
,
"T3"
,
"T4"
,
"T5"
,
"T6"
,
"T7"
];
const
eventItems
=
getAdminNewsSeed
()
.
filter
(
(
item
)
=>
item
.
type
===
"tintuc"
&&
!
item
.
is_hidden
&&
item
.
started_at
,
)
.
sort
(
(
left
,
right
)
=>
new
Date
(
left
.
started_at
).
getTime
()
-
new
Date
(
right
.
started_at
).
getTime
(),
);
function
isTrainingEvent
(
item
:
AdminNewsItem
)
{
return
item
.
tagsearch_values
.
some
((
tag
)
=>
tag
.
toLowerCase
().
includes
(
"đào tạo"
));
}
function
EventsCalendar
()
{
const
firstEventDate
=
eventItems
[
0
]?.
started_at
?
new
Date
(
eventItems
[
0
].
started_at
)
:
new
Date
(
"2026-11-01T00:00:00"
);
const
[
currentMonth
,
setCurrentMonth
]
=
useState
(
new
Date
(
firstEventDate
.
getFullYear
(),
firstEventDate
.
getMonth
(),
1
),
);
const
monthEvents
=
useMemo
(
()
=>
eventItems
.
filter
((
item
)
=>
{
const
date
=
new
Date
(
item
.
started_at
);
return
(
date
.
getMonth
()
===
currentMonth
.
getMonth
()
&&
date
.
getFullYear
()
===
currentMonth
.
getFullYear
()
);
}),
[
currentMonth
],
);
const
days
=
useMemo
(()
=>
{
const
monthStart
=
startOfMonth
(
currentMonth
);
const
startWeekDay
=
getDay
(
monthStart
);
const
start
=
new
Date
(
monthStart
);
start
.
setDate
(
monthStart
.
getDate
()
-
startWeekDay
);
return
Array
.
from
({
length
:
35
},
(
_
,
index
)
=>
{
const
day
=
new
Date
(
start
);
day
.
setDate
(
start
.
getDate
()
+
index
);
return
day
;
});
},
[
currentMonth
]);
const
eventMap
=
useMemo
(()
=>
{
const
map
=
new
Map
<
string
,
AdminNewsItem
[]
>
();
monthEvents
.
forEach
((
item
)
=>
{
const
key
=
dayjs
(
item
.
started_at
).
format
(
"YYYY-MM-DD"
);
const
existing
=
map
.
get
(
key
)
??
[];
existing
.
push
(
item
);
map
.
set
(
key
,
existing
);
});
return
map
;
},
[
monthEvents
]);
const
highlightedEvent
=
monthEvents
[
0
];
const
EventsCalendar
=
()
=>
{
return
(
<
div
className=
"bg-[#063e8e] w-full lg:w-[30%] p-5
"
>
<
aside
>
<
div
className=
"flex justify-between items-center"
>
<
h2
className=
"text-[
18px] sm:text-[20px] font-bold uppercase text-[#e8c518
]"
>
<
aside
className=
"w-full rounded-[28px] bg-white p-4 text-[#24469c] shadow-[0_18px_38px_rgba(16,61,130,0.16)] md:p-5 xl:w-[28%] xl:min-w-[320px]
"
>
<
div
className=
"flex items-start justify-between gap-3"
>
<
div
>
<
h2
className=
"text-[
28px] font-extrabold uppercase tracking-tight md:text-[34px
]"
>
Lịch sự kiện
</
h2
>
<
Link
href=
"/hoat-dong/su-kien"
className=
"text-[#e8c518] hover:underline text-sm sm:text-base"
<
p
className=
"mt-1.5 text-[12px] uppercase tracking-[0.28em] text-[#7f8eab]"
>
{
`THÁNG ${format(currentMonth, "MM/yyyy")}`
}
</
p
>
</
div
>
<
div
className=
"flex gap-2"
>
<
button
type=
"button"
onClick=
{
()
=>
setCurrentMonth
(
subMonths
(
currentMonth
,
1
))
}
className=
"flex h-8 w-8 items-center justify-center rounded-full border border-[#dbe4f2] text-[#7f8eab] transition-colors hover:border-[#24469c] hover:text-[#24469c]"
>
<
ChevronsRight
/>
</
Link
>
<
ChevronLeft
className=
"h-4 w-4"
/>
</
button
>
<
button
type=
"button"
onClick=
{
()
=>
setCurrentMonth
(
addMonths
(
currentMonth
,
1
))
}
className=
"flex h-8 w-8 items-center justify-center rounded-full border border-[#dbe4f2] text-[#7f8eab] transition-colors hover:border-[#24469c] hover:text-[#24469c]"
>
<
ChevronRight
className=
"h-4 w-4"
/>
</
button
>
</
div
>
<
hr
className=
"border-[#e8c518] mb-4"
/>
<
EventCalendar
/>
</
aside
>
</
div
>
)
<
div
className=
"mt-3 h-[4px] w-[60px] rounded-full bg-[#f7b500]"
/>
<
div
className=
"mt-4 border-t border-[#ebf0f8] pt-3.5"
>
<
div
className=
"grid grid-cols-7 gap-y-2.5 text-center text-[11px] font-semibold uppercase text-[#9aabc6]"
>
{
weekDays
.
map
((
day
)
=>
(
<
div
key=
{
day
}
>
{
day
}
</
div
>
))
}
</
div
>
<
div
className=
"mt-2.5 grid grid-cols-7 gap-y-2.5 text-center text-[13px] text-[#5e7090]"
>
{
days
.
map
((
day
)
=>
{
const
key
=
dayjs
(
day
).
format
(
"YYYY-MM-DD"
);
const
items
=
eventMap
.
get
(
key
)
??
[];
const
inMonth
=
day
.
getMonth
()
===
currentMonth
.
getMonth
();
const
hasTraining
=
items
.
some
((
item
)
=>
isTrainingEvent
(
item
));
const
hasEvent
=
items
.
length
>
0
&&
!
hasTraining
;
return
(
<
div
key=
{
key
}
className=
"relative flex items-center justify-center"
>
<
span
className=
{
`relative flex h-7 w-7 items-center justify-center rounded-full ${
!inMonth
? "text-[#c9d2e2]"
: hasTraining
? "bg-[#ffbc11] font-semibold text-[#163b73]"
: hasEvent
? "bg-[#1e3f9a] font-semibold text-white"
: ""
}`
}
>
{
format
(
day
,
"d"
)
}
</
span
>
{
items
.
length
>
0
&&
!
hasTraining
&&
inMonth
?
(
<
span
className=
"absolute bottom-[-5px] h-1.5 w-1.5 rounded-full bg-[#1e3f9a]"
/>
)
:
null
}
{
items
.
length
>
0
&&
hasTraining
&&
inMonth
?
(
<
span
className=
"absolute bottom-[-5px] h-1.5 w-1.5 rounded-full bg-[#ffbc11]"
/>
)
:
null
}
</
div
>
);
})
}
</
div
>
</
div
>
<
div
className=
"mt-4 flex items-center gap-5 text-[12px] font-medium text-[#45608f]"
>
<
div
className=
"flex items-center gap-2"
>
<
span
className=
"h-2.5 w-2.5 rounded-full bg-[#1e3f9a]"
/>
<
span
>
Sự kiện
</
span
>
</
div
>
<
div
className=
"flex items-center gap-2"
>
<
span
className=
"h-2.5 w-2.5 rounded-full bg-[#ffbc11]"
/>
<
span
>
Đào tạo
</
span
>
</
div
>
</
div
>
{
highlightedEvent
?
(
<
div
className=
"mt-4 rounded-[16px] bg-[#f7f9fd] p-3.5 text-[12px] leading-5 text-[#3d547f]"
>
<
div
className=
"flex items-start gap-3"
>
<
span
className=
{
`mt-1 h-2.5 w-2.5 rounded-full ${
isTrainingEvent(highlightedEvent) ? "bg-[#ffbc11]" : "bg-[#1e3f9a]"
}`
}
/>
<
p
className=
"line-clamp-3"
>
{
highlightedEvent
.
title
}
</
p
>
</
div
>
</
div
>
)
:
null
}
</
aside
>
);
}
export
default
EventsCalendar
\ No newline at end of file
export
default
EventsCalendar
;
src/app/(main)/(home)/components/events/index.tsx
View file @
c03ea16b
import
{
useGetEvents
}
from
"@/api/endpoints/event"
;
import
{
EventApiResponse
,
EventItem
}
from
"@/api/types/event"
;
'use client'
;
import
ImageNext
from
"@/components/shared/image-next"
;
import
{
Spinner
}
from
"@/components/ui/spinner"
;
import
{
ChevronsRight
}
from
"lucide-react"
import
Link
from
"next/link"
import
BASE_URL
from
"@/links/index"
;
import
CardEvent
from
"./components/card-event"
;
import
stripImagesAndHtml
from
"@/helpers/stripImageAndHtml"
;
import
{
type
AdminNewsItem
,
getAdminNewsSeed
,
}
from
"@/mockdata/admin-news"
;
import
dayjs
from
"dayjs"
;
import
Link
from
"next/link"
;
const
eventItems
=
getAdminNewsSeed
()
.
filter
(
(
item
)
=>
item
.
type
===
"tintuc"
&&
item
.
header_category_id
===
"activity-events"
&&
!
item
.
is_hidden
&&
item
.
started_at
,
)
.
sort
(
(
left
,
right
)
=>
new
Date
(
left
.
started_at
).
getTime
()
-
new
Date
(
right
.
started_at
).
getTime
(),
);
function
formatEventDate
(
item
:
AdminNewsItem
)
{
return
dayjs
(
item
.
started_at
||
item
.
published_at
||
item
.
created_at
).
format
(
"DD/MM/YYYY"
);
}
function
Events
()
{
const
{
data
,
isLoading
}
=
useGetEvents
<
EventApiResponse
>
();
const
[
featuredEvent
,
...
sideEvents
]
=
eventItems
;
if
(
!
featuredEvent
)
return
null
;
return
(
<
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]"
>
<
div
className=
"flex-1 rounded-[28px] bg-linear-to-br from-[#14488f] to-[#2d67bf] p-4 text-white shadow-[0_18px_38px_rgba(16,61,130,0.24)] md:p-5"
>
<
div
className=
"mb-4 flex items-start justify-between gap-4"
>
<
div
>
<
h2
className=
"text-[28px] font-extrabold uppercase tracking-tight text-white md:text-[34px]"
>
Sự kiện sắp diễn ra
</
h2
>
<
Link
href=
"/hoat-dong/su-kien"
className=
"text-[#e8c518] text-sm sm:text-base"
>
<
ChevronsRight
/>
<
div
className=
"mt-2.5 h-[4px] w-[60px] rounded-full bg-[#f7b500]"
/>
</
div
>
<
Link
href=
"/hoat-dong/su-kien"
className=
"pt-1.5 text-sm font-semibold text-[#ffd34f] transition-colors hover:text-white"
>
Xem sự kiện
</
Link
>
</
div
>
<
hr
className=
"border-[#e8c518] mb-4"
/>
<
div
className=
"flex flex-col md:flex-row gap-5"
>
{
isLoading
?
(
<
div
className=
"flex flex-col justify-center items-center w-full min-h-[180px] sm:min-h-[220px] p-3"
>
<
Spinner
/>
<
div
className=
"grid items-stretch gap-3 xl:grid-cols-[minmax(0,1.02fr)_minmax(270px,0.98fr)]"
>
<
Link
href=
"/hoat-dong/su-kien"
className=
"flex h-full flex-col overflow-hidden rounded-[22px] bg-white text-[#20408f] shadow-[0_14px_28px_rgba(10,39,95,0.18)]"
>
<
div
className=
"h-[220px] overflow-hidden md:h-[235px] xl:h-[248px]"
>
<
ImageNext
src=
{
featuredEvent
.
thumbnail
?.
url
??
"/thumbnail.png"
}
alt=
{
featuredEvent
.
thumbnail
?.
alt
||
featuredEvent
.
title
}
width=
{
720
}
height=
{
520
}
className=
"h-full w-full object-cover"
/>
</
div
>
<
div
className=
"p-3 pt-2.5"
>
<
h3
className=
"line-clamp-2 text-[16px] font-extrabold uppercase leading-[1.28] text-[#22459b] md:text-[18px]"
>
{
featuredEvent
.
title
}
</
h3
>
<
p
className=
"mt-1.5 text-[13px] text-[#90a0bd]"
>
{
formatEventDate
(
featuredEvent
)
}
</
p
>
</
div
>
)
:
(
<>
{
data
?.
responseData
.
rows
.
slice
(
0
,
1
).
map
((
event
:
EventItem
)
=>
(
</
Link
>
<
div
className=
"flex h-full flex-col gap-3"
>
{
sideEvents
.
slice
(
0
,
4
).
map
((
item
)
=>
(
<
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
"
key=
{
item
.
id
}
href=
"/hoat-dong/su-kien"
className=
"flex flex-1 items-center gap-3 rounded-[18px] bg-white/10 p-2.5 shadow-[inset_0_0_0_1px_rgba(255,255,255,0.08)] backdrop-blur-sm transition-colors hover:bg-white/14
"
>
<
div
className=
"w-full aspect-3/2 overflow-hidden
"
>
<
div
className=
"h-[64px] w-[64px] shrink-0 overflow-hidden rounded-[12px]
"
>
<
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"
src=
{
item
.
thumbnail
?.
url
??
"/thumbnail.png"
}
alt=
{
item
.
thumbnail
?.
alt
||
item
.
title
}
width=
{
160
}
height=
{
160
}
className=
"h-full w-full object-cover"
/>
</
div
>
<
div
className=
"flex-1"
>
<
p
className=
"text-[#0056b3] font-bold text-xl line-clamp-2"
>
{
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
className=
"min-w-0"
>
<
h4
className=
"line-clamp-2 text-[15px] font-semibold leading-[1.35] text-white"
>
{
item
.
title
}
</
h4
>
<
p
className=
"mt-1 text-[12px] text-white/78"
>
{
formatEventDate
(
item
)
}
</
p
>
</
div
>
</
Link
>
))
}
<
div
className=
"w-full md:w-1/2"
>
{
data
?.
responseData
.
rows
.
slice
(
0
,
4
).
map
((
event
)
=>
(
<
CardEvent
key=
{
event
.
id
}
event=
{
event
}
/>
))
}
</
div
>
</>
)
}
</
div
>
</
div
>
)
)
;
}
export
default
Events
\ No newline at end of file
export
default
Events
;
src/app/(main)/(home)/components/featured-news/index.tsx
View file @
c03ea16b
'use client'
;
import
{
useGetNews
}
from
"@/api/endpoints/news"
;
import
{
GetNewsResponseType
}
from
"@/api/types/news"
;
import
ImageNext
from
"@/components/shared/image-next"
;
import
{
type
AdminNewsItem
,
getAdminNewsSeed
,
}
from
"@/mockdata/admin-news"
;
import
{
getHeaderCategorySeed
}
from
"@/mockdata/header-config"
;
import
dayjs
from
"dayjs"
;
import
{
ChevronRight
,
Mail
,
Phone
}
from
"lucide-react"
;
import
Link
from
"next/link"
;
import
{
Swiper
,
SwiperSlide
}
from
"swiper/react"
;
import
{
Autoplay
,
Grid
}
from
"swiper/modules"
;
import
BASE_URL
from
"@/links/index"
;
import
{
Spinner
}
from
"@/components/ui/spinner"
;
const
FALLBACK_CATEGORY_LINK
=
"/hoat-dong/tin-tuc"
;
const
headerCategoryMap
=
new
Map
(
getHeaderCategorySeed
().
map
((
item
)
=>
[
item
.
id
,
item
.
static_link
]),
);
const
FEATURED_OVERVIEW_LINK
=
headerCategoryMap
.
get
(
"activity-news"
)
??
FALLBACK_CATEGORY_LINK
;
function
getFeaturedNewsItems
(
items
:
AdminNewsItem
[])
{
return
items
.
filter
(
(
item
)
=>
item
.
type
===
"tintuc"
&&
item
.
is_featured
&&
!
item
.
is_hidden
&&
Boolean
(
item
.
thumbnail
?.
url
),
)
.
slice
(
0
,
3
);
}
function
getNewsLink
(
item
:
AdminNewsItem
)
{
return
headerCategoryMap
.
get
(
item
.
header_category_id
)
??
FALLBACK_CATEGORY_LINK
;
}
function
getBadgeLabel
(
item
:
AdminNewsItem
)
{
if
(
item
.
header_category_id
===
"activity-events"
)
return
"Sự kiện"
;
const
firstTag
=
item
.
tagsearch_values
.
find
(
Boolean
);
if
(
firstTag
)
return
firstTag
;
return
"Tin VCCI"
;
}
const
featuredNewsItems
=
getFeaturedNewsItems
(
getAdminNewsSeed
());
function
FeaturedNews
()
{
const
{
data
,
isLoading
}
=
useGetNews
<
GetNewsResponseType
>
(
{
pageSize
:
'10'
,
}
);
const
[
primaryItem
,
...
secondaryItems
]
=
featuredNewsItems
;
if
(
!
primaryItem
)
return
null
;
return
(
<
section
>
<
div
className=
"flex items-center justify-center py-8 px-4"
>
<
div
className=
"flex items-center w-full max-w-4xl"
>
<
div
className=
"flex-1 h-px bg-linear-to-r from-transparent via-gray-300 to-gray-400"
></
div
>
<
h1
className=
"px-6 text-[20px] sm:text-[24px] md:text-[28px] uppercase font-bold text-[#063e8e] whitespace-nowrap"
>
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
>
{
isLoading
?
(
<
div
className=
"flex justify-center items-center w-full h-64"
>
<
Spinner
/>
</
div
>
)
:
(
<
Swiper
modules=
{
[
Autoplay
]
}
autoplay=
{
{
delay
:
4000
,
disableOnInteraction
:
false
}
}
loop
breakpoints=
{
{
0
:
{
slidesPerView
:
1.1
,
spaceBetween
:
10
},
640
:
{
slidesPerView
:
2
,
spaceBetween
:
16
},
1024
:
{
slidesPerView
:
3
,
spaceBetween
:
24
},
}
}
className=
"pb-5"
<
section
className=
"py-8 md:py-10"
>
<
div
className=
"w-full"
>
<
div
className=
"mb-8 flex items-start justify-between gap-4"
>
<
div
>
<
h2
className=
"text-[28px] font-extrabold uppercase tracking-tight text-[#24469c] md:text-[34px]"
>
Tin nổi bật
</
h2
>
<
div
className=
"mt-3 h-[5px] w-[68px] rounded-full bg-[#f7b500]"
/>
</
div
>
<
Link
href=
{
FEATURED_OVERVIEW_LINK
}
className=
"inline-flex items-center gap-2 pt-2 text-base font-semibold text-[#2b56c0] transition-colors hover:text-[#173f9f]"
>
{
data
?.
responseData
?.
rows
.
map
((
news
)
=>
(
<
SwiperSlide
key=
{
news
.
id
}
>
<
span
>
Xem tất cả
</
span
>
<
ChevronRight
className=
"h-4 w-4"
/>
</
Link
>
</
div
>
<
div
className=
"grid gap-5 xl:grid-cols-[minmax(0,1.14fr)_minmax(0,0.96fr)]"
>
<
Link
href=
{
`${news.external_link}`
}
className=
"relative block bg-white shadow-md overflow-hidden hover:shadow-lg transition-shadow duration-300
"
href=
{
getNewsLink
(
primaryItem
)
}
className=
"group relative block min-h-[260px] overflow-hidden rounded-[24px] bg-[#0d2f5f] shadow-[0_18px_38px_rgba(28,52,120,0.22)] md:min-h-[320px] xl:min-h-[350px]
"
>
<
div
className=
"relative h-full min-h-[260px] md:min-h-[320px] xl:min-h-[350px]"
>
<
ImageNext
src=
{
`${BASE_URL.imageEndpoint}${news.thumbnail}`
}
alt=
{
news
.
title
}
src=
{
primaryItem
.
thumbnail
?.
url
??
"/thumbnail.png"
}
alt=
{
primaryItem
.
thumbnail
?.
alt
||
primaryItem
.
title
}
width=
{
1200
}
height=
{
800
}
className=
"absolute inset-0 h-full w-full object-cover transition-transform duration-500 group-hover:scale-[1.04]"
/>
<
div
className=
"absolute inset-0 bg-linear-to-t from-[#26356d] via-[#53669b]/34 to-transparent"
/>
<
div
className=
"relative flex h-full flex-col justify-end p-4 md:p-5"
>
<
span
className=
"mb-2 inline-flex w-fit rounded-[10px] bg-[#ffc400] px-3 py-1 text-sm font-bold text-[#1d3f90]"
>
{
getBadgeLabel
(
primaryItem
)
}
</
span
>
<
h3
className=
"max-w-3xl text-[20px] font-bold leading-[1.28] text-white md:text-[28px] xl:text-[32px]"
>
{
primaryItem
.
title
}
</
h3
>
<
p
className=
"mt-2 text-base font-medium text-white/78 md:text-[17px]"
>
{
dayjs
(
primaryItem
.
published_at
||
primaryItem
.
created_at
).
format
(
"DD/MM/YYYY"
)
}
</
p
>
</
div
>
</
div
>
</
Link
>
<
div
className=
"grid gap-4"
>
<
div
className=
"grid gap-4 md:grid-cols-2"
>
{
secondaryItems
.
map
((
item
)
=>
(
<
Link
key=
{
item
.
id
}
href=
{
getNewsLink
(
item
)
}
className=
"group relative block min-h-[195px] overflow-hidden rounded-[20px] bg-[#27447f] shadow-[0_16px_32px_rgba(28,52,120,0.2)] md:min-h-[205px] xl:min-h-[215px]"
>
<
div
className=
"relative h-full min-h-[195px] md:min-h-[205px] xl:min-h-[215px]"
>
<
ImageNext
src=
{
item
.
thumbnail
?.
url
??
"/thumbnail.png"
}
alt=
{
item
.
thumbnail
?.
alt
||
item
.
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"
height=
{
420
}
className=
"absolute inset-0 h-full w-full object-cover transition-transform duration-500 group-hover:scale-[1.05]"
/>
<
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
}
<
div
className=
"absolute inset-0 bg-linear-to-t from-[#5a6796] via-[#405083]/34 to-transparent"
/>
<
div
className=
"relative flex h-full flex-col justify-end p-3.5"
>
<
span
className=
"mb-2 inline-flex w-fit rounded-[10px] bg-[#ffc400] px-3 py-1 text-sm font-bold text-[#1d3f90]"
>
{
getBadgeLabel
(
item
)
}
</
span
>
<
h4
className=
"line-clamp-2 text-[16px] font-bold leading-[1.32] text-white md:text-[17px]"
>
{
item
.
title
}
</
h4
>
<
p
className=
"mt-1.5 text-[15px] font-medium text-white/78 md:text-base"
>
{
dayjs
(
item
.
published_at
||
item
.
created_at
).
format
(
"DD/MM/YYYY"
)
}
</
p
>
</
div
>
</
div
>
</
Link
>
</
SwiperSlide
>
))
}
</
Swiper
>
)
}
</
div
>
<
div
className=
"overflow-hidden rounded-[28px] bg-linear-to-r from-[#214b95] to-[#2b66bb] px-5 py-5 text-white shadow-[0_18px_38px_rgba(28,52,120,0.2)] md:px-7"
>
<
div
className=
"flex flex-col gap-4 lg:flex-row lg:items-center lg:justify-between"
>
<
div
>
<
p
className=
"text-[12px] uppercase tracking-[0.4em] text-white/80"
>
Quảng bá
&
tiếp cận
</
p
>
<
h3
className=
"mt-2 max-w-[520px] text-[20px] font-extrabold uppercase leading-[1.02] xl:text-[28px]"
>
Cộng đồng doanh nghiệp
</
h3
>
</
div
>
<
div
className=
"w-full max-w-60 rounded-[999px] bg-white px-4 py-3 text-[#173f88] shadow-[0_10px_24px_rgba(8,25,74,0.12)]"
>
<
div
className=
"flex items-center gap-3 text-sm font-medium"
>
<
Mail
className=
"h-5 w-5 shrink-0"
/>
<
span
>
info@vcci-hcm.org.vn
</
span
>
</
div
>
<
div
className=
"mt-2 flex items-center gap-3 text-sm font-medium"
>
<
Phone
className=
"h-5 w-5 shrink-0"
/>
<
span
>
+84-28-3932 5171
</
span
>
</
div
>
</
div
>
</
div
>
</
div
>
</
div
>
</
div
>
</
div
>
</
section
>
);
}
...
...
src/app/(main)/(home)/components/members/index.tsx
View file @
c03ea16b
'use client'
;
import
ImageNext
from
"@/components/shared/image-next"
;
import
memberImages
from
"@/constants/memberImages"
;
import
{
ChevronsRight
}
from
"lucide-react"
;
import
{
getAdminNewsSeed
,
}
from
"@/mockdata/admin-news"
;
import
Link
from
"next/link"
;
import
{
Autoplay
}
from
"swiper/modules"
;
import
{
Swiper
,
SwiperSlide
}
from
"swiper/react"
;
import
"swiper/css"
;
import
"swiper/css/grid"
;
import
{
GetNewsResponseType
,
NewsItem
}
from
"@/api/types/news"
;
import
BASE_URL
from
"@/links/index"
;
import
{
useGetNews
}
from
"@/api/endpoints/news"
;
import
{
Spinner
}
from
"@/components/ui/spinner"
;
const
Members
=
()
=>
{
const
{
data
,
isLoading
}
=
useGetNews
<
GetNewsResponseType
>
(
{
filters
:
`page_config.code @=ket-noi-hoi-vien`
,
}
const
memberConnectionItems
=
getAdminNewsSeed
()
.
filter
(
(
item
)
=>
item
.
type
===
"tintuc"
&&
!
item
.
is_hidden
&&
(
item
.
category_ids
.
includes
(
"cat-member-connection"
)
||
item
.
tagsearch_values
.
some
((
tag
)
=>
tag
.
toLowerCase
().
includes
(
"kết nối hội viên"
))),
)
.
sort
(
(
left
,
right
)
=>
new
Date
(
right
.
published_at
||
right
.
created_at
).
getTime
()
-
new
Date
(
left
.
published_at
||
left
.
created_at
).
getTime
(),
);
function
Members
()
{
const
featuredConnection
=
memberConnectionItems
[
0
];
return
(
<
section
className=
"flex flex-col
lg:flex-row gap-5 pb-10 mb-0
"
>
{
/* LEFT: HỘI VIÊN TIÊU BIỂU */
}
<
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
]"
>
<
section
className=
"flex flex-col
gap-5 pb-8 xl:flex-row xl:items-stretch
"
>
<
aside
className=
"flex-1 rounded-[22px] bg-[#f7b500] p-4 shadow-[0_18px_34px_rgba(247,181,0,0.18)] md:p-5"
>
<
div
className=
"flex items-start justify-between gap-3
"
>
<
div
>
<
h2
className=
"text-[28px] font-extrabold uppercase tracking-tight text-[#20449a] md:text-[34px
]"
>
Hội viên tiêu biểu
</
h2
>
<
div
className=
"mt-2.5 h-[4px] w-[40px] rounded-full bg-white"
/>
</
div
>
<
Link
href=
"/danh-ba-hoi-vien"
className=
"
text-[#063e8e] hover:underline text-sm font-medium
"
className=
"
pt-1 text-sm font-semibold text-[#1e2f5e] transition-colors hover:text-[#20449a]
"
>
<
ChevronsRight
/>
Xem thêm
</
Link
>
</
div
>
<
hr
className=
"border-[#063e8e] mb
-5"
/>
<
div
className=
"mt-4 border-t border-[#e7aa00] pt
-5"
/>
<
Swiper
modules=
{
[
Autoplay
]
}
autoplay=
{
{
delay
:
4000
,
disableOnInteraction
:
false
}
}
loop
slidesPerView=
{
3
}
spaceBetween=
{
16
}
breakpoints=
{
{
0
:
{
slidesPerView
:
2
,
spaceBetween
:
10
},
640
:
{
slidesPerView
:
3
,
spaceBetween
:
16
},
1024
:
{
slidesPerView
:
3
,
spaceBetween
:
24
},
}
}
className=
"partner-swiper"
<
div
className=
"grid gap-4 sm:grid-cols-3"
>
{
memberImages
.
slice
(
0
,
3
).
map
((
src
,
index
)
=>
(
<
div
key=
{
src
}
className=
"rounded-[24px] bg-white p-[7px] shadow-[0_10px_22px_rgba(158,114,0,0.16)]"
>
{
memberImages
.
map
((
src
,
i
)
=>
(
<
SwiperSlide
key=
{
i
}
>
<
div
className=
"flex justify-center items-center bg-white rounded-lg shadow p-3"
>
<
div
className=
"flex aspect-[1.06/1] items-center justify-center overflow-hidden rounded-[18px] bg-[#f4f7fb] px-4 py-5"
>
<
ImageNext
src=
{
src
}
alt=
{
`member-${i}`
}
width=
{
160
}
height=
{
160
}
sizes=
"(max-width:640px) 25vw,(max-width:1024px) 15vw,10vw"
className=
"object-contain w-full h-full"
alt=
{
`Hội viên tiêu biểu ${index + 1}`
}
width=
{
320
}
height=
{
220
}
className=
"max-h-full max-w-full object-contain"
/>
</
div
>
</
SwiperSlide
>
</
div
>
))
}
</
Swiper
>
</
div
>
</
aside
>
{
/* RIGHT: KẾT NỐI HỘI VIÊN */
}
{
isLoading
?
(
<
div
className=
"flex justify-center items-center w-full h-64"
>
<
Spinner
/>
</
div
>
)
:
(
<
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]"
>
<
aside
className=
"w-full xl:w-[31%] xl:min-w-[320px]"
>
<
div
className=
"mb-4 flex items-start justify-between gap-3"
>
<
div
>
<
h2
className=
"text-[28px] font-extrabold uppercase tracking-tight text-[#24469c] md:text-[34px]"
>
Kết nối hội viên
</
h2
>
<
div
className=
"mt-2.5 h-[4px] w-[40px] rounded-full bg-[#f7b500]"
/>
</
div
>
<
hr
className=
"border-[#063e8e] mb-5"
/
>
<
Swiper
modules=
{
[
Autoplay
]
}
autoplay=
{
{
delay
:
4000
,
disableOnInteraction
:
false
}
}
loop
className=
"
partner-swiper
"
</
div
>
{
featuredConnection
?
(
<
Link
href=
"/danh-ba-hoi-vien"
className=
"
block overflow-hidden rounded-[20px] shadow-[0_16px_32px_rgba(31,59,124,0.12)]
"
>
{
data
?.
responseData
.
rows
.
map
((
news
:
NewsItem
)
=>
(
<
SwiperSlide
key=
{
news
.
id
}
>
<
a
href=
{
`${news.external_link}`
}
>
<
div
className=
"w-full aspect-3/2 relative overflow-hidden mb-5"
>
<
div
className=
"aspect-[1.25/1] overflow-hidden rounded-[20px]"
>
<
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"
src=
{
featuredConnection
.
thumbnail
?.
url
??
"/thumbnail.png"
}
alt=
{
featuredConnection
.
thumbnail
?.
alt
||
featuredConnection
.
title
}
width=
{
520
}
height=
{
420
}
className=
"h-full w-full object-cover"
/>
<
div
className=
"absolute bg-white opacity-90 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
>
</
SwiperSlide
>
))
}
</
Swiper
>
</
Link
>
)
:
null
}
</
aside
>
)
}
</
section
>
);
}
;
}
export
default
Members
;
src/app/(main)/(home)/components/news/index.tsx
View file @
c03ea16b
import
{
useGetNews
}
from
"@/api/endpoints/news"
;
import
{
GetNewsResponseType
,
NewsItem
}
from
"@/api/types/news"
;
'use client'
;
import
ImageNext
from
"@/components/shared/image-next"
;
import
Link
from
"next/link"
;
import
BASE_URL
from
"@/links/index"
;
import
{
ChevronsRight
}
from
"lucide-react"
;
import
{
useState
}
from
"react"
;
import
stripImagesAndHtml
from
"@/helpers/stripImageAndHtml"
;
import
CardNews
from
"./components/card-news"
;
import
{
Spinner
}
from
"@/components/ui/spinner"
;
import
{
type
AdminNewsItem
,
getAdminNewsSeed
,
}
from
"@/mockdata/admin-news"
;
import
dayjs
from
"dayjs"
;
import
Link
from
"next/link"
;
import
{
useMemo
,
useState
}
from
"react"
;
const
News
=
()
=>
{
const
[
tab
,
setTab
]
=
useState
(
"all"
);
const
tabs
=
[
{
id
:
"all"
,
label
:
"Tất cả"
},
{
id
:
"tin-vcci"
,
label
:
"Tin VCCI"
},
{
id
:
"tin-kinh-te"
,
label
:
"Tin Kinh Tế"
},
{
id
:
"chuyen-de"
,
label
:
"Chuyên Đề"
},
];
const
allNewsItems
=
getAdminNewsSeed
().
filter
(
(
item
)
=>
item
.
type
===
"tintuc"
&&
!
item
.
is_hidden
,
);
function
getTabLabel
(
item
:
AdminNewsItem
)
{
const
tags
=
item
.
tagsearch_values
.
map
((
tag
)
=>
tag
.
toLowerCase
());
if
(
tags
.
some
((
tag
)
=>
tag
.
includes
(
"kinh tế"
)
||
tag
.
includes
(
"vĩ mô"
)))
{
return
"Tin Kinh Tế"
;
}
if
(
tags
.
some
((
tag
)
=>
tag
.
includes
(
"chuyên đề"
)
||
tag
.
includes
(
"cẩm nang"
)))
{
return
"Chuyên Đề"
;
}
return
"Tin VCCI"
;
}
function
matchesTab
(
item
:
AdminNewsItem
,
tab
:
string
)
{
if
(
tab
===
"all"
)
return
true
;
const
tags
=
item
.
tagsearch_values
.
map
((
value
)
=>
value
.
toLowerCase
());
if
(
tab
===
"tin-vcci"
)
{
return
tags
.
some
((
tag
)
=>
tag
.
includes
(
"tin vcci"
)
||
tag
.
includes
(
"hợp tác"
));
}
if
(
tab
===
"tin-kinh-te"
)
{
return
tags
.
some
((
tag
)
=>
tag
.
includes
(
"kinh tế"
)
||
tag
.
includes
(
"vĩ mô"
));
}
const
{
data
:
newsSpecial
,
isLoading
:
isLoadingSpecial
}
=
useGetNews
<
GetNewsResponseType
>
({
pageSize
:
'1'
});
const
{
data
:
newsFilters
,
isLoading
:
isLoadingFilters
}
=
useGetNews
<
GetNewsResponseType
>
(
{
pageSize
:
'5'
,
filters
:
tab
===
"all"
?
``
:
`page_config.code @=
${
tab
}
`
,
if
(
tab
===
"chuyen-de"
)
{
return
tags
.
some
((
tag
)
=>
tag
.
includes
(
"chuyên đề"
)
||
tag
.
includes
(
"cẩm nang"
));
}
return
true
;
}
function
News
()
{
const
[
tab
,
setTab
]
=
useState
(
"all"
);
const
filteredItems
=
useMemo
(
()
=>
allNewsItems
.
filter
((
item
)
=>
matchesTab
(
item
,
tab
)),
[
tab
],
);
const
featuredArticle
=
filteredItems
[
0
]
??
allNewsItems
[
0
];
const
listArticles
=
filteredItems
.
slice
(
1
,
5
);
if
(
!
featuredArticle
)
return
null
;
return
(
<
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]"
>
<
div
className=
"mb-6 flex flex-col gap-4 xl:flex-row xl:items-start xl:justify-between"
>
<
div
>
<
h2
className=
"text-[28px] font-extrabold uppercase tracking-tight text-[#24469c] md:text-[34px]"
>
Tin tức
</
Link
>
<
Link
href=
"/thong-tin-truyen-thong/tin-vcci/"
className=
"text-[#063e8e] text-sm sm:text-base"
>
<
ChevronsRight
/>
</
Link
>
</
h2
>
<
div
className=
"mt-3 h-[5px] w-[68px] rounded-full bg-[#f7b500]"
/>
</
div
>
<
hr
className=
"border-[#063e8e] mb-4"
/>
<
div
className=
"flex flex-col md:flex-row gap-5"
>
{
isLoadingSpecial
?
(
<
div
className=
"flex justify-center items-center flex-col w-full md:w-1/2 min-h-[180px] sm:min-h-[220px] gap-3 mb-3"
>
<
Spinner
/>
<
div
className=
"flex flex-wrap gap-3 xl:justify-end"
>
{
tabs
.
map
((
item
)
=>
{
const
active
=
item
.
id
===
tab
;
return
(
<
button
key=
{
item
.
id
}
type=
"button"
onClick=
{
()
=>
setTab
(
item
.
id
)
}
className=
{
`rounded-full px-5 py-2.5 text-[14px] font-semibold transition-all ${
active
? "bg-[#1f5ba9] text-white shadow-[0_10px_20px_rgba(31,91,169,0.18)]"
: "bg-[#f4f7fb] text-[#7f8eab] hover:bg-[#eaf0f8]"
}`
}
>
{
item
.
label
}
</
button
>
);
})
}
</
div
>
</
div
>
)
:
(
newsSpecial
?.
responseData
.
rows
.
slice
(
0
,
1
)
.
map
((
news
:
NewsItem
)
=>
(
<
div
className=
"grid gap-4 xl:grid-cols-[minmax(0,1.02fr)_minmax(320px,0.98fr)]"
>
<
div
>
<
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"
href=
"/hoat-dong/tin-tuc"
className=
"block h-full overflow-hidden rounded-[22px] border border-[#dbe4f2] bg-white shadow-[0_8px_24px_rgba(31,59,124,0.08)]"
>
<
div
className=
"w-full aspect-3/2
overflow-hidden"
>
<
div
className=
"aspect-[1.75/1]
overflow-hidden"
>
<
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"
src=
{
featuredArticle
.
thumbnail
?.
url
??
"/thumbnail.png"
}
alt=
{
featuredArticle
.
thumbnail
?.
alt
||
featuredArticle
.
title
}
width=
{
720
}
height=
{
580
}
className=
"h-full w-full object-cover"
/>
</
div
>
<
div
className=
"flex-1 p-5 pt-1"
>
<
p
className=
"text-[#063E8E] font-bold pb-2 text-xl line-clamp-2"
>
{
news
.
title
}
<
div
className=
"space-y-1.5 p-3"
>
<
span
className=
"inline-flex text-[14px] font-bold text-[#e2a500]"
>
{
getTabLabel
(
featuredArticle
)
}
</
span
>
<
h3
className=
"line-clamp-2 text-[16px] font-bold leading-[1.28] text-[#20408f] md:text-[17px]"
>
{
featuredArticle
.
title
}
</
h3
>
<
p
className=
"line-clamp-2 text-[13px] leading-[1.45] text-[#6c7b96]"
>
{
stripImagesAndHtml
(
featuredArticle
.
summary
)
}
</
p
>
<
p
className=
"text-[14px] text-[#8a9bb6]"
>
{
dayjs
(
featuredArticle
.
published_at
||
featuredArticle
.
created_at
).
format
(
"DD/MM/YYYY"
)
}
</
p
>
<
p
className=
"line-clamp-4 text-justify"
>
{
stripImagesAndHtml
(
news
.
description
)
}
</
p
>
</
div
>
</
Link
>
))
)
}
<
div
className=
"w-full md:w-1/2"
>
<
div
className=
"flex flex-wrap gap-2 sm:gap-3 mb-5"
>
<
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
>
{
isLoadingFilters
?
(
<
div
className=
"flex justify-center py-10"
>
<
Spinner
/>
<
div
className=
"xl:flex xl:h-full xl:flex-col"
>
<
div
className=
"space-y-3 xl:flex xl:flex-1 xl:flex-col"
>
{
listArticles
.
map
((
news
)
=>
(
<
Link
key=
{
news
.
id
}
href=
"/hoat-dong/tin-tuc"
className=
"block rounded-[18px] border border-[#dbe4f2] bg-white px-4 py-2.5 shadow-[0_8px_24px_rgba(31,59,124,0.08)] transition-all hover:-translate-y-0.5 hover:shadow-[0_14px_28px_rgba(31,59,124,0.12)] xl:flex-1"
>
<
h4
className=
"line-clamp-2 text-[15px] font-bold leading-[1.28] text-[#21408f]"
>
{
news
.
title
}
</
h4
>
<
p
className=
"mt-1 text-[13px] text-[#8a9bb6]"
>
{
dayjs
(
news
.
published_at
||
news
.
created_at
).
format
(
"DD/MM/YYYY"
)
}
</
p
>
</
Link
>
))
}
</
div
>
)
:
(
newsFilters
?.
responseData
?.
rows
.
slice
(
0
,
4
).
map
((
news
)
=>
(
<
CardNews
key=
{
news
.
id
}
news=
{
news
}
/>
))
)
}
</
div
>
</
div
>
</
div
>
...
...
src/app/(main)/(home)/components/policies-and-laws/index.tsx
View file @
c03ea16b
import
{
useGetNews
}
from
"@/api/endpoints/news"
;
import
{
GetNewsResponseType
,
NewsItem
}
from
"@/api/types/news"
;
import
ImageNext
from
"@/components/shared/image-next"
;
import
{
Spinner
}
from
"@/components/ui/spinner"
;
import
{
ChevronsRight
}
from
"lucide-react"
;
'use client'
;
import
{
type
AdminNewsItem
,
getAdminNewsSeed
,
}
from
"@/mockdata/admin-news"
;
import
dayjs
from
"dayjs"
;
import
{
ChevronRight
}
from
"lucide-react"
;
import
Link
from
"next/link"
;
import
BASE_URL
from
"@/links/index"
;
import
CardNews
from
"./card-news"
;
const
PolicyAndLaws
=
()
=>
{
const
{
data
,
isLoading
}
=
useGetNews
<
GetNewsResponseType
>
(
{
pageSize
:
'5'
,
filters
:
`page_config.code @=phap-luat`
,
}
const
policyItems
=
getAdminNewsSeed
()
.
filter
(
(
item
)
=>
item
.
type
===
"tintuc"
&&
!
item
.
is_hidden
&&
(
item
.
category_ids
.
includes
(
"cat-policy-law"
)
||
item
.
category_ids
.
includes
(
"cat-policy"
)
||
item
.
tagsearch_values
.
some
((
tag
)
=>
{
const
normalized
=
tag
.
toLowerCase
();
return
normalized
.
includes
(
"chính sách"
)
||
normalized
.
includes
(
"pháp luật"
);
})),
)
.
sort
(
(
left
,
right
)
=>
new
Date
(
right
.
published_at
||
right
.
created_at
).
getTime
()
-
new
Date
(
left
.
published_at
||
left
.
created_at
).
getTime
(),
);
function
formatPublishDate
(
item
:
AdminNewsItem
)
{
return
dayjs
(
item
.
published_at
||
item
.
created_at
).
format
(
"DD/MM/YYYY"
);
}
function
PolicyAndLaws
()
{
const
[
featuredItem
,
...
listItems
]
=
policyItems
;
if
(
!
featuredItem
)
return
null
;
return
(
<
div
className=
"flex-1"
>
<
div
className=
"flex justify-between items-center"
>
<
section
className=
"flex-1"
>
<
div
className=
"mb-4 flex items-center justify-between gap-3"
>
<
div
>
<
h2
className=
"text-[28px] font-extrabold uppercase tracking-tight text-[#24469c] md:text-[34px]"
>
Chính sách
&
pháp luật
</
h2
>
<
div
className=
"mt-2.5 h-[4px] w-[40px] rounded-full bg-[#f7b500]"
/>
</
div
>
<
Link
href=
"/thong-tin-truyen-thong/phap-luat"
className=
"text-[
18px] sm:text-[20px] font-bold uppercase text-[#063e8e
]"
className=
"text-[
#24469c] transition-colors hover:text-[#1b55a1
]"
>
Chính sách
&
pháp luật
<
ChevronRight
className=
"h-5 w-5"
/>
</
Link
>
</
div
>
<
div
className=
"space-y-2.5"
>
{
[
featuredItem
,
...
listItems
.
slice
(
0
,
2
)].
map
((
item
,
index
)
=>
(
<
Link
key=
{
item
.
id
}
href=
"/thong-tin-truyen-thong/phap-luat"
className=
"text-[#063e8e] text-sm sm:text-base"
className=
{
`flex gap-3 rounded-[14px] px-0.5 py-1 transition-colors hover:bg-[#f8fafe] ${
index === 0 ? "pt-0.5" : ""
}`
}
>
<
ChevronsRight
/>
</
Link
>
</
div
>
<
hr
className=
"border-[#063e8e] mb-4"
/>
<
div
className=
"pt-2"
>
{
isLoading
?
(
<
div
className=
"container w-full h-[80vh] flex justify-center items-center"
>
<
Spinner
/>
</
div
>
)
:
(
<>
{
data
?.
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
>
<
span
className=
"mt-1 h-[40px] w-[2px] shrink-0 rounded-full bg-[#f7b500]"
/>
<
div
className=
"min-w-0"
>
<
h3
className=
"line-clamp-2 text-[15px] leading-[1.45] text-[#264798] md:text-[16px]"
>
{
item
.
title
}
</
h3
>
<
p
className=
"mt-1.5 text-[13px] text-[#9aa8c1]"
>
{
formatPublishDate
(
item
)
}
</
p
>
</
div
>
</
Link
>
))
}
{
data
?.
responseData
.
rows
.
slice
(
0
,
3
).
map
((
news
)
=>
(
<
CardNews
key=
{
news
.
id
}
news=
{
news
}
/>
))
}
</>
)
}
</
div
>
</
div
>
</
section
>
);
}
...
...
src/app/(main)/(home)/components/quick-links/index.tsx
View file @
c03ea16b
'use client'
;
import
ImageNext
from
"@/components/shared/image-next"
;
import
Link
from
"next/link"
;
const
quickLinks
=
[
{
href
:
"https://vcci-hcm.org.vn/lien-ket-nhanh/doanh-nghiep-kien-nghi-ve-chinh-sach-va-phap-luat/"
,
label
:
"Doanh nghiệp kiến nghị về chính sách và pháp luật"
,
},
{
href
:
"https://vcci-hcm.org.vn/lien-ket-nhanh/cam-nang-huong-dan-dau-tu-kinh-doanh-tai-viet-nam-2023/"
,
label
:
"Cẩm nang hướng dẫn đầu tư kinh doanh tại Việt Nam"
,
},
];
function
QuickLinks
()
{
return
(
<
aside
className=
"w-full
lg:w-[30%]
"
>
<
div
className=
"
flex justify-between items-center
"
>
<
h2
className=
"text-[
18px] sm:text-[20px] font-semibold uppercase text-[#063e8e
]"
>
<
aside
className=
"w-full
xl:grid xl:w-[32%] xl:grid-rows-[0.74fr_0.88fr] xl:gap-4
"
>
<
div
className=
"
rounded-[22px] border border-[#dbe4f2] bg-white p-4 shadow-[0_8px_24px_rgba(31,59,124,0.08)] xl:h-full
"
>
<
h2
className=
"text-[
28px] md:text-[34px] font-extrabold uppercase tracking-tight text-[#24469c
]"
>
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
>
<
div
className=
"mt-3 h-[5px] w-[68px] rounded-full bg-[#f7b500]"
/
>
<
div
className=
"mt-4 space-y-2.5
"
>
{
quickLinks
.
map
((
item
)
=>
(
<
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/"
key=
{
item
.
href
}
href=
{
item
.
href
}
className=
"flex items-start gap-3 text-[15px] leading-[1.32] text-[#556684] transition-colors hover:text-[#21408f]"
>
🔗 Cẩm nang hướng dẫn đầu tư kinh doanh tại Việt Nam
<
span
className=
"mt-1 text-[#e2a500]"
>
›
</
span
>
<
span
>
{
item
.
label
}
</
span
>
</
Link
>
))
}
</
div
>
</
div
>
<
div
>
<
Link
className=
"text-[#363636]
"
href=
"https://vcci-hcm.org.vn/lien-ket-nhanh/doanh-nghiep-kien-nghi-ve-chinh-sach-va-phap-luat/
"
href=
"https://hardwaretools.com.vn/
"
className=
"mt-4 block overflow-hidden rounded-[28px] shadow-[0_12px_28px_rgba(31,59,124,0.14)] xl:mt-0 xl:h-full
"
>
🔗 Doanh nghiệp kiến nghị về chính sách và pháp luật
</
Link
>
</
div
>
</
div
>
<
div
>
<
Link
href=
"https://hardwaretools.com.vn/"
>
<
div
className=
"aspect-[1.55/1] overflow-hidden xl:h-full xl:aspect-auto"
>
<
ImageNext
src=
"/home/20-2048x1365.webp"
alt=
"
banner
"
alt=
"
Liên kết nhanh
"
width=
{
2048
}
height=
{
1365
}
className=
"h-full w-full object-cover"
/>
</
Link
>
</
div
>
</
Link
>
</
aside
>
);
}
...
...
src/app/(main)/(home)/components/video-and-patners/index.tsx
View file @
c03ea16b
'use client'
;
import
ImageNext
from
"@/components/shared/image-next"
;
import
partnerImages
from
"@/constants/partnerImages"
;
import
{
Chevron
sRight
}
from
"lucide-react"
;
import
{
Chevron
Right
,
Play
}
from
"lucide-react"
;
import
Link
from
"next/link"
;
import
{
Autoplay
,
Grid
}
from
"swiper/modules"
;
import
{
Swiper
,
SwiperSlide
}
from
"swiper/react"
;
import
"swiper/css"
;
import
"swiper/css/grid"
;
const
videos
=
[
{
embedSrc
:
"https://www.youtube.com/embed/J0Iz0iGuAXY"
,
title
:
"VCCI-HCM 2025 IN REVIEW (ENGLISH VERSION)"
,
thumbnail
:
"https://img.youtube.com/vi/J0Iz0iGuAXY/hqdefault.jpg"
,
},
{
embedSrc
:
"https://www.youtube.com/embed/_OnnGWv2ehM"
,
title
:
"Hội nghị Hội viên VCCI - Gala Mừng Xuân 2026"
,
thumbnail
:
"https://img.youtube.com/vi/_OnnGWv2ehM/hqdefault.jpg"
,
},
];
const
VideoAndPartners
=
()
=>
{
function
VideoAndPartners
()
{
return
(
<
section
className=
"flex flex-col lg:flex-row gap-5 pb-10"
>
{
/* LEFT: VIDEO */
}
<
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
>
<
section
className=
"flex flex-col gap-6 pb-10 xl:flex-row xl:items-start"
>
<
div
className=
"flex-1"
>
<
div
className=
"mb-5 flex items-start justify-between gap-3"
>
<
div
>
<
h2
className=
"text-[28px] font-extrabold uppercase tracking-tight text-[#24469c] md:text-[34px]"
>
Video
</
h2
>
<
div
className=
"mt-2.5 h-[4px] w-[40px] rounded-full bg-[#f7b500]"
/>
</
div
>
<
Link
href=
"/video"
className=
"
text-[#063e8e] hover:underline text-sm font-medium
"
className=
"
pt-1 text-[#24469c] transition-colors hover:text-[#1b55a1]
"
>
<
Chevron
sRight
/>
<
Chevron
Right
className=
"h-5 w-5"
/>
</
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
className=
"grid gap-4 md:grid-cols-2"
>
{
videos
.
map
((
video
)
=>
(
<
a
key=
{
video
.
embedSrc
}
href=
{
video
.
embedSrc
.
replace
(
"/embed/"
,
"/watch?v="
)
}
target=
"_blank"
rel=
"noreferrer"
className=
"overflow-hidden rounded-[16px] border border-[#e5ebf4] bg-white shadow-[0_10px_22px_rgba(31,59,124,0.08)] transition-all hover:-translate-y-0.5 hover:shadow-[0_16px_30px_rgba(31,59,124,0.12)]"
>
<
div
className=
"group relative aspect-[1.95/1] overflow-hidden"
>
<
ImageNext
src=
{
video
.
thumbnail
}
alt=
{
video
.
title
}
width=
{
640
}
height=
{
440
}
className=
"h-full w-full object-cover transition-transform duration-500 group-hover:scale-[1.03]"
/>
<
div
className=
"absolute inset-0 bg-black/18"
/>
<
div
className=
"absolute inset-0 flex items-center justify-center"
>
<
span
className=
"flex h-12 w-12 items-center justify-center rounded-full bg-white/92 text-[#2a4ea3] shadow-[0_10px_26px_rgba(0,0,0,0.18)]"
>
<
Play
className=
"ml-1 h-5 w-5 fill-current"
/>
</
span
>
</
div
>
</
div
>
<
p
className=
"mt-2 text-sm text-gray-700 font-medium"
>
<
div
className=
"px-4 py-2.5"
>
<
p
className=
"line-clamp-2 text-[14px] font-semibold leading-[1.32] text-[#264798] md:text-[15px]"
>
{
video
.
title
}
</
p
>
</
div
>
</
a
>
))
}
</
div
>
</
div
>
{
/* RIGHT: ĐỐI TÁC */
}
<
aside
className=
"w-full lg:w-[30%]
"
>
<
div
className=
"flex justify-between items-center mb-3"
>
<
h2
className=
"text-xl font-bold uppercase text-[#063e8e
]"
>
<
aside
className=
"w-full xl:w-[43%]"
>
<
div
className=
"mb-5 flex items-start justify-between gap-3
"
>
<
div
>
<
h2
className=
"text-[28px] font-extrabold uppercase tracking-tight text-[#24469c] md:text-[34px
]"
>
Đối tác
</
h2
>
<
div
className=
"mt-2.5 h-[4px] w-[40px] rounded-full bg-[#f7b500]"
/>
</
div
>
</
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"
}
}
slidesPerView=
{
3
}
spaceBetween=
{
16
}
breakpoints=
{
{
0
:
{
slidesPerView
:
3
,
spaceBetween
:
10
,
grid
:
{
rows
:
2
}
},
640
:
{
slidesPerView
:
3
,
spaceBetween
:
16
,
grid
:
{
rows
:
2
}
},
1024
:
{
slidesPerView
:
3
,
spaceBetween
:
24
,
grid
:
{
rows
:
2
}
},
}
}
className=
"partner-swiper"
<
div
className=
"grid grid-cols-2 gap-4 sm:grid-cols-3"
>
{
partnerImages
.
slice
(
0
,
6
).
map
((
src
,
index
)
=>
(
<
div
key=
{
src
}
className=
"flex h-[96px] items-center justify-center rounded-[14px] border border-[#edf1f7] bg-white px-5 py-4 shadow-[0_8px_20px_rgba(31,59,124,0.05)]"
>
{
partnerImages
.
map
((
src
,
i
)
=>
(
<
SwiperSlide
key=
{
i
}
>
<
div
className=
"flex justify-center items-center bg-white rounded-lg shadow p-3 w-[120px] h-[120px]"
>
<
ImageNext
src=
{
src
}
alt=
{
`partner-${i
}`
}
width=
{
12
0
}
height=
{
120
}
className=
"object-contain w-full h-full
"
alt=
{
`Đối tác ${index + 1
}`
}
width=
{
14
0
}
height=
{
72
}
className=
"max-h-full w-full object-contain
"
/>
</
div
>
</
SwiperSlide
>
))
}
</
Swiper
>
</
div
>
</
aside
>
</
section
>
);
}
;
}
export
default
VideoAndPartners
;
src/app/(main)/(home)/page.tsx
View file @
c03ea16b
'use client'
import
Link
from
'next/link'
import
ImageNext
from
'@/components/shared/image-next'
;
import
FeaturedNews
from
"./components/featured-news"
;
import
QuickLinks
from
"./components/quick-links"
;
import
News
from
"./components/news"
;
...
...
@@ -18,9 +16,9 @@ const Page = () => {
<
div
>
<
Banner
/>
{
/* contents */
}
<
div
className=
"container mx-auto px-3 sm:px-6 lg:px-10 space-y-
12
"
>
<
div
className=
"container mx-auto px-3 sm:px-6 lg:px-10 space-y-
6
"
>
<
FeaturedNews
/>
<
div
>
{
/*
<div>
<Link href="https://hardwaretools.com.vn/">
<ImageNext
src="/home/Standard-Banner-1-2024.png.webp"
...
...
@@ -29,21 +27,21 @@ const Page = () => {
height={720}
/>
</Link>
</
div
>
</div>
*/
}
<
section
className=
"flex flex-col lg:flex-row
gap-5 pb-10
mb-0"
>
<
section
className=
"flex flex-col lg:flex-row
pb-16 gap-5
mb-0"
>
<
News
/>
<
QuickLinks
/>
</
section
>
<
section
className=
"flex flex-col
lg:flex-row gap-5 pb-10 mb-0
"
>
<
section
className=
"flex flex-col
gap-5 xl:flex-row xl:items-stretch
"
>
<
Events
/>
<
EventsCalendar
/>
</
section
>
<
div
className=
"flex flex-col lg:flex-row gap-5 pb-10 mb-0"
>
<
div
className=
"flex flex-col flex-1"
>
<
div
>
{
/*
<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"
...
...
@@ -52,7 +50,7 @@ const Page = () => {
height={720}
/>
</Link>
</
div
>
</div>
*/
}
<
section
className=
"flex flex-col md:flex-row gap-5 pt-8"
>
<
BusinessOpportunities
/>
...
...
@@ -60,7 +58,7 @@ const Page = () => {
</
section
>
</
div
>
<
div
className=
"w-full lg:w-[30%] justify-center items-start flex"
>
{
/*
<div className="w-full lg:w-[30%] justify-center items-start flex">
<Link href="https://smartgara.ecaraid.com/">
<ImageNext
src="/home/eCarAid_web_banner_600x400.webp"
...
...
@@ -69,7 +67,7 @@ const Page = () => {
height={400}
/>
</Link>
</
div
>
</div>
*/
}
</
div
>
<
Members
/>
...
...
src/app/(main)/_lib/layout/footer.tsx
View file @
c03ea16b
This diff is collapsed.
Click to expand it.
src/mockdata/admin-news.ts
View file @
c03ea16b
This diff is collapsed.
Click to expand it.
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