Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
V
vcci-news-frontend
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-frontend
Commits
d67fb27c
Commit
d67fb27c
authored
Oct 27, 2026
by
Phạm Quang Bảo
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
add: partner
parent
6596db21
Changes
4
Show whitespace changes
Inline
Side-by-side
Showing
4 changed files
with
21 additions
and
105 deletions
+21
-105
meu-partner.jpg
public/home/doi-tac/meu-partner.jpg
+0
-0
index.tsx
src/app/(main)/(home)/components/partners/index.tsx
+14
-98
index.tsx
src/app/(main)/(home)/components/videos/index.tsx
+6
-6
page.tsx
src/app/(main)/(home)/page.tsx
+1
-1
No files found.
public/home/doi-tac/meu-partner.jpg
0 → 100644
View file @
d67fb27c
838 KB
src/app/(main)/(home)/components/partners/index.tsx
View file @
d67fb27c
"use client"
;
import
{
getOrganizations
}
from
"@/api/vcci-hcm/endpoints/organizations"
;
import
type
{
Organization
}
from
"@/api/vcci-hcm/models"
;
import
partnerImages
from
"@/constants/partnerImages"
;
import
{
MOCK_PARTNERS_RESPONSE
}
from
"@/mockdata/bff-fallback"
;
import
Image
from
"next/image"
;
import
{
useQuery
}
from
"@tanstack/react-query"
;
import
{
Autoplay
}
from
"swiper/modules"
;
import
{
Swiper
,
SwiperSlide
}
from
"swiper/react"
;
import
"swiper/css"
;
const
VCCI_HCM_SITE_URL
=
"https://vccihcm.vn"
;
const
resolvePartnerImage
=
(
avatar
:
string
|
null
|
undefined
,
index
:
number
)
=>
{
if
(
avatar
?.
startsWith
(
"http://"
)
||
avatar
?.
startsWith
(
"https://"
))
{
return
avatar
;
}
if
(
avatar
?.
startsWith
(
"/"
))
{
return
`
${
VCCI_HCM_SITE_URL
}${
avatar
}
`
;
}
return
partnerImages
[
index
%
partnerImages
.
length
]
??
"/img-error.png"
;
};
const
MOCK_PARTNER_ROWS
=
MOCK_PARTNERS_RESPONSE
.
responseData
.
rows
as
unknown
as
Organization
[];
const
PARTNERS_IMAGE
=
"/home/doi-tac/meu-partner.jpg"
;
export
function
Partners
()
{
const
{
data
:
partnersResponse
}
=
useQuery
<
Organization
[]
|
undefined
>
({
queryKey
:
[
"home-partners"
,
"type-sponsor"
]
as
const
,
queryFn
:
async
()
=>
{
const
response
=
await
getOrganizations
({
filters
:
"type==SPONSOR"
,
pageSize
:
"12"
,
sortField
:
"sort_order"
,
sortOrder
:
"ASC"
,
});
const
rows
=
((
response
as
any
)?.
responseData
?.
rows
??
[])
as
Organization
[];
return
rows
.
length
>
0
?
rows
:
MOCK_PARTNER_ROWS
;
},
staleTime
:
60
*
1000
,
});
const
partners
=
(
partnersResponse
??
MOCK_PARTNER_ROWS
).
slice
(
0
,
12
);
return
(
<
aside
className=
"flex w-full flex-col xl:
w-[43%]
"
>
<
div
className=
"mb-5 flex items-start justify-between gap-3"
>
<
aside
className=
"flex w-full flex-col xl:
contents
"
>
<
div
className=
"mb-5 flex items-start justify-between gap-3
xl:col-start-2 xl:row-start-1 xl:mb-0
"
>
<
div
>
<
h2
className=
"client-section-title uppercase text-[#24469c]"
>
Đối tác
...
...
@@ -56,58 +14,16 @@ export function Partners() {
</
div
>
</
div
>
{
partners
.
length
>
0
?
(
<
Swiper
modules=
{
[
Autoplay
]
}
autoplay=
{
{
delay
:
4200
,
disableOnInteraction
:
false
}
}
observer
observeParents
updateOnWindowResize
slidesPerView=
"auto"
spaceBetween=
{
16
}
className=
"w-full"
>
{
partners
.
map
((
partner
,
index
)
=>
(
<
SwiperSlide
key=
{
partner
.
id
}
className=
"!h-auto !w-full sm:!w-[calc(50%-8px)] xl:!w-[calc(33.333%-10.67px)]"
>
{
partner
.
website
?
(
<
a
href=
{
partner
.
website
}
target=
"_blank"
rel=
"noreferrer"
className=
"block"
>
<
div
className=
"flex h-[96px] items-center justify-center rounded-[16px] border border-[#edf1f7] bg-white px-5 py-4 shadow-[0_8px_20px_rgba(31,59,124,0.05)] transition-all hover:-translate-y-0.5 hover:shadow-[0_14px_24px_rgba(31,59,124,0.1)] xl:h-[151px]"
>
<
div
className=
"relative aspect-[16/9] w-full overflow-hidden rounded-[16px] border border-[#edf1f7] bg-white shadow-[0_8px_20px_rgba(31,59,124,0.05)] xl:col-start-2 xl:row-start-2 xl:h-full xl:w-auto xl:justify-self-start"
>
<
Image
src=
{
resolvePartnerImage
(
partner
.
avatar
,
index
)
}
alt=
{
partner
.
name
??
""
}
width=
{
140
}
height=
{
72
}
className=
"max-h-full w-full object-contain"
src=
{
PARTNERS_IMAGE
}
alt=
"Đối tác"
fill
priority
sizes=
"(min-width: 1280px) 43vw, 100vw"
className=
"object-contain object-left"
/>
</
div
>
</
a
>
)
:
(
<
div
className=
"flex h-[96px] items-center justify-center rounded-[16px] border border-[#edf1f7] bg-white px-5 py-4 shadow-[0_8px_20px_rgba(31,59,124,0.05)] xl:h-[151px]"
>
<
Image
src=
{
resolvePartnerImage
(
partner
.
avatar
,
index
)
}
alt=
{
partner
.
name
??
""
}
width=
{
140
}
height=
{
72
}
className=
"max-h-full w-full object-contain"
/>
</
div
>
)
}
</
SwiperSlide
>
))
}
</
Swiper
>
)
:
(
<
div
className=
"rounded-2xl border border-[#edf1f7] bg-white px-5 py-10 text-center text-sm text-gray-500"
>
Chưa có thông tin.
</
div
>
)
}
</
aside
>
);
}
src/app/(main)/(home)/components/videos/index.tsx
View file @
d67fb27c
...
...
@@ -45,8 +45,8 @@ const fetchVideos = async (): Promise<VideoItem[]> => {
export
function
VideosSkeleton
()
{
return
(
<
div
className=
"flex-1"
>
<
div
className=
"mb-5 flex items-center justify-between gap-3"
>
<
div
className=
"flex-1
xl:contents
"
>
<
div
className=
"mb-5 flex items-center justify-between gap-3
xl:col-start-1 xl:row-start-1 xl:mb-0
"
>
<
div
>
<
h2
className=
"client-section-title uppercase text-[#24469c]"
>
Video
...
...
@@ -62,7 +62,7 @@ export function VideosSkeleton() {
</
Link
>
</
div
>
<
div
className=
"grid gap-4 md:grid-cols-2"
>
<
div
className=
"grid gap-4 md:grid-cols-2
xl:col-start-1 xl:row-start-2
"
>
{
Array
.
from
({
length
:
2
},
(
_
,
index
)
=>
(
<
div
key=
{
`video-skeleton-${index}`
}
...
...
@@ -78,8 +78,8 @@ export async function Videos() {
const
videos
=
await
fetchVideos
();
return
(
<
div
className=
"flex-1"
>
<
div
className=
"mb-5 flex items-center justify-between gap-3"
>
<
div
className=
"flex-1
xl:contents
"
>
<
div
className=
"mb-5 flex items-center justify-between gap-3
xl:col-start-1 xl:row-start-1 xl:mb-0
"
>
<
div
>
<
h2
className=
"client-section-title uppercase text-[#24469c]"
>
Video
...
...
@@ -95,7 +95,7 @@ export async function Videos() {
</
Link
>
</
div
>
<
div
className=
"grid gap-4 md:grid-cols-2"
>
<
div
className=
"grid gap-4 md:grid-cols-2
xl:col-start-1 xl:row-start-2
"
>
{
videos
.
length
?
(
videos
.
map
((
video
)
=>
(
<
a
...
...
src/app/(main)/(home)/page.tsx
View file @
d67fb27c
...
...
@@ -62,7 +62,7 @@ export default function HomePage() {
<
MemberConnection
/>
</
Suspense
>
</
section
>
<
section
className=
"flex flex-col gap-6 pb-10 xl:
flex-row xl:items-stretch
"
>
<
section
className=
"flex flex-col gap-6 pb-10 xl:
grid xl:grid-cols-[minmax(0,1fr)_43%] xl:grid-rows-[auto_1fr] xl:gap-x-6 xl:gap-y-5
"
>
<
Suspense
fallback=
{
<
VideosSkeleton
/>
}
>
<
Videos
/>
</
Suspense
>
...
...
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