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
7cfa96fa
Commit
7cfa96fa
authored
Oct 20, 2026
by
Phạm Quang Bảo
Browse files
Options
Browse Files
Download
Plain Diff
[tag]0.1-vcci
parents
eb79d2ed
1094de1d
Pipeline
#53836
passed with stages
in 2 minutes and 10 seconds
Changes
6
Pipelines
1
Hide whitespace changes
Inline
Side-by-side
Showing
6 changed files
with
3073 additions
and
39 deletions
+3073
-39
DynamicPageClient.tsx
src/app/(main)/[...slug]/DynamicPageClient.tsx
+1
-0
AnPham.tsx
src/app/(main)/[...slug]/static-pages/AnPham.tsx
+12
-13
ThuVienTaiLieu.tsx
src/app/(main)/[...slug]/static-pages/ThuVienTaiLieu.tsx
+12
-13
index.tsx
src/app/(main)/[...slug]/templates/news-page/index.tsx
+12
-13
legacy-redirect-map.ts
src/lib/legacy-redirect-map.ts
+2987
-0
proxy.ts
src/proxy.ts
+49
-0
No files found.
src/app/(main)/[...slug]/DynamicPageClient.tsx
View file @
7cfa96fa
...
@@ -321,6 +321,7 @@ export default function DynamicPageClient() {
...
@@ -321,6 +321,7 @@ export default function DynamicPageClient() {
if
(
resolvedCategory
?.
type
===
"news"
)
{
if
(
resolvedCategory
?.
type
===
"news"
)
{
return
(
return
(
<
NewsPage
<
NewsPage
key=
{
resolvedCategory
.
id
}
category=
{
resolvedCategory
}
category=
{
resolvedCategory
}
allCategories=
{
allCategories
}
allCategories=
{
allCategories
}
/>
/>
...
...
src/app/(main)/[...slug]/static-pages/AnPham.tsx
View file @
7cfa96fa
...
@@ -32,22 +32,15 @@ export default function AnPham({ category, allCategories }: AnPhamProps) {
...
@@ -32,22 +32,15 @@ export default function AnPham({ category, allCategories }: AnPhamProps) {
const
pathname
=
usePathname
();
const
pathname
=
usePathname
();
const
searchParamsString
=
searchParams
.
toString
();
const
searchParamsString
=
searchParams
.
toString
();
const
initialPage
=
Number
(
searchParams
.
get
(
"page"
)
??
"1"
);
const
pageParam
=
Number
(
searchParams
.
get
(
"page"
)
??
"1"
);
const
[
searchInput
,
setSearchInput
]
=
useState
(
""
);
const
initialPage
=
Number
.
isFinite
(
pageParam
)
&&
pageParam
>
0
?
Math
.
floor
(
pageParam
)
:
1
;
const
[
submitSearch
,
setSubmitSearch
]
=
useState
(
""
);
const
initialQuery
=
searchParams
.
get
(
"q"
)
??
""
;
const
[
searchInput
,
setSearchInput
]
=
useState
(
initialQuery
);
const
[
submitSearch
,
setSubmitSearch
]
=
useState
(
initialQuery
);
const
[
page
,
setPage
]
=
useState
(
initialPage
);
const
[
page
,
setPage
]
=
useState
(
initialPage
);
const
pageSize
=
8
;
const
pageSize
=
8
;
const
keyword
=
submitSearch
.
trim
();
const
keyword
=
submitSearch
.
trim
();
// Auto-search with debounce
useEffect
(()
=>
{
const
timer
=
setTimeout
(()
=>
{
setSubmitSearch
(
searchInput
);
setPage
(
1
);
},
500
);
return
()
=>
clearTimeout
(
timer
);
},
[
searchInput
]);
useEffect
(()
=>
{
useEffect
(()
=>
{
const
params
=
new
URLSearchParams
(
searchParamsString
);
const
params
=
new
URLSearchParams
(
searchParamsString
);
if
(
page
>
1
)
{
if
(
page
>
1
)
{
...
@@ -56,6 +49,12 @@ export default function AnPham({ category, allCategories }: AnPhamProps) {
...
@@ -56,6 +49,12 @@ export default function AnPham({ category, allCategories }: AnPhamProps) {
params
.
delete
(
"page"
);
params
.
delete
(
"page"
);
}
}
if
(
keyword
)
{
params
.
set
(
"q"
,
keyword
);
}
else
{
params
.
delete
(
"q"
);
}
const
qs
=
params
.
toString
();
const
qs
=
params
.
toString
();
const
nextUrl
=
qs
?
`
${
pathname
}
?
${
qs
}
`
:
pathname
;
const
nextUrl
=
qs
?
`
${
pathname
}
?
${
qs
}
`
:
pathname
;
const
currentUrl
=
searchParamsString
?
`
${
pathname
}
?
${
searchParamsString
}
`
:
pathname
;
const
currentUrl
=
searchParamsString
?
`
${
pathname
}
?
${
searchParamsString
}
`
:
pathname
;
...
@@ -63,7 +62,7 @@ export default function AnPham({ category, allCategories }: AnPhamProps) {
...
@@ -63,7 +62,7 @@ export default function AnPham({ category, allCategories }: AnPhamProps) {
if
(
nextUrl
!==
currentUrl
)
{
if
(
nextUrl
!==
currentUrl
)
{
router
.
replace
(
nextUrl
,
{
scroll
:
false
});
router
.
replace
(
nextUrl
,
{
scroll
:
false
});
}
}
},
[
page
,
pathname
,
router
,
searchParamsString
]);
},
[
page
,
keyword
,
pathname
,
router
,
searchParamsString
]);
const
filters
=
[
const
filters
=
[
category
?.
id
?
`category.id==
${
category
.
id
}
`
:
null
,
category
?.
id
?
`category.id==
${
category
.
id
}
`
:
null
,
...
...
src/app/(main)/[...slug]/static-pages/ThuVienTaiLieu.tsx
View file @
7cfa96fa
...
@@ -32,22 +32,15 @@ export default function ThuVienTaiLieu({ category, allCategories }: ThuVienTaiLi
...
@@ -32,22 +32,15 @@ export default function ThuVienTaiLieu({ category, allCategories }: ThuVienTaiLi
const
pathname
=
usePathname
();
const
pathname
=
usePathname
();
const
searchParamsString
=
searchParams
.
toString
();
const
searchParamsString
=
searchParams
.
toString
();
const
initialPage
=
Number
(
searchParams
.
get
(
"page"
)
??
"1"
);
const
pageParam
=
Number
(
searchParams
.
get
(
"page"
)
??
"1"
);
const
[
searchInput
,
setSearchInput
]
=
useState
(
""
);
const
initialPage
=
Number
.
isFinite
(
pageParam
)
&&
pageParam
>
0
?
Math
.
floor
(
pageParam
)
:
1
;
const
[
submitSearch
,
setSubmitSearch
]
=
useState
(
""
);
const
initialQuery
=
searchParams
.
get
(
"q"
)
??
""
;
const
[
searchInput
,
setSearchInput
]
=
useState
(
initialQuery
);
const
[
submitSearch
,
setSubmitSearch
]
=
useState
(
initialQuery
);
const
[
page
,
setPage
]
=
useState
(
initialPage
);
const
[
page
,
setPage
]
=
useState
(
initialPage
);
const
pageSize
=
8
;
const
pageSize
=
8
;
const
keyword
=
submitSearch
.
trim
();
const
keyword
=
submitSearch
.
trim
();
// Auto-search with debounce
useEffect
(()
=>
{
const
timer
=
setTimeout
(()
=>
{
setSubmitSearch
(
searchInput
);
setPage
(
1
);
},
500
);
return
()
=>
clearTimeout
(
timer
);
},
[
searchInput
]);
useEffect
(()
=>
{
useEffect
(()
=>
{
const
params
=
new
URLSearchParams
(
searchParamsString
);
const
params
=
new
URLSearchParams
(
searchParamsString
);
if
(
page
>
1
)
{
if
(
page
>
1
)
{
...
@@ -56,6 +49,12 @@ export default function ThuVienTaiLieu({ category, allCategories }: ThuVienTaiLi
...
@@ -56,6 +49,12 @@ export default function ThuVienTaiLieu({ category, allCategories }: ThuVienTaiLi
params
.
delete
(
"page"
);
params
.
delete
(
"page"
);
}
}
if
(
keyword
)
{
params
.
set
(
"q"
,
keyword
);
}
else
{
params
.
delete
(
"q"
);
}
const
qs
=
params
.
toString
();
const
qs
=
params
.
toString
();
const
nextUrl
=
qs
?
`
${
pathname
}
?
${
qs
}
`
:
pathname
;
const
nextUrl
=
qs
?
`
${
pathname
}
?
${
qs
}
`
:
pathname
;
const
currentUrl
=
searchParamsString
?
`
${
pathname
}
?
${
searchParamsString
}
`
:
pathname
;
const
currentUrl
=
searchParamsString
?
`
${
pathname
}
?
${
searchParamsString
}
`
:
pathname
;
...
@@ -63,7 +62,7 @@ export default function ThuVienTaiLieu({ category, allCategories }: ThuVienTaiLi
...
@@ -63,7 +62,7 @@ export default function ThuVienTaiLieu({ category, allCategories }: ThuVienTaiLi
if
(
nextUrl
!==
currentUrl
)
{
if
(
nextUrl
!==
currentUrl
)
{
router
.
replace
(
nextUrl
,
{
scroll
:
false
});
router
.
replace
(
nextUrl
,
{
scroll
:
false
});
}
}
},
[
page
,
pathname
,
router
,
searchParamsString
]);
},
[
page
,
keyword
,
pathname
,
router
,
searchParamsString
]);
const
filters
=
[
const
filters
=
[
category
?.
id
?
`category.id==
${
category
.
id
}
`
:
null
,
category
?.
id
?
`category.id==
${
category
.
id
}
`
:
null
,
...
...
src/app/(main)/[...slug]/templates/news-page/index.tsx
View file @
7cfa96fa
...
@@ -57,22 +57,15 @@ export default function NewsPage({ category, allCategories }: NewsPageProps) {
...
@@ -57,22 +57,15 @@ export default function NewsPage({ category, allCategories }: NewsPageProps) {
const
pathname
=
usePathname
();
const
pathname
=
usePathname
();
const
searchParamsString
=
searchParams
.
toString
();
const
searchParamsString
=
searchParams
.
toString
();
const
initialPage
=
Number
(
searchParams
.
get
(
"page"
)
??
"1"
);
const
pageParam
=
Number
(
searchParams
.
get
(
"page"
)
??
"1"
);
const
[
searchInput
,
setSearchInput
]
=
useState
(
""
);
const
initialPage
=
Number
.
isFinite
(
pageParam
)
&&
pageParam
>
0
?
Math
.
floor
(
pageParam
)
:
1
;
const
[
submitSearch
,
setSubmitSearch
]
=
useState
(
""
);
const
initialQuery
=
searchParams
.
get
(
"q"
)
??
""
;
const
[
searchInput
,
setSearchInput
]
=
useState
(
initialQuery
);
const
[
submitSearch
,
setSubmitSearch
]
=
useState
(
initialQuery
);
const
[
page
,
setPage
]
=
useState
(
initialPage
);
const
[
page
,
setPage
]
=
useState
(
initialPage
);
const
pageSize
=
10
;
const
pageSize
=
10
;
const
keyword
=
submitSearch
.
trim
();
const
keyword
=
submitSearch
.
trim
();
// Auto-search with debounce
useEffect
(()
=>
{
const
timer
=
setTimeout
(()
=>
{
setSubmitSearch
(
searchInput
);
setPage
(
1
);
},
500
);
return
()
=>
clearTimeout
(
timer
);
},
[
searchInput
]);
useEffect
(()
=>
{
useEffect
(()
=>
{
const
params
=
new
URLSearchParams
(
searchParamsString
);
const
params
=
new
URLSearchParams
(
searchParamsString
);
if
(
page
>
1
)
{
if
(
page
>
1
)
{
...
@@ -81,6 +74,12 @@ export default function NewsPage({ category, allCategories }: NewsPageProps) {
...
@@ -81,6 +74,12 @@ export default function NewsPage({ category, allCategories }: NewsPageProps) {
params
.
delete
(
"page"
);
params
.
delete
(
"page"
);
}
}
if
(
keyword
)
{
params
.
set
(
"q"
,
keyword
);
}
else
{
params
.
delete
(
"q"
);
}
const
qs
=
params
.
toString
();
const
qs
=
params
.
toString
();
const
nextUrl
=
qs
?
`
${
pathname
}
?
${
qs
}
`
:
pathname
;
const
nextUrl
=
qs
?
`
${
pathname
}
?
${
qs
}
`
:
pathname
;
const
currentUrl
=
searchParamsString
?
`
${
pathname
}
?
${
searchParamsString
}
`
:
pathname
;
const
currentUrl
=
searchParamsString
?
`
${
pathname
}
?
${
searchParamsString
}
`
:
pathname
;
...
@@ -88,7 +87,7 @@ export default function NewsPage({ category, allCategories }: NewsPageProps) {
...
@@ -88,7 +87,7 @@ export default function NewsPage({ category, allCategories }: NewsPageProps) {
if
(
nextUrl
!==
currentUrl
)
{
if
(
nextUrl
!==
currentUrl
)
{
router
.
replace
(
nextUrl
,
{
scroll
:
false
});
router
.
replace
(
nextUrl
,
{
scroll
:
false
});
}
}
},
[
page
,
pathname
,
router
,
searchParamsString
]);
},
[
page
,
keyword
,
pathname
,
router
,
searchParamsString
]);
const
filters
=
`category.id==
${
category
.
id
}${
keyword
?
`,title@=
${
keyword
}
`
:
""
}
,is_hidden==false,is_active==true,type==news`
;
const
filters
=
`category.id==
${
category
.
id
}${
keyword
?
`,title@=
${
keyword
}
`
:
""
}
,is_hidden==false,is_active==true,type==news`
;
...
...
src/lib/legacy-redirect-map.ts
0 → 100644
View file @
7cfa96fa
This source diff could not be displayed because it is too large. You can
view the blob
instead.
src/proxy.ts
0 → 100644
View file @
7cfa96fa
import
{
NextRequest
,
NextResponse
}
from
"next/server"
;
import
{
LEGACY_ID_MAP
,
LEGACY_SLUG_MAP
}
from
"@/lib/legacy-redirect-map"
;
/**
* Redirect cac link cu cua site WordPress ve slug hien tai trong CMS moi:
* - ?p=<id> (link kieu ?p=11051)
* - .../ten-bai.html (permalink cu co duoi .html)
* - /ten-bai (slug cu bi doi)
* Map tu dong tao boi migration/scripts/build-redirect-map.mjs.
*
* Next.js 16: middleware.ts -> proxy.ts (cu), ham export 'proxy'.
*/
export
default
function
proxy
(
req
:
NextRequest
)
{
// 1. ?p=<id>
const
p
=
req
.
nextUrl
.
searchParams
.
get
(
"p"
);
if
(
p
)
{
const
slug
=
LEGACY_ID_MAP
[
p
];
if
(
slug
)
{
return
NextResponse
.
redirect
(
new
URL
(
"/"
+
slug
,
req
.
nextUrl
.
origin
),
308
);
}
}
// 2. duoi .html
const
pathname
=
req
.
nextUrl
.
pathname
;
if
(
pathname
.
toLowerCase
().
endsWith
(
".html"
))
{
const
trimmed
=
pathname
.
slice
(
0
,
-
5
);
const
last
=
trimmed
.
split
(
"/"
).
filter
(
Boolean
).
pop
();
if
(
last
)
{
const
target
=
LEGACY_SLUG_MAP
[
last
]
??
last
;
return
NextResponse
.
redirect
(
new
URL
(
"/"
+
target
+
req
.
nextUrl
.
search
,
req
.
nextUrl
.
origin
),
308
);
}
}
// 3. slug cu bi doi
const
clean
=
pathname
.
replace
(
/
\/
+$/
,
""
).
replace
(
/^
\/
/
,
""
);
const
mapped
=
LEGACY_SLUG_MAP
[
clean
];
if
(
mapped
)
{
return
NextResponse
.
redirect
(
new
URL
(
"/"
+
mapped
+
req
.
nextUrl
.
search
,
req
.
nextUrl
.
origin
),
308
);
}
return
NextResponse
.
next
();
}
export
const
config
=
{
matcher
:
[
"/((?!api|admin|uploads|_next/static|_next/image|images/|public/|favicon.ico|thumbnail.png|robots.txt|sitemap.xml|ads.txt).*)"
,
],
};
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