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
a99fbda4
Commit
a99fbda4
authored
Nov 05, 2025
by
Phạm Quang Bảo
Browse files
Options
Browse Files
Download
Plain Diff
Merge branch 'develop' into fix/home_page
parents
7e62d507
3a744f98
Changes
56
Hide whitespace changes
Inline
Side-by-side
Showing
56 changed files
with
2703 additions
and
1045 deletions
+2703
-1045
orval.config.ts
orval.config.ts
+2
-1
fallback.png
public/fallback.png
+0
-0
news-page-config.ts
src/api/endpoints/news-page-config.ts
+930
-0
index.ts
src/api/models/index.ts
+1
-0
putNewsPageConfigCategoryIdBody.ts
src/api/models/putNewsPageConfigCategoryIdBody.ts
+11
-0
event.ts
src/api/types/event.ts
+64
-2
page.tsx
src/app/(main)/(home)/[id]/page.tsx
+1
-1
index.tsx
src/app/(main)/(home)/components/card-event/index.tsx
+4
-0
index.tsx
src/app/(main)/(home)/components/card-news/index.tsx
+4
-0
index.tsx
src/app/(main)/(home)/components/event-calendar/index.tsx
+0
-113
page.tsx
src/app/(main)/(home)/page.tsx
+519
-523
footer.tsx
src/app/(main)/_lib/layout/footer.tsx
+2
-2
header.tsx
src/app/(main)/_lib/layout/header.tsx
+28
-19
page.tsx
src/app/(main)/dai-dien-gioi-chu/chu-de/[id]/page.tsx
+4
-0
page.tsx
src/app/(main)/dai-dien-gioi-chu/chu-de/page.tsx
+32
-21
index.tsx
...p/(main)/dai-dien-gioi-chu/components/card-news/index.tsx
+24
-8
sampleHtml.ts
src/app/(main)/dai-dien-gioi-chu/lib/sampleHtml.ts
+1
-1
page.tsx
...app/(main)/dai-dien-gioi-chu/tap-huan-nsdld/[id]/page.tsx
+4
-0
page.tsx
src/app/(main)/dai-dien-gioi-chu/tap-huan-nsdld/page.tsx
+30
-20
page.tsx
src/app/(main)/dai-dien-gioi-chu/tin-lien-quan/[id]/page.tsx
+15
-7
page.tsx
src/app/(main)/dai-dien-gioi-chu/tin-lien-quan/page.tsx
+27
-16
page.tsx
src/app/(main)/hoat-dong/dao-tao/[id]/page.tsx
+4
-0
page.tsx
src/app/(main)/hoat-dong/dao-tao/page.tsx
+26
-16
page.tsx
src/app/(main)/hoat-dong/page.tsx
+1
-1
page.tsx
src/app/(main)/hoat-dong/su-kien/[id]/page.tsx
+18
-3
page.tsx
src/app/(main)/hoat-dong/su-kien/page.tsx
+30
-16
page.tsx
src/app/(main)/hoi-vien/ket-noi-hoi-vien/page.tsx
+32
-22
page.tsx
src/app/(main)/hoi-vien/tin-hoi-vien/page.tsx
+30
-20
page.tsx
src/app/(main)/search/page.tsx
+112
-0
mock-data.ts
src/app/(main)/site-map/_lib/mock-data.ts
+94
-0
page.tsx
src/app/(main)/site-map/page.tsx
+103
-0
page.tsx
...app/(main)/thong-tin-truyen-thong/chuyen-de/[id]/page.tsx
+4
-0
page.tsx
src/app/(main)/thong-tin-truyen-thong/chuyen-de/page.tsx
+32
-20
page.tsx
src/app/(main)/thong-tin-truyen-thong/page.tsx
+1
-1
page.tsx
...yen-thong/thong-tin-chinh-sach-va-phap-luat/[id]/page.tsx
+4
-0
page.tsx
...n-truyen-thong/thong-tin-chinh-sach-va-phap-luat/page.tsx
+14
-11
page.tsx
...n)/thong-tin-truyen-thong/thu-vien-tai-lieu/[id]/page.tsx
+4
-0
page.tsx
.../(main)/thong-tin-truyen-thong/thu-vien-tai-lieu/page.tsx
+32
-20
page.tsx
...in)/thong-tin-truyen-thong/tin-doanh-nghiep/[id]/page.tsx
+4
-0
page.tsx
...p/(main)/thong-tin-truyen-thong/tin-doanh-nghiep/page.tsx
+32
-20
page.tsx
...p/(main)/thong-tin-truyen-thong/tin-kinh-te/[id]/page.tsx
+4
-0
page.tsx
src/app/(main)/thong-tin-truyen-thong/tin-kinh-te/page.tsx
+32
-20
page.tsx
src/app/(main)/thong-tin-truyen-thong/tin-vcci/[id]/page.tsx
+4
-0
page.tsx
src/app/(main)/thong-tin-truyen-thong/tin-vcci/page.tsx
+34
-22
page.tsx
src/app/(main)/xuc-tien-thuong-mai/[id]/page.tsx
+15
-1
page.tsx
...main)/xuc-tien-thuong-mai/co-hoi-kinh-doanh/[id]/page.tsx
+17
-1
page.tsx
...app/(main)/xuc-tien-thuong-mai/co-hoi-kinh-doanh/page.tsx
+34
-21
page.tsx
...main)/xuc-tien-thuong-mai/ho-tro-kinh-doanh/[id]/page.tsx
+17
-1
page.tsx
...app/(main)/xuc-tien-thuong-mai/ho-tro-kinh-doanh/page.tsx
+38
-25
page.tsx
...)/xuc-tien-thuong-mai/moi-truong-kinh-doanh/[id]/page.tsx
+17
-1
page.tsx
...(main)/xuc-tien-thuong-mai/moi-truong-kinh-doanh/page.tsx
+38
-25
page.tsx
src/app/(main)/xuc-tien-thuong-mai/page.tsx
+29
-19
index.tsx
src/components/base/card-news/index.tsx
+24
-7
AppEditorContent.tsx
src/components/shared/editor-content/AppEditorContent.tsx
+38
-18
loading-state.tsx
src/components/ui/loading-state.tsx
+49
-0
common.ts
src/lib/types/common.ts
+33
-0
No files found.
orval.config.ts
View file @
a99fbda4
...
...
@@ -79,7 +79,8 @@ const orvalConfig = async () => {
'UserHistory'
,
'Approvals'
,
'News'
,
'Category'
'Category'
,
'NewsPageConfig'
,
]
}
}
...
...
public/fallback.png
0 → 100644
View file @
a99fbda4
498 Bytes
src/api/endpoints/news-page-config.ts
0 → 100644
View file @
a99fbda4
/**
* Generated by orval v8.0.0-rc.0 🍺
* Do not edit manually.
* VCCI
* Coded by Meu TEAM
* OpenAPI spec version: 1.0.0
*/
import
{
useInfiniteQuery
,
useMutation
,
useQuery
}
from
'@tanstack/react-query'
;
import
type
{
DataTag
,
DefinedInitialDataOptions
,
DefinedUseInfiniteQueryResult
,
DefinedUseQueryResult
,
InfiniteData
,
MutationFunction
,
QueryClient
,
QueryFunction
,
QueryKey
,
UndefinedInitialDataOptions
,
UseInfiniteQueryOptions
,
UseInfiniteQueryResult
,
UseMutationOptions
,
UseMutationResult
,
UseQueryOptions
,
UseQueryResult
}
from
'@tanstack/react-query'
;
import
type
{
PutNewsPageConfigCategoryIdBody
,
Response
}
from
'../models'
;
import
{
useCustomClient
}
from
'../mutator/custom-client'
;
import
type
{
ErrorType
,
BodyType
}
from
'../mutator/custom-client'
;
type
SecondParameter
<
T
extends
(...
args
:
never
)
=>
unknown
>
=
Parameters
<
T
>
[
1
];
/**
* Get page config category by id
*/
export
type
getNewsPageConfigCategoryIdResponse200
=
{
data
:
Response
status
:
200
}
export
type
getNewsPageConfigCategoryIdResponseSuccess
=
(
getNewsPageConfigCategoryIdResponse200
)
&
{
headers
:
Headers
;
};
;
export
type
getNewsPageConfigCategoryIdResponse
=
(
getNewsPageConfigCategoryIdResponseSuccess
)
export
const
getGetNewsPageConfigCategoryIdUrl
=
(
id
:
string
,)
=>
{
return
`/newsPageConfig/category/
${
id
}
`
}
export
const
getNewsPageConfigCategoryId
=
async
(
id
:
string
,
options
?:
RequestInit
):
Promise
<
getNewsPageConfigCategoryIdResponse
>
=>
{
return
useCustomClient
<
getNewsPageConfigCategoryIdResponse
>
(
getGetNewsPageConfigCategoryIdUrl
(
id
),
{
...
options
,
method
:
'GET'
}
);}
export
const
getGetNewsPageConfigCategoryIdInfiniteQueryKey
=
(
id
?:
string
,)
=>
{
return
[
'infinite'
,
`/newsPageConfig/category/
${
id
}
`
]
as
const
;
}
export
const
getGetNewsPageConfigCategoryIdQueryKey
=
(
id
?:
string
,)
=>
{
return
[
`/newsPageConfig/category/
${
id
}
`
]
as
const
;
}
export
const
getGetNewsPageConfigCategoryIdInfiniteQueryOptions
=
<
TData
=
InfiniteData
<
Awaited
<
ReturnType
<
typeof
getNewsPageConfigCategoryId
>>>
,
TError
=
ErrorType
<
unknown
>>
(
id
:
string
,
options
?:
{
query
?:
Partial
<
UseInfiniteQueryOptions
<
Awaited
<
ReturnType
<
typeof
getNewsPageConfigCategoryId
>>
,
TError
,
TData
>>
,
request
?:
SecondParameter
<
typeof
useCustomClient
>
}
)
=>
{
const
{
query
:
queryOptions
,
request
:
requestOptions
}
=
options
??
{};
const
queryKey
=
queryOptions
?.
queryKey
??
getGetNewsPageConfigCategoryIdInfiniteQueryKey
(
id
);
const
queryFn
:
QueryFunction
<
Awaited
<
ReturnType
<
typeof
getNewsPageConfigCategoryId
>>>
=
({
signal
})
=>
getNewsPageConfigCategoryId
(
id
,
{
signal
,
...
requestOptions
});
return
{
queryKey
,
queryFn
,
enabled
:
!!
(
id
),
...
queryOptions
}
as
UseInfiniteQueryOptions
<
Awaited
<
ReturnType
<
typeof
getNewsPageConfigCategoryId
>>
,
TError
,
TData
>
&
{
queryKey
:
DataTag
<
QueryKey
,
TData
,
TError
>
}
}
export
type
GetNewsPageConfigCategoryIdInfiniteQueryResult
=
NonNullable
<
Awaited
<
ReturnType
<
typeof
getNewsPageConfigCategoryId
>>>
export
type
GetNewsPageConfigCategoryIdInfiniteQueryError
=
ErrorType
<
unknown
>
export
function
useGetNewsPageConfigCategoryIdInfinite
<
TData
=
InfiniteData
<
Awaited
<
ReturnType
<
typeof
getNewsPageConfigCategoryId
>>>
,
TError
=
ErrorType
<
unknown
>>
(
id
:
string
,
options
:
{
query
:
Partial
<
UseInfiniteQueryOptions
<
Awaited
<
ReturnType
<
typeof
getNewsPageConfigCategoryId
>>
,
TError
,
TData
>>
&
Pick
<
DefinedInitialDataOptions
<
Awaited
<
ReturnType
<
typeof
getNewsPageConfigCategoryId
>>
,
TError
,
Awaited
<
ReturnType
<
typeof
getNewsPageConfigCategoryId
>>
>
,
'initialData'
>
,
request
?:
SecondParameter
<
typeof
useCustomClient
>
}
,
queryClient
?:
QueryClient
):
DefinedUseInfiniteQueryResult
<
TData
,
TError
>
&
{
queryKey
:
DataTag
<
QueryKey
,
TData
,
TError
>
}
export
function
useGetNewsPageConfigCategoryIdInfinite
<
TData
=
InfiniteData
<
Awaited
<
ReturnType
<
typeof
getNewsPageConfigCategoryId
>>>
,
TError
=
ErrorType
<
unknown
>>
(
id
:
string
,
options
?:
{
query
?:
Partial
<
UseInfiniteQueryOptions
<
Awaited
<
ReturnType
<
typeof
getNewsPageConfigCategoryId
>>
,
TError
,
TData
>>
&
Pick
<
UndefinedInitialDataOptions
<
Awaited
<
ReturnType
<
typeof
getNewsPageConfigCategoryId
>>
,
TError
,
Awaited
<
ReturnType
<
typeof
getNewsPageConfigCategoryId
>>
>
,
'initialData'
>
,
request
?:
SecondParameter
<
typeof
useCustomClient
>
}
,
queryClient
?:
QueryClient
):
UseInfiniteQueryResult
<
TData
,
TError
>
&
{
queryKey
:
DataTag
<
QueryKey
,
TData
,
TError
>
}
export
function
useGetNewsPageConfigCategoryIdInfinite
<
TData
=
InfiniteData
<
Awaited
<
ReturnType
<
typeof
getNewsPageConfigCategoryId
>>>
,
TError
=
ErrorType
<
unknown
>>
(
id
:
string
,
options
?:
{
query
?:
Partial
<
UseInfiniteQueryOptions
<
Awaited
<
ReturnType
<
typeof
getNewsPageConfigCategoryId
>>
,
TError
,
TData
>>
,
request
?:
SecondParameter
<
typeof
useCustomClient
>
}
,
queryClient
?:
QueryClient
):
UseInfiniteQueryResult
<
TData
,
TError
>
&
{
queryKey
:
DataTag
<
QueryKey
,
TData
,
TError
>
}
export
function
useGetNewsPageConfigCategoryIdInfinite
<
TData
=
InfiniteData
<
Awaited
<
ReturnType
<
typeof
getNewsPageConfigCategoryId
>>>
,
TError
=
ErrorType
<
unknown
>>
(
id
:
string
,
options
?:
{
query
?:
Partial
<
UseInfiniteQueryOptions
<
Awaited
<
ReturnType
<
typeof
getNewsPageConfigCategoryId
>>
,
TError
,
TData
>>
,
request
?:
SecondParameter
<
typeof
useCustomClient
>
}
,
queryClient
?:
QueryClient
):
UseInfiniteQueryResult
<
TData
,
TError
>
&
{
queryKey
:
DataTag
<
QueryKey
,
TData
,
TError
>
}
{
const
queryOptions
=
getGetNewsPageConfigCategoryIdInfiniteQueryOptions
(
id
,
options
)
const
query
=
useInfiniteQuery
(
queryOptions
,
queryClient
)
as
UseInfiniteQueryResult
<
TData
,
TError
>
&
{
queryKey
:
DataTag
<
QueryKey
,
TData
,
TError
>
};
query
.
queryKey
=
queryOptions
.
queryKey
;
return
query
;
}
export
const
prefetchGetNewsPageConfigCategoryIdInfiniteQuery
=
async
<
TData
=
Awaited
<
ReturnType
<
typeof
getNewsPageConfigCategoryId
>>
,
TError
=
ErrorType
<
unknown
>>
(
queryClient
:
QueryClient
,
id
:
string
,
options
?:
{
query
?:
Partial
<
UseInfiniteQueryOptions
<
Awaited
<
ReturnType
<
typeof
getNewsPageConfigCategoryId
>>
,
TError
,
TData
>>
,
request
?:
SecondParameter
<
typeof
useCustomClient
>
}
):
Promise
<
QueryClient
>
=>
{
const
queryOptions
=
getGetNewsPageConfigCategoryIdInfiniteQueryOptions
(
id
,
options
)
await
queryClient
.
prefetchInfiniteQuery
(
queryOptions
);
return
queryClient
;
}
export
const
getGetNewsPageConfigCategoryIdQueryOptions
=
<
TData
=
Awaited
<
ReturnType
<
typeof
getNewsPageConfigCategoryId
>>
,
TError
=
ErrorType
<
unknown
>>
(
id
:
string
,
options
?:
{
query
?:
Partial
<
UseQueryOptions
<
Awaited
<
ReturnType
<
typeof
getNewsPageConfigCategoryId
>>
,
TError
,
TData
>>
,
request
?:
SecondParameter
<
typeof
useCustomClient
>
}
)
=>
{
const
{
query
:
queryOptions
,
request
:
requestOptions
}
=
options
??
{};
const
queryKey
=
queryOptions
?.
queryKey
??
getGetNewsPageConfigCategoryIdQueryKey
(
id
);
const
queryFn
:
QueryFunction
<
Awaited
<
ReturnType
<
typeof
getNewsPageConfigCategoryId
>>>
=
({
signal
})
=>
getNewsPageConfigCategoryId
(
id
,
{
signal
,
...
requestOptions
});
return
{
queryKey
,
queryFn
,
enabled
:
!!
(
id
),
...
queryOptions
}
as
UseQueryOptions
<
Awaited
<
ReturnType
<
typeof
getNewsPageConfigCategoryId
>>
,
TError
,
TData
>
&
{
queryKey
:
DataTag
<
QueryKey
,
TData
,
TError
>
}
}
export
type
GetNewsPageConfigCategoryIdQueryResult
=
NonNullable
<
Awaited
<
ReturnType
<
typeof
getNewsPageConfigCategoryId
>>>
export
type
GetNewsPageConfigCategoryIdQueryError
=
ErrorType
<
unknown
>
export
function
useGetNewsPageConfigCategoryId
<
TData
=
Awaited
<
ReturnType
<
typeof
getNewsPageConfigCategoryId
>>
,
TError
=
ErrorType
<
unknown
>>
(
id
:
string
,
options
:
{
query
:
Partial
<
UseQueryOptions
<
Awaited
<
ReturnType
<
typeof
getNewsPageConfigCategoryId
>>
,
TError
,
TData
>>
&
Pick
<
DefinedInitialDataOptions
<
Awaited
<
ReturnType
<
typeof
getNewsPageConfigCategoryId
>>
,
TError
,
Awaited
<
ReturnType
<
typeof
getNewsPageConfigCategoryId
>>
>
,
'initialData'
>
,
request
?:
SecondParameter
<
typeof
useCustomClient
>
}
,
queryClient
?:
QueryClient
):
DefinedUseQueryResult
<
TData
,
TError
>
&
{
queryKey
:
DataTag
<
QueryKey
,
TData
,
TError
>
}
export
function
useGetNewsPageConfigCategoryId
<
TData
=
Awaited
<
ReturnType
<
typeof
getNewsPageConfigCategoryId
>>
,
TError
=
ErrorType
<
unknown
>>
(
id
:
string
,
options
?:
{
query
?:
Partial
<
UseQueryOptions
<
Awaited
<
ReturnType
<
typeof
getNewsPageConfigCategoryId
>>
,
TError
,
TData
>>
&
Pick
<
UndefinedInitialDataOptions
<
Awaited
<
ReturnType
<
typeof
getNewsPageConfigCategoryId
>>
,
TError
,
Awaited
<
ReturnType
<
typeof
getNewsPageConfigCategoryId
>>
>
,
'initialData'
>
,
request
?:
SecondParameter
<
typeof
useCustomClient
>
}
,
queryClient
?:
QueryClient
):
UseQueryResult
<
TData
,
TError
>
&
{
queryKey
:
DataTag
<
QueryKey
,
TData
,
TError
>
}
export
function
useGetNewsPageConfigCategoryId
<
TData
=
Awaited
<
ReturnType
<
typeof
getNewsPageConfigCategoryId
>>
,
TError
=
ErrorType
<
unknown
>>
(
id
:
string
,
options
?:
{
query
?:
Partial
<
UseQueryOptions
<
Awaited
<
ReturnType
<
typeof
getNewsPageConfigCategoryId
>>
,
TError
,
TData
>>
,
request
?:
SecondParameter
<
typeof
useCustomClient
>
}
,
queryClient
?:
QueryClient
):
UseQueryResult
<
TData
,
TError
>
&
{
queryKey
:
DataTag
<
QueryKey
,
TData
,
TError
>
}
export
function
useGetNewsPageConfigCategoryId
<
TData
=
Awaited
<
ReturnType
<
typeof
getNewsPageConfigCategoryId
>>
,
TError
=
ErrorType
<
unknown
>>
(
id
:
string
,
options
?:
{
query
?:
Partial
<
UseQueryOptions
<
Awaited
<
ReturnType
<
typeof
getNewsPageConfigCategoryId
>>
,
TError
,
TData
>>
,
request
?:
SecondParameter
<
typeof
useCustomClient
>
}
,
queryClient
?:
QueryClient
):
UseQueryResult
<
TData
,
TError
>
&
{
queryKey
:
DataTag
<
QueryKey
,
TData
,
TError
>
}
{
const
queryOptions
=
getGetNewsPageConfigCategoryIdQueryOptions
(
id
,
options
)
const
query
=
useQuery
(
queryOptions
,
queryClient
)
as
UseQueryResult
<
TData
,
TError
>
&
{
queryKey
:
DataTag
<
QueryKey
,
TData
,
TError
>
};
query
.
queryKey
=
queryOptions
.
queryKey
;
return
query
;
}
export
const
prefetchGetNewsPageConfigCategoryIdQuery
=
async
<
TData
=
Awaited
<
ReturnType
<
typeof
getNewsPageConfigCategoryId
>>
,
TError
=
ErrorType
<
unknown
>>
(
queryClient
:
QueryClient
,
id
:
string
,
options
?:
{
query
?:
Partial
<
UseQueryOptions
<
Awaited
<
ReturnType
<
typeof
getNewsPageConfigCategoryId
>>
,
TError
,
TData
>>
,
request
?:
SecondParameter
<
typeof
useCustomClient
>
}
):
Promise
<
QueryClient
>
=>
{
const
queryOptions
=
getGetNewsPageConfigCategoryIdQueryOptions
(
id
,
options
)
await
queryClient
.
prefetchQuery
(
queryOptions
);
return
queryClient
;
}
/**
* Update page config category
*/
export
type
putNewsPageConfigCategoryIdResponse200
=
{
data
:
Response
status
:
200
}
export
type
putNewsPageConfigCategoryIdResponseSuccess
=
(
putNewsPageConfigCategoryIdResponse200
)
&
{
headers
:
Headers
;
};
;
export
type
putNewsPageConfigCategoryIdResponse
=
(
putNewsPageConfigCategoryIdResponseSuccess
)
export
const
getPutNewsPageConfigCategoryIdUrl
=
(
id
:
string
,)
=>
{
return
`/newsPageConfig/category/
${
id
}
`
}
export
const
putNewsPageConfigCategoryId
=
async
(
id
:
string
,
putNewsPageConfigCategoryIdBody
:
PutNewsPageConfigCategoryIdBody
,
options
?:
RequestInit
):
Promise
<
putNewsPageConfigCategoryIdResponse
>
=>
{
return
useCustomClient
<
putNewsPageConfigCategoryIdResponse
>
(
getPutNewsPageConfigCategoryIdUrl
(
id
),
{
...
options
,
method
:
'PUT'
,
headers
:
{
'Content-Type'
:
'application/json'
,
...
options
?.
headers
},
body
:
JSON
.
stringify
(
putNewsPageConfigCategoryIdBody
,)
}
);}
export
const
getPutNewsPageConfigCategoryIdMutationOptions
=
<
TError
=
ErrorType
<
unknown
>
,
TContext
=
unknown
>
(
options
?:
{
mutation
?:
UseMutationOptions
<
Awaited
<
ReturnType
<
typeof
putNewsPageConfigCategoryId
>>
,
TError
,{
id
:
string
;
data
:
BodyType
<
PutNewsPageConfigCategoryIdBody
>
},
TContext
>
,
request
?:
SecondParameter
<
typeof
useCustomClient
>
}
):
UseMutationOptions
<
Awaited
<
ReturnType
<
typeof
putNewsPageConfigCategoryId
>>
,
TError
,{
id
:
string
;
data
:
BodyType
<
PutNewsPageConfigCategoryIdBody
>
},
TContext
>
=>
{
const
mutationKey
=
[
'putNewsPageConfigCategoryId'
];
const
{
mutation
:
mutationOptions
,
request
:
requestOptions
}
=
options
?
options
.
mutation
&&
'mutationKey'
in
options
.
mutation
&&
options
.
mutation
.
mutationKey
?
options
:
{...
options
,
mutation
:
{...
options
.
mutation
,
mutationKey
}}
:
{
mutation
:
{
mutationKey
,
},
request
:
undefined
};
const
mutationFn
:
MutationFunction
<
Awaited
<
ReturnType
<
typeof
putNewsPageConfigCategoryId
>>
,
{
id
:
string
;
data
:
BodyType
<
PutNewsPageConfigCategoryIdBody
>
}
>
=
(
props
)
=>
{
const
{
id
,
data
}
=
props
??
{};
return
putNewsPageConfigCategoryId
(
id
,
data
,
requestOptions
)
}
return
{
mutationFn
,
...
mutationOptions
}}
export
type
PutNewsPageConfigCategoryIdMutationResult
=
NonNullable
<
Awaited
<
ReturnType
<
typeof
putNewsPageConfigCategoryId
>>>
export
type
PutNewsPageConfigCategoryIdMutationBody
=
BodyType
<
PutNewsPageConfigCategoryIdBody
>
export
type
PutNewsPageConfigCategoryIdMutationError
=
ErrorType
<
unknown
>
export
const
usePutNewsPageConfigCategoryId
=
<
TError
=
ErrorType
<
unknown
>
,
TContext
=
unknown
>
(
options
?:
{
mutation
?:
UseMutationOptions
<
Awaited
<
ReturnType
<
typeof
putNewsPageConfigCategoryId
>>
,
TError
,{
id
:
string
;
data
:
BodyType
<
PutNewsPageConfigCategoryIdBody
>
},
TContext
>
,
request
?:
SecondParameter
<
typeof
useCustomClient
>
}
,
queryClient
?:
QueryClient
):
UseMutationResult
<
Awaited
<
ReturnType
<
typeof
putNewsPageConfigCategoryId
>>
,
TError
,
{
id
:
string
;
data
:
BodyType
<
PutNewsPageConfigCategoryIdBody
>
},
TContext
>
=>
{
const
mutationOptions
=
getPutNewsPageConfigCategoryIdMutationOptions
(
options
);
return
useMutation
(
mutationOptions
,
queryClient
);
}
/**
* Get page config category
*/
export
type
getNewsPageConfigCategoryResponse200
=
{
data
:
Response
status
:
200
}
export
type
getNewsPageConfigCategoryResponseSuccess
=
(
getNewsPageConfigCategoryResponse200
)
&
{
headers
:
Headers
;
};
;
export
type
getNewsPageConfigCategoryResponse
=
(
getNewsPageConfigCategoryResponseSuccess
)
export
const
getGetNewsPageConfigCategoryUrl
=
()
=>
{
return
`/newsPageConfig/category`
}
export
const
getNewsPageConfigCategory
=
async
(
options
?:
RequestInit
):
Promise
<
getNewsPageConfigCategoryResponse
>
=>
{
return
useCustomClient
<
getNewsPageConfigCategoryResponse
>
(
getGetNewsPageConfigCategoryUrl
(),
{
...
options
,
method
:
'GET'
}
);}
export
const
getGetNewsPageConfigCategoryInfiniteQueryKey
=
()
=>
{
return
[
'infinite'
,
`/newsPageConfig/category`
]
as
const
;
}
export
const
getGetNewsPageConfigCategoryQueryKey
=
()
=>
{
return
[
`/newsPageConfig/category`
]
as
const
;
}
export
const
getGetNewsPageConfigCategoryInfiniteQueryOptions
=
<
TData
=
InfiniteData
<
Awaited
<
ReturnType
<
typeof
getNewsPageConfigCategory
>>>
,
TError
=
ErrorType
<
unknown
>>
(
options
?:
{
query
?:
Partial
<
UseInfiniteQueryOptions
<
Awaited
<
ReturnType
<
typeof
getNewsPageConfigCategory
>>
,
TError
,
TData
>>
,
request
?:
SecondParameter
<
typeof
useCustomClient
>
}
)
=>
{
const
{
query
:
queryOptions
,
request
:
requestOptions
}
=
options
??
{};
const
queryKey
=
queryOptions
?.
queryKey
??
getGetNewsPageConfigCategoryInfiniteQueryKey
();
const
queryFn
:
QueryFunction
<
Awaited
<
ReturnType
<
typeof
getNewsPageConfigCategory
>>>
=
({
signal
})
=>
getNewsPageConfigCategory
({
signal
,
...
requestOptions
});
return
{
queryKey
,
queryFn
,
...
queryOptions
}
as
UseInfiniteQueryOptions
<
Awaited
<
ReturnType
<
typeof
getNewsPageConfigCategory
>>
,
TError
,
TData
>
&
{
queryKey
:
DataTag
<
QueryKey
,
TData
,
TError
>
}
}
export
type
GetNewsPageConfigCategoryInfiniteQueryResult
=
NonNullable
<
Awaited
<
ReturnType
<
typeof
getNewsPageConfigCategory
>>>
export
type
GetNewsPageConfigCategoryInfiniteQueryError
=
ErrorType
<
unknown
>
export
function
useGetNewsPageConfigCategoryInfinite
<
TData
=
InfiniteData
<
Awaited
<
ReturnType
<
typeof
getNewsPageConfigCategory
>>>
,
TError
=
ErrorType
<
unknown
>>
(
options
:
{
query
:
Partial
<
UseInfiniteQueryOptions
<
Awaited
<
ReturnType
<
typeof
getNewsPageConfigCategory
>>
,
TError
,
TData
>>
&
Pick
<
DefinedInitialDataOptions
<
Awaited
<
ReturnType
<
typeof
getNewsPageConfigCategory
>>
,
TError
,
Awaited
<
ReturnType
<
typeof
getNewsPageConfigCategory
>>
>
,
'initialData'
>
,
request
?:
SecondParameter
<
typeof
useCustomClient
>
}
,
queryClient
?:
QueryClient
):
DefinedUseInfiniteQueryResult
<
TData
,
TError
>
&
{
queryKey
:
DataTag
<
QueryKey
,
TData
,
TError
>
}
export
function
useGetNewsPageConfigCategoryInfinite
<
TData
=
InfiniteData
<
Awaited
<
ReturnType
<
typeof
getNewsPageConfigCategory
>>>
,
TError
=
ErrorType
<
unknown
>>
(
options
?:
{
query
?:
Partial
<
UseInfiniteQueryOptions
<
Awaited
<
ReturnType
<
typeof
getNewsPageConfigCategory
>>
,
TError
,
TData
>>
&
Pick
<
UndefinedInitialDataOptions
<
Awaited
<
ReturnType
<
typeof
getNewsPageConfigCategory
>>
,
TError
,
Awaited
<
ReturnType
<
typeof
getNewsPageConfigCategory
>>
>
,
'initialData'
>
,
request
?:
SecondParameter
<
typeof
useCustomClient
>
}
,
queryClient
?:
QueryClient
):
UseInfiniteQueryResult
<
TData
,
TError
>
&
{
queryKey
:
DataTag
<
QueryKey
,
TData
,
TError
>
}
export
function
useGetNewsPageConfigCategoryInfinite
<
TData
=
InfiniteData
<
Awaited
<
ReturnType
<
typeof
getNewsPageConfigCategory
>>>
,
TError
=
ErrorType
<
unknown
>>
(
options
?:
{
query
?:
Partial
<
UseInfiniteQueryOptions
<
Awaited
<
ReturnType
<
typeof
getNewsPageConfigCategory
>>
,
TError
,
TData
>>
,
request
?:
SecondParameter
<
typeof
useCustomClient
>
}
,
queryClient
?:
QueryClient
):
UseInfiniteQueryResult
<
TData
,
TError
>
&
{
queryKey
:
DataTag
<
QueryKey
,
TData
,
TError
>
}
export
function
useGetNewsPageConfigCategoryInfinite
<
TData
=
InfiniteData
<
Awaited
<
ReturnType
<
typeof
getNewsPageConfigCategory
>>>
,
TError
=
ErrorType
<
unknown
>>
(
options
?:
{
query
?:
Partial
<
UseInfiniteQueryOptions
<
Awaited
<
ReturnType
<
typeof
getNewsPageConfigCategory
>>
,
TError
,
TData
>>
,
request
?:
SecondParameter
<
typeof
useCustomClient
>
}
,
queryClient
?:
QueryClient
):
UseInfiniteQueryResult
<
TData
,
TError
>
&
{
queryKey
:
DataTag
<
QueryKey
,
TData
,
TError
>
}
{
const
queryOptions
=
getGetNewsPageConfigCategoryInfiniteQueryOptions
(
options
)
const
query
=
useInfiniteQuery
(
queryOptions
,
queryClient
)
as
UseInfiniteQueryResult
<
TData
,
TError
>
&
{
queryKey
:
DataTag
<
QueryKey
,
TData
,
TError
>
};
query
.
queryKey
=
queryOptions
.
queryKey
;
return
query
;
}
export
const
prefetchGetNewsPageConfigCategoryInfiniteQuery
=
async
<
TData
=
Awaited
<
ReturnType
<
typeof
getNewsPageConfigCategory
>>
,
TError
=
ErrorType
<
unknown
>>
(
queryClient
:
QueryClient
,
options
?:
{
query
?:
Partial
<
UseInfiniteQueryOptions
<
Awaited
<
ReturnType
<
typeof
getNewsPageConfigCategory
>>
,
TError
,
TData
>>
,
request
?:
SecondParameter
<
typeof
useCustomClient
>
}
):
Promise
<
QueryClient
>
=>
{
const
queryOptions
=
getGetNewsPageConfigCategoryInfiniteQueryOptions
(
options
)
await
queryClient
.
prefetchInfiniteQuery
(
queryOptions
);
return
queryClient
;
}
export
const
getGetNewsPageConfigCategoryQueryOptions
=
<
TData
=
Awaited
<
ReturnType
<
typeof
getNewsPageConfigCategory
>>
,
TError
=
ErrorType
<
unknown
>>
(
options
?:
{
query
?:
Partial
<
UseQueryOptions
<
Awaited
<
ReturnType
<
typeof
getNewsPageConfigCategory
>>
,
TError
,
TData
>>
,
request
?:
SecondParameter
<
typeof
useCustomClient
>
}
)
=>
{
const
{
query
:
queryOptions
,
request
:
requestOptions
}
=
options
??
{};
const
queryKey
=
queryOptions
?.
queryKey
??
getGetNewsPageConfigCategoryQueryKey
();
const
queryFn
:
QueryFunction
<
Awaited
<
ReturnType
<
typeof
getNewsPageConfigCategory
>>>
=
({
signal
})
=>
getNewsPageConfigCategory
({
signal
,
...
requestOptions
});
return
{
queryKey
,
queryFn
,
...
queryOptions
}
as
UseQueryOptions
<
Awaited
<
ReturnType
<
typeof
getNewsPageConfigCategory
>>
,
TError
,
TData
>
&
{
queryKey
:
DataTag
<
QueryKey
,
TData
,
TError
>
}
}
export
type
GetNewsPageConfigCategoryQueryResult
=
NonNullable
<
Awaited
<
ReturnType
<
typeof
getNewsPageConfigCategory
>>>
export
type
GetNewsPageConfigCategoryQueryError
=
ErrorType
<
unknown
>
export
function
useGetNewsPageConfigCategory
<
TData
=
Awaited
<
ReturnType
<
typeof
getNewsPageConfigCategory
>>
,
TError
=
ErrorType
<
unknown
>>
(
options
:
{
query
:
Partial
<
UseQueryOptions
<
Awaited
<
ReturnType
<
typeof
getNewsPageConfigCategory
>>
,
TError
,
TData
>>
&
Pick
<
DefinedInitialDataOptions
<
Awaited
<
ReturnType
<
typeof
getNewsPageConfigCategory
>>
,
TError
,
Awaited
<
ReturnType
<
typeof
getNewsPageConfigCategory
>>
>
,
'initialData'
>
,
request
?:
SecondParameter
<
typeof
useCustomClient
>
}
,
queryClient
?:
QueryClient
):
DefinedUseQueryResult
<
TData
,
TError
>
&
{
queryKey
:
DataTag
<
QueryKey
,
TData
,
TError
>
}
export
function
useGetNewsPageConfigCategory
<
TData
=
Awaited
<
ReturnType
<
typeof
getNewsPageConfigCategory
>>
,
TError
=
ErrorType
<
unknown
>>
(
options
?:
{
query
?:
Partial
<
UseQueryOptions
<
Awaited
<
ReturnType
<
typeof
getNewsPageConfigCategory
>>
,
TError
,
TData
>>
&
Pick
<
UndefinedInitialDataOptions
<
Awaited
<
ReturnType
<
typeof
getNewsPageConfigCategory
>>
,
TError
,
Awaited
<
ReturnType
<
typeof
getNewsPageConfigCategory
>>
>
,
'initialData'
>
,
request
?:
SecondParameter
<
typeof
useCustomClient
>
}
,
queryClient
?:
QueryClient
):
UseQueryResult
<
TData
,
TError
>
&
{
queryKey
:
DataTag
<
QueryKey
,
TData
,
TError
>
}
export
function
useGetNewsPageConfigCategory
<
TData
=
Awaited
<
ReturnType
<
typeof
getNewsPageConfigCategory
>>
,
TError
=
ErrorType
<
unknown
>>
(
options
?:
{
query
?:
Partial
<
UseQueryOptions
<
Awaited
<
ReturnType
<
typeof
getNewsPageConfigCategory
>>
,
TError
,
TData
>>
,
request
?:
SecondParameter
<
typeof
useCustomClient
>
}
,
queryClient
?:
QueryClient
):
UseQueryResult
<
TData
,
TError
>
&
{
queryKey
:
DataTag
<
QueryKey
,
TData
,
TError
>
}
export
function
useGetNewsPageConfigCategory
<
TData
=
Awaited
<
ReturnType
<
typeof
getNewsPageConfigCategory
>>
,
TError
=
ErrorType
<
unknown
>>
(
options
?:
{
query
?:
Partial
<
UseQueryOptions
<
Awaited
<
ReturnType
<
typeof
getNewsPageConfigCategory
>>
,
TError
,
TData
>>
,
request
?:
SecondParameter
<
typeof
useCustomClient
>
}
,
queryClient
?:
QueryClient
):
UseQueryResult
<
TData
,
TError
>
&
{
queryKey
:
DataTag
<
QueryKey
,
TData
,
TError
>
}
{
const
queryOptions
=
getGetNewsPageConfigCategoryQueryOptions
(
options
)
const
query
=
useQuery
(
queryOptions
,
queryClient
)
as
UseQueryResult
<
TData
,
TError
>
&
{
queryKey
:
DataTag
<
QueryKey
,
TData
,
TError
>
};
query
.
queryKey
=
queryOptions
.
queryKey
;
return
query
;
}
export
const
prefetchGetNewsPageConfigCategoryQuery
=
async
<
TData
=
Awaited
<
ReturnType
<
typeof
getNewsPageConfigCategory
>>
,
TError
=
ErrorType
<
unknown
>>
(
queryClient
:
QueryClient
,
options
?:
{
query
?:
Partial
<
UseQueryOptions
<
Awaited
<
ReturnType
<
typeof
getNewsPageConfigCategory
>>
,
TError
,
TData
>>
,
request
?:
SecondParameter
<
typeof
useCustomClient
>
}
):
Promise
<
QueryClient
>
=>
{
const
queryOptions
=
getGetNewsPageConfigCategoryQueryOptions
(
options
)
await
queryClient
.
prefetchQuery
(
queryOptions
);
return
queryClient
;
}
/**
* Get
*/
export
type
getNewsPageConfigGetAllLinksResponse200
=
{
data
:
Response
status
:
200
}
export
type
getNewsPageConfigGetAllLinksResponseSuccess
=
(
getNewsPageConfigGetAllLinksResponse200
)
&
{
headers
:
Headers
;
};
;
export
type
getNewsPageConfigGetAllLinksResponse
=
(
getNewsPageConfigGetAllLinksResponseSuccess
)
export
const
getGetNewsPageConfigGetAllLinksUrl
=
()
=>
{
return
`/newsPageConfig/getAllLinks`
}
export
const
getNewsPageConfigGetAllLinks
=
async
(
options
?:
RequestInit
):
Promise
<
getNewsPageConfigGetAllLinksResponse
>
=>
{
return
useCustomClient
<
getNewsPageConfigGetAllLinksResponse
>
(
getGetNewsPageConfigGetAllLinksUrl
(),
{
...
options
,
method
:
'GET'
}
);}
export
const
getGetNewsPageConfigGetAllLinksInfiniteQueryKey
=
()
=>
{
return
[
'infinite'
,
`/newsPageConfig/getAllLinks`
]
as
const
;
}
export
const
getGetNewsPageConfigGetAllLinksQueryKey
=
()
=>
{
return
[
`/newsPageConfig/getAllLinks`
]
as
const
;
}
export
const
getGetNewsPageConfigGetAllLinksInfiniteQueryOptions
=
<
TData
=
InfiniteData
<
Awaited
<
ReturnType
<
typeof
getNewsPageConfigGetAllLinks
>>>
,
TError
=
ErrorType
<
unknown
>>
(
options
?:
{
query
?:
Partial
<
UseInfiniteQueryOptions
<
Awaited
<
ReturnType
<
typeof
getNewsPageConfigGetAllLinks
>>
,
TError
,
TData
>>
,
request
?:
SecondParameter
<
typeof
useCustomClient
>
}
)
=>
{
const
{
query
:
queryOptions
,
request
:
requestOptions
}
=
options
??
{};
const
queryKey
=
queryOptions
?.
queryKey
??
getGetNewsPageConfigGetAllLinksInfiniteQueryKey
();
const
queryFn
:
QueryFunction
<
Awaited
<
ReturnType
<
typeof
getNewsPageConfigGetAllLinks
>>>
=
({
signal
})
=>
getNewsPageConfigGetAllLinks
({
signal
,
...
requestOptions
});
return
{
queryKey
,
queryFn
,
...
queryOptions
}
as
UseInfiniteQueryOptions
<
Awaited
<
ReturnType
<
typeof
getNewsPageConfigGetAllLinks
>>
,
TError
,
TData
>
&
{
queryKey
:
DataTag
<
QueryKey
,
TData
,
TError
>
}
}
export
type
GetNewsPageConfigGetAllLinksInfiniteQueryResult
=
NonNullable
<
Awaited
<
ReturnType
<
typeof
getNewsPageConfigGetAllLinks
>>>
export
type
GetNewsPageConfigGetAllLinksInfiniteQueryError
=
ErrorType
<
unknown
>
export
function
useGetNewsPageConfigGetAllLinksInfinite
<
TData
=
InfiniteData
<
Awaited
<
ReturnType
<
typeof
getNewsPageConfigGetAllLinks
>>>
,
TError
=
ErrorType
<
unknown
>>
(
options
:
{
query
:
Partial
<
UseInfiniteQueryOptions
<
Awaited
<
ReturnType
<
typeof
getNewsPageConfigGetAllLinks
>>
,
TError
,
TData
>>
&
Pick
<
DefinedInitialDataOptions
<
Awaited
<
ReturnType
<
typeof
getNewsPageConfigGetAllLinks
>>
,
TError
,
Awaited
<
ReturnType
<
typeof
getNewsPageConfigGetAllLinks
>>
>
,
'initialData'
>
,
request
?:
SecondParameter
<
typeof
useCustomClient
>
}
,
queryClient
?:
QueryClient
):
DefinedUseInfiniteQueryResult
<
TData
,
TError
>
&
{
queryKey
:
DataTag
<
QueryKey
,
TData
,
TError
>
}
export
function
useGetNewsPageConfigGetAllLinksInfinite
<
TData
=
InfiniteData
<
Awaited
<
ReturnType
<
typeof
getNewsPageConfigGetAllLinks
>>>
,
TError
=
ErrorType
<
unknown
>>
(
options
?:
{
query
?:
Partial
<
UseInfiniteQueryOptions
<
Awaited
<
ReturnType
<
typeof
getNewsPageConfigGetAllLinks
>>
,
TError
,
TData
>>
&
Pick
<
UndefinedInitialDataOptions
<
Awaited
<
ReturnType
<
typeof
getNewsPageConfigGetAllLinks
>>
,
TError
,
Awaited
<
ReturnType
<
typeof
getNewsPageConfigGetAllLinks
>>
>
,
'initialData'
>
,
request
?:
SecondParameter
<
typeof
useCustomClient
>
}
,
queryClient
?:
QueryClient
):
UseInfiniteQueryResult
<
TData
,
TError
>
&
{
queryKey
:
DataTag
<
QueryKey
,
TData
,
TError
>
}
export
function
useGetNewsPageConfigGetAllLinksInfinite
<
TData
=
InfiniteData
<
Awaited
<
ReturnType
<
typeof
getNewsPageConfigGetAllLinks
>>>
,
TError
=
ErrorType
<
unknown
>>
(
options
?:
{
query
?:
Partial
<
UseInfiniteQueryOptions
<
Awaited
<
ReturnType
<
typeof
getNewsPageConfigGetAllLinks
>>
,
TError
,
TData
>>
,
request
?:
SecondParameter
<
typeof
useCustomClient
>
}
,
queryClient
?:
QueryClient
):
UseInfiniteQueryResult
<
TData
,
TError
>
&
{
queryKey
:
DataTag
<
QueryKey
,
TData
,
TError
>
}
export
function
useGetNewsPageConfigGetAllLinksInfinite
<
TData
=
InfiniteData
<
Awaited
<
ReturnType
<
typeof
getNewsPageConfigGetAllLinks
>>>
,
TError
=
ErrorType
<
unknown
>>
(
options
?:
{
query
?:
Partial
<
UseInfiniteQueryOptions
<
Awaited
<
ReturnType
<
typeof
getNewsPageConfigGetAllLinks
>>
,
TError
,
TData
>>
,
request
?:
SecondParameter
<
typeof
useCustomClient
>
}
,
queryClient
?:
QueryClient
):
UseInfiniteQueryResult
<
TData
,
TError
>
&
{
queryKey
:
DataTag
<
QueryKey
,
TData
,
TError
>
}
{
const
queryOptions
=
getGetNewsPageConfigGetAllLinksInfiniteQueryOptions
(
options
)
const
query
=
useInfiniteQuery
(
queryOptions
,
queryClient
)
as
UseInfiniteQueryResult
<
TData
,
TError
>
&
{
queryKey
:
DataTag
<
QueryKey
,
TData
,
TError
>
};
query
.
queryKey
=
queryOptions
.
queryKey
;
return
query
;
}
export
const
prefetchGetNewsPageConfigGetAllLinksInfiniteQuery
=
async
<
TData
=
Awaited
<
ReturnType
<
typeof
getNewsPageConfigGetAllLinks
>>
,
TError
=
ErrorType
<
unknown
>>
(
queryClient
:
QueryClient
,
options
?:
{
query
?:
Partial
<
UseInfiniteQueryOptions
<
Awaited
<
ReturnType
<
typeof
getNewsPageConfigGetAllLinks
>>
,
TError
,
TData
>>
,
request
?:
SecondParameter
<
typeof
useCustomClient
>
}
):
Promise
<
QueryClient
>
=>
{
const
queryOptions
=
getGetNewsPageConfigGetAllLinksInfiniteQueryOptions
(
options
)
await
queryClient
.
prefetchInfiniteQuery
(
queryOptions
);
return
queryClient
;
}
export
const
getGetNewsPageConfigGetAllLinksQueryOptions
=
<
TData
=
Awaited
<
ReturnType
<
typeof
getNewsPageConfigGetAllLinks
>>
,
TError
=
ErrorType
<
unknown
>>
(
options
?:
{
query
?:
Partial
<
UseQueryOptions
<
Awaited
<
ReturnType
<
typeof
getNewsPageConfigGetAllLinks
>>
,
TError
,
TData
>>
,
request
?:
SecondParameter
<
typeof
useCustomClient
>
}
)
=>
{
const
{
query
:
queryOptions
,
request
:
requestOptions
}
=
options
??
{};
const
queryKey
=
queryOptions
?.
queryKey
??
getGetNewsPageConfigGetAllLinksQueryKey
();
const
queryFn
:
QueryFunction
<
Awaited
<
ReturnType
<
typeof
getNewsPageConfigGetAllLinks
>>>
=
({
signal
})
=>
getNewsPageConfigGetAllLinks
({
signal
,
...
requestOptions
});
return
{
queryKey
,
queryFn
,
...
queryOptions
}
as
UseQueryOptions
<
Awaited
<
ReturnType
<
typeof
getNewsPageConfigGetAllLinks
>>
,
TError
,
TData
>
&
{
queryKey
:
DataTag
<
QueryKey
,
TData
,
TError
>
}
}
export
type
GetNewsPageConfigGetAllLinksQueryResult
=
NonNullable
<
Awaited
<
ReturnType
<
typeof
getNewsPageConfigGetAllLinks
>>>
export
type
GetNewsPageConfigGetAllLinksQueryError
=
ErrorType
<
unknown
>
export
function
useGetNewsPageConfigGetAllLinks
<
TData
=
Awaited
<
ReturnType
<
typeof
getNewsPageConfigGetAllLinks
>>
,
TError
=
ErrorType
<
unknown
>>
(
options
:
{
query
:
Partial
<
UseQueryOptions
<
Awaited
<
ReturnType
<
typeof
getNewsPageConfigGetAllLinks
>>
,
TError
,
TData
>>
&
Pick
<
DefinedInitialDataOptions
<
Awaited
<
ReturnType
<
typeof
getNewsPageConfigGetAllLinks
>>
,
TError
,
Awaited
<
ReturnType
<
typeof
getNewsPageConfigGetAllLinks
>>
>
,
'initialData'
>
,
request
?:
SecondParameter
<
typeof
useCustomClient
>
}
,
queryClient
?:
QueryClient
):
DefinedUseQueryResult
<
TData
,
TError
>
&
{
queryKey
:
DataTag
<
QueryKey
,
TData
,
TError
>
}
export
function
useGetNewsPageConfigGetAllLinks
<
TData
=
Awaited
<
ReturnType
<
typeof
getNewsPageConfigGetAllLinks
>>
,
TError
=
ErrorType
<
unknown
>>
(
options
?:
{
query
?:
Partial
<
UseQueryOptions
<
Awaited
<
ReturnType
<
typeof
getNewsPageConfigGetAllLinks
>>
,
TError
,
TData
>>
&
Pick
<
UndefinedInitialDataOptions
<
Awaited
<
ReturnType
<
typeof
getNewsPageConfigGetAllLinks
>>
,
TError
,
Awaited
<
ReturnType
<
typeof
getNewsPageConfigGetAllLinks
>>
>
,
'initialData'
>
,
request
?:
SecondParameter
<
typeof
useCustomClient
>
}
,
queryClient
?:
QueryClient
):
UseQueryResult
<
TData
,
TError
>
&
{
queryKey
:
DataTag
<
QueryKey
,
TData
,
TError
>
}
export
function
useGetNewsPageConfigGetAllLinks
<
TData
=
Awaited
<
ReturnType
<
typeof
getNewsPageConfigGetAllLinks
>>
,
TError
=
ErrorType
<
unknown
>>
(
options
?:
{
query
?:
Partial
<
UseQueryOptions
<
Awaited
<
ReturnType
<
typeof
getNewsPageConfigGetAllLinks
>>
,
TError
,
TData
>>
,
request
?:
SecondParameter
<
typeof
useCustomClient
>
}
,
queryClient
?:
QueryClient
):
UseQueryResult
<
TData
,
TError
>
&
{
queryKey
:
DataTag
<
QueryKey
,
TData
,
TError
>
}
export
function
useGetNewsPageConfigGetAllLinks
<
TData
=
Awaited
<
ReturnType
<
typeof
getNewsPageConfigGetAllLinks
>>
,
TError
=
ErrorType
<
unknown
>>
(
options
?:
{
query
?:
Partial
<
UseQueryOptions
<
Awaited
<
ReturnType
<
typeof
getNewsPageConfigGetAllLinks
>>
,
TError
,
TData
>>
,
request
?:
SecondParameter
<
typeof
useCustomClient
>
}
,
queryClient
?:
QueryClient
):
UseQueryResult
<
TData
,
TError
>
&
{
queryKey
:
DataTag
<
QueryKey
,
TData
,
TError
>
}
{
const
queryOptions
=
getGetNewsPageConfigGetAllLinksQueryOptions
(
options
)
const
query
=
useQuery
(
queryOptions
,
queryClient
)
as
UseQueryResult
<
TData
,
TError
>
&
{
queryKey
:
DataTag
<
QueryKey
,
TData
,
TError
>
};
query
.
queryKey
=
queryOptions
.
queryKey
;
return
query
;
}
export
const
prefetchGetNewsPageConfigGetAllLinksQuery
=
async
<
TData
=
Awaited
<
ReturnType
<
typeof
getNewsPageConfigGetAllLinks
>>
,
TError
=
ErrorType
<
unknown
>>
(
queryClient
:
QueryClient
,
options
?:
{
query
?:
Partial
<
UseQueryOptions
<
Awaited
<
ReturnType
<
typeof
getNewsPageConfigGetAllLinks
>>
,
TError
,
TData
>>
,
request
?:
SecondParameter
<
typeof
useCustomClient
>
}
):
Promise
<
QueryClient
>
=>
{
const
queryOptions
=
getGetNewsPageConfigGetAllLinksQueryOptions
(
options
)
await
queryClient
.
prefetchQuery
(
queryOptions
);
return
queryClient
;
}
/**
* Get
*/
export
type
getNewsPageConfigResponse200
=
{
data
:
Response
status
:
200
}
export
type
getNewsPageConfigResponseSuccess
=
(
getNewsPageConfigResponse200
)
&
{
headers
:
Headers
;
};
;
export
type
getNewsPageConfigResponse
=
(
getNewsPageConfigResponseSuccess
)
export
const
getGetNewsPageConfigUrl
=
()
=>
{
return
`/newsPageConfig`
}
export
const
getNewsPageConfig
=
async
(
options
?:
RequestInit
):
Promise
<
getNewsPageConfigResponse
>
=>
{
return
useCustomClient
<
getNewsPageConfigResponse
>
(
getGetNewsPageConfigUrl
(),
{
...
options
,
method
:
'GET'
}
);}
export
const
getGetNewsPageConfigInfiniteQueryKey
=
()
=>
{
return
[
'infinite'
,
`/newsPageConfig`
]
as
const
;
}
export
const
getGetNewsPageConfigQueryKey
=
()
=>
{
return
[
`/newsPageConfig`
]
as
const
;
}
export
const
getGetNewsPageConfigInfiniteQueryOptions
=
<
TData
=
InfiniteData
<
Awaited
<
ReturnType
<
typeof
getNewsPageConfig
>>>
,
TError
=
ErrorType
<
unknown
>>
(
options
?:
{
query
?:
Partial
<
UseInfiniteQueryOptions
<
Awaited
<
ReturnType
<
typeof
getNewsPageConfig
>>
,
TError
,
TData
>>
,
request
?:
SecondParameter
<
typeof
useCustomClient
>
}
)
=>
{
const
{
query
:
queryOptions
,
request
:
requestOptions
}
=
options
??
{};
const
queryKey
=
queryOptions
?.
queryKey
??
getGetNewsPageConfigInfiniteQueryKey
();
const
queryFn
:
QueryFunction
<
Awaited
<
ReturnType
<
typeof
getNewsPageConfig
>>>
=
({
signal
})
=>
getNewsPageConfig
({
signal
,
...
requestOptions
});
return
{
queryKey
,
queryFn
,
...
queryOptions
}
as
UseInfiniteQueryOptions
<
Awaited
<
ReturnType
<
typeof
getNewsPageConfig
>>
,
TError
,
TData
>
&
{
queryKey
:
DataTag
<
QueryKey
,
TData
,
TError
>
}
}
export
type
GetNewsPageConfigInfiniteQueryResult
=
NonNullable
<
Awaited
<
ReturnType
<
typeof
getNewsPageConfig
>>>
export
type
GetNewsPageConfigInfiniteQueryError
=
ErrorType
<
unknown
>
export
function
useGetNewsPageConfigInfinite
<
TData
=
InfiniteData
<
Awaited
<
ReturnType
<
typeof
getNewsPageConfig
>>>
,
TError
=
ErrorType
<
unknown
>>
(
options
:
{
query
:
Partial
<
UseInfiniteQueryOptions
<
Awaited
<
ReturnType
<
typeof
getNewsPageConfig
>>
,
TError
,
TData
>>
&
Pick
<
DefinedInitialDataOptions
<
Awaited
<
ReturnType
<
typeof
getNewsPageConfig
>>
,
TError
,
Awaited
<
ReturnType
<
typeof
getNewsPageConfig
>>
>
,
'initialData'
>
,
request
?:
SecondParameter
<
typeof
useCustomClient
>
}
,
queryClient
?:
QueryClient
):
DefinedUseInfiniteQueryResult
<
TData
,
TError
>
&
{
queryKey
:
DataTag
<
QueryKey
,
TData
,
TError
>
}
export
function
useGetNewsPageConfigInfinite
<
TData
=
InfiniteData
<
Awaited
<
ReturnType
<
typeof
getNewsPageConfig
>>>
,
TError
=
ErrorType
<
unknown
>>
(
options
?:
{
query
?:
Partial
<
UseInfiniteQueryOptions
<
Awaited
<
ReturnType
<
typeof
getNewsPageConfig
>>
,
TError
,
TData
>>
&
Pick
<
UndefinedInitialDataOptions
<
Awaited
<
ReturnType
<
typeof
getNewsPageConfig
>>
,
TError
,
Awaited
<
ReturnType
<
typeof
getNewsPageConfig
>>
>
,
'initialData'
>
,
request
?:
SecondParameter
<
typeof
useCustomClient
>
}
,
queryClient
?:
QueryClient
):
UseInfiniteQueryResult
<
TData
,
TError
>
&
{
queryKey
:
DataTag
<
QueryKey
,
TData
,
TError
>
}
export
function
useGetNewsPageConfigInfinite
<
TData
=
InfiniteData
<
Awaited
<
ReturnType
<
typeof
getNewsPageConfig
>>>
,
TError
=
ErrorType
<
unknown
>>
(
options
?:
{
query
?:
Partial
<
UseInfiniteQueryOptions
<
Awaited
<
ReturnType
<
typeof
getNewsPageConfig
>>
,
TError
,
TData
>>
,
request
?:
SecondParameter
<
typeof
useCustomClient
>
}
,
queryClient
?:
QueryClient
):
UseInfiniteQueryResult
<
TData
,
TError
>
&
{
queryKey
:
DataTag
<
QueryKey
,
TData
,
TError
>
}
export
function
useGetNewsPageConfigInfinite
<
TData
=
InfiniteData
<
Awaited
<
ReturnType
<
typeof
getNewsPageConfig
>>>
,
TError
=
ErrorType
<
unknown
>>
(
options
?:
{
query
?:
Partial
<
UseInfiniteQueryOptions
<
Awaited
<
ReturnType
<
typeof
getNewsPageConfig
>>
,
TError
,
TData
>>
,
request
?:
SecondParameter
<
typeof
useCustomClient
>
}
,
queryClient
?:
QueryClient
):
UseInfiniteQueryResult
<
TData
,
TError
>
&
{
queryKey
:
DataTag
<
QueryKey
,
TData
,
TError
>
}
{
const
queryOptions
=
getGetNewsPageConfigInfiniteQueryOptions
(
options
)
const
query
=
useInfiniteQuery
(
queryOptions
,
queryClient
)
as
UseInfiniteQueryResult
<
TData
,
TError
>
&
{
queryKey
:
DataTag
<
QueryKey
,
TData
,
TError
>
};
query
.
queryKey
=
queryOptions
.
queryKey
;
return
query
;
}
export
const
prefetchGetNewsPageConfigInfiniteQuery
=
async
<
TData
=
Awaited
<
ReturnType
<
typeof
getNewsPageConfig
>>
,
TError
=
ErrorType
<
unknown
>>
(
queryClient
:
QueryClient
,
options
?:
{
query
?:
Partial
<
UseInfiniteQueryOptions
<
Awaited
<
ReturnType
<
typeof
getNewsPageConfig
>>
,
TError
,
TData
>>
,
request
?:
SecondParameter
<
typeof
useCustomClient
>
}
):
Promise
<
QueryClient
>
=>
{
const
queryOptions
=
getGetNewsPageConfigInfiniteQueryOptions
(
options
)
await
queryClient
.
prefetchInfiniteQuery
(
queryOptions
);
return
queryClient
;
}
export
const
getGetNewsPageConfigQueryOptions
=
<
TData
=
Awaited
<
ReturnType
<
typeof
getNewsPageConfig
>>
,
TError
=
ErrorType
<
unknown
>>
(
options
?:
{
query
?:
Partial
<
UseQueryOptions
<
Awaited
<
ReturnType
<
typeof
getNewsPageConfig
>>
,
TError
,
TData
>>
,
request
?:
SecondParameter
<
typeof
useCustomClient
>
}
)
=>
{
const
{
query
:
queryOptions
,
request
:
requestOptions
}
=
options
??
{};
const
queryKey
=
queryOptions
?.
queryKey
??
getGetNewsPageConfigQueryKey
();
const
queryFn
:
QueryFunction
<
Awaited
<
ReturnType
<
typeof
getNewsPageConfig
>>>
=
({
signal
})
=>
getNewsPageConfig
({
signal
,
...
requestOptions
});
return
{
queryKey
,
queryFn
,
...
queryOptions
}
as
UseQueryOptions
<
Awaited
<
ReturnType
<
typeof
getNewsPageConfig
>>
,
TError
,
TData
>
&
{
queryKey
:
DataTag
<
QueryKey
,
TData
,
TError
>
}
}
export
type
GetNewsPageConfigQueryResult
=
NonNullable
<
Awaited
<
ReturnType
<
typeof
getNewsPageConfig
>>>
export
type
GetNewsPageConfigQueryError
=
ErrorType
<
unknown
>
export
function
useGetNewsPageConfig
<
TData
=
Awaited
<
ReturnType
<
typeof
getNewsPageConfig
>>
,
TError
=
ErrorType
<
unknown
>>
(
options
:
{
query
:
Partial
<
UseQueryOptions
<
Awaited
<
ReturnType
<
typeof
getNewsPageConfig
>>
,
TError
,
TData
>>
&
Pick
<
DefinedInitialDataOptions
<
Awaited
<
ReturnType
<
typeof
getNewsPageConfig
>>
,
TError
,
Awaited
<
ReturnType
<
typeof
getNewsPageConfig
>>
>
,
'initialData'
>
,
request
?:
SecondParameter
<
typeof
useCustomClient
>
}
,
queryClient
?:
QueryClient
):
DefinedUseQueryResult
<
TData
,
TError
>
&
{
queryKey
:
DataTag
<
QueryKey
,
TData
,
TError
>
}
export
function
useGetNewsPageConfig
<
TData
=
Awaited
<
ReturnType
<
typeof
getNewsPageConfig
>>
,
TError
=
ErrorType
<
unknown
>>
(
options
?:
{
query
?:
Partial
<
UseQueryOptions
<
Awaited
<
ReturnType
<
typeof
getNewsPageConfig
>>
,
TError
,
TData
>>
&
Pick
<
UndefinedInitialDataOptions
<
Awaited
<
ReturnType
<
typeof
getNewsPageConfig
>>
,
TError
,
Awaited
<
ReturnType
<
typeof
getNewsPageConfig
>>
>
,
'initialData'
>
,
request
?:
SecondParameter
<
typeof
useCustomClient
>
}
,
queryClient
?:
QueryClient
):
UseQueryResult
<
TData
,
TError
>
&
{
queryKey
:
DataTag
<
QueryKey
,
TData
,
TError
>
}
export
function
useGetNewsPageConfig
<
TData
=
Awaited
<
ReturnType
<
typeof
getNewsPageConfig
>>
,
TError
=
ErrorType
<
unknown
>>
(
options
?:
{
query
?:
Partial
<
UseQueryOptions
<
Awaited
<
ReturnType
<
typeof
getNewsPageConfig
>>
,
TError
,
TData
>>
,
request
?:
SecondParameter
<
typeof
useCustomClient
>
}
,
queryClient
?:
QueryClient
):
UseQueryResult
<
TData
,
TError
>
&
{
queryKey
:
DataTag
<
QueryKey
,
TData
,
TError
>
}
export
function
useGetNewsPageConfig
<
TData
=
Awaited
<
ReturnType
<
typeof
getNewsPageConfig
>>
,
TError
=
ErrorType
<
unknown
>>
(
options
?:
{
query
?:
Partial
<
UseQueryOptions
<
Awaited
<
ReturnType
<
typeof
getNewsPageConfig
>>
,
TError
,
TData
>>
,
request
?:
SecondParameter
<
typeof
useCustomClient
>
}
,
queryClient
?:
QueryClient
):
UseQueryResult
<
TData
,
TError
>
&
{
queryKey
:
DataTag
<
QueryKey
,
TData
,
TError
>
}
{
const
queryOptions
=
getGetNewsPageConfigQueryOptions
(
options
)
const
query
=
useQuery
(
queryOptions
,
queryClient
)
as
UseQueryResult
<
TData
,
TError
>
&
{
queryKey
:
DataTag
<
QueryKey
,
TData
,
TError
>
};
query
.
queryKey
=
queryOptions
.
queryKey
;
return
query
;
}
export
const
prefetchGetNewsPageConfigQuery
=
async
<
TData
=
Awaited
<
ReturnType
<
typeof
getNewsPageConfig
>>
,
TError
=
ErrorType
<
unknown
>>
(
queryClient
:
QueryClient
,
options
?:
{
query
?:
Partial
<
UseQueryOptions
<
Awaited
<
ReturnType
<
typeof
getNewsPageConfig
>>
,
TError
,
TData
>>
,
request
?:
SecondParameter
<
typeof
useCustomClient
>
}
):
Promise
<
QueryClient
>
=>
{
const
queryOptions
=
getGetNewsPageConfigQueryOptions
(
options
)
await
queryClient
.
prefetchQuery
(
queryOptions
);
return
queryClient
;
}
src/api/models/index.ts
View file @
a99fbda4
...
...
@@ -140,6 +140,7 @@ export * from './putEventsLinkParams';
export
*
from
'./putEventsParams'
;
export
*
from
'./putFooterParams'
;
export
*
from
'./putMembershipFeeParams'
;
export
*
from
'./putNewsPageConfigCategoryIdBody'
;
export
*
from
'./putNotificationsMarkAsReadParams'
;
export
*
from
'./putNotificationsParams'
;
export
*
from
'./putOrderPaymentParams'
;
...
...
src/api/models/putNewsPageConfigCategoryIdBody.ts
0 → 100644
View file @
a99fbda4
/**
* Generated by orval v8.0.0-rc.0 🍺
* Do not edit manually.
* VCCI
* Coded by Meu TEAM
* OpenAPI spec version: 1.0.0
*/
export
type
PutNewsPageConfigCategoryIdBody
=
{
category_ids
?:
string
[];
};
src/api/types/event.ts
View file @
a99fbda4
import
{
ResponseType
}
from
'@lib/types/common'
export
type
EventStatus
=
{
id
:
string
;
name
:
string
;
...
...
@@ -22,7 +23,68 @@ export type EventOrganization = {
add_info
:
unknown
|
null
;
organization
:
unknown
|
null
;
};
// useGetEventsId
export
type
GetEventsIdQueryResponseType
=
ResponseType
<
{
accept_entries
:
boolean
|
null
counter_cost
:
number
counter_count
:
number
created_at
:
string
description
:
string
end_time
:
string
event_organizations
:
Array
<
{
add_info
:
string
|
null
created_at
:
string
guest_image
:
string
|
null
guest_name
:
string
|
null
id
:
string
org_counter_count
:
number
|
null
org_table_count
:
number
|
null
organization
:
{
address
:
string
avatar
:
string
|
null
club_link
:
string
|
null
club_name
:
string
|
null
id
:
string
name
:
string
org_categories
:
string
[]
org_link
:
string
|
null
org_status_id
:
string
|
null
organization_products
:
Array
<
{
id
:
string
images
:
string
[]
}
>
province
:
string
[]
tax_code
:
string
users
:
Array
<
{
id
:
string
}
>
website
:
string
}
|
null
role
:
'PARTAKER'
|
'MAIN'
|
'SUPPORT'
|
'SUPPORT_1'
|
'SUPPORT_2'
|
'SUPPORT_3'
|
'GUEST'
status
:
string
|
null
}
>
host_club
:
string
|
null
id
:
string
image
:
string
introduction
:
string
|
null
location
:
string
name
:
string
org_support_titles
:
string
[]
|
null
province
:
string
seo_text
:
string
seo_text_en
:
string
|
null
start_time
:
string
status
:
string
status_status
:
{
code
:
string
id
:
string
name
:
string
name_en
:
string
}
table_cost
:
number
table_count
:
number
updated_at
:
string
|
null
}
>
export
type
EventItem
=
{
id
:
string
;
name
:
string
;
...
...
@@ -71,4 +133,4 @@ export type EventApiResponse = {
status
:
string
;
timeStamp
:
string
;
violations
:
null
|
unknown
;
};
\ No newline at end of file
};
src/app/(main)/(home)/[id]/page.tsx
View file @
a99fbda4
...
...
@@ -41,7 +41,7 @@ const NewsDetailPage = () => {
<
div
className=
"grid grid-cols-1 lg:grid-cols-3 gap-5"
>
{
/* Main content */
}
<
main
className=
"lg:col-span-2 bg-white border rounded-md p-7"
>
<
div
className=
'pb-5 text-
blue-900
text-2xl leading-normal font-medium'
>
<
div
className=
'pb-5 text-
primary
text-2xl leading-normal font-medium'
>
{
data
?.
responseData
?.
title
}
</
div
>
<
div
className=
'flex items-center gap-2 text-sm mb-4'
>
...
...
src/app/(main)/(home)/components/card-event/index.tsx
View file @
a99fbda4
...
...
@@ -13,6 +13,10 @@ function CardEvent({ event }: { event: EventItem }) {
src=
{
`${BASE_URL.imageEndpoint}${event.image}`
}
alt=
{
event
.
name
}
className=
'w-[100px] md:w-[130px] aspect-3/2 object-cover'
onError=
{
(
e
)
=>
{
e
.
currentTarget
.
onerror
=
null
e
.
currentTarget
.
src
=
"/fallback.png"
}
}
/>
<
div
className=
'flex-1'
>
<
p
className=
'text-[#0056b3] font-bold text-sm line-clamp-2'
>
...
...
src/app/(main)/(home)/components/card-news/index.tsx
View file @
a99fbda4
...
...
@@ -13,6 +13,10 @@ function CardNews({ news }: { news: NewsAdminItem }) {
src=
{
`${BASE_URL.imageEndpoint}${news.thumbnail}`
}
alt=
{
news
.
title
}
className=
"w-[100px] md:w-[130px] aspect-3/2 object-cover"
onError=
{
(
e
)
=>
{
e
.
currentTarget
.
onerror
=
null
e
.
currentTarget
.
src
=
"/fallback.png"
}
}
/>
<
div
className=
"flex-1"
>
<
p
className=
"text-[#363636] font-bold text-sm line-clamp-2"
>
...
...
src/app/(main)/(home)/components/event-calendar/index.tsx
deleted
100644 → 0
View file @
7e62d507
"use client"
;
import
React
,
{
useState
}
from
"react"
;
import
{
ArrowRight
,
ArrowLeft
}
from
"lucide-react"
;
export
default
function
EventCalendar
()
{
const
mockCalendar
=
{
month
:
10
,
year
:
2025
,
highlighted
:
[
6
,
9
,
12
],
};
const
[
month
,
setMonth
]
=
useState
<
number
>
(
mockCalendar
.
month
);
const
[
year
,
setYear
]
=
useState
<
number
>
(
mockCalendar
.
year
);
return
(
<
div
className=
"bg-white border rounded-md p-4"
>
<
div
className=
"flex items-center justify-between mb-3"
>
<
div
className=
"text-sm font-medium"
>
THÁNG
{
month
}
/
{
year
}
</
div
>
<
div
className=
"flex items-center gap-2"
>
<
div
className=
"group"
>
<
button
aria
-
label=
"Tháng trước"
onClick=
{
()
=>
{
// prev month
if
(
month
===
1
)
{
setMonth
(
12
);
setYear
((
y
)
=>
y
-
1
);
}
else
{
setMonth
((
m
)
=>
m
-
1
);
}
}
}
className=
"group-hover:border-primary p-1 h-10 w-10 flex items-center justify-center rounded-full border-2 border-[#363636] "
>
<
ArrowLeft
size=
{
24
}
className=
"group-hover:text-muted-foreground text-[#363636]"
/>
</
button
>
</
div
>
<
div
className=
"group"
>
<
button
aria
-
label=
"Tháng sau"
onClick=
{
()
=>
{
// next month
if
(
month
===
12
)
{
setMonth
(
1
);
setYear
((
y
)
=>
y
+
1
);
}
else
{
setMonth
((
m
)
=>
m
+
1
);
}
}
}
className=
"p-1 group-hover:border-primary h-10 w-10 flex items-center justify-center rounded-full border-2 border-[#363636]"
>
<
ArrowRight
size=
{
24
}
className=
"group-hover:text-muted-foreground"
/>
</
button
>
</
div
>
</
div
>
</
div
>
<
div
className=
"grid grid-cols-7 gap-1 text-center text-xs"
>
{
[
"CN"
,
"T2"
,
"T3"
,
"T4"
,
"T5"
,
"T6"
,
"T7"
].
map
((
d
)
=>
(
<
div
key=
{
d
}
className=
"text-gray-400 py-1"
>
{
d
}
</
div
>
))
}
{
(()
=>
{
const
totalDays
=
new
Date
(
year
,
month
,
0
).
getDate
()
const
firstDayIndex
=
new
Date
(
year
,
month
-
1
,
1
).
getDay
()
// 0 (Sun) - 6 (Sat)
// previous month total days
const
prevMonthTotalDays
=
new
Date
(
year
,
month
-
1
,
0
).
getDate
()
const
totalCells
=
firstDayIndex
+
totalDays
const
trailingCount
=
(
7
-
(
totalCells
%
7
))
%
7
return
(
<>
{
Array
.
from
({
length
:
firstDayIndex
}).
map
((
_
,
i
)
=>
{
const
dayNum
=
prevMonthTotalDays
-
(
firstDayIndex
-
1
)
+
i
return
(
<
div
key=
{
`prev-${i}`
}
className=
"py-2 text-sm text-gray-300"
>
{
dayNum
}
</
div
>
)
})
}
{
Array
.
from
({
length
:
totalDays
},
(
_
,
i
)
=>
i
+
1
).
map
((
day
)
=>
{
const
isHighlighted
=
mockCalendar
.
highlighted
.
includes
(
day
)
return
(
<
div
key=
{
day
}
className=
{
`py-2 rounded-full w-10 h-10 flex flex-col justify-center items-center text-sm ${isHighlighted ? 'bg-yellow-500 text-white' : 'text-gray-700'
}`
}
>
{
day
}
</
div
>
)
})
}
{
Array
.
from
({
length
:
trailingCount
}).
map
((
_
,
i
)
=>
(
<
div
key=
{
`next-${i}`
}
className=
"py-2 text-sm text-gray-300"
>
{
i
+
1
}
</
div
>
))
}
</>
)
})()
}
</
div
>
</
div
>
);
}
src/app/(main)/(home)/page.tsx
View file @
a99fbda4
...
...
@@ -12,7 +12,7 @@ import BASE_URL from "@/links/index";
import
{
Spinner
}
from
"@/components/ui"
;
import
CardNews
from
"./components/card-news"
;
import
CardEvent
from
"./components/card-event"
;
import
EventCalendar
from
"
./components
/event-calendar"
;
import
EventCalendar
from
"
@components/base
/event-calendar"
;
import
dayjs
from
"dayjs"
;
import
AppEditorContent
from
"@/components/shared/editor-content"
;
...
...
@@ -30,12 +30,11 @@ const Page = () => {
const
[
currentIndex
,
setCurrentIndex
]
=
useState
(
0
);
const
swiperRef
=
useRef
<
SwiperType
|
null
>
(
null
);
const
{
data
:
categoryData
,
isLoading
:
isLoadingCategory
}
=
useGetCategory
<
GetCategoryAdminResponseType
>
();
const
{
data
:
newsData
,
isLoading
:
isLoadingNews
}
=
useGetNews
<
GetNewsAdminResponseType
>
();
const
{
data
:
eventData
,
isLoading
:
isLoadingEvent
}
=
useGetEvents
<
EventApiResponse
>
();
const
{
data
:
categoryData
,
isLoading
:
isLoadingCategory
}
=
useGetCategory
<
GetCategoryAdminResponseType
>
();
const
{
data
:
newsData
,
isLoading
:
isLoadingNews
}
=
useGetNews
<
GetNewsAdminResponseType
>
(
{
pageSize
:
'999'
}
);
const
{
data
:
eventData
,
isLoading
:
isLoadingEvent
}
=
useGetEvents
<
EventApiResponse
>
();
// filter category
const
rows
=
newsData
?.
responseData
?.
rows
??
[];
...
...
@@ -72,582 +71,579 @@ const Page = () => {
"/home/hoi-vien-tieu-bieu/UOB-logo_Vuong.jpeg.webp"
,
];
if
(
isLoadingNews
||
isLoadingCategory
||
isLoadingEvent
)
return
(
<
div
className=
"w-full h-[80vh] flex justify-center items-center"
>
return
(
(
isLoadingNews
||
isLoadingCategory
||
isLoadingEvent
)
?
(
<
div
className=
"
container
w-full h-[80vh] flex justify-center items-center"
>
<
Spinner
/>
</
div
>
);
return
(
<>
{
/* Banner */
}
<
Swiper
modules=
{
[
Autoplay
]
}
autoplay=
{
{
delay
:
4000
,
disableOnInteraction
:
false
}
}
loop
slidesPerView=
{
1
}
onSwiper=
{
(
s
)
=>
(
swiperRef
.
current
=
s
)
}
onSlideChange=
{
(
s
)
=>
setCurrentIndex
(
typeof
s
.
realIndex
===
"number"
?
s
.
realIndex
:
s
.
activeIndex
)
}
>
<
SwiperSlide
>
<
img
src=
"https://vcci-hcm.org.vn/wp-content/uploads/2025/10/1.1.-Hero-Banner-CEO-2025-Bi-Sai-Nam-2025-Nhe-2560x720-Px.jpg"
alt=
"Banner"
className=
"w-full h-[200px] sm:h-[300px] md:h-[400px] lg:h-[500px] object-cover"
/>
</
SwiperSlide
>
<
SwiperSlide
>
<
img
src=
"https://vcci-hcm.org.vn/wp-content/uploads/2022/07/Landscape-HCM_3-01.png"
alt=
"Banner"
className=
"w-full h-[200px] sm:h-[300px] md:h-[400px] lg:h-[500px] object-cover"
/>
</
SwiperSlide
>
</
Swiper
>
<
div
className=
"container mx-auto px-3 sm:px-6 lg:px-10 space-y-12"
>
{
/* Featured News */
}
<
section
>
<
div
className=
"flex items-center justify-center py-8 px-4"
>
<
div
className=
"flex items-center w-full max-w-4xl"
>
<
div
className=
"flex-1 h-[1px] bg-gradient-to-r from-transparent via-gray-300 to-gray-400"
></
div
>
<
h1
className=
"px-6 text-[20px] sm:text-[24px] md:text-[28px] uppercase font-bold text-[#063e8e] whitespace-nowrap"
>
Tin Nổi Bật
</
h1
>
<
div
className=
"flex-1 h-[1px] bg-gradient-to-l from-transparent via-gray-300 to-gray-400"
></
div
>
</
div
>
</
div
>
<
Swiper
modules=
{
[
Autoplay
]
}
autoplay=
{
{
delay
:
4000
,
disableOnInteraction
:
false
}
}
loop
breakpoints=
{
{
0
:
{
slidesPerView
:
1.1
,
spaceBetween
:
10
},
640
:
{
slidesPerView
:
2
,
spaceBetween
:
16
},
1024
:
{
slidesPerView
:
3
,
spaceBetween
:
24
},
}
}
className=
"pb-5"
>
{
rows
.
map
((
news
)
=>
(
<
SwiperSlide
key=
{
news
.
id
}
>
<
a
href=
{
`/${news.id}`
}
className=
"relative block bg-white shadow-md overflow-hidden hover:shadow-lg transition-shadow duration-300"
>
<
img
src=
{
`${BASE_URL.imageEndpoint}${news.thumbnail}`
}
alt=
{
news
.
title
}
className=
"w-full aspect-3/2 sm:h-56 md:h-64 object-cover"
/>
<
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"
>
<
p
className=
"text-white text-center font-semibold line-clamp-2 text-sm sm:text-base leading-snug"
>
{
news
.
title
}
</
p
>
</
div
>
</
a
>
</
SwiperSlide
>
))
}
</
Swiper
>
</
section
>
)
:
(
<>
{
/* Banner */
}
<
Swiper
modules=
{
[
Autoplay
]
}
autoplay=
{
{
delay
:
4000
,
disableOnInteraction
:
false
}
}
loop
slidesPerView=
{
1
}
onSwiper=
{
(
s
)
=>
(
swiperRef
.
current
=
s
)
}
onSlideChange=
{
(
s
)
=>
setCurrentIndex
(
typeof
s
.
realIndex
===
"number"
?
s
.
realIndex
:
s
.
activeIndex
)
}
>
<
SwiperSlide
>
<
img
src=
"https://vcci-hcm.org.vn/wp-content/uploads/2025/10/1.1.-Hero-Banner-CEO-2025-Bi-Sai-Nam-2025-Nhe-2560x720-Px.jpg"
alt=
"Banner"
className=
"w-full h-[200px] sm:h-[300px] md:h-[400px] lg:h-[500px] object-cover"
/>
</
SwiperSlide
>
<
SwiperSlide
>
<
img
src=
"https://vcci-hcm.org.vn/wp-content/uploads/2022/07/Landscape-HCM_3-01.png"
alt=
"Banner"
className=
"w-full h-[200px] sm:h-[300px] md:h-[400px] lg:h-[500px] object-cover"
/>
</
SwiperSlide
>
</
Swiper
>
<
div
>
<
a
href=
"https://hardwaretools.com.vn/"
>
<
img
src=
"/home/Standard-Banner-1-2024.png.webp"
alt=
"banner"
/>
</
a
>
</
div
>
{
/* Tin tức + Liên kết nhanh */
}
<
section
className=
"flex flex-col lg:flex-row gap-5 pb-10 mb-0"
>
{
/* Left */
}
<
div
className=
"flex-1"
>
<
div
className=
"flex justify-between items-center"
>
<
a
href=
"/thong-tin-truyen-thong/tin-vcci/"
className=
"text-[18px] sm:text-[20px] font-semibold uppercase text-[#063e8e]"
>
Tin tức
</
a
>
<
a
href=
"/thong-tin-truyen-thong/tin-vcci/"
className=
"text-[#063e8e] text-sm sm:text-base"
>
<
ChevronsRight
/>
</
a
>
<
div
className=
"container mx-auto px-3 sm:px-6 lg:px-10 space-y-12"
>
{
/* Featured News */
}
<
section
>
<
div
className=
"flex items-center justify-center py-8 px-4"
>
<
div
className=
"flex items-center w-full max-w-4xl"
>
<
div
className=
"flex-1 h-[1px] bg-gradient-to-r from-transparent via-gray-300 to-gray-400"
></
div
>
<
h1
className=
"px-6 text-[20px] sm:text-[24px] md:text-[28px] uppercase font-bold text-[#063e8e] whitespace-nowrap"
>
Tin Nổi Bật
</
h1
>
<
div
className=
"flex-1 h-[1px] bg-gradient-to-l from-transparent via-gray-300 to-gray-400"
></
div
>
</
div
>
</
div
>
<
hr
className=
"border-[#063e8e] mb-4"
/>
<
div
className=
"flex flex-col md:flex-row gap-5"
>
{
newsData
?.
responseData
.
rows
.
slice
(
0
,
1
)
.
map
((
news
:
NewsAdminItem
)
=>
(
<
Swiper
modules=
{
[
Autoplay
]
}
autoplay=
{
{
delay
:
4000
,
disableOnInteraction
:
false
}
}
loop
breakpoints=
{
{
0
:
{
slidesPerView
:
1.1
,
spaceBetween
:
10
},
640
:
{
slidesPerView
:
2
,
spaceBetween
:
16
},
1024
:
{
slidesPerView
:
3
,
spaceBetween
:
24
},
}
}
className=
"pb-5"
>
{
rows
.
map
((
news
)
=>
(
<
SwiperSlide
key=
{
news
.
id
}
>
<
a
key=
{
news
.
id
}
href=
{
`${news.id}`
}
className=
"flex flex-col w-full md:w-1/2 min-h-[180px] sm:min-h-[220px] gap-3 mb-3 bg-[#eee]"
href=
{
`/${news.id}`
}
className=
"relative block bg-white shadow-md overflow-hidden hover:shadow-lg transition-shadow duration-300"
>
<
div
className=
"w-full aspect-3/2 overflow-hidden"
>
<
img
src=
{
`${BASE_URL.imageEndpoint}${news.thumbnail}`
}
alt=
{
news
.
title
}
className=
"w-full h-full object-cover"
/>
</
div
>
<
div
className=
"flex-1"
>
<
p
className=
"text-[#063E8E] font-bold text-xl line-clamp-2"
>
<
img
src=
{
`${BASE_URL.imageEndpoint}${news.thumbnail}`
}
alt=
{
news
.
title
}
className=
"w-full aspect-3/2 sm:h-56 md:h-64 object-cover"
/>
<
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"
>
<
p
className=
"text-white text-center font-semibold line-clamp-2 text-sm sm:text-base leading-snug"
>
{
news
.
title
}
</
p
>
<
p
className=
"text-gray-500 text-sm my-1"
>
{
dayjs
(
news
.
release_at
).
format
(
"DD/MM/YYYY"
)
}
</
p
>
<
AppEditorContent
className=
"line-clamp-4"
value=
{
news
.
description
}
/>
</
div
>
</
a
>
))
}
<
div
className=
"w-full md:w-1/2"
>
<
div
className=
"flex flex-wrap gap-2 sm:gap-3 mb-5"
>
<
button
className=
{
`flex-1 py-[3px] text-sm transition-colors cursor-pointer ${
tab === "all"
? " bg-[#d3d3d3] text-[#063e8e] font-semibold"
: "border-gray-300 text-[#363636] bg-[#e8e8e8] hover:bg-[#e8e8e8] hover:text-[#063e8e] font-semibold"
}`
}
onClick=
{
()
=>
setTab
(
"all"
)
}
>
Tất cả
</
button
>
{
categoryData
?.
responseData
.
rows
.
slice
(
0
,
3
)
.
map
((
category
)
=>
(
<
button
key=
{
category
.
id
}
className=
{
`flex-1 py-[3px] text-[14px] transition-colors cursor-pointer ${
category.name === tab
? "bg-[#d3d3d3] text-[#063e8e] font-semibold"
: "border-gray-300 text-[#363636] bg-[#e8e8e8] hover:bg-[#e8e8e8] hover:text-[#063e8e] font-semibold"
}`
}
onClick=
{
()
=>
setTab
(
category
.
name
)
}
>
{
category
.
name
}
</
button
>
))
}
</
div
>
</
SwiperSlide
>
))
}
</
Swiper
>
</
section
>
{
filteredRows
.
slice
(
0
,
4
).
map
((
news
)
=>
(
<
CardNews
key=
{
news
.
id
}
news=
{
news
}
/>
))
}
</
div
>
</
div
>
<
div
>
<
a
href=
"https://hardwaretools.com.vn/"
>
<
img
src=
"/home/Standard-Banner-1-2024.png.webp"
alt=
"banner"
/>
</
a
>
</
div
>
{
/* Right */
}
<
aside
className=
"w-full lg:w-[30%]"
>
<
div
className=
"flex justify-between items-center"
>
<
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"
>
<
div
>
{
/* Tin tức + Liên kết nhanh */
}
<
section
className=
"flex flex-col lg:flex-row gap-5 pb-10 mb-0"
>
{
/* Left */
}
<
div
className=
"flex-1"
>
<
div
className=
"flex justify-between items-center"
>
<
a
className=
"text-[#363636]
"
href=
"https://vcci-hcm.org.vn/lien-ket-nhanh/cam-nang-huong-dan-dau-tu-kinh-doanh-tai-viet-nam-2023/
"
href=
"/thong-tin-truyen-thong/tin-vcci/
"
className=
"text-[18px] sm:text-[20px] font-semibold uppercase text-[#063e8e]
"
>
<
Link
size=
{
16
}
className=
"inline mr-2 font-semibold text-[#063e8e]"
style=
{
{
verticalAlign
:
"middle"
,
marginTop
:
"-2px"
}
}
/>
Cẩm nang hướng dẫn đầu tư kinh doanh tại Việt Nam
Tin tức
</
a
>
</
div
>
<
div
>
<
a
className=
"text-[#363636]
"
href=
"https://vcci-hcm.org.vn/lien-ket-nhanh/doanh-nghiep-kien-nghi-ve-chinh-sach-va-phap-luat/
"
href=
"/thong-tin-truyen-thong/tin-vcci/
"
className=
"text-[#063e8e] text-sm sm:text-base
"
>
<
Link
size=
{
16
}
className=
"inline mr-2 font-semibold text-[#063e8e]"
style=
{
{
verticalAlign
:
"middle"
,
marginTop
:
"-2px"
}
}
/>
Doanh nghiệp kiến nghị về chính sách và pháp luật
<
ChevronsRight
/>
</
a
>
</
div
>
</
div
>
<
div
>
<
a
href=
"https://hardwaretools.com.vn/"
>
<
img
src=
"/home/20-2048x1365.webp"
alt=
"banner"
/>
</
a
>
</
div
>
</
aside
>
</
section
>
<
hr
className=
"border-[#063e8e] mb-4"
/>
{
/* Sự kiện */
}
<
section
className=
"flex flex-col lg:flex-row gap-5 pb-10 mb-0"
>
<
div
className=
"flex-1 bg-[#063e8e] p-5"
>
<
div
className=
"flex justify-between items-center"
>
<
h2
className=
"text-[18px] sm:text-[20px] font-bold uppercase text-[#e8c518]"
>
Sự kiện sắp diễn ra
</
h2
>
<
a
href=
"#"
className=
"text-[#e8c518] text-sm sm:text-base"
>
<
ChevronsRight
/>
</
a
>
</
div
>
<
hr
className=
"border-[#e8c518] mb-4"
/>
<
div
className=
"flex flex-col md:flex-row gap-5"
>
{
newsData
?.
responseData
.
rows
.
slice
(
0
,
1
)
.
map
((
news
:
NewsAdminItem
)
=>
(
<
a
key=
{
news
.
id
}
href=
{
`${news.id}`
}
className=
"flex flex-col w-full md:w-1/2 min-h-[180px] sm:min-h-[220px] gap-3 mb-3 bg-white"
>
<
div
className=
"w-full aspect-3/2 overflow-hidden"
>
<
img
src=
{
`${BASE_URL.imageEndpoint}${news.thumbnail}`
}
alt=
{
news
.
title
}
className=
"w-full h-full object-cover"
/>
</
div
>
<
div
className=
"flex flex-col md:flex-row gap-5"
>
{
eventData
?.
responseData
.
rows
.
slice
(
0
,
1
)
.
map
((
event
:
EventItem
)
=>
(
<
a
key=
{
event
.
id
}
href=
{
`${event.id}`
}
className=
"flex flex-col w-full md:w-1/2 min-h-[180px] sm:min-h-[220px] gap-3 mb-3 border border-gray-200 bg-white rounded-md p-3"
>
<
div
className=
"w-full aspect-3/2 overflow-hidden"
>
<
img
src=
{
`${BASE_URL.imageEndpoint}${event.image}`
}
alt=
{
event
.
name
}
className=
"w-full h-full object-cover"
/>
</
div
>
<
div
className=
"flex-1 p-5"
>
<
p
className=
"text-[#063E8E] font-bold text-xl line-clamp-2"
>
{
news
.
title
}
</
p
>
<
p
className=
"text-gray-500 text-sm my-1"
>
{
dayjs
(
news
.
release_at
).
format
(
"DD/MM/YYYY"
)
}
</
p
>
<
AppEditorContent
className=
"line-clamp-4"
value=
{
news
.
description
}
/>
</
div
>
</
a
>
))
}
<
div
className=
"flex-1"
>
<
p
className=
"text-[#0056b3] font-bold text-xl line-clamp-2"
>
{
event
.
name
}
</
p
>
<
p
className=
"text-gray-500 text-sm my-1"
>
{
dayjs
(
event
.
start_time
).
format
(
"DD/MM/YYYY"
)
}
</
p
>
<
AppEditorContent
className=
"line-clamp-3"
value=
{
event
.
description
}
/>
</
div
>
</
a
>
))
}
<
div
className=
"w-full md:w-1/2"
>
{
eventData
?.
responseData
.
rows
.
slice
(
0
,
4
).
map
((
event
)
=>
(
<
CardEvent
key=
{
event
.
id
}
event=
{
event
}
/>
))
}
<
div
className=
"w-full md:w-1/2"
>
<
div
className=
"flex flex-wrap gap-2 sm:gap-3 mb-5"
>
<
button
className=
{
`flex-1 py-[3px] text-sm transition-colors cursor-pointer ${tab === "all"
? " bg-[#d3d3d3] text-[#063e8e] font-semibold"
: "border-gray-300 text-[#363636] bg-[#e8e8e8] hover:bg-[#e8e8e8] hover:text-[#063e8e] font-semibold"
}`
}
onClick=
{
()
=>
setTab
(
"all"
)
}
>
Tất cả
</
button
>
{
categoryData
?.
responseData
.
rows
.
slice
(
0
,
3
)
.
map
((
category
)
=>
(
<
button
key=
{
category
.
id
}
className=
{
`flex-1 py-[3px] text-[14px] transition-colors cursor-pointer ${category.name === tab
? "bg-[#d3d3d3] text-[#063e8e] font-semibold"
: "border-gray-300 text-[#363636] bg-[#e8e8e8] hover:bg-[#e8e8e8] hover:text-[#063e8e] font-semibold"
}`
}
onClick=
{
()
=>
setTab
(
category
.
name
)
}
>
{
category
.
name
}
</
button
>
))
}
</
div
>
{
filteredRows
.
slice
(
0
,
4
).
map
((
news
)
=>
(
<
CardNews
key=
{
news
.
id
}
news=
{
news
}
/>
))
}
</
div
>
</
div
>
</
div
>
</
div
>
<
div
className=
"bg-[#063e8e] w-full lg:w-[30%] p-5"
>
<
aside
>
{
/* Right */
}
<
aside
className=
"w-full lg:w-[30%]"
>
<
div
className=
"flex justify-between items-center"
>
<
h2
className=
"text-[18px] sm:text-[20px] font-
bold uppercase text-[#e8c518
]"
>
L
ịch sự kiện
<
h2
className=
"text-[18px] sm:text-[20px] font-
semibold uppercase text-[#063e8e
]"
>
L
iên kết nhanh
</
h2
>
<
a
href=
"#"
className=
"text-[#e8c518] hover:underline text-sm sm:text-base"
>
<
a
href=
"#"
className=
"text-[#063e8e] text-sm sm:text-base"
>
<
ChevronsRight
/>
</
a
>
</
div
>
<
hr
className=
"border-[#e8c518] mb-4"
/>
<
EventCalendar
/>
</
aside
>
</
div
>
</
section
>
{
/* Cơ hội kinh doanh + Chính sách */
}
<
div
className=
"flex flex-col lg:flex-row gap-5 pb-10 mb-0"
>
<
div
className=
"flex flex-col flex-1"
>
<
div
>
<
a
href=
"https://vcci-hcm.org.vn/wp-content/uploads/2022/11/MEDIA-KIT_VCCI-HCM-2022-Final.pdf"
>
<
img
src=
"/home/Standard-Banner-1-2024.png.webp"
alt=
"banner"
/>
</
a
>
</
div
>
<
section
className=
"flex flex-col md:flex-row gap-5 pt-8"
>
<
div
className=
"flex-1"
>
<
div
className=
"flex justify-between items-center"
>
<
hr
className=
"border-[#063e8e] mb-4"
/>
<
div
className=
"space-y-2 text-[#063e8e] text-sm md:text-base pb-10"
>
<
div
>
<
a
href=
"/xuc-tien-thuong-mai/co-hoi-kinh-doanh/
"
className=
"text-[18px] sm:text-[20px] font-bold uppercase text-[#063e8e]
"
className=
"text-[#363636]
"
href=
"https://vcci-hcm.org.vn/lien-ket-nhanh/cam-nang-huong-dan-dau-tu-kinh-doanh-tai-viet-nam-2023/
"
>
Cơ hội kinh doanh
<
Link
size=
{
16
}
className=
"inline mr-2 font-semibold text-[#063e8e]"
style=
{
{
verticalAlign
:
"middle"
,
marginTop
:
"-2px"
}
}
/>
Cẩm nang hướng dẫn đầu tư kinh doanh tại Việt Nam
</
a
>
</
div
>
<
div
>
<
a
href=
"/xuc-tien-thuong-mai/co-hoi-kinh-doanh/
"
className=
"text-[#063e8e] text-sm sm:text-base
"
className=
"text-[#363636]
"
href=
"https://vcci-hcm.org.vn/lien-ket-nhanh/doanh-nghiep-kien-nghi-ve-chinh-sach-va-phap-luat/
"
>
<
ChevronsRight
/>
<
Link
size=
{
16
}
className=
"inline mr-2 font-semibold text-[#063e8e]"
style=
{
{
verticalAlign
:
"middle"
,
marginTop
:
"-2px"
}
}
/>
Doanh nghiệp kiến nghị về chính sách và pháp luật
</
a
>
</
div
>
<
hr
className=
"border-[#063e8e] mb-4"
/>
<
div
className=
"pt-2"
>
{
newsData
?.
responseData
.
rows
.
slice
(
0
,
1
)
.
map
((
news
:
NewsAdminItem
)
=>
(
<
a
key=
{
news
.
id
}
href=
{
`${news.id}`
}
>
<
div
className=
"w-full aspect-3/2 relative overflow-hidden mb-5"
>
<
img
src=
{
`${BASE_URL.imageEndpoint}${news.thumbnail}`
}
alt=
{
news
.
title
}
className=
"w-full h-full object-cover"
/>
<
div
className=
"absolute bg-white opacity-80 bottom-5 left-5 right-5 p-5"
>
<
p
className=
"text-[#063e8e] font-semibold text-sm sm:text-base z-10 line-clamp-3"
>
{
news
.
title
}
</
p
>
</
div
>
</
div
>
</
a
>
))
}
</
div
>
<
div
>
<
a
href=
"https://hardwaretools.com.vn/"
>
<
img
src=
"/home/20-2048x1365.webp"
alt=
"banner"
/>
</
a
>
</
div
>
</
aside
>
</
section
>
{
rows
.
slice
(
0
,
3
).
map
((
news
)
=>
(
<
CardNews
key=
{
news
.
id
}
news=
{
news
}
/>
{
/* Sự kiện */
}
<
section
className=
"flex flex-col lg:flex-row gap-5 pb-10 mb-0"
>
<
div
className=
"flex-1 bg-[#063e8e] p-5"
>
<
div
className=
"flex justify-between items-center"
>
<
h2
className=
"text-[18px] sm:text-[20px] font-bold uppercase text-[#e8c518]"
>
Sự kiện sắp diễn ra
</
h2
>
<
a
href=
"#"
className=
"text-[#e8c518] text-sm sm:text-base"
>
<
ChevronsRight
/>
</
a
>
</
div
>
<
hr
className=
"border-[#e8c518] mb-4"
/>
<
div
className=
"flex flex-col md:flex-row gap-5"
>
{
eventData
?.
responseData
.
rows
.
slice
(
0
,
1
)
.
map
((
event
:
EventItem
)
=>
(
<
a
key=
{
event
.
id
}
href=
{
`${event.id}`
}
className=
"flex flex-col w-full md:w-1/2 min-h-[180px] sm:min-h-[220px] gap-3 mb-3 border border-gray-200 bg-white rounded-md p-3"
>
<
div
className=
"w-full aspect-3/2 overflow-hidden"
>
<
img
src=
{
`${BASE_URL.imageEndpoint}${event.image}`
}
alt=
{
event
.
name
}
className=
"w-full h-full object-cover"
/>
</
div
>
<
div
className=
"flex-1"
>
<
p
className=
"text-[#0056b3] font-bold text-xl line-clamp-2"
>
{
event
.
name
}
</
p
>
<
p
className=
"text-gray-500 text-sm my-1"
>
{
dayjs
(
event
.
start_time
).
format
(
"DD/MM/YYYY"
)
}
</
p
>
<
AppEditorContent
className=
"line-clamp-3"
value=
{
event
.
description
}
/>
</
div
>
</
a
>
))
}
<
div
className=
"w-full md:w-1/2"
>
{
eventData
?.
responseData
.
rows
.
slice
(
0
,
4
).
map
((
event
)
=>
(
<
CardEvent
key=
{
event
.
id
}
event=
{
event
}
/>
))
}
</
div
>
</
div
>
<
div
className=
"flex-1"
>
</
div
>
<
div
className=
"bg-[#063e8e] w-full lg:w-[30%] p-5"
>
<
aside
>
<
div
className=
"flex justify-between items-center"
>
<
h2
className=
"text-[18px] sm:text-[20px] font-bold uppercase text-[#e8c518]"
>
Lịch sự kiện
</
h2
>
<
a
href=
"/thong-tin-truyen-thong/thong-tin-chinh-sach-va-phap-luat"
className=
"text-[18px] sm:text-[20px] font-bold uppercase text-[#063e8e]"
>
Chính sách
&
pháp luật
</
a
>
<
a
href=
"/thong-tin-truyen-thong/thong-tin-chinh-sach-va-phap-luat"
className=
"text-[#063e8e] text-sm sm:text-base"
href=
"#"
className=
"text-[#e8c518] hover:underline text-sm sm:text-base"
>
<
ChevronsRight
/>
</
a
>
</
div
>
<
hr
className=
"border-[#063e8e] mb-4"
/>
<
div
className=
"pt-2"
>
{
newsData
?.
responseData
.
rows
.
slice
(
0
,
1
)
.
map
((
news
:
NewsAdminItem
)
=>
(
<
a
key=
{
news
.
id
}
href=
{
`${news.id}`
}
>
<
div
className=
"w-full aspect-3/2 relative overflow-hidden mb-5"
>
<
img
src=
{
`${BASE_URL.imageEndpoint}${news.thumbnail}`
}
alt=
{
news
.
title
}
className=
"w-full h-full object-cover"
/>
<
div
className=
"absolute bg-white opacity-80 bottom-5 left-5 right-5 p-5"
>
<
p
className=
"text-[#063e8e] font-semibold text-sm sm:text-base z-10 line-clamp-3"
>
{
news
.
title
}
</
p
>
</
div
>
</
div
>
</
a
>
))
}
<
hr
className=
"border-[#e8c518] mb-4"
/>
<
EventCalendar
/>
</
aside
>
</
div
>
</
section
>
{
rows
.
slice
(
0
,
3
).
map
((
news
)
=>
(
<
CardNews
key=
{
news
.
id
}
news=
{
news
}
/>
))
}
</
div
>
{
/* Cơ hội kinh doanh + Chính sách */
}
<
div
className=
"flex flex-col lg:flex-row gap-5 pb-10 mb-0"
>
<
div
className=
"flex flex-col flex-1"
>
<
div
>
<
a
href=
"https://vcci-hcm.org.vn/wp-content/uploads/2022/11/MEDIA-KIT_VCCI-HCM-2022-Final.pdf"
>
<
img
src=
"/home/Standard-Banner-1-2024.png.webp"
alt=
"banner"
/>
</
a
>
</
div
>
</
section
>
</
div
>
<
div
className=
"w-full lg:w-[30%] justify-center items-start flex"
>
<
a
href=
"https://smartgara.ecaraid.com/"
>
<
img
src=
"/home/eCarAid_web_banner_600x400.webp"
alt=
"banner"
/>
</
a
>
</
div
>
</
div
>
{
/* Hội viên tiêu biểu */
}
<
section
className=
"flex flex-col lg:flex-row gap-5 pb-10 mb-0"
>
{
/* left */
}
<
aside
className=
"w-full lg:w-1/3 flex-1 bg-[#e8c518] p-5"
>
<
div
className=
"flex justify-between items-center mb-3"
>
<
h2
className=
"text-xl font-bold uppercase text-[#063e8e]"
>
Hội viên tiêu biểu
</
h2
>
<
a
href=
"#"
className=
"text-[#063e8e] hover:underline text-sm font-medium"
>
<
ChevronsRight
/>
</
a
>
</
div
>
<
hr
className=
"border-[#063e8e] mb-5"
/>
<
div
>
<
Swiper
modules=
{
[
Autoplay
,
Grid
]
}
autoplay=
{
{
delay
:
4000
,
disableOnInteraction
:
false
}
}
loop
grid=
{
{
rows
:
1
,
fill
:
"row"
}
}
slidesPerGroup=
{
3
}
breakpoints=
{
{
0
:
{
slidesPerView
:
2
,
spaceBetween
:
10
},
640
:
{
slidesPerView
:
3
,
spaceBetween
:
16
},
1024
:
{
slidesPerView
:
3
,
spaceBetween
:
24
},
}
}
className=
"partner-swiper"
>
{
hoivien
.
map
((
src
,
i
)
=>
(
<
SwiperSlide
key=
{
i
}
>
<
div
className=
"aspect-square flex justify-center items-center bg-white rounded-lg shadow"
>
<
img
src=
{
src
}
alt=
{
`partner-${i}`
}
className=
"w-3/4 h-3/4 object-contain"
/>
</
div
>
</
SwiperSlide
>
))
}
</
Swiper
>
</
div
>
</
aside
>
<
section
className=
"flex flex-col md:flex-row gap-5 pt-8"
>
<
div
className=
"flex-1"
>
<
div
className=
"flex justify-between items-center"
>
<
a
href=
"/xuc-tien-thuong-mai/co-hoi-kinh-doanh/"
className=
"text-[18px] sm:text-[20px] font-bold uppercase text-[#063e8e]"
>
Cơ hội kinh doanh
</
a
>
<
a
href=
"/xuc-tien-thuong-mai/co-hoi-kinh-doanh/"
className=
"text-[#063e8e] text-sm sm:text-base"
>
<
ChevronsRight
/>
</
a
>
</
div
>
<
hr
className=
"border-[#063e8e] mb-4"
/>
<
div
className=
"pt-2"
>
{
newsData
?.
responseData
.
rows
.
slice
(
0
,
1
)
.
map
((
news
:
NewsAdminItem
)
=>
(
<
a
key=
{
news
.
id
}
href=
{
`${news.id}`
}
>
<
div
className=
"w-full aspect-3/2 relative overflow-hidden mb-5"
>
<
img
src=
{
`${BASE_URL.imageEndpoint}${news.thumbnail}`
}
alt=
{
news
.
title
}
className=
"w-full h-full object-cover"
/>
<
div
className=
"absolute bg-white opacity-80 bottom-5 left-5 right-5 p-5"
>
<
p
className=
"text-[#063e8e] font-semibold text-sm sm:text-base z-10 line-clamp-3"
>
{
news
.
title
}
</
p
>
</
div
>
</
div
>
</
a
>
))
}
{
/* right */
}
<
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"
>
<
Swiper
modules=
{
[
Autoplay
,
Grid
]
}
autoplay=
{
{
delay
:
4000
,
disableOnInteraction
:
false
}
}
loop
grid=
{
{
rows
:
2
,
fill
:
"row"
}
}
slidesPerGroup=
{
3
}
breakpoints=
{
{
0
:
{
slidesPerView
:
2
,
spaceBetween
:
10
},
640
:
{
slidesPerView
:
3
,
spaceBetween
:
16
},
1024
:
{
slidesPerView
:
3
,
spaceBetween
:
24
},
}
}
className=
"partner-swiper"
>
{
images
.
map
((
src
,
i
)
=>
(
<
SwiperSlide
key=
{
i
}
>
<
div
className=
"aspect-square flex justify-center items-center bg-white rounded-lg shadow"
>
<
img
src=
{
src
}
alt=
{
`partner-${i}`
}
className=
"w-3/4 h-3/4 object-contain"
/>
</
div
>
</
SwiperSlide
>
))
}
</
Swiper
>
</
div
>
</
aside
>
</
section
>
{
rows
.
slice
(
0
,
3
).
map
((
news
)
=>
(
<
CardNews
key=
{
news
.
id
}
news=
{
news
}
/>
))
}
</
div
>
</
div
>
<
div
className=
"flex-1"
>
<
div
className=
"flex justify-between items-center"
>
<
a
href=
"/thong-tin-truyen-thong/thong-tin-chinh-sach-va-phap-luat"
className=
"text-[18px] sm:text-[20px] font-bold uppercase text-[#063e8e]"
>
Chính sách
&
pháp luật
</
a
>
<
a
href=
"/thong-tin-truyen-thong/thong-tin-chinh-sach-va-phap-luat"
className=
"text-[#063e8e] text-sm sm:text-base"
>
<
ChevronsRight
/>
</
a
>
</
div
>
<
hr
className=
"border-[#063e8e] mb-4"
/>
<
div
className=
"pt-2"
>
{
newsData
?.
responseData
.
rows
.
slice
(
0
,
1
)
.
map
((
news
:
NewsAdminItem
)
=>
(
<
a
key=
{
news
.
id
}
href=
{
`${news.id}`
}
>
<
div
className=
"w-full aspect-3/2 relative overflow-hidden mb-5"
>
<
img
src=
{
`${BASE_URL.imageEndpoint}${news.thumbnail}`
}
alt=
{
news
.
title
}
className=
"w-full h-full object-cover"
/>
<
div
className=
"absolute bg-white opacity-80 bottom-5 left-5 right-5 p-5"
>
<
p
className=
"text-[#063e8e] font-semibold text-sm sm:text-base z-10 line-clamp-3"
>
{
news
.
title
}
</
p
>
</
div
>
</
div
>
</
a
>
))
}
{
/* Video + đối tác */
}
<
section
className=
"flex flex-col lg:flex-row gap-5 pb-10"
>
{
/* left */
}
<
div
className=
"flex flex-col flex-1"
>
<
div
className=
"flex justify-between items-center mb-3"
>
<
h2
className=
"text-xl font-bold uppercase text-[#063e8e]"
>
Video
</
h2
>
<
a
href=
"#"
className=
"text-[#063e8e] hover:underline text-sm font-medium"
>
<
ChevronsRight
/>
</
a
>
</
div
>
<
hr
className=
"border-[#063e8e] mb-5"
/>
<
div
className=
"flex flex-col md:flex-row gap-4 md:gap-6"
>
{
[
{
src
:
"https://www.youtube.com/embed/J0Iz0iGuAXY"
,
title
:
"VCCI-HCM 2024 IN REVIEW (ENGLISH VERSION)"
,
},
{
src
:
"https://www.youtube.com/embed/_OnnGWv2ehM"
,
title
:
"Hội nghị Hội viên VCCI - Gala Mừng Xuân Ất Tỵ 2025"
,
},
].
map
((
video
,
i
)
=>
(
<
div
key=
{
i
}
className=
"w-full md:w-1/2"
>
<
div
className=
"aspect-video rounded-lg overflow-hidden shadow"
>
<
iframe
className=
"w-full h-full font-bold"
src=
{
video
.
src
}
title=
{
video
.
title
}
frameBorder=
"0"
allow=
"accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share"
referrerPolicy=
"strict-origin-when-cross-origin"
allowFullScreen
/>
{
rows
.
slice
(
0
,
3
).
map
((
news
)
=>
(
<
CardNews
key=
{
news
.
id
}
news=
{
news
}
/>
))
}
</
div
>
<
p
className=
"mt-2 text-sm text-gray-700 font-medium"
>
{
video
.
title
}
</
p
>
</
div
>
))
}
</
section
>
</
div
>
</
div
>
{
/* right */
}
<
aside
className=
"w-full lg:w-[30%]"
>
<
div
className=
"flex justify-between items-center mb-3"
>
<
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
/>
<
div
className=
"w-full lg:w-[30%] justify-center items-start flex"
>
<
a
href=
"https://smartgara.ecaraid.com/"
>
<
img
src=
"/home/eCarAid_web_banner_600x400.webp"
alt=
"banner"
/>
</
a
>
</
div
>
<
hr
className=
"border-[#063e8e] mb-5"
/>
<
div
className=
"pb-10"
>
<
Swiper
modules=
{
[
Autoplay
,
Grid
]
}
autoplay=
{
{
delay
:
4000
,
disableOnInteraction
:
false
}
}
loop
grid=
{
{
rows
:
2
,
fill
:
"row"
}
}
slidesPerGroup=
{
3
}
breakpoints=
{
{
0
:
{
slidesPerView
:
2
,
spaceBetween
:
10
},
640
:
{
slidesPerView
:
3
,
spaceBetween
:
16
},
1024
:
{
slidesPerView
:
3
,
spaceBetween
:
24
},
}
}
className=
"partner-swiper"
>
{
images
.
map
((
src
,
i
)
=>
(
<
SwiperSlide
key=
{
i
}
>
<
div
className=
"aspect-square flex justify-center items-center bg-white rounded-lg shadow"
>
<
img
src=
{
src
}
alt=
{
`partner-${i}`
}
className=
"w-3/4 h-3/4 object-contain"
</
div
>
{
/* Hội viên tiêu biểu */
}
<
section
className=
"flex flex-col lg:flex-row gap-5 pb-10 mb-0"
>
{
/* left */
}
<
aside
className=
"w-full lg:w-1/3 flex-1 bg-[#e8c518] p-5"
>
<
div
className=
"flex justify-between items-center mb-3"
>
<
h2
className=
"text-xl font-bold uppercase text-[#063e8e]"
>
Hội viên tiêu biểu
</
h2
>
<
a
href=
"#"
className=
"text-[#063e8e] hover:underline text-sm font-medium"
>
<
ChevronsRight
/>
</
a
>
</
div
>
<
hr
className=
"border-[#063e8e] mb-5"
/>
<
div
>
<
Swiper
modules=
{
[
Autoplay
,
Grid
]
}
autoplay=
{
{
delay
:
4000
,
disableOnInteraction
:
false
}
}
loop
grid=
{
{
rows
:
1
,
fill
:
"row"
}
}
slidesPerGroup=
{
3
}
breakpoints=
{
{
0
:
{
slidesPerView
:
2
,
spaceBetween
:
10
},
640
:
{
slidesPerView
:
3
,
spaceBetween
:
16
},
1024
:
{
slidesPerView
:
3
,
spaceBetween
:
24
},
}
}
className=
"partner-swiper"
>
{
hoivien
.
map
((
src
,
i
)
=>
(
<
SwiperSlide
key=
{
i
}
>
<
div
className=
"aspect-square flex justify-center items-center bg-white rounded-lg shadow"
>
<
img
src=
{
src
}
alt=
{
`partner-${i}`
}
className=
"w-3/4 h-3/4 object-contain"
/>
</
div
>
</
SwiperSlide
>
))
}
</
Swiper
>
</
div
>
</
aside
>
{
/* right */
}
<
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"
>
<
Swiper
modules=
{
[
Autoplay
,
Grid
]
}
autoplay=
{
{
delay
:
4000
,
disableOnInteraction
:
false
}
}
loop
grid=
{
{
rows
:
2
,
fill
:
"row"
}
}
slidesPerGroup=
{
3
}
breakpoints=
{
{
0
:
{
slidesPerView
:
2
,
spaceBetween
:
10
},
640
:
{
slidesPerView
:
3
,
spaceBetween
:
16
},
1024
:
{
slidesPerView
:
3
,
spaceBetween
:
24
},
}
}
className=
"partner-swiper"
>
{
images
.
map
((
src
,
i
)
=>
(
<
SwiperSlide
key=
{
i
}
>
<
div
className=
"aspect-square flex justify-center items-center bg-white rounded-lg shadow"
>
<
img
src=
{
src
}
alt=
{
`partner-${i}`
}
className=
"w-3/4 h-3/4 object-contain"
/>
</
div
>
</
SwiperSlide
>
))
}
</
Swiper
>
</
div
>
</
aside
>
</
section
>
{
/* Video + đối tác */
}
<
section
className=
"flex flex-col lg:flex-row gap-5 pb-10"
>
{
/* left */
}
<
div
className=
"flex flex-col flex-1"
>
<
div
className=
"flex justify-between items-center mb-3"
>
<
h2
className=
"text-xl font-bold uppercase text-[#063e8e]"
>
Video
</
h2
>
<
a
href=
"#"
className=
"text-[#063e8e] hover:underline text-sm font-medium"
>
<
ChevronsRight
/>
</
a
>
</
div
>
<
hr
className=
"border-[#063e8e] mb-5"
/>
<
div
className=
"flex flex-col md:flex-row gap-4 md:gap-6"
>
{
[
{
src
:
"https://www.youtube.com/embed/J0Iz0iGuAXY"
,
title
:
"VCCI-HCM 2024 IN REVIEW (ENGLISH VERSION)"
,
},
{
src
:
"https://www.youtube.com/embed/_OnnGWv2ehM"
,
title
:
"Hội nghị Hội viên VCCI - Gala Mừng Xuân Ất Tỵ 2025"
,
},
].
map
((
video
,
i
)
=>
(
<
div
key=
{
i
}
className=
"w-full md:w-1/2"
>
<
div
className=
"aspect-video rounded-lg overflow-hidden shadow"
>
<
iframe
className=
"w-full h-full font-bold"
src=
{
video
.
src
}
title=
{
video
.
title
}
frameBorder=
"0"
allow=
"accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share"
referrerPolicy=
"strict-origin-when-cross-origin"
allowFullScreen
/>
</
div
>
</
SwiperSlide
>
<
p
className=
"mt-2 text-sm text-gray-700 font-medium"
>
{
video
.
title
}
</
p
>
</
div
>
))
}
</
Swiper
>
</
div
>
</
aside
>
</
section
>
</
div
>
</>
</
div
>
</
div
>
{
/* right */
}
<
aside
className=
"w-full lg:w-[30%]"
>
<
div
className=
"flex justify-between items-center mb-3"
>
<
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"
>
<
Swiper
modules=
{
[
Autoplay
,
Grid
]
}
autoplay=
{
{
delay
:
4000
,
disableOnInteraction
:
false
}
}
loop
grid=
{
{
rows
:
2
,
fill
:
"row"
}
}
slidesPerGroup=
{
3
}
breakpoints=
{
{
0
:
{
slidesPerView
:
2
,
spaceBetween
:
10
},
640
:
{
slidesPerView
:
3
,
spaceBetween
:
16
},
1024
:
{
slidesPerView
:
3
,
spaceBetween
:
24
},
}
}
className=
"partner-swiper"
>
{
images
.
map
((
src
,
i
)
=>
(
<
SwiperSlide
key=
{
i
}
>
<
div
className=
"aspect-square flex justify-center items-center bg-white rounded-lg shadow"
>
<
img
src=
{
src
}
alt=
{
`partner-${i}`
}
className=
"w-3/4 h-3/4 object-contain"
/>
</
div
>
</
SwiperSlide
>
))
}
</
Swiper
>
</
div
>
</
aside
>
</
section
>
</
div
>
</>
)
);
};
...
...
src/app/(main)/_lib/layout/footer.tsx
View file @
a99fbda4
...
...
@@ -14,7 +14,7 @@ import {
import
Image
from
"next/image"
;
import
vietnamMap
from
"@/assets/vietnam-map-white.png.webp"
;
function
f
ooter
()
{
function
F
ooter
()
{
const
emailRef
=
useRef
<
HTMLInputElement
>
(
null
);
const
checkBoxRef
=
useRef
<
HTMLInputElement
>
(
null
);
const
[
emailError
,
setEmailError
]
=
useState
(
false
);
...
...
@@ -212,4 +212,4 @@ function footer() {
);
}
export
default
f
ooter
;
export
default
F
ooter
;
src/app/(main)/_lib/layout/header.tsx
View file @
a99fbda4
"use client"
;
import
React
,
{
useState
}
from
"react"
;
import
{
useRouter
}
from
"next/navigation"
;
import
{
Menu
,
X
,
Facebook
,
Linkedin
,
Twitter
,
Youtube
}
from
"lucide-react"
;
import
logo
from
"@/assets/VCCI-HCM-logo-VN-2025.png"
;
import
Image
from
"next/image"
;
import
MenuItem
from
"./MenuItem"
;
import
Link
from
"next/link"
;
function
Header
()
{
const
[
toggleMenu
,
setToggleMenu
]
=
useState
<
boolean
>
(
false
);
const
router
=
useRouter
();
return
(
<>
<
div
className=
"sticky top-0 w-full h-
[56px]
hidden lg:flex items-center justify-center bg-[#063e8e]"
>
<
div
className=
"sticky top-0 w-full h-
14
hidden lg:flex items-center justify-center bg-[#063e8e]"
>
<
div
className=
"container w-full px-4 flex items-center justify-between"
>
<
div
className=
"flex items-center gap-3"
>
<
div
className=
"w-[130px] h-
[36px]
bg-[#e8c518] flex items-center justify-center border-4 rounded-sm border-[#647792]"
>
<
a
className=
"font-
[600] text-[14px] text-[#063E8E]
hover:text-white transition"
href=
"
#
"
<
div
className=
"w-[130px] h-
9
bg-[#e8c518] flex items-center justify-center border-4 rounded-sm border-[#647792]"
>
<
Link
className=
"font-
semibold text-[14px] text-primary
hover:text-white transition"
href=
"
https://vccihcm.vn/dang-ky
"
>
Đăng Ký Hội Viên
</
a
>
</
Link
>
</
div
>
<
a
<
Link
className=
"px-3 py-2 text-[14px] text-white hover:opacity-80"
href=
"
#
"
href=
"
/site-map
"
>
s
itemap
</
a
>
<
a
S
itemap
</
Link
>
<
Link
className=
"px-3 py-2 text-[14px] text-white hover:opacity-80"
href=
"
#
"
href=
"
https://vccihcm.vn/lien-he
"
>
Liên hệ
</
a
>
</
Link
>
</
div
>
<
div
className=
"flex items-center gap-8"
>
...
...
@@ -41,6 +42,14 @@ function Header() {
className=
"bg-white h-12 rounded-sm outline-none px-4 w-64 placeholder:text-sm"
type=
"text"
placeholder=
"Tìm kiếm"
onKeyDown=
{
(
e
)
=>
{
if
(
e
.
key
===
"Enter"
)
{
const
value
=
(
e
.
currentTarget
as
HTMLInputElement
).
value
||
""
;
const
encoded
=
encodeURIComponent
(
value
);
router
.
push
(
`/search?q=${encoded}`
);
}
}
}
/>
<
div
className=
"flex gap-2"
>
{
[
Facebook
,
Twitter
,
Youtube
,
Linkedin
].
map
((
Icon
,
i
)
=>
(
...
...
@@ -57,17 +66,17 @@ function Header() {
</
div
>
</
div
>
<
div
className=
"sticky top-0 z-50 bg-[#ededed] shadow-md"
>
<
div
className=
"sticky top-0 z-50 bg-[#ededed] shadow-md
py-4
"
>
<
div
className=
"container m-auto"
>
<
div
className=
"w-full flex justify-between items-center"
>
{
/* Logo */
}
<
a
href=
"/"
className=
"flex items-center
"
>
<
Link
href=
"/
"
>
<
Image
className=
"w-[140px] object-contain
lg:ml-0 ml-10
"
className=
"w-[140px] object-contain"
src=
{
logo
}
alt=
"VCCI HCM"
/>
</
a
>
</
Link
>
{
/* Desktop Menu */
}
<
nav
className=
"hidden lg:flex items-center"
>
...
...
@@ -112,7 +121,7 @@ function Header() {
items=
{
[
{
title
:
"Định Nghĩa Chung"
,
link
:
"
dinh-nghia-chung
"
,
link
:
""
,
},
{
title
:
"Mục Đích Của C/O"
,
...
...
src/app/(main)/dai-dien-gioi-chu/chu-de/[id]/page.tsx
View file @
a99fbda4
...
...
@@ -20,6 +20,10 @@ const Page: React.FC = () => {
<
div
className=
"grid grid-cols-1 lg:grid-cols-3 gap-6"
>
{
/* Main content */
}
<
main
className=
"lg:col-span-2 bg-white border rounded-md p-6"
>
<
div
className=
'pb-5 text-primary text-2xl leading-normal font-medium'
>
{
data
?.
responseData
?.
title
}
</
div
>
<
hr
className=
"py-2"
/>
<
div
className=
"p-7.5 prose tiptap overflow-hidden"
>
{
parse
(
data
?.
responseData
?.
description
??
''
)
}
</
div
>
</
main
>
...
...
src/app/(main)/dai-dien-gioi-chu/chu-de/page.tsx
View file @
a99fbda4
...
...
@@ -9,15 +9,17 @@ import Image from "next/image";
import
{
useGetNews
}
from
"@api/endpoints/news"
;
import
{
GetNewsResponseType
}
from
"@api/types/NewsPage.type"
;
import
{
PATHS
}
from
"@constants/paths"
;
import
{
Spinner
}
from
"@components/ui/spinner"
;
export
default
function
Page
()
{
const
[
submitSearch
]
=
useState
(
""
);
const
[
page
,
setPage
]
=
useState
(
1
);
const
pageSize
=
5
;
const
{
data
:
allData
}
=
useGetNews
<
GetNewsResponseType
>
({
const
{
data
:
allData
,
isLoading
}
=
useGetNews
<
GetNewsResponseType
>
({
pageSize
:
String
(
pageSize
),
currentPage
:
String
(
page
),
filters
:
submitSearch
?
`title @=
${
submitSearch
}
`
:
undefined
,
// filters: submitSearch ? `title @=${submitSearch}` : undefined,
filters
:
'category@=Chủ đề'
});
return
(
<
div
className=
"min-h-screen container mx-auto p-4"
>
...
...
@@ -28,26 +30,35 @@ export default function Page() {
{
/* Main content */
}
<
main
className=
"lg:col-span-2 bg-background "
>
<
div
className=
"pb-5 overflow-hidden"
>
{
allData
?.
responseData
.
rows
.
map
((
news
)
=>
(
<
NewsContent
key=
{
news
.
id
}
news=
{
news
}
link=
{
`${PATHS.ownerRepresentatives}/chu-de/${news.id}`
}
/>
))
}
{
isLoading
?
(
<
div
className=
"flex justify-center items-center py-12"
>
<
Spinner
className=
"size-8"
/>
<
span
className=
"ml-2 text-gray-600"
>
Đang tải dữ liệu...
</
span
>
</
div
>
)
:
(
<>
{
allData
?.
responseData
.
rows
.
map
((
news
)
=>
(
<
NewsContent
key=
{
news
.
id
}
news=
{
news
}
link=
{
`${PATHS.ownerRepresentatives}/chu-de/${news.id}`
}
/>
))
}
<
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
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
>
...
...
src/app/(main)/dai-dien-gioi-chu/components/card-news/index.tsx
View file @
a99fbda4
...
...
@@ -2,8 +2,24 @@
import
{
NewsItem
}
from
'@app/dai-dien-gioi-chu/lib/types/NewsPage.type'
;
import
Links
from
'@links/index'
import
dayjs
from
'dayjs'
;
import
parse
from
'html-react-parser'
function
NewsContent
({
news
,
link
}:
{
news
:
NewsItem
,
link
:
string
})
{
import
{
EventItem
}
from
"@api/types/event"
;
// Helper: remove <img> tags and extract plain text from HTML
const
stripImagesAndHtml
=
(
html
?:
string
)
=>
{
if
(
!
html
)
return
''
// remove img tags first
const
withoutImgs
=
html
.
replace
(
/<img
[^
>
]
*>/gi
,
''
)
// use DOMParser on client for robust extraction
if
(
typeof
window
!==
'undefined'
&&
typeof
DOMParser
!==
'undefined'
)
{
try
{
const
doc
=
new
DOMParser
().
parseFromString
(
withoutImgs
,
'text/html'
)
return
doc
.
body
.
textContent
||
''
}
catch
{
// fallback to regex
}
}
return
withoutImgs
.
replace
(
/<
[^
>
]
*>/g
,
''
)
}
function
NewsContent
({
news
,
link
,
event
}:
{
news
?:
NewsItem
,
link
:
string
,
event
?:
EventItem
})
{
return
(
<
a
...
...
@@ -11,8 +27,8 @@ function NewsContent({ news ,link}: { news: NewsItem ,link:string}) {
className=
"flex flex-col hover:no-underline sm:flex-row gap-2 mb-6 bg-white rounded-lg shadow-sm p-4 border items-start min-w-0"
>
<
img
src=
{
`${Links.imageEndpoint}${news
.thumbnail
}`
}
alt=
{
news
.
titl
e
}
src=
{
`${Links.imageEndpoint}${news
?news.thumbnail:event?.image
}`
}
alt=
{
news
?
news
.
title
:
event
?.
nam
e
}
className=
"w-full sm:w-56 md:w-64 h-40 md:h-36 object-cover shrink-0"
onError=
{
(
e
)
=>
{
e
.
currentTarget
.
src
=
"/img-error.png"
...
...
@@ -21,14 +37,14 @@ function NewsContent({ news ,link}: { news: NewsItem ,link:string}) {
/>
<
div
className=
"flex-1 min-w-0 pl-0 sm:pl-4"
>
<
p
className=
"text-primary font-semibold text-base md:text-lg hover:underline line-clamp-2 wrap-break-word hover:no-underline
"
>
{
news
.
titl
e
}
<
p
className=
"text-primary font-semibold text-base md:text-lg hover:underline line-clamp-2 wrap-break-word
"
>
{
news
?.
title
}{
event
?.
nam
e
}
</
p
>
<
div
className=
"text-sm my-2 text-[#00AED5]"
>
{
dayjs
(
news
.
release
_at
).
format
(
'DD/MM/YYYY'
)
}
</
div
>
<
div
className=
"text-sm my-2 text-[#00AED5]"
>
{
dayjs
(
news
?
news
?.
created_at
:
event
?.
created
_at
).
format
(
'DD/MM/YYYY'
)
}
</
div
>
<
div
className=
"text-sm text-[#777] line-clamp-3"
>
<
div
className=
"text-sm prose tiptap"
>
{
parse
(
news
.
description
)
}
</
div
>
<
div
className=
"text-sm prose tiptap"
>
{
stripImagesAndHtml
(
news
?
news
.
description
:
event
?
.
description
)
}
</
div
>
</
div
>
</
div
>
</
a
>
...
...
src/app/(main)/dai-dien-gioi-chu/lib/sampleHtml.ts
View file @
a99fbda4
export
const
SAMPLE_HTML
=
`
<div class="document">
<h1
style="font-size:18px; font-weight:700; margin-bottom:8
px;">Chức năng Đại diện Người sử dụng lao động</h1>
<h1
class="text-primary" style="font-size:20px; font-weight:700; margin-bottom:12
px;">Chức năng Đại diện Người sử dụng lao động</h1>
<p>Chức năng Đại diện Người sử dụng lao động (NSDLĐ):</p>
...
...
src/app/(main)/dai-dien-gioi-chu/tap-huan-nsdld/[id]/page.tsx
View file @
a99fbda4
...
...
@@ -20,6 +20,10 @@ const Page: React.FC = () => {
<
div
className=
"grid grid-cols-1 lg:grid-cols-3 gap-6"
>
{
/* Main content */
}
<
main
className=
"lg:col-span-2 bg-white border rounded-md p-6"
>
<
div
className=
'pb-5 text-primary text-2xl leading-normal font-medium'
>
{
data
?.
responseData
?.
title
}
</
div
>
<
hr
className=
"py-2"
/>
<
div
className=
"p-7.5 prose tiptap overflow-hidden"
>
{
parse
(
data
?.
responseData
?.
description
??
''
)
}
</
div
>
</
main
>
...
...
src/app/(main)/dai-dien-gioi-chu/tap-huan-nsdld/page.tsx
View file @
a99fbda4
...
...
@@ -9,12 +9,13 @@ import Image from "next/image";
import
{
useGetNews
}
from
"@api/endpoints/news"
;
import
{
GetNewsResponseType
}
from
"@api/types/NewsPage.type"
;
import
{
PATHS
}
from
"@constants/paths"
;
import
{
Spinner
}
from
"@components/ui/spinner"
;
export
default
function
Page
()
{
const
[
submitSearch
]
=
useState
(
""
);
const
[
page
,
setPage
]
=
useState
(
1
);
const
pageSize
=
5
;
const
{
data
:
allData
}
=
useGetNews
<
GetNewsResponseType
>
({
const
{
data
:
allData
,
isLoading
}
=
useGetNews
<
GetNewsResponseType
>
({
pageSize
:
String
(
pageSize
),
currentPage
:
String
(
page
),
filters
:
submitSearch
?
`title @=
${
submitSearch
}
`
:
undefined
,
...
...
@@ -28,26 +29,35 @@ export default function Page() {
{
/* Main content */
}
<
main
className=
"lg:col-span-2 bg-background "
>
<
div
className=
"pb-5 overflow-hidden"
>
{
allData
?.
responseData
.
rows
.
map
((
news
)
=>
(
<
NewsContent
key=
{
news
.
id
}
news=
{
news
}
link=
{
`${PATHS.ownerRepresentatives}/tap-huan-nsdld/${news.id}`
}
/>
))
}
{
isLoading
?
(
<
div
className=
"flex justify-center items-center py-12"
>
<
Spinner
className=
"size-8"
/>
<
span
className=
"ml-2 text-gray-600"
>
Đang tải tập huấn NSDLĐ...
</
span
>
</
div
>
)
:
(
<>
{
allData
?.
responseData
.
rows
.
map
((
news
)
=>
(
<
NewsContent
key=
{
news
.
id
}
news=
{
news
}
link=
{
`${PATHS.ownerRepresentatives}/tap-huan-nsdld/${news.id}`
}
/>
))
}
<
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
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
>
...
...
src/app/(main)/dai-dien-gioi-chu/tin-lien-quan/[id]/page.tsx
View file @
a99fbda4
...
...
@@ -4,23 +4,31 @@ import Image from "next/image";
import
ListCategory
from
"../../components/list-category"
;
import
{
OWNER_REPRESENTATIVES_CATEGORIES
}
from
"@constants/categories"
;
import
ListFilter
from
"../../components/list-filter"
;
import
{
useGetNewsId
}
from
'@/api/endpoints/news'
;
import
{
useGetNewsId
}
from
"@/api/endpoints/news"
;
import
parse
from
"html-react-parser"
;
import
{
useParams
}
from
'next/navigation'
import
{
GetNewsDetailResponseType
}
from
'@lib/types/news-detail-response-data'
;
import
{
useParams
}
from
"next/navigation"
;
import
{
GetNewsDetailResponseType
}
from
"@lib/types/news-detail-response-data"
;
// ...existing code...
const
Page
:
React
.
FC
=
()
=>
{
const
{
id
}
=
useParams
()
const
{
data
,
isLoading
}
=
useGetNewsId
<
GetNewsDetailResponseType
>
(
id
as
string
)
const
{
id
}
=
useParams
();
const
{
data
,
isLoading
}
=
useGetNewsId
<
GetNewsDetailResponseType
>
(
id
as
string
);
return
(
<
div
className=
"min-h-screen w-full container mx-auto p-4"
>
<
div
className=
"w-full flex flex-col gap-5"
>
<
ListCategory
categories=
{
OWNER_REPRESENTATIVES_CATEGORIES
}
/>
<
ListCategory
categories=
{
OWNER_REPRESENTATIVES_CATEGORIES
}
/>
<
div
className=
"grid grid-cols-1 lg:grid-cols-3 gap-6"
>
{
/* Main content */
}
<
main
className=
"lg:col-span-2 bg-white border rounded-md p-6"
>
<
div
className=
"p-7.5 prose tiptap overflow-hidden"
>
{
parse
(
data
?.
responseData
?.
description
??
''
)
}
</
div
>
<
div
className=
"pb-5 text-primary text-2xl leading-normal font-medium"
>
{
data
?.
responseData
?.
title
}
</
div
>
<
hr
className=
"py-2"
/>
<
div
className=
"p-7.5 prose tiptap overflow-hidden"
>
{
parse
(
data
?.
responseData
?.
description
??
""
)
}
</
div
>
</
main
>
{
/* Sidebar */
}
...
...
src/app/(main)/dai-dien-gioi-chu/tin-lien-quan/page.tsx
View file @
a99fbda4
...
...
@@ -9,14 +9,16 @@ import Image from "next/image";
import
{
useGetNews
}
from
"@api/endpoints/news"
;
import
{
GetNewsResponseType
}
from
"@api/types/NewsPage.type"
;
import
{
PATHS
}
from
"@constants/paths"
;
import
{
Spinner
}
from
"@components/ui/spinner"
;
export
default
function
Page
()
{
const
[
submitSearch
]
=
useState
(
""
);
const
[
submitSearch
,
setsubmitSearch
]
=
useState
(
""
);
const
[
page
,
setPage
]
=
useState
(
1
);
const
pageSize
=
5
;
const
{
data
:
allData
}
=
useGetNews
<
GetNewsResponseType
>
({
const
{
data
:
allData
,
isLoading
}
=
useGetNews
<
GetNewsResponseType
>
({
pageSize
:
String
(
pageSize
),
currentPage
:
String
(
page
),
filters
:
submitSearch
?
`title @=
${
submitSearch
}
`
:
undefined
,
filters
:
submitSearch
?
`title @=
${
submitSearch
}
,category@=Tin liên quan`
:
'category@=Tin liên quan'
,
});
return
(
<
div
className=
"min-h-screen container mx-auto p-4"
>
...
...
@@ -27,25 +29,34 @@ export default function Page() {
{
/* Main content */
}
<
main
className=
"lg:col-span-2 bg-background "
>
<
div
className=
"pb-5 overflow-hidden"
>
{
allData
?.
responseData
.
rows
.
map
((
news
)
=>
(
<
NewsContent
key=
{
news
.
id
}
news=
{
news
}
link=
{
`${PATHS.ownerRepresentatives}/tin-lien-quan/${news.id}`
}
/>
))
}
{
isLoading
?
(
<
div
className=
"flex justify-center items-center py-12"
>
<
Spinner
className=
"size-8"
/>
<
span
className=
"ml-2 text-gray-600"
>
Đang tải tin liên quan...
</
span
>
</
div
>
)
:
(
<>
{
allData
?.
responseData
.
rows
.
map
((
news
)
=>
(
<
NewsContent
key=
{
news
.
id
}
news=
{
news
}
link=
{
`${PATHS.ownerRepresentatives}/tin-lien-quan/${news.id}`
}
/>
))
}
<
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
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 order-first lg:order-last"
>
<
ListFilter
/>
<
ListFilter
onSearch=
{
setsubmitSearch
}
/>
<
div
className=
"bg-white border rounded-md overflow-hidden hidden lg:block"
>
<
div
className=
"w-full h-56 relative bg-gray-100"
>
...
...
src/app/(main)/hoat-dong/dao-tao/[id]/page.tsx
View file @
a99fbda4
...
...
@@ -20,6 +20,10 @@ const Page: React.FC = () => {
<
div
className=
"grid grid-cols-1 lg:grid-cols-3 gap-6"
>
{
/* Main content */
}
<
main
className=
"lg:col-span-2 bg-white border rounded-md p-6"
>
<
div
className=
'pb-5 text-primary text-2xl leading-normal font-medium'
>
{
data
?.
responseData
?.
title
}
</
div
>
<
hr
className=
"py-2"
/>
<
div
className=
"p-7.5 prose tiptap overflow-hidden"
>
{
parse
(
data
?.
responseData
?.
description
??
''
)
}
</
div
>
</
main
>
...
...
src/app/(main)/hoat-dong/dao-tao/page.tsx
View file @
a99fbda4
...
...
@@ -4,43 +4,53 @@ import ListCategory from "@app/dai-dien-gioi-chu/components/list-category";
import
{
EVENT_CATEGORIES
}
from
"@constants/categories"
;
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
{
Pagination
}
from
"@components/base/pagination"
;
import
Image
from
"next/image"
;
import
{
useGetNews
}
from
"@api/endpoints/news"
;
import
{
GetNewsResponseType
}
from
"@api/types/NewsPage.type"
;
import
{
PATHS
}
from
"@constants/paths"
;
import
{
Spinner
}
from
"@components/ui/spinner"
;
export
default
function
Page
()
{
const
[
submitSearch
]
=
useState
(
""
);
const
[
page
,
setPage
]
=
useState
(
1
);
const
pageSize
=
5
;
const
{
data
:
allData
}
=
useGetNews
<
GetNewsResponseType
>
({
const
{
data
:
allData
,
isLoading
}
=
useGetNews
<
GetNewsResponseType
>
({
pageSize
:
String
(
pageSize
),
currentPage
:
String
(
page
),
filters
:
submitSearch
?
`title @=
${
submitSearch
}
`
:
undefined
,
filters
:
submitSearch
?
`title @=
${
submitSearch
}
`
:
'category @=Đào tạo'
,
});
return
(
<
div
className=
"min-h-screen container mx-auto p-4"
>
<
div
className=
"w-full flex flex-col gap-5"
>
<
ListCategory
categories=
{
EVENT_CATEGORIES
}
/>
<
ListCategory
categories=
{
EVENT_CATEGORIES
}
/>
<
div
className=
"grid grid-cols-1 lg:grid-cols-3 gap-6"
>
{
/* Main content */
}
<
main
className=
"lg:col-span-2 bg-background "
>
<
div
className=
"pb-5 overflow-hidden"
>
{
allData
?.
responseData
.
rows
.
map
((
news
)
=>
(
<
NewsContent
key=
{
news
.
id
}
news=
{
news
}
link=
{
`${PATHS.event}/dao-tao/${news.id}`
}
/>
))
}
{
isLoading
?
(
<
div
className=
"flex justify-center items-center py-12"
>
<
Spinner
className=
"size-8"
/>
<
span
className=
"ml-2 text-gray-600"
>
Đang tải khóa đào tạo...
</
span
>
</
div
>
)
:
(
<>
{
allData
?.
responseData
.
rows
.
map
((
news
)
=>
(
<
NewsContent
key=
{
news
.
id
}
news=
{
news
}
link=
{
`${PATHS.event}/dao-tao/${news.id}`
}
/>
))
}
<
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
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
>
...
...
src/app/(main)/hoat-dong/page.tsx
View file @
a99fbda4
...
...
@@ -15,7 +15,7 @@ export default function Page() {
return
(
<
div
className=
"min-h-screen container mx-auto p-4"
>
<
div
className=
"w-full flex flex-col gap-5"
>
<
ListCategory
categories=
{
EVENT_CATEGORIES
}
/>
<
ListCategory
categories=
{
EVENT_CATEGORIES
}
/>
</
div
>
</
div
>
);
...
...
src/app/(main)/hoat-dong/su-kien/[id]/page.tsx
View file @
a99fbda4
...
...
@@ -4,14 +4,16 @@ import Image from "next/image";
import
ListCategory
from
"@app/dai-dien-gioi-chu/components/list-category"
;
import
{
EVENT_CATEGORIES
}
from
"@constants/categories"
;
import
ListFilter
from
"@app/dai-dien-gioi-chu/components/list-filter"
;
import
{
useGetNewsId
}
from
'@/api/endpoints/news
'
;
import
{
useGetEventsId
}
from
'@/api/endpoints/event
'
;
import
parse
from
"html-react-parser"
;
import
{
useParams
}
from
'next/navigation'
import
{
GetNewsDetailResponseType
}
from
'@lib/types/news-detail-response-data'
;
import
{
GetEventsIdQueryResponseType
}
from
'@api/types/event'
;
import
{
Spinner
}
from
"@components/ui/spinner"
;
// ...existing code...
const
Page
:
React
.
FC
=
()
=>
{
const
{
id
}
=
useParams
()
const
{
data
,
isLoading
}
=
useGet
NewsId
<
GetNewsDetail
ResponseType
>
(
id
as
string
)
const
{
data
,
isLoading
}
=
useGet
EventsId
<
GetEventsIdQuery
ResponseType
>
(
id
as
string
)
return
(
<
div
className=
"min-h-screen w-full container mx-auto p-4"
>
<
div
className=
"w-full flex flex-col gap-5"
>
...
...
@@ -20,7 +22,20 @@ const Page: React.FC = () => {
<
div
className=
"grid grid-cols-1 lg:grid-cols-3 gap-6"
>
{
/* Main content */
}
<
main
className=
"lg:col-span-2 bg-white border rounded-md p-6"
>
<
div
className=
"p-7.5 prose tiptap overflow-hidden"
>
{
parse
(
data
?.
responseData
?.
description
??
''
)
}
</
div
>
{
isLoading
?
(
<
div
className=
"flex justify-center items-center py-12"
>
<
Spinner
className=
"size-8"
/>
<
span
className=
"ml-2 text-gray-600"
>
Đang tải chi tiết sự kiện...
</
span
>
</
div
>
)
:
(
<>
<
div
className=
'pb-5 text-primary text-2xl leading-normal font-medium'
>
{
data
?.
responseData
?.
name
}
</
div
>
<
hr
className=
"py-2"
/>
<
div
className=
"p-7.5 prose tiptap overflow-hidden"
>
{
parse
(
data
?.
responseData
?.
description
??
''
)
}
</
div
>
</>
)
}
</
main
>
{
/* Sidebar */
}
...
...
src/app/(main)/hoat-dong/su-kien/page.tsx
View file @
a99fbda4
...
...
@@ -5,43 +5,57 @@ import { EVENT_CATEGORIES } from "@constants/categories";
import
EventFilter
from
"@app/dai-dien-gioi-chu/components/event-filter"
;
import
NewsContent
from
"@app/dai-dien-gioi-chu/components/card-news"
;
// ...existing code...
import
{
Pagination
}
from
"@components/base/pagination"
;
import
{
Pagination
}
from
"@components/base/pagination"
;
import
Image
from
"next/image"
;
import
{
useGetEvents
}
from
'@api/endpoints/event'
import
{
EventApiResponse
}
from
'@api/types/event'
import
{
useGetNews
}
from
"@api/endpoints/news"
;
import
{
GetNewsResponseType
}
from
"@api/types/NewsPage.type"
;
import
{
PATHS
}
from
"@constants/paths"
;
import
{
Spinner
}
from
"@components/ui/spinner"
;
export
default
function
Page
()
{
const
[
submitSearch
]
=
useState
(
""
);
const
[
page
,
setPage
]
=
useState
(
1
);
const
pageSize
=
5
;
const
{
data
:
allData
}
=
useGetNews
<
GetNewsResponseTyp
e
>
({
const
{
data
:
allData
,
isLoading
}
=
useGetEvents
<
EventApiRespons
e
>
({
pageSize
:
String
(
pageSize
),
currentPage
:
String
(
page
),
filters
:
submitSearch
?
`title @=
${
submitSearch
}
`
:
undefined
,
sortField
:
'start_time'
,
sortOrder
:
'ASC'
,
filters
:
submitSearch
?
`title @=
${
submitSearch
}
,start_time>
${
new
Date
()}
`
:
`start_time>
${
new
Date
()}
`
,
});
return
(
<
div
className=
"min-h-screen container mx-auto p-4"
>
<
div
className=
"w-full flex flex-col gap-5"
>
<
ListCategory
categories=
{
EVENT_CATEGORIES
}
/>
<
ListCategory
categories=
{
EVENT_CATEGORIES
}
/>
<
div
className=
"grid grid-cols-1 lg:grid-cols-3 gap-6"
>
{
/* Main content */
}
<
main
className=
"lg:col-span-2 bg-background "
>
<
div
className=
"pb-5 overflow-hidden"
>
{
allData
?.
responseData
.
rows
.
map
((
news
)
=>
(
<
NewsContent
key=
{
news
.
id
}
news=
{
news
}
link=
{
`${PATHS.event}/su-kien/${news.id}`
}
/>
))
}
{
isLoading
?
(
<
div
className=
"flex justify-center items-center py-12"
>
<
Spinner
className=
"size-8"
/>
<
span
className=
"ml-2 text-gray-600"
>
Đang tải sự kiện...
</
span
>
</
div
>
)
:
(
<>
{
allData
?.
responseData
.
rows
.
map
((
event
)
=>
(
<
NewsContent
key=
{
event
.
id
}
event=
{
event
}
link=
{
`${PATHS.event}/su-kien/${event.id}`
}
/>
))
}
<
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
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
>
...
...
src/app/(main)/hoi-vien/ket-noi-hoi-vien/page.tsx
View file @
a99fbda4
...
...
@@ -7,6 +7,7 @@ import { Pagination } from "@components/base/pagination";
import
Image
from
"next/image"
;
import
{
useGetNews
}
from
"@api/endpoints/news"
;
import
{
GetNewsResponseType
}
from
"@api/types/NewsPage.type"
;
import
{
Spinner
}
from
"@components/ui/spinner"
;
export
default
function
Page
()
{
const
[
submitSearch
]
=
useState
(
""
);
...
...
@@ -16,7 +17,7 @@ export default function Page() {
const
{
data
:
allData
,
isLoading
}
=
useGetNews
<
GetNewsResponseType
>
({
pageSize
:
String
(
pageSize
),
currentPage
:
String
(
page
),
filters
:
submitSearch
?
`title @=
${
submitSearch
}
`
:
undefined
,
filters
:
submitSearch
?
`title @=
${
submitSearch
}
`
:
`category @=Kết nối hội viên`
,
});
return
(
<
div
className=
"min-h-screen container mx-auto pb-4"
>
...
...
@@ -26,28 +27,37 @@ export default function Page() {
{
/* Main content */
}
<
main
className=
"lg:col-span-2 bg-background"
>
<
div
className=
"pb-5 overflow-hidden"
>
{
allData
?.
responseData
.
rows
.
map
((
news
)
=>
(
<
CardNews
key=
{
news
.
id
}
news=
{
news
}
/>
))
}
{
isLoading
?
(
<
div
className=
"flex justify-center items-center py-12"
>
<
Spinner
className=
"size-8"
/>
<
span
className=
"ml-2 text-gray-600"
>
Đang tải dữ liệu kết nối hội viên...
</
span
>
</
div
>
)
:
(
<>
{
allData
?.
responseData
.
rows
.
map
((
news
)
=>
(
<
CardNews
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
>
<
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"
>
...
...
src/app/(main)/hoi-vien/tin-hoi-vien/page.tsx
View file @
a99fbda4
...
...
@@ -7,6 +7,7 @@ import { Pagination } from '@components/base/pagination'
import
Image
from
"next/image"
;
import
{
useGetNews
}
from
'@api/endpoints/news'
import
{
GetNewsResponseType
}
from
'@api/types/NewsPage.type'
import
{
Spinner
}
from
"@components/ui/spinner"
;
export
default
function
Page
()
{
const
[
submitSearch
]
=
useState
(
''
)
...
...
@@ -16,7 +17,7 @@ export default function Page() {
const
{
data
:
allData
,
isLoading
}
=
useGetNews
<
GetNewsResponseType
>
({
pageSize
:
String
(
pageSize
),
currentPage
:
String
(
page
),
filters
:
submitSearch
?
`title @=
${
submitSearch
}
`
:
undefined
,
filters
:
submitSearch
?
`title @=
${
submitSearch
}
`
:
'category @=Tin hội viên'
,
})
return
(
<
div
className=
"min-h-screen container mx-auto pb-4"
>
...
...
@@ -26,26 +27,35 @@ export default function Page() {
{
/* Main content */
}
<
main
className=
"lg:col-span-2 bg-background"
>
<
div
className=
'pb-5 overflow-hidden'
>
{
allData
?.
responseData
.
rows
.
map
((
news
)
=>
(
<
CardNews
key=
{
news
.
id
}
news=
{
news
}
/>
))
}
{
isLoading
?
(
<
div
className=
"flex justify-center items-center py-12"
>
<
Spinner
className=
"size-8"
/>
<
span
className=
"ml-2 text-gray-600"
>
Đang tải tin hội viên...
</
span
>
</
div
>
)
:
(
<>
{
allData
?.
responseData
.
rows
.
map
((
news
)
=>
(
<
CardNews
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
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
>
...
...
src/app/(main)/search/page.tsx
0 → 100644
View file @
a99fbda4
"use client"
;
import
React
,
{
useState
,
Suspense
}
from
"react"
;
import
ListCategory
from
"@app/dai-dien-gioi-chu/components/list-category"
;
import
{
OWNER_REPRESENTATIVES_CATEGORIES
}
from
"@constants/categories"
;
import
ListFilter
from
"@app/dai-dien-gioi-chu/components/list-filter"
;
import
NewsContent
from
"@app/dai-dien-gioi-chu/components/card-news"
;
import
{
Pagination
}
from
"@components/base/pagination"
;
import
Image
from
"next/image"
;
import
{
useGetNews
}
from
"@api/endpoints/news"
;
import
{
GetNewsResponseType
}
from
"@api/types/NewsPage.type"
;
import
{
Spinner
}
from
"@components/ui/spinner"
;
import
{
PATHS
}
from
"@constants/paths"
;
import
{
useSearchParams
}
from
'next/navigation'
function
SearchContent
()
{
const
[
page
,
setPage
]
=
useState
(
1
);
const
searchParams
=
useSearchParams
()
const
query
=
searchParams
.
get
(
'q'
)
//
const
pageSize
=
5
;
const
{
data
:
allData
,
isLoading
}
=
useGetNews
<
GetNewsResponseType
>
({
pageSize
:
String
(
pageSize
),
currentPage
:
String
(
page
),
filters
:
query
?
`title @=
${
query
}
`
:
undefined
,
});
return
(
<
div
className=
"min-h-screen container mx-auto p-4"
>
<
div
className=
"w-full flex flex-col gap-5"
>
<
div
className=
"border-t border-gray-200 bg-white p-2.5"
>
<
div
className=
"w-full px-4 sm:px-6 lg:px-8"
>
<
div
className=
"py-3"
>
<
h1
className=
"text-md md:text-lg font-semibold leading-6 text-gray-900"
>
{
" "
}
Search Results for:
{
query
}
</
h1
>
</
div
>
</
div
>
</
div
>
<
div
className=
"grid grid-cols-1 lg:grid-cols-3 gap-6"
>
{
/* Main content */
}
<
main
className=
"lg:col-span-2 bg-background "
>
<
div
className=
"pb-5 overflow-hidden"
>
{
isLoading
?
(
<
div
className=
"flex justify-center items-center py-12"
>
<
Spinner
className=
"size-8"
/>
<
span
className=
"ml-2 text-gray-600"
>
Đang tìm kiếm...
</
span
>
</
div
>
)
:
(
<>
{
allData
?.
responseData
.
rows
.
map
((
news
)
=>
(
<
NewsContent
key=
{
news
.
id
}
news=
{
news
}
link=
{
`${PATHS.mediaInformation}/tin-vcci/${news.id}`
}
/>
))
}
<
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 order-first lg:order-last"
>
<
div
className=
"bg-white border rounded-md overflow-hidden hidden lg:block"
>
<
div
className=
"w-full h-62 relative bg-gray-100"
>
<
Image
src=
"/banner.webp"
alt=
"Quảng cáo"
fill
className=
"object-cover"
/>
</
div
>
</
div
>
</
aside
>
</
div
>
</
div
>
</
div
>
);
}
export
default
function
Page
()
{
return
(
<
Suspense
fallback=
{
<
div
className=
"min-h-screen container mx-auto p-4 flex items-center justify-center"
>
<
div
className=
"text-center"
>
<
div
className=
"animate-spin rounded-full h-12 w-12 border-b-2 border-[#063e8e] mx-auto"
></
div
>
<
p
className=
"mt-4 text-gray-600"
>
Loading search results...
</
p
>
</
div
>
</
div
>
}
>
<
SearchContent
/>
</
Suspense
>
);
}
src/app/(main)/site-map/_lib/mock-data.ts
0 → 100644
View file @
a99fbda4
export
type
SiteMapItem
=
{
label
:
string
;
url
:
string
;
children
?:
SiteMapItem
[];
};
export
const
MOCK_SITEMAP
:
SiteMapItem
[]
=
[
{
label
:
"Trang chủ"
,
url
:
"/homepage"
,
children
:
[]
},
{
label
:
"Giới thiệu"
,
url
:
"/gioi-thieu"
,
children
:
[
{
label
:
"Về VCCI-HCM"
,
url
:
"/gioi-thieu/ve-vcci-hcm"
,
children
:
[]
},
{
label
:
"Chức năng và nhiệm vụ"
,
url
:
"/gioi-thieu/chuc-nang-nhiem-vu"
,
children
:
[]
},
{
label
:
"Sơ đồ tổ chức"
,
url
:
"/gioi-thieu/so-do-to-chuc"
,
children
:
[]
},
{
label
:
"Dịch vụ cung cấp"
,
url
:
"/gioi-thieu/dich-vu-cung-cap"
,
children
:
[]
},
],
},
{
label
:
"Hội viên"
,
url
:
"/hoi-vien"
,
children
:
[
{
label
:
"Lợi ích của hội viên VCCI"
,
url
:
"/hoi-vien/loi-ich-hoi-vien"
,
children
:
[]
},
{
label
:
"Đăng ký hội viên"
,
url
:
"/hoi-vien/dang-ky-hoi-vien"
,
children
:
[]
},
{
label
:
"Kết nối hội viên"
,
url
:
"/hoi-vien/ket-noi-hoi-vien"
,
children
:
[]
},
{
label
:
"Tin hội viên"
,
url
:
"/hoi-vien/tin-hoi-vien"
,
children
:
[]
},
],
},
{
label
:
"Hoạt động"
,
url
:
"/hoat-dong"
,
children
:
[
{
label
:
"Sự kiện"
,
url
:
"/hoat-dong/su-kien"
,
children
:
[]
},
{
label
:
"Đào tạo"
,
url
:
"/hoat-dong/dao-tao"
,
children
:
[]
},
],
},
{
label
:
"Xuất xứ hàng hóa"
,
url
:
"/xuat-xu-hang-hoa"
,
children
:
[
{
label
:
"Định nghĩa chung"
,
url
:
"/xuat-xu-hang-hoa"
,
children
:
[]
},
{
label
:
"Mục đích của C/O"
,
url
:
"/xuat-xu-hang-hoa/muc-dich-co"
,
children
:
[]
},
{
label
:
"Luật áp dụng về C/O"
,
url
:
"/xuat-xu-hang-hoa/luat-ap-dung-co"
,
children
:
[]
},
{
label
:
"Thủ tục cấp C/O"
,
url
:
"/xuat-xu-hang-hoa/thu-tuc-cap-co"
,
children
:
[]
},
{
label
:
"Biểu mẫu C/O và cách khai"
,
url
:
"/xuat-xu-hang-hoa/bieu-mau-co"
,
children
:
[]
},
{
label
:
"Phí và lệ phí cấp C/O"
,
url
:
"/xuat-xu-hang-hoa/phi-le-phi-cap-co"
,
children
:
[]
},
{
label
:
"Điểm cấp và thời gian cấp C/O"
,
url
:
"/xuat-xu-hang-hoa/diem-cap-thoi-gian"
,
children
:
[]
},
{
label
:
"Thông tin liên hệ"
,
url
:
"/xuat-xu-hang-hoa/lien-he"
,
children
:
[]
},
],
},
{
label
:
"Đại diện giới chủ"
,
url
:
"/dai-dien-gioi-chu"
,
children
:
[
{
label
:
"Chức năng đại diện người sử dụng lao động"
,
url
:
"/dai-dien-gioi-chu/chuc-nang"
,
children
:
[]
},
{
label
:
"Sự kiện - tập huấn NSDLĐ"
,
url
:
"/dai-dien-gioi-chu/su-kien-tap-huan"
,
children
:
[]
},
{
label
:
"Tin liên quan"
,
url
:
"/dai-dien-gioi-chu/tin-lien-quan"
,
children
:
[]
},
{
label
:
"Chủ đề"
,
url
:
"/dai-dien-gioi-chu/chu-de"
,
children
:
[]
},
],
},
{
label
:
"Xúc tiến thương mại"
,
url
:
"/xuc-tien-thuong-mai"
,
children
:
[
{
label
:
"Hồ sơ thị trường"
,
url
:
"/xuc-tien-thuong-mai/ho-so-thi-truong"
,
children
:
[]
},
{
label
:
"Môi trường kinh doanh"
,
url
:
"/xuc-tien-thuong-mai/doi-song-kinh-doanh"
,
children
:
[]
},
{
label
:
"Cơ hội kinh doanh"
,
url
:
"/xuc-tien-thuong-mai/co-hoi-kinh-doanh"
,
children
:
[]
},
{
label
:
"Hỗ trợ kinh doanh"
,
url
:
"/xuc-tien-thuong-mai/ho-tro-kinh-doanh"
,
children
:
[]
},
],
},
{
label
:
"Thông tin truyền thông"
,
url
:
"/thong-tin-truyen-thong"
,
children
:
[
{
label
:
"Tin VCCI"
,
url
:
"/thong-tin-truyen-thong/tin-vcci"
,
children
:
[]
},
{
label
:
"Tin kinh tế"
,
url
:
"/thong-tin-truyen-thong/tin-kinh-te"
,
children
:
[]
},
{
label
:
"Tin doanh nghiệp"
,
url
:
"/thong-tin-truyen-thong/tin-doanh-nghiep"
,
children
:
[]
},
{
label
:
"Chuyên đề"
,
url
:
"/thong-tin-truyen-thong/chuyen-de"
,
children
:
[]
},
{
label
:
"Thông tin chính sách và pháp luật"
,
url
:
"/thong-tin-truyen-thong/chinh-sach-phap-luat"
,
children
:
[]
},
{
label
:
"Ấn phẩm"
,
url
:
"/thong-tin-truyen-thong/an-pham"
,
children
:
[]
},
{
label
:
"Thư viện tài liệu"
,
url
:
"/thong-tin-truyen-thong/thu-vien-tai-lieu"
,
children
:
[]
},
],
},
];
export
default
MOCK_SITEMAP
;
src/app/(main)/site-map/page.tsx
0 → 100644
View file @
a99fbda4
"use client"
;
import
React
from
"react"
;
import
{
MOCK_SITEMAP
}
from
"./_lib/mock-data"
;
import
Link
from
"next/link"
;
function
SiteMapPage
()
{
const
homepage
=
MOCK_SITEMAP
[
0
];
const
sections
=
MOCK_SITEMAP
.
slice
(
1
);
return
(
<
div
className=
"min-h-screen bg-gray-50 py-12"
>
<
div
className=
"container mx-auto px-4"
>
<
h1
className=
"text-3xl font-bold text-center mb-12 text-[#063e8e]"
>
SƠ ĐỒ TRANG WEB
</
h1
>
{
/* Sitemap Structure */
}
<
div
className=
"relative flex flex-col items-center"
>
{
/* Homepage - Top Level */
}
<
div
className=
"relative mb-20"
>
<
Link
href=
{
homepage
.
url
}
className=
"block bg-[#063e8e] text-white px-8 py-4 rounded-lg font-semibold text-center hover:bg-[#0a4fb5] transition shadow-lg min-w-[200px]"
>
{
homepage
.
label
.
toUpperCase
()
}
</
Link
>
{
/* Vertical line from homepage down */
}
<
div
className=
"absolute left-1/2 -translate-x-1/2 top-full h-16 w-0.5 bg-gray-600"
></
div
>
</
div
>
{
/* Main Sections - Second Level */
}
<
div
className=
"relative w-full max-w-[1400px]"
>
{
/* Horizontal line connecting all sections */
}
<
div
className=
"absolute top-0 left-[7%] right-[7%] h-0.5 bg-gray-600 z-0"
></
div
>
<
div
className=
"grid grid-cols-2 md:grid-cols-4 lg:grid-cols-7 gap-6 relative pt-4"
>
{
sections
.
map
((
section
,
idx
)
=>
(
<
div
key=
{
idx
}
className=
"relative flex flex-col items-center"
>
{
/* Vertical line from horizontal bar down to section */
}
<
div
className=
"absolute -top-4 left-1/2 -translate-x-1/2 h-4 w-0.5 bg-gray-600 z-10"
></
div
>
{
/* Section Box */
}
<
div
className=
"relative z-20"
>
<
Link
href=
{
section
.
url
}
className=
"flex bg-[#063e8e] text-white px-4 py-3 rounded-md font-medium text-center hover:bg-[#0a4fb5] transition shadow-md w-full text-sm min-h-20 items-center justify-center"
>
<
span
className=
"leading-tight"
>
{
section
.
label
.
toUpperCase
()
}
</
span
>
</
Link
>
{
/* Vertical line from section down to children */
}
{
section
.
children
&&
section
.
children
.
length
>
0
&&
(
<
div
className=
"absolute left-1/2 -translate-x-1/2 top-full h-6 w-0.5 bg-gray-600 z-10"
></
div
>
)
}
</
div
>
{
/* Children - Third Level */
}
{
section
.
children
&&
section
.
children
.
length
>
0
&&
(
<
div
className=
"mt-6 flex flex-col gap-3 w-full relative z-20"
>
{
section
.
children
.
map
((
child
,
childIdx
)
=>
(
<
div
key=
{
childIdx
}
className=
"relative"
>
{
/* Vertical line connecting child to parent */
}
{
childIdx
===
0
?
(
// First child connects to the line from parent
<
div
className=
"absolute left-1/2 -translate-x-1/2 -top-6 h-6 w-0.5 bg-gray-600"
></
div
>
)
:
(
// Other children connect to the vertical spine
<
div
className=
"absolute left-1/2 -translate-x-1/2 -top-[calc(1.5rem+0.375rem)] bottom-1/2 w-0.5 bg-gray-600"
></
div
>
)
}
{
/* Horizontal line from spine to child box */
}
<
div
className=
"absolute left-1/2 top-1/2 -translate-y-1/2 w-1/2 h-0.5 bg-gray-600 -translate-x-full"
></
div
>
<
Link
href=
{
child
.
url
}
className=
"block bg-gray-400 text-white px-3 py-2.5 rounded text-xs font-medium text-center hover:bg-gray-500 transition shadow-sm leading-tight relative z-10"
>
{
child
.
label
.
toUpperCase
()
}
</
Link
>
</
div
>
))
}
</
div
>
)
}
</
div
>
))
}
</
div
>
</
div
>
</
div
>
</
div
>
<
style
jsx
>
{
`
@media (max-width: 768px) {
.grid {
grid-template-columns: repeat(2, 1fr);
}
}
`
}
</
style
>
</
div
>
);
}
export
default
SiteMapPage
;
src/app/(main)/thong-tin-truyen-thong/chuyen-de/[id]/page.tsx
View file @
a99fbda4
...
...
@@ -20,6 +20,10 @@ const Page: React.FC = () => {
<
div
className=
"grid grid-cols-1 lg:grid-cols-3 gap-6"
>
{
/* Main content */
}
<
main
className=
"lg:col-span-2 bg-white border rounded-md p-6"
>
<
div
className=
'pb-5 text-primary text-2xl leading-normal font-medium'
>
{
data
?.
responseData
?.
title
}
</
div
>
<
hr
className=
"py-2"
/>
<
div
className=
"p-7.5 prose tiptap overflow-hidden"
>
{
parse
(
data
?.
responseData
?.
description
??
''
)
}
</
div
>
</
main
>
...
...
src/app/(main)/thong-tin-truyen-thong/chuyen-de/page.tsx
View file @
a99fbda4
...
...
@@ -5,46 +5,58 @@ import { MEDIA_INFORMATION_CATEGORIES } from "@constants/categories";
// ...existing code...
import
NewsContent
from
"@app/dai-dien-gioi-chu/components/card-news"
;
import
ListFilter
from
"@app/dai-dien-gioi-chu/components/list-filter"
;
import
{
Pagination
}
from
"@components/base/pagination"
;
import
{
Pagination
}
from
"@components/base/pagination"
;
import
Image
from
"next/image"
;
import
{
useGetNews
}
from
"@api/endpoints/news"
;
import
{
GetNewsResponseType
}
from
"@api/types/NewsPage.type"
;
import
{
PATHS
}
from
"@constants/paths"
;
import
{
Spinner
}
from
"@components/ui/spinner"
;
export
default
function
Page
()
{
const
[
submitSearch
]
=
useState
(
""
);
const
[
page
,
setPage
]
=
useState
(
1
);
const
pageSize
=
5
;
const
{
data
:
allData
}
=
useGetNews
<
GetNewsResponseType
>
({
const
{
data
:
allData
,
isLoading
}
=
useGetNews
<
GetNewsResponseType
>
({
pageSize
:
String
(
pageSize
),
currentPage
:
String
(
page
),
filters
:
submitSearch
?
`title @=
${
submitSearch
}
`
:
undefined
,
filters
:
submitSearch
?
`title @=
${
submitSearch
}
,category @=Chuyên đề`
:
'category @=Chuyên đề'
,
});
return
(
<
div
className=
"min-h-screen container mx-auto p-4"
>
<
div
className=
"w-full flex flex-col gap-5"
>
<
ListCategory
categories=
{
MEDIA_INFORMATION_CATEGORIES
}
/>
<
ListCategory
categories=
{
MEDIA_INFORMATION_CATEGORIES
}
/>
<
div
className=
"grid grid-cols-1 lg:grid-cols-3 gap-6"
>
{
/* Main content */
}
<
main
className=
"lg:col-span-2 bg-background "
>
<
div
className=
"pb-5 overflow-hidden"
>
{
allData
?.
responseData
.
rows
.
map
((
news
)
=>
(
<
NewsContent
key=
{
news
.
id
}
news=
{
news
}
link=
{
`${PATHS.mediaInformation}/chuyen-de/${news.id}`
}
/>
))
}
{
isLoading
?
(
<
div
className=
"flex justify-center items-center py-12"
>
<
Spinner
className=
"size-8"
/>
<
span
className=
"ml-2 text-gray-600"
>
Đang tải chuyên đề...
</
span
>
</
div
>
)
:
allData
?.
responseData
.
rows
.
length
===
0
?
(
<
p
className=
"text-center py-4"
>
Không có dữ liệu
</
p
>
)
:
(
<>
{
allData
?.
responseData
.
rows
.
map
((
news
)
=>
(
<
NewsContent
key=
{
news
.
id
}
news=
{
news
}
link=
{
`${PATHS.mediaInformation}/chuyen-de/${news.id}`
}
/>
))
}
<
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
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
>
...
...
src/app/(main)/thong-tin-truyen-thong/page.tsx
View file @
a99fbda4
...
...
@@ -15,7 +15,7 @@ export default function Page() {
return
(
<
div
className=
"min-h-screen container mx-auto p-4"
>
<
div
className=
"w-full flex flex-col gap-5"
>
<
ListCategory
categories=
{
MEDIA_INFORMATION_CATEGORIES
}
/>
<
ListCategory
categories=
{
MEDIA_INFORMATION_CATEGORIES
}
/>
</
div
>
</
div
>
...
...
src/app/(main)/thong-tin-truyen-thong/thong-tin-chinh-sach-va-phap-luat/[id]/page.tsx
View file @
a99fbda4
...
...
@@ -20,6 +20,10 @@ const Page: React.FC = () => {
<
div
className=
"grid grid-cols-1 lg:grid-cols-3 gap-6"
>
{
/* Main content */
}
<
main
className=
"lg:col-span-2 bg-white border rounded-md p-6"
>
<
div
className=
'pb-5 text-primary text-2xl leading-normal font-medium'
>
{
data
?.
responseData
?.
title
}
</
div
>
<
hr
className=
"py-2"
/>
<
div
className=
"p-7.5 prose tiptap overflow-hidden"
>
{
parse
(
data
?.
responseData
?.
description
??
''
)
}
</
div
>
</
main
>
...
...
src/app/(main)/thong-tin-truyen-thong/thong-tin-chinh-sach-va-phap-luat/page.tsx
View file @
a99fbda4
...
...
@@ -5,7 +5,7 @@ import { MEDIA_INFORMATION_CATEGORIES } from "@constants/categories";
// ...existing code...
import
NewsContent
from
"@app/dai-dien-gioi-chu/components/card-news"
;
import
ListFilter
from
"@app/dai-dien-gioi-chu/components/list-filter"
;
import
{
Pagination
}
from
"@components/base/pagination"
;
import
{
Pagination
}
from
"@components/base/pagination"
;
import
Image
from
"next/image"
;
import
{
useGetNews
}
from
"@api/endpoints/news"
;
import
{
GetNewsResponseType
}
from
"@api/types/NewsPage.type"
;
...
...
@@ -15,27 +15,30 @@ export default function Page() {
const
[
page
,
setPage
]
=
useState
(
1
);
const
pageSize
=
5
;
const
{
data
:
allData
}
=
useGetNews
<
GetNewsResponseType
>
({
const
{
data
:
allData
,
isLoading
}
=
useGetNews
<
GetNewsResponseType
>
({
pageSize
:
String
(
pageSize
),
currentPage
:
String
(
page
),
filters
:
submitSearch
?
`title @=
${
submitSearch
}
`
:
undefined
,
filters
:
submitSearch
?
`title @=
${
submitSearch
}
,category @=Thông tin chính sách và pháp luật`
:
'category @=Thông tin chính sách và pháp luật'
,
});
return
(
<
div
className=
"min-h-screen container mx-auto p-4"
>
<
div
className=
"w-full flex flex-col gap-5"
>
<
ListCategory
categories=
{
MEDIA_INFORMATION_CATEGORIES
}
/>
<
ListCategory
categories=
{
MEDIA_INFORMATION_CATEGORIES
}
/>
<
div
className=
"grid grid-cols-1 lg:grid-cols-3 gap-6"
>
{
/* Main content */
}
<
main
className=
"lg:col-span-2 bg-background "
>
<
div
className=
"pb-5 overflow-hidden"
>
{
allData
?.
responseData
.
rows
.
map
((
news
)
=>
(
<
NewsContent
key=
{
news
.
id
}
news=
{
news
}
link=
{
`${PATHS.mediaInformation}/thong-tin-chinh-sach-va-phap-luat/${news.id}`
}
/>
))
}
{
allData
?.
responseData
.
rows
.
length
===
0
?
(
<
p
className=
"text-center py-4"
>
Không có dữ liệu
</
p
>
)
:
(
allData
?.
responseData
.
rows
.
map
((
news
)
=>
(
<
NewsContent
key=
{
news
.
id
}
news=
{
news
}
link=
{
`${PATHS.mediaInformation}/thong-tin-chinh-sach-va-phap-luat/${news.id}`
}
/>
)))
}
<
div
className=
"w-full flex justify-center mt-4"
>
<
Pagination
...
...
src/app/(main)/thong-tin-truyen-thong/thu-vien-tai-lieu/[id]/page.tsx
View file @
a99fbda4
...
...
@@ -20,6 +20,10 @@ const Page: React.FC = () => {
<
div
className=
"grid grid-cols-1 lg:grid-cols-3 gap-6"
>
{
/* Main content */
}
<
main
className=
"lg:col-span-2 bg-white border rounded-md p-6"
>
<
div
className=
'pb-5 text-primary text-2xl leading-normal font-medium'
>
{
data
?.
responseData
?.
title
}
</
div
>
<
hr
className=
"py-2"
/>
<
div
className=
"p-7.5 prose tiptap overflow-hidden"
>
{
parse
(
data
?.
responseData
?.
description
??
''
)
}
</
div
>
</
main
>
...
...
src/app/(main)/thong-tin-truyen-thong/thu-vien-tai-lieu/page.tsx
View file @
a99fbda4
...
...
@@ -4,48 +4,60 @@ import ListCategory from "@app/dai-dien-gioi-chu/components/list-category";
import
{
MEDIA_INFORMATION_CATEGORIES
}
from
"@constants/categories"
;
// ...existing code...
import
NewsContent
from
"@app/dai-dien-gioi-chu/components/card-news"
;
import
{
Pagination
}
from
"@components/base/pagination"
;
import
{
Pagination
}
from
"@components/base/pagination"
;
import
ListFilter
from
"@app/dai-dien-gioi-chu/components/list-filter"
;
import
Image
from
"next/image"
;
import
{
useGetNews
}
from
"@api/endpoints/news"
;
import
{
GetNewsResponseType
}
from
"@api/types/NewsPage.type"
;
import
{
PATHS
}
from
"@constants/paths"
;
import
{
Spinner
}
from
"@components/ui/spinner"
;
export
default
function
Page
()
{
const
[
submitSearch
]
=
useState
(
""
);
const
[
page
,
setPage
]
=
useState
(
1
);
const
pageSize
=
5
;
const
{
data
:
allData
}
=
useGetNews
<
GetNewsResponseType
>
({
const
{
data
:
allData
,
isLoading
}
=
useGetNews
<
GetNewsResponseType
>
({
pageSize
:
String
(
pageSize
),
currentPage
:
String
(
page
),
filters
:
submitSearch
?
`title @=
${
submitSearch
}
`
:
undefined
,
filters
:
submitSearch
?
`title @=
${
submitSearch
}
,category @=Thư viện tài liệu'`
:
'category @=Thư viện tài liệu'
,
});
return
(
<
div
className=
"min-h-screen container mx-auto p-4"
>
<
div
className=
"w-full flex flex-col gap-5"
>
<
ListCategory
categories=
{
MEDIA_INFORMATION_CATEGORIES
}
/>
<
ListCategory
categories=
{
MEDIA_INFORMATION_CATEGORIES
}
/>
<
div
className=
"grid grid-cols-1 lg:grid-cols-3 gap-6"
>
{
/* Main content */
}
<
main
className=
"lg:col-span-2 bg-background "
>
<
div
className=
"pb-5 overflow-hidden"
>
{
allData
?.
responseData
.
rows
.
map
((
news
)
=>
(
<
NewsContent
key=
{
news
.
id
}
news=
{
news
}
link=
{
`${PATHS.mediaInformation}/thu-vien-tai-lieu/${news.id}`
}
/>
))
}
{
isLoading
?
(
<
div
className=
"flex justify-center items-center py-12"
>
<
Spinner
className=
"size-8"
/>
<
span
className=
"ml-2 text-gray-600"
>
Đang tải thư viện tài liệu...
</
span
>
</
div
>
)
:
allData
?.
responseData
.
rows
.
length
===
0
?
(
<
p
className=
"text-center py-4"
>
Không có dữ liệu
</
p
>
)
:
(
<>
{
allData
?.
responseData
.
rows
.
map
((
news
)
=>
(
<
NewsContent
key=
{
news
.
id
}
news=
{
news
}
link=
{
`${PATHS.mediaInformation}/thu-vien-tai-lieu/${news.id}`
}
/>
))
}
<
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
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
>
...
...
src/app/(main)/thong-tin-truyen-thong/tin-doanh-nghiep/[id]/page.tsx
View file @
a99fbda4
...
...
@@ -20,6 +20,10 @@ const Page: React.FC = () => {
<
div
className=
"grid grid-cols-1 lg:grid-cols-3 gap-6"
>
{
/* Main content */
}
<
main
className=
"lg:col-span-2 bg-white border rounded-md p-6"
>
<
div
className=
'pb-5 text-primary text-2xl leading-normal font-medium'
>
{
data
?.
responseData
?.
title
}
</
div
>
<
hr
className=
"py-2"
/>
<
div
className=
"p-7.5 prose tiptap overflow-hidden"
>
{
parse
(
data
?.
responseData
?.
description
??
''
)
}
</
div
>
</
main
>
...
...
src/app/(main)/thong-tin-truyen-thong/tin-doanh-nghiep/page.tsx
View file @
a99fbda4
...
...
@@ -5,47 +5,59 @@ import { MEDIA_INFORMATION_CATEGORIES } from "@constants/categories";
// ...existing code...
import
ListFilter
from
"@app/dai-dien-gioi-chu/components/list-filter"
;
import
NewsContent
from
"@app/dai-dien-gioi-chu/components/card-news"
;
import
{
Pagination
}
from
"@components/base/pagination"
;
import
{
Pagination
}
from
"@components/base/pagination"
;
import
Image
from
"next/image"
;
import
{
useGetNews
}
from
"@api/endpoints/news"
;
import
{
GetNewsResponseType
}
from
"@api/types/NewsPage.type"
;
import
{
PATHS
}
from
"@constants/paths"
;
import
{
Spinner
}
from
"@components/ui/spinner"
;
export
default
function
Page
()
{
const
[
submitSearch
]
=
useState
(
""
);
const
[
page
,
setPage
]
=
useState
(
1
);
const
pageSize
=
5
;
const
{
data
:
allData
}
=
useGetNews
<
GetNewsResponseType
>
({
const
{
data
:
allData
,
isLoading
}
=
useGetNews
<
GetNewsResponseType
>
({
pageSize
:
String
(
pageSize
),
currentPage
:
String
(
page
),
filters
:
submitSearch
?
`title @=
${
submitSearch
}
`
:
undefined
,
filters
:
submitSearch
?
`title @=
${
submitSearch
}
,category @=Tin doanh nghiệp`
:
'category @=Tin doanh nghiệp'
,
});
return
(
<
div
className=
"min-h-screen container mx-auto p-4"
>
<
div
className=
"w-full flex flex-col gap-5"
>
<
ListCategory
categories=
{
MEDIA_INFORMATION_CATEGORIES
}
/>
<
ListCategory
categories=
{
MEDIA_INFORMATION_CATEGORIES
}
/>
<
div
className=
"grid grid-cols-1 lg:grid-cols-3 gap-6"
>
{
/* Main content */
}
<
main
className=
"lg:col-span-2 bg-background "
>
<
div
className=
"pb-5 overflow-hidden"
>
{
allData
?.
responseData
.
rows
.
map
((
news
)
=>
(
<
NewsContent
key=
{
news
.
id
}
news=
{
news
}
link=
{
`${PATHS.mediaInformation}/tin-doanh-nghiep/${news.id}`
}
/>
))
}
{
isLoading
?
(
<
div
className=
"flex justify-center items-center py-12"
>
<
Spinner
className=
"size-8"
/>
<
span
className=
"ml-2 text-gray-600"
>
Đang tải tin doanh nghiệp...
</
span
>
</
div
>
)
:
allData
?.
responseData
.
rows
.
length
===
0
?
(
<
p
className=
"text-center py-4"
>
Không có dữ liệu
</
p
>
)
:
(
<>
{
allData
?.
responseData
.
rows
.
map
((
news
)
=>
(
<
NewsContent
key=
{
news
.
id
}
news=
{
news
}
link=
{
`${PATHS.mediaInformation}/tin-doanh-nghiep/${news.id}`
}
/>
))
}
<
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
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
>
...
...
src/app/(main)/thong-tin-truyen-thong/tin-kinh-te/[id]/page.tsx
View file @
a99fbda4
...
...
@@ -20,6 +20,10 @@ const Page: React.FC = () => {
<
div
className=
"grid grid-cols-1 lg:grid-cols-3 gap-6"
>
{
/* Main content */
}
<
main
className=
"lg:col-span-2 bg-white border rounded-md p-6"
>
<
div
className=
'pb-5 text-primary text-2xl leading-normal font-medium'
>
{
data
?.
responseData
?.
title
}
</
div
>
<
hr
className=
"py-2"
/>
<
div
className=
"p-7.5 prose tiptap overflow-hidden"
>
{
parse
(
data
?.
responseData
?.
description
??
''
)
}
</
div
>
</
main
>
...
...
src/app/(main)/thong-tin-truyen-thong/tin-kinh-te/page.tsx
View file @
a99fbda4
...
...
@@ -4,47 +4,59 @@ import ListCategory from "@app/dai-dien-gioi-chu/components/list-category";
import
{
MEDIA_INFORMATION_CATEGORIES
}
from
"@constants/categories"
;
// ...existing code...
import
NewsContent
from
"@app/dai-dien-gioi-chu/components/card-news"
;
import
{
Pagination
}
from
"@components/base/pagination"
;
import
{
Pagination
}
from
"@components/base/pagination"
;
import
Image
from
"next/image"
;
import
{
useGetNews
}
from
"@api/endpoints/news"
;
import
{
GetNewsResponseType
}
from
"@api/types/NewsPage.type"
;
import
{
PATHS
}
from
"@constants/paths"
;
import
{
Spinner
}
from
"@components/ui/spinner"
;
export
default
function
Page
()
{
const
[
submitSearch
]
=
useState
(
""
);
const
[
page
,
setPage
]
=
useState
(
1
);
const
pageSize
=
5
;
const
{
data
:
allData
}
=
useGetNews
<
GetNewsResponseType
>
({
const
{
data
:
allData
,
isLoading
}
=
useGetNews
<
GetNewsResponseType
>
({
pageSize
:
String
(
pageSize
),
currentPage
:
String
(
page
),
filters
:
submitSearch
?
`title @=
${
submitSearch
}
`
:
undefined
,
filters
:
submitSearch
?
`title @=
${
submitSearch
}
,category @=Tin kinh tế`
:
'category @=Tin kinh tế'
,
});
return
(
<
div
className=
"min-h-screen container mx-auto p-4"
>
<
div
className=
"w-full flex flex-col gap-5"
>
<
ListCategory
categories=
{
MEDIA_INFORMATION_CATEGORIES
}
/>
<
ListCategory
categories=
{
MEDIA_INFORMATION_CATEGORIES
}
/>
<
div
className=
"grid grid-cols-1 lg:grid-cols-3 gap-6"
>
{
/* Main content */
}
<
main
className=
"lg:col-span-2 bg-background "
>
<
div
className=
"pb-5 overflow-hidden"
>
{
allData
?.
responseData
.
rows
.
map
((
news
)
=>
(
<
NewsContent
key=
{
news
.
id
}
news=
{
news
}
link=
{
`${PATHS.mediaInformation}/tin-kinh-te/${news.id}`
}
/>
))
}
{
isLoading
?
(
<
div
className=
"flex justify-center items-center py-12"
>
<
Spinner
className=
"size-8"
/>
<
span
className=
"ml-2 text-gray-600"
>
Đang tải tin kinh tế...
</
span
>
</
div
>
)
:
allData
?.
responseData
.
rows
.
length
===
0
?
(
<
p
className=
"text-center py-4"
>
Không có dữ liệu
</
p
>
)
:
(
<>
{
allData
?.
responseData
.
rows
.
map
((
news
)
=>
(
<
NewsContent
key=
{
news
.
id
}
news=
{
news
}
link=
{
`${PATHS.mediaInformation}/tin-kinh-te/${news.id}`
}
/>
))
}
<
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
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
>
...
...
src/app/(main)/thong-tin-truyen-thong/tin-vcci/[id]/page.tsx
View file @
a99fbda4
...
...
@@ -20,6 +20,10 @@ const Page: React.FC = () => {
<
div
className=
"grid grid-cols-1 lg:grid-cols-3 gap-6"
>
{
/* Main content */
}
<
main
className=
"lg:col-span-2 bg-white border rounded-md p-6"
>
<
div
className=
'pb-5 text-primary text-2xl leading-normal font-medium'
>
{
data
?.
responseData
?.
title
}
</
div
>
<
hr
className=
"py-2"
/>
<
div
className=
"p-7.5 prose tiptap overflow-hidden"
>
{
parse
(
data
?.
responseData
?.
description
??
''
)
}
</
div
>
</
main
>
...
...
src/app/(main)/thong-tin-truyen-thong/tin-vcci/page.tsx
View file @
a99fbda4
...
...
@@ -6,17 +6,18 @@ import { MEDIA_INFORMATION_CATEGORIES } from "@constants/categories";
import
NewsContent
from
"@app/dai-dien-gioi-chu/components/card-news"
;
import
ListFilter
from
"@app/dai-dien-gioi-chu/components/list-filter"
;
import
EventCalendar
from
"@app/dai-dien-gioi-chu/components/event-calendar"
;
import
{
Pagination
}
from
"@components/base/pagination"
;
import
{
Pagination
}
from
"@components/base/pagination"
;
import
Image
from
"next/image"
;
import
{
useGetNews
}
from
"@api/endpoints/news"
;
import
{
PATHS
}
from
"@constants/paths"
;
import
{
GetNewsResponseType
}
from
"@api/types/NewsPage.type"
;
import
{
Spinner
}
from
"@components/ui/spinner"
;
export
default
function
Page
()
{
const
[
submitSearch
]
=
useState
(
""
);
const
[
submitSearch
,
setSubmitSearch
]
=
useState
(
""
);
const
[
page
,
setPage
]
=
useState
(
1
);
const
pageSize
=
5
;
const
{
data
:
allData
}
=
useGetNews
<
GetNewsResponseType
>
({
const
{
data
:
allData
,
isLoading
}
=
useGetNews
<
GetNewsResponseType
>
({
pageSize
:
String
(
pageSize
),
currentPage
:
String
(
page
),
filters
:
submitSearch
?
`title @=
${
submitSearch
}
`
:
undefined
,
...
...
@@ -24,36 +25,47 @@ export default function Page() {
return
(
<
div
className=
"min-h-screen container mx-auto p-4"
>
<
div
className=
"w-full flex flex-col gap-5"
>
<
ListCategory
categories=
{
MEDIA_INFORMATION_CATEGORIES
}
/>
<
ListCategory
categories=
{
MEDIA_INFORMATION_CATEGORIES
}
/>
<
div
className=
"grid grid-cols-1 lg:grid-cols-3 gap-6"
>
{
/* Main content */
}
<
main
className=
"lg:col-span-2 bg-background "
>
<
div
className=
"pb-5 overflow-hidden"
>
{
allData
?.
responseData
.
rows
.
map
((
news
)
=>
(
<
NewsContent
key=
{
news
.
id
}
news=
{
news
}
link=
{
`${PATHS.mediaInformation}/tin-vcci/${news.id}`
}
/>
))
}
{
isLoading
?
(
<
div
className=
"flex justify-center items-center py-12"
>
<
Spinner
className=
"size-8"
/>
<
span
className=
"ml-2 text-gray-600"
>
Đang tải tin VCCI...
</
span
>
</
div
>
)
:
allData
?.
responseData
.
rows
.
length
===
0
?
(
<
p
className=
"text-center py-4"
>
Không có dữ liệu
</
p
>
)
:
(
<>
{
allData
?.
responseData
.
rows
.
map
((
news
)
=>
(
<
NewsContent
key=
{
news
.
id
}
news=
{
news
}
link=
{
`${PATHS.mediaInformation}/tin-vcci/${news.id}`
}
/>
))
}
<
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
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"
>
<
ListFilter
/>
<
EventCalendar
/>
<
ListFilter
onSearch=
{
setSubmitSearch
}
/>
<
EventCalendar
/>
<
div
className=
"bg-white border rounded-md overflow-hidden"
>
<
div
className=
"w-full h-56 relative bg-gray-100"
>
<
Image
...
...
src/app/(main)/xuc-tien-thuong-mai/[id]/page.tsx
View file @
a99fbda4
...
...
@@ -8,6 +8,7 @@ import { useGetNewsId } from '@/api/endpoints/news';
import
parse
from
"html-react-parser"
;
import
{
useParams
}
from
'next/navigation'
import
{
GetNewsDetailResponseType
}
from
'@lib/types/news-detail-response-data'
;
import
{
Spinner
}
from
"@components/ui/spinner"
;
// ...existing code...
const
Page
:
React
.
FC
=
()
=>
{
const
{
id
}
=
useParams
()
...
...
@@ -20,7 +21,20 @@ const Page: React.FC = () => {
<
div
className=
"grid grid-cols-1 lg:grid-cols-3 gap-6"
>
{
/* Main content */
}
<
main
className=
"lg:col-span-2 bg-white border rounded-md p-6"
>
<
div
className=
"p-7.5 prose tiptap overflow-hidden"
>
{
parse
(
data
?.
responseData
?.
description
??
''
)
}
</
div
>
{
isLoading
?
(
<
div
className=
"flex justify-center items-center py-12"
>
<
Spinner
className=
"size-8"
/>
<
span
className=
"ml-2 text-gray-600"
>
Đang tải nội dung...
</
span
>
</
div
>
)
:
(
<>
<
div
className=
'pb-5 text-primary text-2xl leading-normal font-medium'
>
{
data
?.
responseData
?.
title
}
</
div
>
<
hr
className=
"py-2"
/>
<
div
className=
"p-7.5 prose tiptap overflow-hidden"
>
{
parse
(
data
?.
responseData
?.
description
??
''
)
}
</
div
>
</>
)
}
</
main
>
{
/* Sidebar */
}
...
...
src/app/(main)/xuc-tien-thuong-mai/co-hoi-kinh-doanh/[id]/page.tsx
View file @
a99fbda4
...
...
@@ -8,6 +8,7 @@ import { useGetNewsId } from '@/api/endpoints/news';
import
parse
from
"html-react-parser"
;
import
{
useParams
}
from
'next/navigation'
import
{
GetNewsDetailResponseType
}
from
'@lib/types/news-detail-response-data'
;
import
{
Spinner
}
from
"@components/ui/spinner"
;
// ...existing code...
const
Page
:
React
.
FC
=
()
=>
{
const
{
id
}
=
useParams
()
...
...
@@ -20,7 +21,22 @@ const Page: React.FC = () => {
<
div
className=
"grid grid-cols-1 lg:grid-cols-3 gap-6"
>
{
/* Main content */
}
<
main
className=
"lg:col-span-2 bg-white border rounded-md p-6"
>
<
div
className=
"p-7.5 prose tiptap overflow-hidden"
>
{
parse
(
data
?.
responseData
?.
description
??
''
)
}
</
div
>
{
isLoading
?
(
<
div
className=
"flex justify-center items-center py-12"
>
<
Spinner
className=
"size-8"
/>
<
span
className=
"ml-2 text-gray-600"
>
Đang tải chi tiết cơ hội kinh doanh...
</
span
>
</
div
>
)
:
data
?.
responseData
?
(
<>
<
div
className=
'pb-5 text-primary text-2xl leading-normal font-medium'
>
{
data
?.
responseData
?.
title
}
</
div
>
<
hr
className=
"py-2"
/>
<
div
className=
"p-7.5 prose tiptap overflow-hidden"
>
{
parse
(
data
?.
responseData
?.
description
??
''
)
}
</
div
>
</>
)
:
(
<
p
className=
"text-center py-4"
>
Không có dữ liệu
</
p
>
)
}
</
main
>
{
/* Sidebar */
}
...
...
src/app/(main)/xuc-tien-thuong-mai/co-hoi-kinh-doanh/page.tsx
View file @
a99fbda4
...
...
@@ -9,15 +9,17 @@ import Image from "next/image";
import
{
useGetNews
}
from
"@api/endpoints/news"
;
import
{
GetNewsResponseType
}
from
"@api/types/NewsPage.type"
;
import
{
PATHS
}
from
"@constants/paths"
;
import
{
Spinner
}
from
"@components/ui/spinner"
;
export
default
function
Page
()
{
const
[
submitSearch
]
=
useState
(
""
);
const
[
page
,
setPage
]
=
useState
(
1
);
const
pageSize
=
5
;
const
{
data
:
allData
}
=
useGetNews
<
GetNewsResponseType
>
({
const
{
data
:
allData
,
isLoading
}
=
useGetNews
<
GetNewsResponseType
>
({
pageSize
:
String
(
pageSize
),
currentPage
:
String
(
page
),
filters
:
submitSearch
?
`title @=
${
submitSearch
}
`
:
undefined
,
// filters: submitSearch ? `title @=${submitSearch}` : undefined,
filters
:
'category@=Cơ hội kinh doanh'
});
return
(
<
div
className=
"min-h-screen container mx-auto p-4"
>
...
...
@@ -28,26 +30,37 @@ export default function Page() {
{
/* Main content */
}
<
main
className=
"lg:col-span-2 bg-background "
>
<
div
className=
"pb-5 overflow-hidden"
>
{
allData
?.
responseData
.
rows
.
map
((
news
)
=>
(
<
NewsContent
key=
{
news
.
id
}
news=
{
news
}
link=
{
`${PATHS.tradePromotion}/${news.id}`
}
/>
))
}
{
isLoading
?
(
<
div
className=
"flex justify-center items-center py-12"
>
<
Spinner
className=
"size-8"
/>
<
span
className=
"ml-2 text-gray-600"
>
Đang tải cơ hội kinh doanh...
</
span
>
</
div
>
)
:
allData
?.
responseData
.
rows
.
length
===
0
?
(
<
p
className=
"text-center py-4"
>
Không có dữ liệu
</
p
>
)
:
(
<>
{
allData
?.
responseData
.
rows
.
map
((
news
)
=>
(
<
NewsContent
key=
{
news
.
id
}
news=
{
news
}
link=
{
`${PATHS.tradePromotion}/${news.id}`
}
/>
))
}
<
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
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
>
...
...
src/app/(main)/xuc-tien-thuong-mai/ho-tro-kinh-doanh/[id]/page.tsx
View file @
a99fbda4
...
...
@@ -8,6 +8,7 @@ import { useGetNewsId } from '@/api/endpoints/news';
import
parse
from
"html-react-parser"
;
import
{
useParams
}
from
'next/navigation'
import
{
GetNewsDetailResponseType
}
from
'@lib/types/news-detail-response-data'
;
import
{
Spinner
}
from
"@components/ui/spinner"
;
// ...existing code...
const
Page
:
React
.
FC
=
()
=>
{
const
{
id
}
=
useParams
()
...
...
@@ -20,7 +21,22 @@ const Page: React.FC = () => {
<
div
className=
"grid grid-cols-1 lg:grid-cols-3 gap-6"
>
{
/* Main content */
}
<
main
className=
"lg:col-span-2 bg-white border rounded-md p-6"
>
<
div
className=
"p-7.5 prose tiptap overflow-hidden"
>
{
parse
(
data
?.
responseData
?.
description
??
''
)
}
</
div
>
{
isLoading
?
(
<
div
className=
"flex justify-center items-center py-12"
>
<
Spinner
className=
"size-8"
/>
<
span
className=
"ml-2 text-gray-600"
>
Đang tải chi tiết hỗ trợ kinh doanh...
</
span
>
</
div
>
)
:
data
?.
responseData
?
(
<>
<
div
className=
'pb-5 text-primary text-2xl leading-normal font-medium'
>
{
data
?.
responseData
?.
title
}
</
div
>
<
hr
className=
"py-2"
/>
<
div
className=
"p-7.5 prose tiptap overflow-hidden"
>
{
parse
(
data
?.
responseData
?.
description
??
''
)
}
</
div
>
</>
)
:
(
<
p
className=
"text-center py-4"
>
Không có dữ liệu
</
p
>
)
}
</
main
>
{
/* Sidebar */
}
...
...
src/app/(main)/xuc-tien-thuong-mai/ho-tro-kinh-doanh/page.tsx
View file @
a99fbda4
...
...
@@ -8,15 +8,17 @@ import Image from "next/image";
import
{
useGetNews
}
from
"@api/endpoints/news"
;
import
{
GetNewsResponseType
}
from
"@api/types/NewsPage.type"
;
import
{
PATHS
}
from
"@constants/paths"
;
import
{
Spinner
}
from
"@components/ui/spinner"
;
export
default
function
Page
()
{
const
[
submitSearch
]
=
useState
(
""
);
const
[
page
,
setPage
]
=
useState
(
1
);
const
pageSize
=
5
;
const
{
data
:
allData
}
=
useGetNews
<
GetNewsResponseType
>
({
const
{
data
:
allData
,
isLoading
}
=
useGetNews
<
GetNewsResponseType
>
({
pageSize
:
String
(
pageSize
),
currentPage
:
String
(
page
),
filters
:
submitSearch
?
`title @=
${
submitSearch
}
`
:
undefined
,
// filters: submitSearch ? `title @=${submitSearch}` : undefined,
filters
:
'category@=Hỗ trợ kinh doanh'
});
return
(
<
div
className=
"min-h-screen container mx-auto p-4"
>
...
...
@@ -27,30 +29,41 @@ export default function Page() {
{
/* Main content */
}
<
main
className=
"lg:col-span-2 bg-background "
>
<
div
className=
"pb-5 overflow-hidden"
>
{
allData
?.
responseData
.
rows
.
map
((
news
)
=>
(
<
NewsContent
key=
{
news
.
id
}
news=
{
news
}
link=
{
`${PATHS.tradePromotion}/${news.id}`
}
/>
))
}
{
isLoading
?
(
<
div
className=
"flex justify-center items-center py-12"
>
<
Spinner
className=
"size-8"
/>
<
span
className=
"ml-2 text-gray-600"
>
Đang tải hỗ trợ kinh doanh...
</
span
>
</
div
>
)
:
allData
?.
responseData
.
rows
.
length
===
0
?
(
<
p
className=
"text-center py-4"
>
Không có dữ liệu
</
p
>
)
:
(
<>
{
allData
?.
responseData
.
rows
.
map
((
news
)
=>
(
<
NewsContent
key=
{
news
.
id
}
news=
{
news
}
link=
{
`${PATHS.tradePromotion}/${news.id}`
}
/>
))
}
<
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
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
>
...
...
src/app/(main)/xuc-tien-thuong-mai/moi-truong-kinh-doanh/[id]/page.tsx
View file @
a99fbda4
...
...
@@ -8,6 +8,7 @@ import { useGetNewsId } from '@/api/endpoints/news';
import
parse
from
"html-react-parser"
;
import
{
useParams
}
from
'next/navigation'
import
{
GetNewsDetailResponseType
}
from
'@lib/types/news-detail-response-data'
;
import
{
Spinner
}
from
"@components/ui/spinner"
;
// ...existing code...
const
Page
:
React
.
FC
=
()
=>
{
const
{
id
}
=
useParams
()
...
...
@@ -20,7 +21,22 @@ const Page: React.FC = () => {
<
div
className=
"grid grid-cols-1 lg:grid-cols-3 gap-6"
>
{
/* Main content */
}
<
main
className=
"lg:col-span-2 bg-white border rounded-md p-6"
>
<
div
className=
"p-7.5 prose tiptap overflow-hidden"
>
{
parse
(
data
?.
responseData
?.
description
??
''
)
}
</
div
>
{
isLoading
?
(
<
div
className=
"flex justify-center items-center py-12"
>
<
Spinner
className=
"size-8"
/>
<
span
className=
"ml-2 text-gray-600"
>
Đang tải chi tiết môi trường kinh doanh...
</
span
>
</
div
>
)
:
data
?.
responseData
?
(
<>
<
div
className=
'pb-5 text-primary text-2xl leading-normal font-medium'
>
{
data
?.
responseData
?.
title
}
</
div
>
<
hr
className=
"py-2"
/>
<
div
className=
"p-7.5 prose tiptap overflow-hidden"
>
{
parse
(
data
?.
responseData
?.
description
??
''
)
}
</
div
>
</>
)
:
(
<
p
className=
"text-center py-4"
>
Không có dữ liệu
</
p
>
)
}
</
main
>
{
/* Sidebar */
}
...
...
src/app/(main)/xuc-tien-thuong-mai/moi-truong-kinh-doanh/page.tsx
View file @
a99fbda4
...
...
@@ -9,15 +9,17 @@ import EventCalendar from "@app/dai-dien-gioi-chu/components/event-calendar";
import
{
useGetNews
}
from
"@api/endpoints/news"
;
import
{
GetNewsResponseType
}
from
"@api/types/NewsPage.type"
;
import
{
PATHS
}
from
"@constants/paths"
;
import
{
Spinner
}
from
"@components/ui/spinner"
;
export
default
function
Page
()
{
const
[
submitSearch
]
=
useState
(
""
);
const
[
page
,
setPage
]
=
useState
(
1
);
const
pageSize
=
5
;
const
{
data
:
allData
}
=
useGetNews
<
GetNewsResponseType
>
({
const
{
data
:
allData
,
isLoading
}
=
useGetNews
<
GetNewsResponseType
>
({
pageSize
:
String
(
pageSize
),
currentPage
:
String
(
page
),
filters
:
submitSearch
?
`title @=
${
submitSearch
}
`
:
undefined
,
// filters: submitSearch ? `title @=${submitSearch}` : undefined,
filters
:
'category@=Môi trường kinh doanh'
});
return
(
<
div
className=
"min-h-screen container mx-auto p-4"
>
...
...
@@ -28,30 +30,41 @@ export default function Page() {
{
/* Main content */
}
<
main
className=
"lg:col-span-2 bg-background "
>
<
div
className=
"pb-5 overflow-hidden"
>
{
allData
?.
responseData
.
rows
.
map
((
news
)
=>
(
<
NewsContent
key=
{
news
.
id
}
news=
{
news
}
link=
{
`${PATHS.tradePromotion}/${news.id}`
}
/>
))
}
{
isLoading
?
(
<
div
className=
"flex justify-center items-center py-12"
>
<
Spinner
className=
"size-8"
/>
<
span
className=
"ml-2 text-gray-600"
>
Đang tải môi trường kinh doanh...
</
span
>
</
div
>
)
:
allData
?.
responseData
.
rows
.
length
===
0
?
(
<
p
className=
"text-center py-4"
>
Không có dữ liệu
</
p
>
)
:
(
<>
{
allData
?.
responseData
.
rows
.
map
((
news
)
=>
(
<
NewsContent
key=
{
news
.
id
}
news=
{
news
}
link=
{
`${PATHS.tradePromotion}/${news.id}`
}
/>
))
}
<
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
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
>
...
...
src/app/(main)/xuc-tien-thuong-mai/page.tsx
View file @
a99fbda4
...
...
@@ -8,12 +8,13 @@ import Image from "next/image";
import
{
useGetNews
}
from
"@api/endpoints/news"
;
import
{
GetNewsResponseType
}
from
"@api/types/NewsPage.type"
;
import
{
PATHS
}
from
"@constants/paths"
;
import
{
Spinner
}
from
"@components/ui/spinner"
;
export
default
function
Page
()
{
const
[
submitSearch
]
=
useState
(
""
);
const
[
page
,
setPage
]
=
useState
(
1
);
const
pageSize
=
5
;
const
{
data
:
allData
}
=
useGetNews
<
GetNewsResponseType
>
({
const
{
data
:
allData
,
isLoading
}
=
useGetNews
<
GetNewsResponseType
>
({
pageSize
:
String
(
pageSize
),
currentPage
:
String
(
page
),
filters
:
submitSearch
?
`title @=
${
submitSearch
}
`
:
undefined
,
...
...
@@ -27,26 +28,35 @@ export default function Page() {
{
/* Main content */
}
<
main
className=
"lg:col-span-2 bg-background "
>
<
div
className=
"pb-5 overflow-hidden"
>
{
allData
?.
responseData
.
rows
.
map
((
news
)
=>
(
<
NewsContent
key=
{
news
.
id
}
news=
{
news
}
link=
{
`${PATHS.tradePromotion}/${news.id}`
}
/>
))
}
{
isLoading
?
(
<
div
className=
"flex justify-center items-center py-12"
>
<
Spinner
className=
"size-8"
/>
<
span
className=
"ml-2 text-gray-600"
>
Đang tải tin xúc tiến thương mại...
</
span
>
</
div
>
)
:
(
<>
{
allData
?.
responseData
.
rows
.
map
((
news
)
=>
(
<
NewsContent
key=
{
news
.
id
}
news=
{
news
}
link=
{
`${PATHS.tradePromotion}/${news.id}`
}
/>
))
}
<
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
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
>
</>
)
}
</
div
>
</
main
>
...
...
src/components/base/card-news/index.tsx
View file @
a99fbda4
...
...
@@ -2,8 +2,24 @@
import
{
NewsItem
}
from
'@app/dai-dien-gioi-chu/lib/types/NewsPage.type'
;
import
Links
from
'@links/index'
import
dayjs
from
'dayjs'
;
import
parse
from
'html-react-parser'
function
NewsContent
({
news
,
link
}:
{
news
:
NewsItem
,
link
:
string
})
{
// Helper: remove <img> tags and extract plain text from HTML
const
stripImagesAndHtml
=
(
html
?:
string
)
=>
{
if
(
!
html
)
return
''
// remove img tags first
const
withoutImgs
=
html
.
replace
(
/<img
[^
>
]
*>/gi
,
''
)
// use DOMParser on client for robust extraction
if
(
typeof
window
!==
'undefined'
&&
typeof
DOMParser
!==
'undefined'
)
{
try
{
const
doc
=
new
DOMParser
().
parseFromString
(
withoutImgs
,
'text/html'
)
return
doc
.
body
.
textContent
||
''
}
catch
{
// fallback to regex
}
}
return
withoutImgs
.
replace
(
/<
[^
>
]
*>/g
,
''
)
}
function
NewsContent
({
news
,
link
}:
{
news
:
NewsItem
,
link
:
string
})
{
return
(
<
a
...
...
@@ -14,20 +30,21 @@ function NewsContent({ news, link }: { news: NewsItem, link: string }) {
src=
{
`${Links.imageEndpoint}${news.thumbnail}`
}
alt=
{
news
.
title
}
className=
"w-full sm:w-56 md:w-64 h-40 md:h-36 object-cover shrink-0"
onError=
{
(
e
)
=>
{
e
.
currentTarget
.
src
=
"/img-error.png"
}
}
onError=
{
(
e
)
=>
{
e
.
currentTarget
.
src
=
"/img-error.png"
}
}
/>
<
div
className=
"flex-1 min-w-0 pl-0 sm:pl-4"
>
<
p
className=
"text-primary font-semibold text-base md:text-lg line-clamp-2 wrap-break-word hover:no-underline
"
>
<
p
className=
"text-primary font-semibold text-base md:text-lg hover:underline line-clamp-2 wrap-break-word
"
>
{
news
.
title
}
</
p
>
<
div
className=
"text-sm my-2 text-[#00AED5]"
>
{
dayjs
(
news
.
release_at
).
format
(
'DD/MM/YYYY'
)
}
</
div
>
<
div
className=
"text-sm text-[#777] line-clamp-3"
>
<
div
className=
"text-sm prose tiptap"
>
{
parse
(
news
.
description
)
}
</
div
>
<
div
className=
"text-sm prose tiptap"
>
{
stripImagesAndHtml
(
news
.
description
)
}
</
div
>
</
div
>
</
div
>
</
a
>
...
...
src/components/shared/editor-content/AppEditorContent.tsx
View file @
a99fbda4
...
...
@@ -3,28 +3,48 @@ import htmlParse, { DOMNode, Element, Text } from "html-react-parser";
import
{
AppEditorContentProps
}
from
"./AppEditorContent.type"
;
import
"./AppEditorContent.css"
;
const
AppEditorContent
:
FC
<
AppEditorContentProps
>
=
({
value
=
""
,
className
=
""
,
})
=>
{
const
transform
=
(
node
:
DOMNode
):
JSX
.
Element
|
null
=>
{
if
(
node
instanceof
Element
&&
node
.
tagName
===
"strong"
)
{
const
AppEditorContent
:
FC
<
AppEditorContentProps
>
=
({
value
=
''
,
className
=
''
})
=>
{
const
transform
=
(
node
:
DOMNode
):
JSX
.
Element
|
string
|
undefined
|
null
=>
{
// 1. Xử lý Text Node
if
(
node
instanceof
Text
)
{
return
node
.
data
;
}
if
(
!
(
node
instanceof
Element
))
return
undefined
;
const
tagName
=
node
.
tagName
.
toLowerCase
();
// ✅ FIX LỖI: Ép kiểu (as DOMNode) để Type 'ChildNode' khớp với 'DOMNode'
const
children
=
node
.
children
?
node
.
children
.
map
(
child
=>
transform
(
child
as
DOMNode
))
:
[];
// --- LOGIC XỬ LÝ THEO YÊU CẦU ---
// 2. ✅ Xóa thẻ <img>
if
(
tagName
===
'img'
)
{
return
<></>;
}
// 3. ✅ Xóa thẻ <a> nhưng giữ lại nội dung
if
(
tagName
===
'a'
)
{
// Trả về children đã được xử lý (làm phẳng thẻ <a>)
return
<>
{
children
}
</>;
}
// 4. ✅ Xử lý thẻ <strong>
if
(
tagName
===
'strong'
)
{
return
(
<
strong
className=
"custom-strong"
>
{
node
.
children
&&
Array
.
isArray
(
node
.
children
)
?
node
.
children
.
map
((
child
,
index
)
=>
{
if
(
typeof
child
===
"string"
)
{
return
child
;
}
else
if
(
child
instanceof
Text
)
{
return
child
.
data
;
}
return
null
;
})
:
null
}
{
children
}
</
strong
>
);
}
return
null
;
// 5. ✅ Render các thẻ HTML khác (Fallback)
// Trả về undefined để thư viện tự động render các thẻ còn lại (p, div, br,...)
return
undefined
;
};
return
(
...
...
@@ -36,4 +56,4 @@ const AppEditorContent: FC<AppEditorContentProps> = ({
);
};
export
default
AppEditorContent
;
export
default
AppEditorContent
;
\ No newline at end of file
src/components/ui/loading-state.tsx
0 → 100644
View file @
a99fbda4
import
{
Spinner
}
from
"@/components/ui/spinner"
;
import
{
cn
}
from
"@/lib/utils"
;
interface
LoadingStateProps
{
message
?:
string
;
size
?:
"sm"
|
"md"
|
"lg"
;
className
?:
string
;
showMessage
?:
boolean
;
}
export
function
LoadingState
({
message
=
"Đang tải dữ liệu..."
,
size
=
"md"
,
className
,
showMessage
=
true
}:
LoadingStateProps
)
{
const
sizeClasses
=
{
sm
:
"size-4"
,
md
:
"size-6"
,
lg
:
"size-8"
};
const
paddingClasses
=
{
sm
:
"py-6"
,
md
:
"py-8"
,
lg
:
"py-12"
};
return
(
<
div
className=
{
cn
(
"flex justify-center items-center"
,
paddingClasses
[
size
],
className
)
}
>
<
Spinner
className=
{
sizeClasses
[
size
]
}
/>
{
showMessage
&&
(
<
span
className=
"ml-2 text-gray-600"
>
{
message
}
</
span
>
)
}
</
div
>
);
}
// Các preset thường dùng
export
const
LoadingPresets
=
{
News
:
()
=>
<
LoadingState
message=
"Đang tải tin tức..."
/>,
Search
:
()
=>
<
LoadingState
message=
"Đang tìm kiếm..."
/>,
Content
:
()
=>
<
LoadingState
message=
"Đang tải nội dung..."
/>,
Data
:
()
=>
<
LoadingState
message=
"Đang tải dữ liệu..."
/>,
Members
:
()
=>
<
LoadingState
message=
"Đang tải thông tin hội viên..."
/>,
Events
:
()
=>
<
LoadingState
message=
"Đang tải sự kiện..."
/>,
Small
:
(
message
?:
string
)
=>
<
LoadingState
size=
"sm"
message=
{
message
}
/>,
Large
:
(
message
?:
string
)
=>
<
LoadingState
size=
"lg"
message=
{
message
}
/>,
};
\ No newline at end of file
src/lib/types/common.ts
0 → 100644
View file @
a99fbda4
// Query response type
interface
ResponseType
<
T
=
any
>
{
data
:
any
message
:
string
|
null
message_en
:
string
|
null
responseData
:
T
status
:
'fail'
|
'success'
statusCode
:
number
timeStamp
:
string
violations
:
Array
<
{
code
:
number
message
:
string
action
:
Array
<
{
location
:
string
msg
:
{
en
:
string
vi
:
string
}
path
:
string
value
:
string
}
>
}
>
|
null
}
// Infinite query statuses type
interface
InfiniteQueryStatusesType
{
isFetchingInitialPage
:
boolean
isFetchingNextPage
:
boolean
isEmptyData
:
boolean
isError
?:
boolean
}
export
type
{
ResponseType
,
InfiniteQueryStatusesType
}
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