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
9625caf9
Commit
9625caf9
authored
Nov 04, 2025
by
Phan Ngọc Quốc Văn
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
feat/publication_page
parent
702e56b2
Changes
11
Hide whitespace changes
Inline
Side-by-side
Showing
11 changed files
with
502 additions
and
108 deletions
+502
-108
A-Guide-2023_Cover-725x1024.webp
public/an-pham/A-Guide-2023_Cover-725x1024.webp
+0
-0
Trang-bia_Chuyen-doi-so_2022-750x1024.webp
public/an-pham/Trang-bia_Chuyen-doi-so_2022-750x1024.webp
+0
-0
Trang-bia_Connections_2022-2023-725x1024.jpg.webp
...an-pham/Trang-bia_Connections_2022-2023-725x1024.jpg.webp
+0
-0
bantintet-1.webp
public/an-pham/bantintet-1.webp
+0
-0
bia-ban-tin-quy-4-1.webp
public/an-pham/bia-ban-tin-quy-4-1.webp
+0
-0
doing-in-business-cover-1-1.webp
public/an-pham/doing-in-business-cover-1-1.webp
+0
-0
page.tsx
src/app/(main)/thong-tin-truyen-thong/an-pham/[id]/page.tsx
+123
-0
index.tsx
...ng-tin-truyen-thong/an-pham/components/calendar/index.tsx
+222
-0
index.tsx
...truyen-thong/an-pham/components/publicationList/index.tsx
+92
-0
page.tsx
src/app/(main)/thong-tin-truyen-thong/an-pham/page.tsx
+20
-54
index.tsx
src/components/base/list-category/index.tsx
+45
-54
No files found.
public/an-pham/A-Guide-2023_Cover-725x1024.webp
0 → 100644
View file @
9625caf9
File added
public/an-pham/Trang-bia_Chuyen-doi-so_2022-750x1024.webp
0 → 100644
View file @
9625caf9
File added
public/an-pham/Trang-bia_Connections_2022-2023-725x1024.jpg.webp
0 → 100644
View file @
9625caf9
File added
public/an-pham/bantintet-1.webp
0 → 100644
View file @
9625caf9
File added
public/an-pham/bia-ban-tin-quy-4-1.webp
0 → 100644
View file @
9625caf9
File added
public/an-pham/doing-in-business-cover-1-1.webp
0 → 100644
View file @
9625caf9
File added
src/app/(main)/thong-tin-truyen-thong/an-pham/[id]/page.tsx
0 → 100644
View file @
9625caf9
"use client"
;
import
ListCategory
from
"@/components/base/list-category"
;
import
{
MEDIA_INFORMATION_CATEGORIES
}
from
"@/constants/categories"
;
import
Image
from
"next/image"
;
import
Link
from
"next/link"
;
import
{
notFound
,
useParams
}
from
"next/navigation"
;
import
Calendar
from
"../components/calendar"
;
const
publications
=
[
{
id
:
"huong-dan-dau-tu-2024"
,
title
:
"Cẩm nang Hướng dẫn đầu tư kinh doanh tại Việt Nam"
,
date
:
"18/10/2023"
,
link
:
"https://vcci-hcm.org.vn/wp-content/uploads/2023/10/Doing-Business-in-Vietnam-2023_Upload.pdf"
,
title_link
:
"“DOING BUSINESS IN VIETNAM 2023”"
,
img
:
"/an-pham/A-Guide-2023_Cover-725x1024.webp"
,
},
{
id
:
"connections-2022-2023"
,
title
:
"Danh bạ Hội viên CONNECTIONS 2022-2023"
,
date
:
"19/01/2023"
,
link
:
"https://vcci-hcm.org.vn/wp-content/uploads/2022/12/Danh-ba-HV-Connections_2022-2023.pdf"
,
title_link
:
"“DANH BẠ HỘI VIÊN CONNECTIONS 2022-2023”"
,
img
:
"/an-pham/Trang-bia_Connections_2022-2023-725x1024.jpg.webp"
,
},
{
id
:
"chuyen-doi-so"
,
title
:
"Chuyển đổi số – Động lực phục hồi và phát triển kinh tế"
,
date
:
"19/01/2023"
,
// ← Sửa: 119 → 19
link
:
"https://vcci-hcm.org.vn/wp-content/uploads/2022/12/CHUYEN-DOI-SO-2022_Final_19.12.2022.pdf"
,
title_link
:
"“CHUYỂN ĐỔI SỐ – ĐỘNG LỰC PHỤC HỒI VÀ PHÁT TRIỂN KINH TẾ”"
,
img
:
"/an-pham/Trang-bia_Chuyen-doi-so_2022-750x1024.webp"
,
},
{
id
:
"huong-dan-dau-tu-2021"
,
title
:
"Cẩm nang Hướng dẫn đầu tư kinh doanh tại Việt Nam 2021"
,
date
:
"14/03/2022"
,
link
:
"https://vcci-hcm.org.vn/wp-content/uploads/2023/10/Doing-Business-in-Vietnam-2023_Upload.pdf"
,
title_link
:
"“DOING BUSINESS IN VIETNAM 2021”"
,
img
:
"/an-pham/doing-in-business-cover-1-1.webp"
,
},
{
id
:
"ban-tin-quy-4-2020"
,
title
:
"Bản tin Quý IV năm 2020"
,
date
:
"04/01/2021"
,
link
:
"https://vcci-hcm.org.vn/wp-content/uploads/2021/01/No3-092020_VCCI-NEWS-FINAL.pdf"
,
title_link
:
"Bản Tin Quý IV năm 2020"
,
img
:
"/an-pham/bia-ban-tin-quy-4-1.webp"
,
},
{
id
:
"ban-tin-quy-1-2020"
,
title
:
"Bản tin Quý I năm 2020"
,
date
:
"16/07/2020"
,
link
:
"https://vcci-hcm.org.vn/wp-content/uploads/2020/07/VCCI-NEWS-012020_XUAN.pdf"
,
title_link
:
"Bản tin Quý I năm 2020"
,
img
:
"/an-pham/bantintet-1.webp"
,
// ← Sửa: iimg → img
},
];
// ĐÚNG: Không async, params là object
export
default
function
PublicationDetail
()
{
const
params
=
useParams
();
// Dùng hook
const
id
=
params
.
id
as
string
;
// Ép kiểu an toàn
const
publication
=
publications
.
find
((
p
)
=>
p
.
id
===
id
);
if
(
!
publication
)
return
notFound
();
return
(
<
div
className=
"bg-[#f6f6f6] min-h-screen"
>
<
div
className=
"max-w-[1200px] mx-auto flex flex-col gap-5 mb-[50px]"
>
<
div
className=
"border-[#e5e7f2] border-[1px]"
>
<
ListCategory
categories=
{
MEDIA_INFORMATION_CATEGORIES
}
/>
</
div
>
<
div
className=
"w-full flex gap-5 flex-wrap"
>
<
div
className=
"lg:w-[calc(65%-10px)] w-full border-[#e5e7f2] border-[1px] bg-white p-[30px] flex flex-col gap-[15px]"
>
<
h1
className=
"text-[22px] font-semibold text-[#003366]"
>
{
publication
.
title
}
</
h1
>
<
p
className=
"text-[#00AED5] text-sm"
>
{
publication
.
date
}
</
p
>
<
hr
/>
<
p
className=
"text-[16px] text-[#363636]"
>
Tải về ấn phẩm:
{
" "
}
<
Link
href=
{
publication
.
link
}
target=
"_blank"
className=
"text-[#0073e6] hover:text-[#e8c518]"
>
{
publication
.
title_link
}
</
Link
>
</
p
>
<
div
className=
"flex justify-center"
>
<
Link
href=
{
publication
.
link
}
target=
"_blank"
>
<
Image
src=
{
publication
.
img
}
alt=
{
publication
.
title
}
width=
{
416
}
height=
{
566
}
className=
"rounded-lg transition-all duration-300"
/>
</
Link
>
</
div
>
</
div
>
<
div
className=
"lg:w-[calc(35%-10px)] w-full"
>
<
Calendar
/>
<
div
className=
"relative w-full mt-4 h-[300px] aspect-video rounded-lg overflow-hidden"
>
<
Image
src=
"/banner.webp"
alt=
"Quảng cáo"
fill
className=
"object-contain"
/>
</
div
>
</
div
>
</
div
>
</
div
>
</
div
>
);
}
src/app/(main)/thong-tin-truyen-thong/an-pham/components/calendar/index.tsx
0 → 100644
View file @
9625caf9
"use client"
;
import
React
,
{
useState
,
useMemo
}
from
"react"
;
import
{
format
,
startOfMonth
,
endOfMonth
,
eachDayOfInterval
,
isSameMonth
,
isSameDay
,
addMonths
,
subMonths
,
}
from
"date-fns"
;
import
{
ArrowLeft
,
ArrowRight
,
ChevronLeft
,
ChevronRight
}
from
"lucide-react"
;
import
{
vi
}
from
"date-fns/locale"
;
interface
Event
{
date
:
Date
;
title
:
string
;
type
:
"event"
|
"training"
;
description
?:
string
;
}
export
default
function
Calendar
()
{
const
[
currentMonth
,
setCurrentMonth
]
=
useState
(
new
Date
());
const
today
=
new
Date
();
// Dữ liệu mẫu
const
events
:
Event
[]
=
[
{
date
:
new
Date
(
2025
,
10
,
1
),
title
:
"Đào tạo nội bộ"
,
type
:
"training"
,
description
:
"Khóa học kỹ năng mềm"
,
},
{
date
:
new
Date
(
2025
,
10
,
3
),
title
:
"Họp cổ đông"
,
type
:
"event"
,
description
:
"Báo cáo Q3"
,
},
{
date
:
new
Date
(
2025
,
10
,
13
),
title
:
"Đào tạo kỹ thuật"
,
type
:
"training"
,
description
:
"React Advanced"
,
},
{
date
:
new
Date
(
2025
,
10
,
14
),
title
:
"Đào tạo an toàn"
,
type
:
"training"
,
description
:
"An toàn lao động"
,
},
{
date
:
new
Date
(
2025
,
10
,
20
),
title
:
"Hội thảo thuế"
,
type
:
"event"
,
description
:
"Cập nhật luật thuế thu nhập doanh nghiệp số 67/2025/QH15..."
,
},
{
date
:
new
Date
(
2025
,
10
,
28
),
title
:
"Sự kiện nội bộ"
,
type
:
"event"
,
description
:
"Team building"
,
},
];
const
monthStart
=
startOfMonth
(
currentMonth
);
const
monthEnd
=
endOfMonth
(
currentMonth
);
const
monthDays
=
eachDayOfInterval
({
start
:
monthStart
,
end
:
monthEnd
});
const
firstDayOfWeek
=
monthStart
.
getDay
();
// 0 = CN, 1 = T2...
const
startDate
=
new
Date
(
monthStart
);
startDate
.
setDate
(
startDate
.
getDate
()
-
firstDayOfWeek
);
const
days
=
[];
for
(
let
i
=
0
;
i
<
42
;
i
++
)
{
const
day
=
new
Date
(
startDate
);
day
.
setDate
(
startDate
.
getDate
()
+
i
);
days
.
push
(
day
);
}
const
getEventForDay
=
(
date
:
Date
)
=>
events
.
filter
((
e
)
=>
isSameDay
(
e
.
date
,
date
));
const
formatMonthTitle
=
()
=>
{
return
`THÁNG
${
format
(
currentMonth
,
"M/yyyy"
)}
`
.
toUpperCase
();
};
return
(
<>
<
div
className=
"w-full mx-auto bg-white rounded-lg p-4 "
>
{
/* Header */
}
<
div
className=
"flex items-center justify-between mb-4 px-3"
>
<
h2
className=
"text-[15px] font-bold text-[#063E8E]"
>
{
formatMonthTitle
()
}
</
h2
>
<
div
className=
"flex gap-3"
>
<
button
onClick=
{
()
=>
setCurrentMonth
(
subMonths
(
currentMonth
,
1
))
}
className=
"p-2 cursor-pointer rounded-full group border-3 border-[#363636] hover:border-[#063e8e] transition"
>
<
ArrowLeft
className=
"group-hover:text-[#e8c518] text-[#363636] w-5 h-5"
/>
</
button
>
<
button
onClick=
{
()
=>
setCurrentMonth
(
addMonths
(
currentMonth
,
1
))
}
className=
"p-2 cursor-pointer rounded-full group border-3 border-[#363636] hover:border-[#063e8e] transition"
>
<
ArrowRight
className=
"group-hover:text-[#e8c518] text-[#363636] w-5 h-5"
/>
</
button
>
</
div
>
</
div
>
{
/* Days of week */
}
<
div
className=
"grid grid-cols-7 text-center text-sm font-medium text-gray-600 mb-1"
>
{
[
"CN"
,
"T2"
,
"T3"
,
"T4"
,
"T5"
,
"T6"
,
"T7"
].
map
((
day
)
=>
(
<
div
key=
{
day
}
className=
"py-2 text-[15px] text-[#063E8E]"
>
{
day
}
</
div
>
))
}
</
div
>
{
/* Calendar grid */
}
<
div
className=
"grid grid-cols-7 gap-1 text-sm"
>
{
days
.
map
((
day
,
idx
)
=>
{
const
dayEvents
=
getEventForDay
(
day
);
const
isCurrentMonth
=
isSameMonth
(
day
,
currentMonth
);
const
isToday
=
isSameDay
(
day
,
today
);
const
hasEvent
=
dayEvents
.
some
((
e
)
=>
e
.
type
===
"event"
);
const
hasTraining
=
dayEvents
.
some
((
e
)
=>
e
.
type
===
"training"
);
return
(
<
div
key=
{
idx
}
className=
{
`
relative group aspect-square flex items-center justify-center rounded-full
${!isCurrentMonth ? "text-[#A4A4A4]" : "text-[#333333]"}
${hasEvent || hasTraining ? "text-white" : "text-[#333333]"}
${isToday ? "text-red-600 font-bold" : ""}
hover:bg-gray-50 transition
`
}
>
<
span
className=
"relative z-10"
>
{
format
(
day
,
"d"
)
}
</
span
>
{
/* Event/Training dots */
}
{
(
hasEvent
||
hasTraining
)
&&
(
<
div
className=
"absolute inset-0 flex items-center justify-center pointer-events-none"
>
<
div
className=
"flex"
>
{
hasEvent
&&
(
<
div
className=
"w-10 h-10 bg-blue-600 rounded-full"
></
div
>
)
}
{
hasTraining
&&
(
<
div
className=
"w-10 h-10 bg-yellow-500 rounded-full"
></
div
>
)
}
</
div
>
</
div
>
)
}
{
/* Tooltip on hover */
}
{
dayEvents
.
length
>
0
&&
(
<
div
className=
"absolute top-full left-1/2 -translate-x-1/2 mt-2
w-64 p-3 bg-gray-900 text-white text-xs rounded-lg
shadow-xl opacity-0 pointer-events-none
group-hover:opacity-90 transition-opacity z-50"
>
<
div
className=
"space-y-2"
>
{
dayEvents
.
map
((
event
,
i
)
=>
(
<
div
key=
{
i
}
className=
"border-b border-gray-700 border-opacity-20 last:border-0 pb-2 last:pb-0"
>
<
div
className=
"flex items-center gap-2"
>
<
div
className=
{
`w-3 h-3 rounded-full ${
event.type === "event"
? "bg-blue-400"
: "bg-yellow-400"
}`
}
></
div
>
<
span
className=
"font-medium"
>
{
event
.
title
}
</
span
>
</
div
>
{
event
.
description
&&
(
<
p
className=
"text-gray-300 mt-1 text-xs"
>
{
event
.
description
}
</
p
>
)
}
</
div
>
))
}
</
div
>
{
/* Mũi tên nhọn HƯỚNG LÊN (chỉ vào ngày) */
}
<
div
className=
"absolute bottom-full left-1/2 -translate-x-1/2 -mb-1
w-0 h-0
border-l-8 border-l-transparent
border-r-8 border-r-transparent
border-b-8 border-b-gray-900"
></
div
>
</
div
>
)
}
</
div
>
);
})
}
</
div
>
{
/* Legend */
}
<
div
className=
"flex justify-center gap-6 mt-4 text-xs"
>
<
div
className=
"flex items-center gap-2"
>
<
div
className=
"w-3 h-3 bg-blue-600 rounded-full"
></
div
>
<
span
>
Sự kiện
</
span
>
</
div
>
<
div
className=
"flex items-center gap-2"
>
<
div
className=
"w-3 h-3 bg-yellow-500 rounded-full"
></
div
>
<
span
>
Đào tạo
</
span
>
</
div
>
</
div
>
</
div
>
</>
);
}
src/app/(main)/thong-tin-truyen-thong/an-pham/components/publicationList/index.tsx
0 → 100644
View file @
9625caf9
"use client"
;
import
React
,
{
useState
}
from
"react"
;
import
Image
from
"next/image"
;
import
Link
from
"next/link"
;
import
{
Pagination
}
from
"@components/base/pagination"
;
import
{
useGetNews
}
from
"@api/endpoints/news"
;
import
{
GetNewsResponseType
}
from
"@api/types/NewsPage.type"
;
const
publications
=
[
{
id
:
"huong-dan-dau-tu-2024"
,
title
:
"Cẩm nang Hướng dẫn đầu tư kinh doanh tại Việt Nam"
,
img
:
"/an-pham/A-Guide-2023_Cover-725x1024.webp"
,
},
{
id
:
"connections-2022-2023"
,
title
:
"Danh bạ Hội viên CONNECTIONS 2022-2023"
,
img
:
"/an-pham/Trang-bia_Connections_2022-2023-725x1024.jpg.webp"
,
},
{
id
:
"chuyen-doi-so"
,
title
:
"Chuyển đổi số – Động lực phục hồi và phát triển kinh tế"
,
img
:
"/an-pham/Trang-bia_Chuyen-doi-so_2022-750x1024.webp"
,
},
{
id
:
"huong-dan-dau-tu-2021"
,
title
:
"Cẩm nang Hướng dẫn đầu tư kinh doanh tại Việt Nam 2021"
,
img
:
"/an-pham/doing-in-business-cover-1-1.webp"
,
},
{
id
:
"ban-tin-quy-4-2020"
,
title
:
"Bản tin Quý IV năm 2020"
,
img
:
"/an-pham/bia-ban-tin-quy-4-1.webp"
,
},
{
id
:
"ban-tin-quy-1-2020"
,
title
:
"Bản tin Quý I năm 2020"
,
img
:
"/an-pham/bantintet-1.webp"
,
},
];
export
default
function
PublicationList
()
{
const
[
page
,
setPage
]
=
useState
(
1
);
const
pageSize
=
5
;
const
{
data
:
allData
}
=
useGetNews
<
GetNewsResponseType
>
({
pageSize
:
String
(
pageSize
),
currentPage
:
String
(
page
),
});
return
(
<
div
className=
"lg:w-[calc(65%-10px)] w-full flex flex-col gap-[15px]"
>
<
div
className=
"grid grid-cols-1 sm:grid-cols-2 md:grid-cols-3 gap-8"
>
{
publications
.
map
((
pub
)
=>
(
<
Link
href=
{
`/thong-tin-truyen-thong/an-pham/${pub.id}`
}
key=
{
pub
.
id
}
className=
"flex flex-col items-center text-center h-full max-h-[342px] bg-white group"
>
<
div
className=
"w-full max-w-[260px] aspect-[3/4] overflow-hidden rounded-lg"
>
<
Image
src=
{
pub
.
img
}
alt=
{
pub
.
title
}
width=
{
300
}
height=
{
400
}
className=
"object-contain w-full h-full transition-transform duration-300 group-hover:scale-105"
/>
</
div
>
<
h3
className=
"mt-3 text-[15px] font-semibold text-[#124588] group-hover:text-[#E8C518] leading-snug"
>
{
pub
.
title
}
</
h3
>
</
Link
>
))
}
</
div
>
<
div
className=
"w-full flex justify-center mt-4"
>
<
Pagination
pageCount=
{
Number
(
allData
?.
responseData
.
totalPages
??
1
)
}
page=
{
Number
(
allData
?.
responseData
.
currentPage
??
page
)
}
onChangePage=
{
(
p
)
=>
setPage
(
p
)
}
onGoToPreviousPage=
{
()
=>
setPage
(
Math
.
max
(
1
,
page
-
1
))
}
onGoToNextPage=
{
()
=>
setPage
(
Math
.
min
(
Number
(
allData
?.
responseData
.
totalPages
??
1
),
page
+
1
)
)
}
/>
</
div
>
</
div
>
);
}
src/app/(main)/thong-tin-truyen-thong/an-pham/page.tsx
View file @
9625caf9
"use client"
;
import
ListCategory
from
"@/components/base/list-category"
;
import
React
,
{
useState
}
from
"react"
;
import
{
MEDIA_INFORMATION_CATEGORIES
}
from
"@/constants/categories"
;
import
ListCategory
from
"@app/dai-dien-gioi-chu/components/list-category"
;
import
{
MEDIA_INFORMATION_CATEGORIES
}
from
"@constants/categories"
;
import
EventFilter
from
"@app/dai-dien-gioi-chu/components/event-filter"
;
import
EventFilter
from
"@app/dai-dien-gioi-chu/components/event-filter"
;
import
NewsContent
from
"@app/dai-dien-gioi-chu/components/card-news"
;
import
{
Pagination
}
from
"@components/base/pagination"
;
import
Image
from
"next/image"
;
import
Image
from
"next/image"
;
import
{
useGetNews
}
from
"@api/endpoints/news"
;
import
PublicationList
from
"./components/publicationList"
;
import
{
GetNewsResponseType
}
from
"@api/types/NewsPage.type"
;
export
default
function
Page
()
{
const
[
submitSearch
]
=
useState
(
""
);
const
[
page
,
setPage
]
=
useState
(
1
);
const
pageSize
=
5
;
export
default
function
Page
()
{
const
{
data
:
allData
}
=
useGetNews
<
GetNewsResponseType
>
({
pageSize
:
String
(
pageSize
),
currentPage
:
String
(
page
),
filters
:
submitSearch
?
`title @=
${
submitSearch
}
`
:
undefined
,
});
return
(
return
(
<
div
className=
"min-h-screen container mx-auto p-4"
>
<
div
className=
"bg-[#f6f6f6]"
>
<
div
className=
"w-full flex flex-col gap-5"
>
<
div
className=
"max-w-[1200px] m-auto flex flex-col gap-5 mb-[50px]"
>
<
ListCategory
categories=
{
MEDIA_INFORMATION_CATEGORIES
}
/>
<
div
className=
"border-[#e5e7f2] border-[1px]"
>
<
ListCategory
categories=
{
MEDIA_INFORMATION_CATEGORIES
}
/>
<
div
className=
"grid grid-cols-1 lg:grid-cols-3 gap-6"
>
</
div
>
{
/* Main content */
}
<
div
className=
"w-full flex gap-5 flex-wrap"
>
<
main
className=
"lg:col-span-2 bg-background "
>
<
PublicationList
/>
<
div
className=
"pb-5 overflow-hidden"
>
<
div
className=
"lg:w-[calc(35%-10px)] w-full"
>
{
allData
?.
responseData
.
rows
.
map
((
news
)
=>
(
<
NewsContent
key=
{
news
.
id
}
news=
{
news
}
/>
))
}
<
div
className=
"w-full flex justify-center mt-4"
>
<
Pagination
pageCount=
{
Number
(
allData
?.
responseData
.
totalPages
??
1
)
}
page=
{
Number
(
allData
?.
responseData
.
currentPage
??
page
)
}
onChangePage=
{
(
p
)
=>
setPage
(
p
)
}
onGoToPreviousPage=
{
()
=>
setPage
(
Math
.
max
(
1
,
page
-
1
))
}
onGoToNextPage=
{
()
=>
setPage
(
Math
.
min
(
Number
(
allData
?.
responseData
.
totalPages
??
1
),
page
+
1
))
}
/>
</
div
>
</
div
>
</
main
>
{
/* Sidebar */
}
<
aside
className=
"space-y-6"
>
<
EventFilter
/>
<
EventFilter
/>
<
div
className=
"relative w-full mt-4 h-[300px] aspect-video rounded-lg overflow-hidden"
>
<
div
className=
"bg-white border rounded-md overflow-hidden"
>
<
Image
<
div
className=
"w-full h-56 relative bg-gray-100"
>
src=
"/banner.webp"
<
Image
alt=
"Quảng cáo"
src=
"/banner.webp"
fill
alt=
"Quảng cáo"
className=
"object-contain"
fill
/>
className=
"object-cover"
/>
</
div
>
</
div
>
</
div
>
</
aside
>
</
div
>
</
div
>
</
div
>
</
div
>
</
div
>
</
div
>
</
div
>
...
...
src/components/base/list-category/index.tsx
View file @
9625caf9
"use client"
"use client"
;
import
{
usePathname
}
from
"next/navigation"
import
React
from
"react"
import
{
MenuItem
}
from
'../menu-category'
import
{
usePathname
}
from
"next/navigation"
;
import
React
from
"react"
;
import
{
MenuItem
}
from
"../menu-category"
;
// Local Menu shape compatible with MenuItem
// Local Menu shape compatible with MenuItem
type
Menu
=
{
type
Menu
=
{
id
:
string
|
number
id
:
string
|
number
;
name
:
string
name
:
string
;
link
?:
string
link
?:
string
;
}
}
;
type
Category
=
{
type
Category
=
{
title
:
string
title
:
string
;
href
:
string
href
:
string
;
}
};
const
CATEGORIES
:
Category
[]
=
[
// Default categories removed — component now accepts `categories` via props.
{
title
:
"Về VCCI-HCM"
,
href
:
"/dai-dien-gioi-chu"
},
{
title
:
"Chức năng và Nhiệm vụ"
,
href
:
"/gioi-thieu/chuc-nang"
},
const
ListCategory
:
React
.
FC
<
{
categories
?:
Category
[]
}
>
=
({
{
title
:
"Sơ đồ Tổ chức"
,
href
:
"/gioi-thieu/so-do"
},
categories
=
[],
{
title
:
"Dịch vụ cung cấp"
,
href
:
"/gioi-thieu/dich-vu"
},
})
=>
{
{
title
:
"Dịch vụ cung cấp"
,
href
:
"/gioi-thieu/dich-vu"
},
const
pathname
=
usePathname
()
||
""
;
{
title
:
"Dịch vụ cung cấp"
,
href
:
"/gioi-thieu/dich-vu"
},
{
title
:
"Dịch vụ cung cấp"
,
href
:
"/gioi-thieu/dich-vu"
},
const
isActive
=
(
href
:
string
)
=>
{
{
title
:
"Dịch vụ cung cấp"
,
href
:
"/gioi-thieu/dich-vu"
},
// treat the base path as active for nested routes as well
// if (href === "/gioi-thieu") return pathname === href || pathname.startsWith(href + "/")
]
return
pathname
===
href
;
};
const
ListCategory
:
React
.
FC
=
()
=>
{
const
pathname
=
usePathname
()
||
""
return
(
<
div
className=
"border-t border-gray-200 bg-white p-2.5"
>
const
isActive
=
(
href
:
string
)
=>
{
<
div
className=
"w-full px-4 sm:px-6 lg:px-8"
>
// treat the base path as active for nested routes as well
<
div
className=
"py-3"
>
if
(
href
===
"/gioi-thieu"
)
return
pathname
===
href
||
pathname
.
startsWith
(
href
+
"/"
)
<
div
className=
"flex flex-wrap items-center max-w-full overflow-x-auto"
>
return
pathname
===
href
{
categories
.
map
((
c
)
=>
{
}
const
menu
:
Menu
=
{
id
:
c
.
href
,
name
:
c
.
title
,
link
:
c
.
href
};
const
active
=
isActive
(
c
.
href
);
return
(
return
(
<
div
className=
"border-t border-gray-200 bg-white p-2.5"
>
<
div
key=
{
c
.
href
}
className=
"shrink-0"
>
<
div
className=
"w-full px-4 sm:px-6 lg:px-8"
>
<
MenuItem
menu=
{
menu
}
active=
{
active
}
/>
<
nav
aria
-
label=
"Danh mục"
className=
"py-3"
>
</
div
>
<
ul
className=
"flex flex-wrap gap-4 sm:gap-8 items-center max-w-full overflow-x-auto"
>
);
{
CATEGORIES
.
map
((
c
)
=>
{
})
}
const
menu
:
Menu
=
{
id
:
c
.
href
,
name
:
c
.
title
,
link
:
c
.
href
}
</
div
>
const
active
=
isActive
(
c
.
href
)
return
(
<
li
key=
{
c
.
href
}
className=
"shrink-0"
>
<
MenuItem
menu=
{
menu
}
active=
{
active
}
/>
</
li
>
)
})
}
</
ul
>
</
nav
>
</
div
>
</
div
>
</
div
>
)
</
div
>
}
</
div
>
);
};
export
default
ListCategory
export
default
ListCategory
;
\ 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