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
e7182338
Commit
e7182338
authored
Nov 18, 2025
by
Văn Hoàng
Browse files
Options
Browse Files
Download
Plain Diff
[tag]0.1-vcci
parents
fe00b726
2642b7b8
Pipeline
#44188
passed with stages
in 8 minutes and 50 seconds
Changes
15
Pipelines
1
Show whitespace changes
Inline
Side-by-side
Showing
15 changed files
with
97 additions
and
99 deletions
+97
-99
img-error.png
public/img-error.png
+0
-0
index.tsx
src/app/(main)/(home)/components/card-event/index.tsx
+1
-1
index.tsx
src/app/(main)/(home)/components/card-news/index.tsx
+1
-1
page.tsx
src/app/(main)/(home)/page.tsx
+8
-23
page.tsx
src/app/(main)/[...slug]/page.tsx
+1
-1
EventDetailPage.tsx
src/app/(main)/[...slug]/templates/EventDetailPage.tsx
+12
-12
EventPage.tsx
src/app/(main)/[...slug]/templates/EventPage.tsx
+4
-4
InformationPage.tsx
src/app/(main)/[...slug]/templates/InformationPage.tsx
+2
-2
header.tsx
src/app/(main)/_lib/layout/header.tsx
+28
-9
page.tsx
src/app/(main)/danh-ba-hoi-vien/page.tsx
+7
-0
page.tsx
src/app/(main)/search/page.tsx
+3
-2
page.tsx
src/app/(main)/video/page.tsx
+7
-0
CardNews.type.ts
src/components/base/card-news/CardNews.type.ts
+0
-31
index.tsx
src/components/base/card-news/index.tsx
+2
-2
index.tsx
src/components/base/menu-item/index.tsx
+21
-11
No files found.
public/
VCCI-Chung-300x200-1
.png
→
public/
img-error
.png
View file @
e7182338
File moved
src/app/(main)/(home)/components/card-event/index.tsx
View file @
e7182338
...
...
@@ -15,7 +15,7 @@ function CardEvent({ event }: { event: EventItem }) {
className=
'w-[100px] md:w-[130px] aspect-3/2 object-cover'
onError=
{
(
e
)
=>
{
e
.
currentTarget
.
onerror
=
null
e
.
currentTarget
.
src
=
"/
VCCI-Chung-300x200-1
.png"
e
.
currentTarget
.
src
=
"/
img-error
.png"
}
}
/>
<
div
className=
'flex-1'
>
...
...
src/app/(main)/(home)/components/card-news/index.tsx
View file @
e7182338
...
...
@@ -15,7 +15,7 @@ function CardNews({ news }: { news: NewsItem }) {
className=
"w-[100px] md:w-[130px] aspect-3/2 object-cover"
onError=
{
(
e
)
=>
{
e
.
currentTarget
.
onerror
=
null
e
.
currentTarget
.
src
=
"/
VCCI-Chung-300x200-1
.png"
e
.
currentTarget
.
src
=
"/
img-error
.png"
}
}
/>
<
div
className=
"flex-1"
>
...
...
src/app/(main)/(home)/page.tsx
View file @
e7182338
...
...
@@ -186,7 +186,7 @@ const Page = () => {
className=
"w-full aspect-3/2 sm:h-56 md:h-64 object-cover"
onError=
{
(
e
)
=>
{
e
.
currentTarget
.
onerror
=
null
e
.
currentTarget
.
src
=
"/
VCCI-Chung-300x200-1
.png"
e
.
currentTarget
.
src
=
"/
img-error
.png"
}
}
/>
<
div
className=
"absolute bottom-0 left-0 right-0 h-20 md:h-24 bg-linear-to-t from-black/80 to-transparent flex items-center justify-center p-3"
>
...
...
@@ -242,7 +242,7 @@ const Page = () => {
className=
"w-full h-full object-cover"
onError=
{
(
e
)
=>
{
e
.
currentTarget
.
onerror
=
null
e
.
currentTarget
.
src
=
"/
VCCI-Chung-300x200-1
.png"
e
.
currentTarget
.
src
=
"/
img-error
.png"
}
}
/>
</
div
>
...
...
@@ -312,9 +312,6 @@ const Page = () => {
<
h2
className=
"text-[18px] sm:text-[20px] font-semibold uppercase text-[#063e8e]"
>
Liên kết nhanh
</
h2
>
<
a
href=
"#"
className=
"text-[#063e8e] text-sm sm:text-base"
>
<
ChevronsRight
/>
</
a
>
</
div
>
<
hr
className=
"border-[#063e8e] mb-4"
/>
<
div
className=
"space-y-2 text-[#063e8e] text-sm md:text-base pb-10"
>
...
...
@@ -380,7 +377,7 @@ const Page = () => {
className=
"w-full h-full object-cover"
onError=
{
(
e
)
=>
{
(
e
.
target
as
HTMLImageElement
).
src
=
"/
VCCI-Chung-300x200-1
.png"
;
"/
img-error
.png"
;
}
}
/>
</
div
>
...
...
@@ -459,7 +456,7 @@ const Page = () => {
className=
"w-full h-full object-cover"
onError=
{
(
e
)
=>
{
e
.
currentTarget
.
onerror
=
null
e
.
currentTarget
.
src
=
"/
VCCI-Chung-300x200-1
.png"
e
.
currentTarget
.
src
=
"/
img-error
.png"
}
}
/>
<
div
className=
"absolute bg-white opacity-80 bottom-5 left-5 right-5 p-5"
>
...
...
@@ -504,7 +501,7 @@ const Page = () => {
className=
"w-full h-full object-cover"
onError=
{
(
e
)
=>
{
e
.
currentTarget
.
onerror
=
null
e
.
currentTarget
.
src
=
"/
VCCI-Chung-300x200-1
.png"
e
.
currentTarget
.
src
=
"/
img-error
.png"
}
}
/>
<
div
className=
"absolute bg-white opacity-80 bottom-5 left-5 right-5 p-5"
>
...
...
@@ -539,7 +536,7 @@ const Page = () => {
Hội viên tiêu biểu
</
h2
>
<
a
href=
"
#
"
href=
"
/danh-ba-hoi-vien
"
className=
"text-[#063e8e] hover:underline text-sm font-medium"
>
<
ChevronsRight
/>
...
...
@@ -576,17 +573,11 @@ const Page = () => {
</
aside
>
{
/* right */
}
<
aside
className=
"w-full lg:w-[30%] py-5"
>
<
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
>
<
a
href=
"#"
className=
"text-[#063e8e] hover:underline text-sm font-medium"
>
<
ChevronsRight
/>
</
a
>
</
div
>
<
hr
className=
"border-[#063e8e] mb-5"
/>
<
div
className=
"pb-10"
>
...
...
@@ -628,7 +619,7 @@ const Page = () => {
Video
</
h2
>
<
a
href=
"
#
"
href=
"
/video
"
className=
"text-[#063e8e] hover:underline text-sm font-medium"
>
<
ChevronsRight
/>
...
...
@@ -672,12 +663,6 @@ const Page = () => {
<
h2
className=
"text-xl font-bold uppercase text-[#063e8e]"
>
Đối tác
</
h2
>
<
a
href=
"#"
className=
"text-[#063e8e] hover:underline text-sm font-medium"
>
<
ChevronsRight
/>
</
a
>
</
div
>
<
hr
className=
"border-[#063e8e] mb-5"
/>
<
div
className=
"pb-10"
>
...
...
src/app/(main)/[...slug]/page.tsx
View file @
e7182338
...
...
@@ -46,7 +46,7 @@ export default function DynamicPage() {
}
if
(
slug
.
length
===
2
)
{
return
category
?.
responseData
.
is_article
?
<
ArticlePage
/>
:
<
InformationPage
/>;
return
category
?.
responseData
?
.
is_article
?
<
ArticlePage
/>
:
<
InformationPage
/>;
}
if
(
slug
.
length
===
3
)
{
...
...
src/app/(main)/[...slug]/templates/EventDetailPage.tsx
View file @
e7182338
...
...
@@ -45,7 +45,7 @@ export default function EventDetailPage() {
<
div
className=
"grid grid-cols-1 lg:grid-cols-3 gap-5"
>
<
main
className=
"lg:col-span-2 bg-white border rounded-md p-8"
>
<
div
className=
'pb-5 text-primary text-2xl leading-normal font-medium'
>
{
eventsDetail
?.
responseData
?.
rows
[
0
].
name
}
{
eventsDetail
?.
responseData
?.
rows
[
0
]
?
.
name
}
</
div
>
<
hr
className=
"py-2"
/>
...
...
@@ -55,8 +55,8 @@ export default function EventDetailPage() {
{
eventsDetail
?.
responseData
?.
rows
[
0
].
image
?
(
<
div
className=
"w-full h-52 relative "
>
<
EventImage
src=
{
`${BASE_URL.imageEndpoint}${eventsDetail
.responseData
.rows[0].image}`
}
alt=
{
eventsDetail
.
responseData
.
rows
[
0
]
.
name
||
"image"
}
src=
{
`${BASE_URL.imageEndpoint}${eventsDetail
?.responseData?
.rows[0].image}`
}
alt=
{
eventsDetail
?.
responseData
?.
rows
[
0
]?
.
name
||
"image"
}
/>
</
div
>
)
:
(
...
...
@@ -89,8 +89,8 @@ export default function EventDetailPage() {
<
div
className=
"text-sm text-gray-500 flex items-center gap-2"
>
<
MapPin
className=
"h-5 w-5 text-blue-600"
/>
<
div
className=
"text-sm font-medium text-gray-800"
>
Địa điểm:
{
eventsDetail
?.
responseData
?.
rows
[
0
].
location
??
eventsDetail
?.
responseData
?.
rows
[
0
].
province
??
Địa điểm:
{
eventsDetail
?.
responseData
?.
rows
[
0
]
?
.
location
??
eventsDetail
?.
responseData
?.
rows
[
0
]
?
.
province
??
"-"
}
</
div
>
</
div
>
...
...
@@ -98,9 +98,9 @@ export default function EventDetailPage() {
<
div
className=
"text-sm text-gray-500 flex items-center gap-2"
>
<
CreditCard
className=
"h-5 w-5 text-yellow-400"
/>
<
div
className=
"text-sm font-medium text-gray-800"
>
Phí tham dự:
{
eventsDetail
?.
responseData
?.
rows
[
0
].
table_cost
?
`${eventsDetail
.responseData.rows[0]
.table_count
} Bàn : ${eventsDetail
.responseData.rows[0]
.table_cost.toLocaleString()} đ`
Phí tham dự:
{
eventsDetail
?.
responseData
?.
rows
[
0
]
?
.
table_cost
?
`${eventsDetail
?.responseData?.rows[0]?
.table_count
} Bàn : ${eventsDetail
?.responseData?.rows[0]?
.table_cost.toLocaleString()} đ`
:
"Vui lòng xem chi tiết trong bài"
}
</
div
>
</
div
>
...
...
@@ -110,7 +110,7 @@ export default function EventDetailPage() {
{
/* Full description */
}
<
div
className=
"p-7.5 prose tiptap overflow-hidden"
>
{
parse
(
eventsDetail
?.
responseData
?.
rows
[
0
].
description
??
""
)
}
{
parse
(
eventsDetail
?.
responseData
?.
rows
[
0
]
?
.
description
??
""
)
}
</
div
>
</
main
>
...
...
@@ -118,12 +118,12 @@ export default function EventDetailPage() {
<
aside
className=
"space-y-6"
>
<
EventCalendar
/>
<
div
className=
"bg-white border rounded-md overflow-hidden"
>
<
div
className=
"w-full h-
56
relative bg-gray-100"
>
<
div
className=
"w-full h-
75
relative bg-gray-100"
>
<
Image
src=
"/banner.webp"
alt=
"Quảng cáo"
fill
className=
"object-co
ver
"
className=
"object-co
ntain
"
/>
</
div
>
</
div
>
...
...
@@ -152,7 +152,7 @@ function EventImage({ src, alt }: EventImageProps) {
className=
"object-cover"
onError=
{
()
=>
{
// swap to local fallback file when Next/Image fails to load the provided URL
if
(
imgSrc
!==
"/
VCCI-Chung-300x200-1.png"
)
setImgSrc
(
"/VCCI-Chung-300x200-1
.png"
);
if
(
imgSrc
!==
"/
img-error.png"
)
setImgSrc
(
"/img-error
.png"
);
}
}
/>
);
...
...
src/app/(main)/[...slug]/templates/EventPage.tsx
View file @
e7182338
...
...
@@ -50,7 +50,7 @@ export default function EventPage() {
<
div
className=
"grid grid-cols-1 lg:grid-cols-3 gap-6"
>
<
main
className=
"lg:col-span-2 bg-background"
>
<
div
className=
"pb-5 overflow-hidden"
>
{
events
?.
responseData
.
rows
.
map
((
item
)
=>
(
{
events
?.
responseData
?.
rows
?
.
map
((
item
)
=>
(
<
CardEvents
key=
{
item
.
id
}
event=
{
item
}
...
...
@@ -59,12 +59,12 @@ export default function EventPage() {
))
}
<
div
className=
"w-full flex justify-center mt-4"
>
<
Pagination
pageCount=
{
Number
(
events
?.
responseData
.
totalPages
??
1
)
}
page=
{
Number
(
events
?.
responseData
.
currentPage
??
page
)
}
pageCount=
{
Number
(
events
?.
responseData
?
.
totalPages
??
1
)
}
page=
{
Number
(
events
?.
responseData
?
.
currentPage
??
page
)
}
onChangePage=
{
setPage
}
onGoToPreviousPage=
{
()
=>
setPage
(
Math
.
max
(
1
,
page
-
1
))
}
onGoToNextPage=
{
()
=>
setPage
(
Math
.
min
(
Number
(
events
?.
responseData
.
totalPages
??
1
),
page
+
1
))
setPage
(
Math
.
min
(
Number
(
events
?.
responseData
?
.
totalPages
??
1
),
page
+
1
))
}
/>
</
div
>
...
...
src/app/(main)/[...slug]/templates/InformationPage.tsx
View file @
e7182338
...
...
@@ -37,7 +37,7 @@ export default function InformationPage() {
<
ListCategory
categories=
{
category
?.
responseData
?.
children
}
/>
<
main
className=
" bg-white border rounded-md py-10 px-30"
>
<
div
className=
'text-primary text-2xl leading-normal font-bold'
>
{
data
?.
responseData
?.
rows
[
0
].
title
}
{
data
?.
responseData
?.
rows
[
0
]
?
.
title
}
</
div
>
{
/* <div className='flex items-center gap-2 text-sm mb-4'>
<span className='text-base text-blue-700'>
...
...
@@ -47,7 +47,7 @@ export default function InformationPage() {
<
hr
className=
"my-5"
/>
<
div
className=
'flex-1 text-app-grey text-base overflow-hidden'
>
<
div
className=
"prose tiptap overflow-hidden"
>
{
parse
(
data
?.
responseData
?.
rows
[
0
].
description
??
''
)
}
{
parse
(
data
?.
responseData
?.
rows
[
0
]
?
.
description
??
''
)
}
</
div
>
</
div
>
</
main
>
...
...
src/app/(main)/_lib/layout/header.tsx
View file @
e7182338
...
...
@@ -57,15 +57,34 @@ function Header() {
}
}
/>
<
div
className=
"flex gap-2"
>
{
[
Facebook
,
Twitter
,
Youtube
,
Linkedin
].
map
((
Icon
,
i
)
=>
(
<
a
key=
{
i
}
href=
"#
"
href=
"https://www.facebook.com/VCCIHCMC/"
target=
"_blank
"
className=
"bg-white size-7 rounded-full flex items-center justify-center text-[#063e8e] hover:opacity-80 transition"
>
<
Icon
size=
{
16
}
/>
<
Facebook
size=
{
16
}
/>
</
a
>
<
a
href=
"https://twitter.com/VCCI_HCM"
target=
"_blank"
className=
"bg-white size-7 rounded-full flex items-center justify-center text-[#063e8e] hover:opacity-80 transition"
>
<
Twitter
size=
{
16
}
/>
</
a
>
<
a
href=
"https://www.youtube.com/user/VCCIHCMC"
target=
"_blank"
className=
"bg-white size-7 rounded-full flex items-center justify-center text-[#063e8e] hover:opacity-80 transition"
>
<
Youtube
size=
{
16
}
/>
</
a
>
<
a
href=
"https://www.linkedin.com/company/vietnam-chamber-of-commerce-and-industry-ho-chi-minh-city-branch-vcci-hcm-?trk=biz-companies-cym"
target=
"_blank"
className=
"bg-white size-7 rounded-full flex items-center justify-center text-[#063e8e] hover:opacity-80 transition"
>
<
Linkedin
size=
{
16
}
/>
</
a
>
))
}
</
div
>
</
div
>
</
div
>
...
...
src/app/(main)/danh-ba-hoi-vien/page.tsx
0 → 100644
View file @
e7182338
export
default
function
Page
()
{
return
(
<
div
className=
"container flex justify-center items-center h-full py-20"
>
Danh bạ hội viên đang được xây dựng
</
div
>
);
}
\ No newline at end of file
src/app/(main)/search/page.tsx
View file @
e7182338
"use client"
;
import
React
,
{
useState
,
Suspense
}
from
"react"
;
import
ListCategory
from
"@/components/base/list-category"
;
import
ListFilter
from
"@/components/base/list-filter"
;
...
...
@@ -13,7 +14,7 @@ import { useSearchParams } from 'next/navigation'
function
SearchContent
()
{
const
[
page
,
setPage
]
=
useState
(
1
);
const
searchParams
=
useSearchParams
()
const
query
=
searchParams
.
get
(
'q'
)
//
const
query
=
searchParams
.
get
(
'q'
)
||
''
;
const
pageSize
=
5
;
const
{
data
:
allData
,
isLoading
}
=
useGetNews
<
GetNewsResponseType
>
({
pageSize
:
String
(
pageSize
),
...
...
@@ -50,7 +51,7 @@ function SearchContent() {
<
CardNews
key=
{
news
.
id
}
news=
{
news
}
link=
{
`${news.page_config.static_link}/${news.id}`
}
link=
{
news
.
external_link
}
/>
))
}
...
...
src/app/(main)/video/page.tsx
0 → 100644
View file @
e7182338
export
default
function
Page
()
{
return
(
<
div
className=
"container flex justify-center items-center h-full py-20"
>
Trang video đang được xây dựng
</
div
>
);
}
\ No newline at end of file
src/components/base/card-news/CardNews.type.ts
deleted
100644 → 0
View file @
fe00b726
export
interface
NewsDetailItem
{
id
:
string
title
:
string
thumbnail
:
string
external_link
:
string
description
:
string
release_at
:
string
is_active
:
boolean
created_at
:
string
created_by
:
string
|
null
updated_at
:
string
updated_by
:
string
|
null
mode
:
'NOW'
|
string
category
:
string
}
export
interface
NewsDetailResponseData
{
count
:
number
rows
:
NewsDetailItem
[]
totalPages
:
number
currentPage
:
number
}
export
interface
GetNewsDetailResponseType
{
message
:
string
message_en
:
string
responseData
:
NewsDetailItem
status
:
'success'
|
'error'
timeStamp
:
string
violations
:
any
|
null
}
\ No newline at end of file
src/components/base/card-news/index.tsx
View file @
e7182338
import
{
News
DetailItem
}
from
'./CardNews.type
'
;
import
{
News
Item
}
from
'@/api/types/news
'
;
import
Links
from
'@links/index'
import
dayjs
from
'dayjs'
;
...
...
@@ -20,7 +20,7 @@ const stripImagesAndHtml = (html?: string) => {
return
withoutImgs
.
replace
(
/<
[^
>
]
*>/g
,
''
)
}
const
CardNews
=
({
news
,
link
}:
{
news
:
News
Detail
Item
,
link
:
string
})
=>
{
const
CardNews
=
({
news
,
link
}:
{
news
:
NewsItem
,
link
:
string
})
=>
{
return
(
<
a
href=
{
`${link}`
}
...
...
src/components/base/menu-item/index.tsx
View file @
e7182338
import
{
usePathname
}
from
"next/navigation"
;
import
Link
from
"next/link"
;
type
MenuItemProps
=
{
...
...
@@ -7,26 +8,35 @@ type MenuItemProps = {
};
const
MenuItem
=
({
title
,
link
,
items
}:
MenuItemProps
)
=>
{
const
pathname
=
usePathname
();
const
isActive
=
!!
link
&&
(
pathname
===
link
||
(
link
!==
"/"
&&
pathname
.
startsWith
(
link
)));
return
(
<
div
className=
"group relative"
>
<
Link
href=
{
`${link || ""}`
}
className=
"px-3 py-5 text-[16px] font-semibold text-[#124588] hover:text-[#E8C518] transition block"
href=
{
link
??
"#"
}
className=
{
`px-3 py-5 text-[16px] font-semibold transition block
${isActive ? "text-[#E8C518]" : "text-[#124588] hover:text-[#E8C518]"}
`
}
>
{
title
}
</
Link
>
{
/* Dropdown */
}
<
div
className=
"absolute left-0 top-full hidden group-hover:block bg-[#124588]/98 text-white text-[14px] font-medium min-w-[220px] shadow-lg"
>
{
items
.
map
((
item
,
i
)
=>
(
{
items
.
map
((
item
,
i
)
=>
{
const
isItemActive
=
pathname
===
item
.
link
;
return
(
<
Link
key=
{
i
}
href=
{
`${item.link}`
}
className=
"block px-5 py-3 hover:bg-[#e8c518]/80 cursor-pointer whitespace-nowrap transition"
href=
{
item
.
link
}
className=
{
`block px-5 py-3 cursor-pointer whitespace-nowrap transition ${isItemActive ? "bg-[#e8c518]/80" : "hover:bg-[#e8c518]/80"
}`
}
>
{
item
.
title
}
</
Link
>
))
}
);
})
}
</
div
>
</
div
>
);
...
...
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