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
4a0686d2
Commit
4a0686d2
authored
Aug 25, 2026
by
Lê Bảo Hồng Đức
☄
Browse files
Options
Browse Files
Download
Plain Diff
[tag]0.1-vcci
parents
c01bc430
0a8e85f9
Pipeline
#52801
passed with stages
in 4 minutes and 45 seconds
Changes
3
Pipelines
1
Hide whitespace changes
Inline
Side-by-side
Showing
3 changed files
with
42 additions
and
10 deletions
+42
-10
page.tsx
src/app/admin/login/page.tsx
+16
-2
post-history-viewer.tsx
src/components/admin/post-history-viewer.tsx
+4
-5
rich-text-editor.tsx
src/components/admin/rich-text-editor.tsx
+22
-3
No files found.
src/app/admin/login/page.tsx
View file @
4a0686d2
...
...
@@ -26,6 +26,7 @@ import logo from "@/assets/VCCI-HCM-logo-VN-2025.png";
import
links
,
{
resolveUploadUrl
}
from
"@/links"
;
import
{
loginAdmin
}
from
"@/lib/auth/admin-auth"
;
import
useAuthStore
from
"@/store/useAuthStore"
;
import
{
fetchCmsFileById
,
resolveCmsFileUrl
}
from
"@/lib/api/files"
;
type
AuthMode
=
"login"
|
"forgot"
;
...
...
@@ -120,6 +121,19 @@ function AuthShell({
}
);
// Resolve logo URL: prefer logo_url, otherwise fetch file by file_id.
const
{
data
:
logoFile
}
=
useQuery
({
queryKey
:
[
"cms-file"
,
logoData
?.
file_id
],
queryFn
:
()
=>
fetchCmsFileById
(
logoData
!
.
file_id
),
enabled
:
!!
logoData
?.
file_id
&&
!
logoData
?.
logo_url
,
});
const
logoSrc
=
logoData
?.
logo_url
?
resolveUploadUrl
(
logoData
.
logo_url
)
:
logoFile
?
resolveCmsFileUrl
(
logoFile
.
path
)
:
logo
;
const
title
=
mode
===
"login"
?
"Đăng nhập quản trị"
...
...
@@ -141,7 +155,7 @@ function AuthShell({
<
div
className=
"flex items-center gap-4"
>
<
div
className=
"flex h-16 w-16 items-center justify-center rounded-2xl border border-[#063e8e]/10 bg-white shadow-sm"
>
<
Image
src=
{
logo
Data
?.
logo_url
?
resolveUploadUrl
(
logoData
.
logo_url
)
:
logo
}
src=
{
logo
Src
}
alt=
{
logoData
?.
logo_name
||
"VCCI HCM"
}
width=
{
48
}
height=
{
48
}
...
...
@@ -195,7 +209,7 @@ function AuthShell({
<
div
className=
"mb-8 flex items-center gap-3 lg:hidden"
>
<
div
className=
"flex h-12 w-12 items-center justify-center rounded-2xl border border-[#063e8e]/10 bg-[#f8fbff]"
>
<
Image
src=
{
logo
Data
?.
logo_url
?
resolveUploadUrl
(
logoData
.
logo_url
)
:
logo
}
src=
{
logo
Src
}
alt=
{
logoData
?.
logo_name
||
"VCCI HCM"
}
width=
{
36
}
height=
{
36
}
...
...
src/components/admin/post-history-viewer.tsx
View file @
4a0686d2
...
...
@@ -182,11 +182,10 @@ interface PostHistoryViewerProps {
export
function
PostHistoryViewer
({
postId
}:
PostHistoryViewerProps
)
{
const
[
isOpen
,
setIsOpen
]
=
React
.
useState
(
false
);
const
{
data
,
isLoading
,
isError
,
error
}
=
useGetApiV10PostIdHistory
(
postId
,
{
query
:
{
enabled
:
isOpen
,
},
});
// Always fetch history so the count is correct even before the user opens
// the panel. Previously `enabled: isOpen` caused the count to show "0 thay
// đổi" until the panel was opened.
const
{
data
,
isLoading
,
isError
,
error
}
=
useGetApiV10PostIdHistory
(
postId
);
const
historyItems
=
React
.
useMemo
(()
=>
{
// useQuery's `data` IS the API response body: { responseData: [...], ... }
...
...
src/components/admin/rich-text-editor.tsx
View file @
4a0686d2
...
...
@@ -333,12 +333,31 @@ export function AdminRichTextEditor({
format
:
"json"
,
buildData
:
(
data
:
FormData
)
=>
{
const
next
=
new
FormData
();
let
firstFileAppended
=
false
;
data
.
forEach
((
value
,
key
)
=>
{
// Jodit sends files as files[0], files[1], ... — multer expects
// a single field named "file". Only keep the first file and drop
// the rest (backend uses multer.single("file")).
if
(
/^files
\[\d
+
\]
$/
.
test
(
key
)
&&
value
instanceof
File
)
{
if
(
!
firstFileAppended
)
{
next
.
append
(
"file"
,
value
);
firstFileAppended
=
true
;
}
return
;
}
// Also handle legacy "files[]" pattern just in case.
if
(
key
===
"files[]"
&&
value
instanceof
File
)
{
next
.
append
(
"file"
,
value
);
}
else
{
next
.
append
(
key
,
value
);
if
(
!
firstFileAppended
)
{
next
.
append
(
"file"
,
value
);
firstFileAppended
=
true
;
}
return
;
}
// Drop Jodit-internal fields the backend doesn't use.
if
(
key
===
"source"
||
key
===
"extension"
||
key
===
"mimetype"
)
{
return
;
}
next
.
append
(
key
,
value
);
});
return
next
;
},
...
...
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