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
ff506f27
Commit
ff506f27
authored
Oct 20, 2026
by
Phạm Quang Bảo
Browse files
Options
Browse Files
Download
Plain Diff
[tag]0.1-vcci
parents
7cfa96fa
5984db4a
Pipeline
#53840
passed with stages
in 2 minutes and 17 seconds
Changes
2
Pipelines
1
Hide whitespace changes
Inline
Side-by-side
Showing
2 changed files
with
61 additions
and
14 deletions
+61
-14
news-listing.tsx
src/app/(main)/news/news-listing.tsx
+49
-13
page.tsx
src/app/(main)/news/page.tsx
+12
-1
No files found.
src/app/(main)/news/news-listing.tsx
View file @
ff506f27
"use client"
;
"use client"
;
import
{
useMemo
,
useState
}
from
"react"
;
import
{
useMemo
}
from
"react"
;
import
Link
from
"next/link"
;
import
Link
from
"next/link"
;
import
Image
from
"next/image"
;
import
Image
from
"next/image"
;
import
{
usePathname
,
useRouter
,
useSearchParams
}
from
"next/navigation"
;
import
{
useGetApiV10Post
}
from
"@/api/vcci-news/endpoints/post"
;
import
{
useGetApiV10Post
}
from
"@/api/vcci-news/endpoints/post"
;
import
type
{
DynamicPostItem
}
from
"@/api/vcci-news/types/post"
;
import
type
{
DynamicPostItem
}
from
"@/api/vcci-news/types/post"
;
import
{
Pagination
}
from
"@/components/base/pagination"
;
import
{
Pagination
}
from
"@/components/base/pagination"
;
...
@@ -19,7 +20,7 @@ import {
...
@@ -19,7 +20,7 @@ import {
const
PAGE_SIZE
=
10
;
const
PAGE_SIZE
=
10
;
const
CURRENT_YEAR
=
new
Date
().
getFullYear
();
const
CURRENT_YEAR
=
new
Date
().
getFullYear
();
const
START_YEAR
=
20
20
;
const
START_YEAR
=
20
09
;
const
YEAR_OPTIONS
=
Array
.
from
(
const
YEAR_OPTIONS
=
Array
.
from
(
{
length
:
CURRENT_YEAR
-
START_YEAR
+
1
},
{
length
:
CURRENT_YEAR
-
START_YEAR
+
1
},
...
@@ -40,8 +41,40 @@ const formatPostDate = (value?: string | null) => {
...
@@ -40,8 +41,40 @@ const formatPostDate = (value?: string | null) => {
};
};
export
default
function
NewsListing
()
{
export
default
function
NewsListing
()
{
const
[
year
,
setYear
]
=
useState
<
number
|
null
>
(
null
);
const
router
=
useRouter
();
const
[
page
,
setPage
]
=
useState
(
1
);
const
pathname
=
usePathname
();
const
searchParams
=
useSearchParams
();
const
searchParamsString
=
searchParams
.
toString
();
const
yearParam
=
Number
(
searchParams
.
get
(
"year"
));
const
year
=
YEAR_OPTIONS
.
includes
(
yearParam
)
?
yearParam
:
null
;
const
pageParam
=
Number
(
searchParams
.
get
(
"page"
)
??
"1"
);
const
page
=
Number
.
isFinite
(
pageParam
)
&&
pageParam
>
0
?
Math
.
floor
(
pageParam
)
:
1
;
const
updateSearchParams
=
(
next
:
{
year
?:
number
|
null
;
page
?:
number
})
=>
{
const
params
=
new
URLSearchParams
(
searchParamsString
);
const
nextYear
=
next
.
year
===
undefined
?
year
:
next
.
year
;
const
nextPage
=
next
.
page
??
page
;
if
(
nextYear
)
{
params
.
set
(
"year"
,
String
(
nextYear
));
}
else
{
params
.
delete
(
"year"
);
}
if
(
nextPage
>
1
)
{
params
.
set
(
"page"
,
String
(
nextPage
));
}
else
{
params
.
delete
(
"page"
);
}
const
queryString
=
params
.
toString
();
router
.
replace
(
queryString
?
`
${
pathname
}
?
${
queryString
}
`
:
pathname
,
{
scroll
:
false
,
});
};
const
filterParts
=
useMemo
(()
=>
{
const
filterParts
=
useMemo
(()
=>
{
const
parts
=
[
"is_hidden==false"
,
"is_active==true"
,
"type==news"
];
const
parts
=
[
"is_hidden==false"
,
"is_active==true"
,
"type==news"
];
...
@@ -70,8 +103,11 @@ export default function NewsListing() {
...
@@ -70,8 +103,11 @@ export default function NewsListing() {
);
);
const
handleSelectYear
=
(
value
:
number
|
null
)
=>
{
const
handleSelectYear
=
(
value
:
number
|
null
)
=>
{
setYear
(
value
);
updateSearchParams
({
year
:
value
,
page
:
1
});
setPage
(
1
);
};
const
handleChangePage
=
(
nextPage
:
number
)
=>
{
updateSearchParams
({
page
:
nextPage
});
};
};
return
(
return
(
...
@@ -159,12 +195,12 @@ export default function NewsListing() {
...
@@ -159,12 +195,12 @@ export default function NewsListing() {
<
Pagination
<
Pagination
pageCount=
{
totalPages
}
pageCount=
{
totalPages
}
page=
{
currentPage
}
page=
{
currentPage
}
onChangePage=
{
set
Page
}
onChangePage=
{
handleChange
Page
}
onGoToPreviousPage=
{
()
=>
onGoToPreviousPage=
{
()
=>
set
Page
(
Math
.
max
(
1
,
currentPage
-
1
))
handleChange
Page
(
Math
.
max
(
1
,
currentPage
-
1
))
}
}
onGoToNextPage=
{
()
=>
onGoToNextPage=
{
()
=>
set
Page
(
Math
.
min
(
totalPages
,
currentPage
+
1
))
handleChange
Page
(
Math
.
min
(
totalPages
,
currentPage
+
1
))
}
}
/>
/>
</
div
>
</
div
>
...
@@ -183,8 +219,8 @@ export default function NewsListing() {
...
@@ -183,8 +219,8 @@ export default function NewsListing() {
type=
"button"
type=
"button"
onClick=
{
()
=>
handleSelectYear
(
null
)
}
onClick=
{
()
=>
handleSelectYear
(
null
)
}
className=
{
`rounded-full px-4 py-2 text-sm font-semibold transition-all ${year === null
className=
{
`rounded-full px-4 py-2 text-sm font-semibold transition-all ${year === null
? "bg-[#14519f] text-white shadow-[0_10px_20px_rgba(20,81,159,0.18)]"
? "bg-[#14519f] text-white shadow-[0_10px_20px_rgba(20,81,159,0.18)]"
: "bg-[#f4f7fb] text-[#5f6875] hover:bg-[#eaf0f8]"
: "bg-[#f4f7fb] text-[#5f6875] hover:bg-[#eaf0f8]"
}`
}
}`
}
>
>
Tất cả
Tất cả
...
@@ -199,8 +235,8 @@ export default function NewsListing() {
...
@@ -199,8 +235,8 @@ export default function NewsListing() {
type=
"button"
type=
"button"
onClick=
{
()
=>
handleSelectYear
(
active
?
null
:
option
)
}
onClick=
{
()
=>
handleSelectYear
(
active
?
null
:
option
)
}
className=
{
`rounded-full px-4 py-2 text-sm font-semibold transition-all ${active
className=
{
`rounded-full px-4 py-2 text-sm font-semibold transition-all ${active
? "bg-[#14519f] text-white shadow-[0_10px_20px_rgba(20,81,159,0.18)]"
? "bg-[#14519f] text-white shadow-[0_10px_20px_rgba(20,81,159,0.18)]"
: "bg-[#f4f7fb] text-[#5f6875] hover:bg-[#eaf0f8]"
: "bg-[#f4f7fb] text-[#5f6875] hover:bg-[#eaf0f8]"
}`
}
}`
}
>
>
{
option
}
{
option
}
...
...
src/app/(main)/news/page.tsx
View file @
ff506f27
import
{
Suspense
}
from
"react"
;
import
type
{
Metadata
}
from
"next"
;
import
type
{
Metadata
}
from
"next"
;
import
links
from
"@links/index"
;
import
links
from
"@links/index"
;
import
NewsListing
from
"./news-listing"
;
import
NewsListing
from
"./news-listing"
;
...
@@ -12,5 +13,15 @@ export const metadata: Metadata = {
...
@@ -12,5 +13,15 @@ export const metadata: Metadata = {
};
};
export
default
function
NewsPage
()
{
export
default
function
NewsPage
()
{
return
<
NewsListing
/>;
return
(
<
Suspense
fallback=
{
<
div
className=
"flex min-h-[60vh] w-full items-center justify-center"
>
<
span
className=
"text-sm text-gray-500"
>
Đang tải...
</
span
>
</
div
>
}
>
<
NewsListing
/>
</
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