Commit 28465113 authored by Văn Hoàng's avatar Văn Hoàng

[tag]0.1-vcci

parents 5e763d67 5ca290c8
Pipeline #43367 passed with stages
in 47 minutes and 8 seconds
......@@ -79,7 +79,8 @@ const orvalConfig = async () => {
'UserHistory',
'Approvals',
'News',
'Category'
'Category',
'NewsPageConfig',
]
}
}
......
......@@ -134,6 +134,9 @@ importers:
react:
specifier: 19.2.0
version: 19.2.0
react-country-flag:
specifier: ^3.1.0
version: 3.1.0(react@19.2.0)
react-day-picker:
specifier: ^9.11.1
version: 9.11.1(react@19.2.0)
......@@ -3270,6 +3273,12 @@ packages:
queue-microtask@1.2.3:
resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==}
react-country-flag@3.1.0:
resolution: {integrity: sha512-JWQFw1efdv9sTC+TGQvTKXQg1NKbDU2mBiAiRWcKM9F1sK+/zjhP2yGmm8YDddWyZdXVkR8Md47rPMJmo4YO5g==}
engines: {node: '>=12'}
peerDependencies:
react: '>=16'
react-day-picker@9.11.1:
resolution: {integrity: sha512-l3ub6o8NlchqIjPKrRFUCkTUEq6KwemQlfv3XZzzwpUeGwmDJ+0u0Upmt38hJyd7D/vn2dQoOoLV/qAp0o3uUw==}
engines: {node: '>=18'}
......@@ -7246,6 +7255,10 @@ snapshots:
queue-microtask@1.2.3: {}
react-country-flag@3.1.0(react@19.2.0):
dependencies:
react: 19.2.0
react-day-picker@9.11.1(react@19.2.0):
dependencies:
'@date-fns/tz': 1.4.1
......
/**
* 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;
}
......@@ -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';
......
/**
* 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[];
};
import { ResponseType } from '@lib/types/common'
export type EventStatus = {
id: string;
name: string;
name_en: string;
code: string;
};
export type EventOrganization = {
id: string;
organization_id: string | null;
event_id: string;
status: string | null;
created_at: string;
updated_at: string | null;
created_by: string;
updated_by: string | null;
role: string;
guest_name: string;
guest_image: string;
org_table_count: number | null;
org_counter_count: number | null;
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;
start_time: string;
end_time: string;
created_at: string;
created_by: string;
updated_at: string | null;
updated_by: string | null;
status: string;
image: string;
description: string;
location: string;
province: string;
table_count: number;
counter_count: number;
seo_text: string;
seo_text_en: string | null;
table_cost: number;
counter_cost: number;
table_min_pick: number | null;
counter_min_pick: number | null;
table_max_pick: number | null;
counter_max_pick: number | null;
org_support_titles: string[];
accept_entries: boolean;
type: string;
introduction: string;
host_club: string | null;
config: unknown | null;
event_organizations: EventOrganization[];
status_status: EventStatus;
};
export type EventResponseData = {
count: number;
rows: EventItem[];
totalPages: number;
currentPage: number;
};
export type EventApiResponse = {
message: string;
message_en: string;
responseData: EventResponseData;
status: string;
timeStamp: string;
violations: null | unknown;
};
......@@ -12,6 +12,9 @@ import { GetNewsDetailResponseType } from './page.type';
import { Link, CalendarFold, Book } from 'lucide-react';
import { useEffect, useMemo } from 'react';
import { useParams } from 'next/navigation'
import ListCategory from '@/components/base/list-category'
import { MEDIA_INFORMATION_CATEGORIES } from '@/constants/categories'
import EventCalendar from '@/components/base/event-calendar'
// import { t } from 'i18next'
// Component
......@@ -33,82 +36,34 @@ const NewsDetailPage = () => {
<Spinner />
) : (
<div>
{/* Banner */}
<img className='w-full h-100' src={`${BASE_URLS.imageEndpoint}${data?.responseData?.thumbnail}`} alt='Banner' />
{/* Breadcrumb */}
<div className='container py-10'>
<div className='flex gap-4 items-stretch'>
{/* <Breadcrumbs aria-label='breadcrumb'>
<Link
underline='hover'
color='inherit'
href='/'
className='!text-app-blue-secondary !text-sm !leading-normal'
>
{t('breadcrumbHomePage')}
<p>Trang chủ</p>
</Link>
<Link
underline='hover'
color='inherit'
href='/tin-tuc'
className='!text-app-blue-secondary !text-sm !leading-normal'
>
{t('breadcrumbNewsPage')}
<p>Tin tức</p>
</Link>
<Typography className='!text-sm !text-black !leading-normal'>
{t('breadcrumbNewsDetailPage')}
<p>Chi tiết</p>
</Typography>
</Breadcrumbs> */}
</div>
</div>
<div className='container bg-gray-100 rounded p-10 flex flex-col gap-10'>
{/* Heading */}
<div className='text-blue-900 text-[32px] leading-normal font-medium text-center'>{data?.responseData?.title}</div>
{/* body */}
<div className='flex items-start gap-8 flex-col lg:flex-row'>
{/* Info */}
<div className='lg:w-[332px] bg-white p-4 rounded shadow-app-quaternary flex flex-col gap-2'>
<div className='text-base text-app-grey font-semibold leading-normal text-center'>
{/* {t('information')} */}
<div className='container flex flex-col gap-5'>
<ListCategory categories={MEDIA_INFORMATION_CATEGORIES} />
<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-primary text-2xl leading-normal font-medium'>
{data?.responseData?.title}
</div>
{/* date */}
<div className='flex items-center gap-2 text-app-grey'>
<div className='flex items-center gap-2 text-sm mb-4'>
<CalendarFold />
<span className='text-base'>{dayjs(data?.responseData?.created_at).format('DD/MM/YYYY')}</span>
</div>
{/* author */}
{/* <div className='flex items-center gap-2 text-app-grey'>
<PersonIcon />
<span className='text-base'>{data?.responseData?.created_by}</span>
</div> */}
{/* Category */}
<div className='flex items-center gap-2 text-app-grey'>
<Book />
<span className='text-base'>{data?.responseData?.category}</span>
<span className='text-base text-blue-700'>{dayjs(data?.responseData?.created_at).format('DD/MM/YYYY')}</span>
</div>
<div className='py-5' >
<hr />
</div>
{/* Content */}
<div className='flex-1 text-app-grey text-base overflow-hidden'>
<AppEditorContent value={data?.responseData?.description ?? ''} />
</div>
</main>
{/* Sidebar */}
<aside className="space-y-6">
<EventCalendar />
</aside>
</div>
</div>
{/* Related News */}
{/* <RelatedNews newsQuery={getRelatedNewsQuery} lang={lang} newsId={infoNews.id} /> */}
</div>
)}
</div>
</div >
)
}
......
import { EventItem } from '@/api/types/event'
import BASE_URL from '@/links'
import dayjs from 'dayjs';
import AppEditorContent from '@/components/shared/editor-content';
function CardEvent({ event }: { event: EventItem }) {
return (
<a
href={`${event.id}`}
className='flex flex-row gap-2 mb-2 sm:gap-3 sm:mb-3 p-2 sm:p-3 border border-gray-200 bg-white rounded-md'
>
<img
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'>
{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-2' value={event.description} /> */}
</div>
</a>
);
}
export default CardEvent;
\ No newline at end of file
......@@ -7,13 +7,18 @@ function CardNews({ news }: { news: NewsAdminItem }) {
return (
<a
href={`${news.id}`}
className='flex flex-row gap-3 mb-3 border border-gray-200 bg-white rounded-md p-3'
className='flex flex-row gap-2 mb-2 sm:gap-3 sm:mb-3 p-2 sm:p-3 border border-gray-200 bg-white rounded-md'
>
<img
src={`${BASE_URL.imageEndpoint}${news.thumbnail}`}
alt={news.title}
className='w-[120px] h-20 object-cover rounded-sm'
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'>
{news.title}
......
......@@ -9,29 +9,34 @@ import 'swiper/css'
import 'swiper/css/navigation'
import 'swiper/css/pagination'
import BASE_URL from '@/links/index'
import NewsContent from './components/card-news'
import { Spinner } from '@/components/ui'
import CardNews from './components/card-news'
import CardEvent from './components/card-event'
import EventCalendar from './components/event-calendar'
import dayjs from 'dayjs'
import AppEditorContent from '@/components/shared/editor-content'
// server
import { useGetEvents } from '@/api/endpoints/event'
import { useGetCategory } from '@/api/endpoints/category'
import { useGetNews } from '@/api/endpoints/news'
import { GetCategoryAdminResponseType } from '@/api/types/category'
import { GetNewsAdminResponseType } from '@/api/types/news'
import EventCalendar from './components/event-calendar'
import { GetNewsAdminResponseType, NewsAdminItem } from '@/api/types/news'
import { EventApiResponse, EventItem } from '@/api/types/event'
const Page = () => {
const [tab, setTab] = useState('all')
const [search, setSearch] = useState('')
const [submitSearch, setSubmitSearch] = useState('')
const [currentIndex, setCurrentIndex] = useState(0)
const [slidesPerView, setSlidesPerView] = useState<number>(3)
const swiperRef = useRef<SwiperType | null>(null)
const { data: categoryData } = useGetCategory<GetCategoryAdminResponseType>()
const { data: allData, isLoading } = useGetNews<GetNewsAdminResponseType>({
pageSize: '999',
filters: submitSearch ? `title @=${submitSearch}` : undefined,
})
const { data: categoryData, isLoading: isLoadingCategory } = useGetCategory<GetCategoryAdminResponseType>()
const { data: newsData, isLoading: isLoadingNews } = useGetNews<GetNewsAdminResponseType>(
{ pageSize: '999' },
)
const { data: eventData, isLoading: isLoadingEvent } = useGetEvents<EventApiResponse>()
const rows = allData?.responseData?.rows ?? []
// filter category
const rows = newsData?.responseData?.rows ?? []
const filteredRows = tab === 'all' ? rows : rows.filter((n) => n.category === tab)
const images = [
......@@ -64,19 +69,7 @@ const Page = () => {
'/home/hoi-vien-tieu-bieu/UOB-logo_Vuong.jpeg.webp',
]
useEffect(() => {
const getSlides = (w: number) => {
if (w >= 1024) return 3
if (w >= 640) return 2
return 1
}
const update = () => setSlidesPerView(getSlides(window.innerWidth))
update()
window.addEventListener('resize', update)
return () => window.removeEventListener('resize', update)
}, [])
if (isLoading)
if (isLoadingNews || isLoadingCategory || isLoadingEvent)
return (
<div className="w-full h-[80vh] flex justify-center items-center">
<Spinner />
......@@ -136,13 +129,13 @@ const Page = () => {
{rows.map((news) => (
<SwiperSlide key={news.id}>
<a
href={`/tin-tuc/${news.id}`}
href={`/${news.id}`}
className="relative block bg-white rounded-xl shadow-md overflow-hidden hover:shadow-lg transition-shadow duration-300"
>
<img
src={`${BASE_URL.imageEndpoint}${news.thumbnail}`}
alt={news.title}
className="w-full h-48 sm:h-56 md:h-64 object-cover"
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">
......@@ -166,20 +159,46 @@ const Page = () => {
{/* Left */}
<div className="flex-1">
<div className="flex justify-between items-center">
<h2 className="text-[18px] sm:text-[20px] font-bold uppercase text-blue-900">
<a href="/thong-tin-truyen-thong/tin-vcci/" className="text-[18px] sm:text-[20px] font-bold uppercase text-blue-900">
Tin tức
</h2>
<a href="#" className="text-blue-900 hover:underline text-sm sm:text-base">
</a>
<a href="/thong-tin-truyen-thong/tin-vcci/" className="text-blue-900 text-sm sm:text-base">
{'>>'}
</a>
</div>
<hr className="border-blue-900 mb-4" />
<div className="flex flex-col md:flex-row gap-5">
<div className="w-full md:w-1/2 bg-gray-500 flex items-center justify-center rounded-lg p-4 min-h-[180px] sm:min-h-[220px]">
<p className="text-white text-center">Khung tin tức VIP</p>
{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 border border-gray-200 bg-white rounded-md p-3"
>
<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"
onError={(e) => {
e.currentTarget.onerror = null
e.currentTarget.src = "/fallback.png"
}}
/>
</div>
<div className="flex-1">
<p className="text-[#0056b3] 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="w-full md:w-1/2">
<div className="flex flex-wrap gap-2 sm:gap-3 mb-3">
<button
......@@ -205,8 +224,8 @@ const Page = () => {
))}
</div>
{filteredRows.slice(0, 5).map((news) => (
<NewsContent key={news.id} news={news} />
{filteredRows.slice(0, 4).map((news) => (
<CardNews key={news.id} news={news} />
))}
</div>
</div>
......@@ -257,12 +276,37 @@ const Page = () => {
<hr className="border-[#e8c518] mb-4" />
<div className="flex flex-col md:flex-row gap-5">
<div className="w-full md:w-1/2 bg-gray-500 flex items-center justify-center rounded-lg p-4 min-h-[180px] sm:min-h-[220px]">
<p className="text-white">Khung tin tức VIP</p>
{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"
onError={(e) => {
e.currentTarget.onerror = null
e.currentTarget.src = "/fallback.png"
}} />
</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">
{rows.slice(0, 5).map((news) => (
<NewsContent key={news.id} news={news} />
{eventData?.responseData.rows.slice(0, 4).map((event) => (
<CardEvent key={event.id} event={event} />
))}
</div>
</div>
......@@ -294,33 +338,73 @@ const Page = () => {
<section className="flex flex-col md:flex-row gap-5 pt-8">
<div className="flex-1">
<div className="flex justify-between items-center">
<h2 className="text-[18px] sm:text-[20px] font-bold uppercase text-blue-900">
<a href="/xuc-tien-thuong-mai/co-hoi-kinh-doanh/" className="text-[18px] sm:text-[20px] font-bold uppercase text-blue-900">
Cơ hội kinh doanh
</h2>
<a href="#" className="text-blue-900 text-sm sm:text-base">
</a>
<a href="/xuc-tien-thuong-mai/co-hoi-kinh-doanh/" className="text-blue-900 text-sm sm:text-base">
{'>>'}
</a>
</div>
<hr className="border-blue-900 mb-4" />
<div className="pt-2 space-y-3">
{rows.slice(0, 4).map((news) => (
<NewsContent key={news.id} news={news} />
<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-blue-900 font-semibold text-sm sm:text-base z-10 line-clamp-3">
{news.title}
</p>
</div>
</div>
</a>
))}
{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">
<h2 className="text-[18px] sm:text-[20px] font-bold uppercase text-blue-900">
<a href="/thong-tin-truyen-thong/thong-tin-chinh-sach-va-phap-luat" className="text-[18px] sm:text-[20px] font-bold uppercase text-blue-900">
Chính sách & pháp luật
</h2>
<a href="#" className="text-blue-900 text-sm sm:text-base">
</a>
<a href="/thong-tin-truyen-thong/thong-tin-chinh-sach-va-phap-luat" className="text-blue-900 text-sm sm:text-base">
{'>>'}
</a>
</div>
<hr className="border-blue-900 mb-4" />
<div className="pt-2 space-y-3">
{rows.slice(0, 4).map((news) => (
<NewsContent key={news.id} news={news} />
<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-blue-900 font-semibold text-sm sm:text-base z-10 line-clamp-3">
{news.title}
</p>
</div>
</div>
</a>
))}
{rows.slice(0, 3).map((news) => (
<CardNews key={news.id} news={news} />
))}
</div>
</div>
......@@ -331,12 +415,12 @@ const Page = () => {
<img src="/home/eCarAid_web_banner_600x400.webp" alt="banner" />
</a>
</div>
</div>
</div >
{/* Hội viên tiêu biểu */}
<section className="flex flex-col lg:flex-row gap-5 pb-10 mb-0">
< 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">
< 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-blue-900">Hội viên tiêu biểu</h2>
<a
......@@ -374,10 +458,10 @@ const Page = () => {
))}
</Swiper>
</div>
</aside>
</aside >
{/* right */}
<aside className="w-full lg:w-[30%] py-5">
< aside className="w-full lg:w-[30%] py-5" >
<div className="flex justify-between items-center mb-3">
<h2 className="text-xl font-bold uppercase text-blue-900">Kết nối hội viên</h2>
<a
......@@ -415,13 +499,13 @@ const Page = () => {
))}
</Swiper>
</div>
</aside>
</section>
</aside >
</section >
{/* Video + đối tác */}
<section className="flex flex-col lg:flex-row gap-5 pb-10">
< section className="flex flex-col lg:flex-row gap-5 pb-10" >
{/* left */}
<div className="flex flex-col flex-1">
< div className="flex flex-col flex-1" >
<div className="flex justify-between items-center mb-3">
<h2 className="text-xl font-bold uppercase text-blue-900">Video</h2>
<a
......@@ -459,10 +543,10 @@ const Page = () => {
</div>
))}
</div>
</div>
</div >
{/* right */}
<aside className="w-full lg:w-[30%]">
< aside className="w-full lg:w-[30%]" >
<div className="flex justify-between items-center mb-3">
<h2 className="text-xl font-bold uppercase text-blue-900">Đối tác</h2>
<a
......@@ -500,9 +584,9 @@ const Page = () => {
))}
</Swiper>
</div>
</aside>
</section>
</div>
</aside >
</section >
</div >
</>
)
}
......
const MenuItem = ({ title, items, link }: { title: string; items: string[]; link?: string }) => (
import Link from "next/link";
type MenuItemProps = {
title: string;
link?: string;
items: { title: string; link: string }[];
};
const MenuItem = ({ title, link, items }: MenuItemProps) => {
return (
<div className="group relative">
<a
<Link
href={`/${link || ""}`}
className="px-3 py-5 text-[16px] font-[600] text-[#124588] hover:text-[#E8C518] transition block"
href={`${link}`}
>
{title}
</a>
</Link>
{/* Dropdown */}
<div className="absolute left-0 top-full hidden group-hover:block bg-[#124588]/98 text-white text-[14px] font-[500] min-w-[220px] shadow-lg">
{items.map((item, i) => (
<div
<Link
key={i}
className="px-5 py-3 hover:bg-[#e8c518]/80 cursor-pointer whitespace-nowrap"
href={`/${link}/${item.link}`}
className="block px-5 py-3 hover:bg-[#e8c518]/80 cursor-pointer whitespace-nowrap transition"
>
{item}
</div>
{item.title}
</Link>
))}
</div>
</div>
);
);
};
export default MenuItem;
......@@ -14,7 +14,7 @@ import {
import Image from "next/image";
import vietnamMap from "@/assets/vietnam-map-white.png.webp";
function footer() {
function Footer() {
const emailRef = useRef<HTMLInputElement>(null);
const checkBoxRef = useRef<HTMLInputElement>(null);
const [emailError, setEmailError] = useState(false);
......@@ -212,4 +212,4 @@ function footer() {
);
}
export default footer;
export default Footer;
"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="max-w-[1215px] w-full px-4 flex items-center justify-between">
<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"
>
sitemap
</a>
<a
Sitemap
</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">
......@@ -40,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) => (
......@@ -56,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">
......@@ -75,35 +85,72 @@ function Header() {
title="Giới thiệu"
link="gioi-thieu"
items={[
"Về VCCI-HCM",
"Chức Năng Và Nhiệm Vụ",
"Sơ Đồ Tổ Chức",
"Dịch Vụ Cung Cấp",
{ title: "Về VCCI-HCM", link: "ve-vcci-hcm" },
{
title: "Chức Năng Và Nhiệm Vụ",
link: "chuc-nang-va-nhiem-vu",
},
{ title: "Sơ Đồ Tổ Chức", link: "so-do-to-chuc" },
{ title: "Dịch Vụ Cung Cấp", link: "dich-vu-cung-cap" },
]}
/>
<MenuItem
title="Hội viên"
link="hoi-vien"
items={[
"Lợi Ích Của Hội Viên VCCI",
"Đăng Ký Hội Viên",
"Kết Nối Hội Viên",
"Tin Hội Viên",
{
title: "Lợi Ích Của Hội Viên VCCI",
link: "",
},
{ title: "Đăng Ký Hội Viên", link: "dang-ky-hoi-vien" },
{ title: "Kết Nối Hội Viên", link: "ket-noi-hoi-vien" },
{ title: "Tin Hội Viên", link: "tin-hoi-vien" },
]}
/>
<MenuItem
title="Hoạt động"
link="hoat-dong"
items={[
{ title: "Sự Kiện", link: "" },
{ title: "Đào Tạo", link: "dao-tao" },
]}
/>
<MenuItem title="Hoạt động" items={["Sự Kiện", "Đào Tạo"]} />
<MenuItem
title="Xuất Xứ Hàng Hóa"
link="xuat-xu-hang-hoa"
items={[
"Định Nghĩa Chung",
"Mục Đích Của C/O",
"Luật Áp Dụng Về C/O",
"Thủ Tục Cấp C/O",
"Biểu Mẫu C/O Và Cách Khai",
"Phí Và Lệ Phí Cấp C/O",
"Điểm Cấp Và Thời Gian Cấp C/O",
"Thông Tin Liên Hệ",
{
title: "Định Nghĩa Chung",
link: "",
},
{
title: "Mục Đích Của C/O",
link: "muc-dich",
},
{
title: "Luật Áp Dụng Về C/O",
link: "luat-ap-dung",
},
{
title: "Thủ Tục Cấp C/O",
link: "thu-tuc-cap",
},
{
title: "Biểu Mẫu C/O Và Cách Khai",
link: "bieu-mau-c-o-va-cach-khai",
},
{
title: "Phí Và Lệ Phí Cấp C/O",
link: "phi-va-le-phi-cap",
},
{
title: "Điểm Cấp Và Thời Gian Cấp C/O",
link: "diem-cap-va-thoi-gian-cap",
},
{
title: "Thông Tin Liên Hệ",
link: "thong-tin-lien-he",
},
]}
/>
......@@ -117,20 +164,34 @@ function Header() {
</a>
<div className="absolute left-0 top-full hidden group-hover:block bg-[#124588]/98 text-white text-[14px] font-[500] min-w-[280px] shadow-lg">
<div className="flex flex-col">
<div className="px-5 py-3 hover:bg-[#e8c518]/80 cursor-pointer">
<Link
href={
"/dai-dien-gioi-chu/chuc-nang-dai-dien-nguoi-su-dung-lao-dong"
}
className="px-5 py-3 hover:bg-[#e8c518]/80 cursor-pointer"
>
Chức Năng Đại Diện Người Sử Dụng Lao Động
</div>
<div className="px-5 py-3 hover:bg-[#e8c518]/80 cursor-pointer">
</Link>
<Link
href={"/dai-dien-gioi-chu/tap-huan-nsdld"}
className="px-5 py-3 hover:bg-[#e8c518]/80 cursor-pointer"
>
Sự Kiện – Tập Huấn NSDLĐ
</div>
<div className="px-5 py-3 hover:bg-[#e8c518]/80 cursor-pointer">
</Link>
<Link
href={"/dai-dien-gioi-chu/tin-lien-quan"}
className="px-5 py-3 hover:bg-[#e8c518]/80 cursor-pointer"
>
Tin Liên Quan
</div>
</Link>
<div className="relative group/submenu">
<div className="px-5 py-3 hover:bg-[#e8c518]/80 cursor-pointer flex justify-between items-center">
<Link
href={"/dai-dien-gioi-chu/chu-de"}
className="px-5 py-3 hover:bg-[#e8c518]/80 cursor-pointer flex justify-between items-center"
>
Chủ Đề <span className="ml-2 text-xs"></span>
</div>
</Link>
<div className="absolute left-full top-0 hidden group-hover/submenu:block bg-[#124588]/98 text-white text-[14px] font-[500] min-w-[220px] shadow-lg">
<div className="px-5 py-3 hover:bg-[#e8c518]/80 cursor-pointer">
Quan Hệ Lao Động
......@@ -151,23 +212,29 @@ function Header() {
title="Xúc tiến thương mại"
link="xuc-tien-thuong-mai"
items={[
"Hồ Sơ Thị Trường",
"Môi Trường Kinh Doanh",
"Cơ Hội Kinh Doanh",
"Hỗ Trợ Kinh Doanh",
{ title: "Hồ Sơ Thị Trường", link: "ho-so-thi-truong" },
{
title: "Môi Trường Kinh Doanh",
link: "moi-truong-kinh-doanh",
},
{ title: "Cơ Hội Kinh Doanh", link: "co-hoi-kinh-doanh" },
{ title: "Hỗ Trợ Kinh Doanh", link: "ho-tro-kinh-doanh" },
]}
/>
<MenuItem
title="Thông tin truyền thông"
link="thong-tin-truyen-thong"
items={[
"Tin VCCI",
"Tin Kinh Tế",
"Tin Doanh Nghiệp",
"Chuyên Đề",
"Thông Tin Chính Sách Và Pháp Luật",
"Ấn Phẩm",
"Thư Viện Tài Liệu",
{ title: "Tin VCCI", link: "tin-vcci" },
{ title: "Tin Kinh Tế", link: "tin-kinh-te" },
{ title: "Tin Doanh Nghiệp", link: "tin-doanh-nghiep" },
{ title: "Chuyên Đề", link: "chuyen-de" },
{
title: "Thông Tin Chính Sách Và Pháp Luật",
link: "thong-tin-chinh-sach-va-phap-luat",
},
{ title: "Ấn Phẩm", link: "an-pham" },
{ title: "Thư Viện Tài Liệu", link: "thu-vien-tai-lieu" },
]}
/>
</nav>
......@@ -184,7 +251,8 @@ function Header() {
{/* Mobile Menu */}
<div
className={`lg:hidden bg-white shadow-lg transition-all duration-300 overflow-hidden ${toggleMenu ? "max-h-96 opacity-100" : "max-h-0 opacity-0"
className={`lg:hidden bg-white shadow-lg transition-all duration-300 overflow-hidden ${
toggleMenu ? "max-h-96 opacity-100" : "max-h-0 opacity-0"
}`}
>
{[
......
// Core
"use client";
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 parse from "html-react-parser";
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)
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} />
<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>
{/* Sidebar */}
<aside className="space-y-6">
<ListFilter />
<div className="bg-white border rounded-md overflow-hidden">
<div className="w-full h-56 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 Page;
......@@ -8,15 +8,18 @@ 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}` : undefined,
filters:'category@=Chủ đề'
});
return (
<div className="min-h-screen container mx-auto p-4">
......@@ -27,8 +30,15 @@ export default function Page() {
{/* 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ải dữ liệu...</span>
</div>
) : (
<>
{allData?.responseData.rows.map((news) => (
<NewsContent key={news.id} news={news} />
<NewsContent key={news.id} news={news} link={`${PATHS.ownerRepresentatives}/chu-de/${news.id}`}/>
))}
<div className="w-full flex justify-center mt-4">
......@@ -47,6 +57,8 @@ export default function Page() {
}
/>
</div>
</>
)}
</div>
</main>
......
......@@ -2,17 +2,33 @@
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 }: { news: NewsItem }) {
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
href={`/tin-tuc/${news.id}`}
href={`${link}`}
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.title}
src={`${Links.imageEndpoint}${news?news.thumbnail:event?.image}`}
alt={news?news.title:event?.name}
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 }: { news: NewsItem }) {
/>
<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.title}
<p className="text-primary font-semibold text-base md:text-lg hover:underline line-clamp-2 wrap-break-word">
{news?.title}{event?.name}
</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>
......
export const SAMPLE_HTML = `
<div class="document">
<h1 style="font-size:18px; font-weight:700; margin-bottom:8px;">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:12px;">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>
......
// Core
"use client";
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 parse from "html-react-parser";
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)
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} />
<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>
{/* Sidebar */}
<aside className="space-y-6">
<ListFilter />
<div className="bg-white border rounded-md overflow-hidden">
<div className="w-full h-56 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 Page;
......@@ -8,12 +8,14 @@ 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,
......@@ -27,8 +29,15 @@ export default function Page() {
{/* 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ải tập huấn NSDLĐ...</span>
</div>
) : (
<>
{allData?.responseData.rows.map((news) => (
<NewsContent key={news.id} news={news} />
<NewsContent key={news.id} news={news} link={`${PATHS.ownerRepresentatives}/tap-huan-nsdld/${news.id}`} />
))}
<div className="w-full flex justify-center mt-4">
......@@ -47,6 +56,8 @@ export default function Page() {
}
/>
</div>
</>
)}
</div>
</main>
......
// Core
"use client";
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 parse from "html-react-parser";
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
);
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} />
<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>
{/* Sidebar */}
<aside className="space-y-6">
<ListFilter />
<div className="bg-white border rounded-md overflow-hidden">
<div className="w-full h-56 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 Page;
......@@ -8,15 +8,17 @@ 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 [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,8 +29,15 @@ export default function Page() {
{/* 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ải tin liên quan...</span>
</div>
) : (
<>
{allData?.responseData.rows.map((news) => (
<NewsContent key={news.id} news={news} />
<NewsContent key={news.id} news={news} link={`${PATHS.ownerRepresentatives}/tin-lien-quan/${news.id}`} />
))}
<div className="w-full flex justify-center mt-4">
......@@ -40,12 +49,14 @@ export default function Page() {
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">
......
'use client'
import React from "react";
import ListCategory from "../components/list-category";
import EventCalendar from "../components/event-calendar";
import ImageGallery from "../components/image-gallery";
const Page = () => {
const images = [
'/gioi-thieu/chuc-nang-nhiem-vu/qddt_01.jpg',
'/gioi-thieu/chuc-nang-nhiem-vu/qddt_02.jpg',
'/gioi-thieu/chuc-nang-nhiem-vu/qddt_03.jpg',
'/gioi-thieu/chuc-nang-nhiem-vu/qddt_04.jpg',
'/gioi-thieu/chuc-nang-nhiem-vu/qddt_05.jpg',
'/gioi-thieu/chuc-nang-nhiem-vu/qddt_06.jpg',
'/gioi-thieu/chuc-nang-nhiem-vu/qddt_07.jpg',
'/gioi-thieu/chuc-nang-nhiem-vu/qddt_08.jpg',
'/gioi-thieu/chuc-nang-nhiem-vu/qddt_09.jpg',
'/gioi-thieu/chuc-nang-nhiem-vu/qddt_10.jpg',
'/gioi-thieu/chuc-nang-nhiem-vu/qddt_11.jpg',
'/gioi-thieu/chuc-nang-nhiem-vu/qddt_12.jpg',
'/gioi-thieu/chuc-nang-nhiem-vu/qddt_13.jpg',
'/gioi-thieu/chuc-nang-nhiem-vu/qddt_14.jpg',
'/gioi-thieu/chuc-nang-nhiem-vu/qddt_15.jpg',
'/gioi-thieu/chuc-nang-nhiem-vu/qddt_16.jpg',
'/gioi-thieu/chuc-nang-nhiem-vu/qddt_17.jpg',
'/gioi-thieu/chuc-nang-nhiem-vu/qddt_18.jpg',
'/gioi-thieu/chuc-nang-nhiem-vu/qddt_19.jpg',
];
return (
<div className="min-h-screen container mx-auto px-4 sm:px-6 lg:px-0 pb-4">
<div className="w-full flex flex-col gap-5">
<ListCategory />
{/* Main content */}
<main className="lg:col-span-2 bg-white border rounded-md py-10 px-5 md:px-10 xl:px-50 text-justify">
<h1 className="text-2xl font-bold text-[#153e8e]">Chức năng và Nhiệm vụ</h1>
<hr className="my-5" />
<div className="flex flex-col justify-center items-center">
<div className="max-w-5xl mb-5 mx-auto space-y-10">
{/* chức năng */}
<section>
<h2 className="text-xl sm:text-2xl mb-4">Chức năng</h2>
<p className="mb-4">
Liên đoàn Thương mại và Công nghiệp Việt Nam có các chức năng sau:
</p>
<ol className="list-decimal list-inside pl-5 sm:pl-6 md:pl-8 space-y-2">
<li>Đại diện để thúc đẩy và bảo vệ quyền lợi hợp pháp, chính đáng của cộng đồng doanh nghiệp Việt Nam trong các quan hệ trong nước và quốc tế;</li>
<li>Thúc đẩy sự phát triển của cộng đồng doanh nghiệp; xúc tiến và hỗ trợ các hoạt động thương mại, đầu tư, hợp tác khoa học – công nghệ và các hoạt động kinh doanh khác của cộng đồng doanh nghiệp ở Việt Nam và nước ngoài; xúc tiến, thúc đẩy xây dựng mối quan hệ lao động hài hòa trong các doanh nghiệp.</li>
</ol>
</section>
{/* nhiệm vụ */}
<section>
<h2 className="text-xl sm:text-2xl mb-4">Nhiệm vụ</h2>
<p className="mb-4">
Liên đoàn Thương mại và Công nghiệp Việt Nam có các nhiệm vụ sau:
</p>
<ol className="list-decimal list-inside pl-5 sm:pl-6 md:pl-8 space-y-2">
<li>Tập hợp, nghiên cứu thực trạng và kiến nghị với Đảng và Nhà nước các vấn đề về pháp luật, chính sách, chiến lược phát triển kinh tế – xã hội nhằm cải thiện môi trường kinh doanh và xây dựng quan hệ lao động hài hòa; tổ chức các diễn đàn, đối thoại, các cuộc tiếp xúc, làm đầu mối liên kết các doanh nghiệp, làm cầu nối giữa cộng đồng doanh nghiệp với các cơ quan Đảng, Nhà nước và với các tổ chức hữu quan khác ở trong và ngoài nước để trao đổi thông tin, ý kiến và đề xuất các giải pháp xử lý vướng mắc, hoàn thiện chính sách, pháp luật liên quan đến doanh nghiệp, môi trường kinh doanh và quan hệ lao động.</li>
<li>Đại diện cho cộng đồng doanh nghiệp tham gia vào quá trình xây dựng ban hành văn bản quy phạm pháp luật có liên quan đến doanh nghiệp, hoạt động kinh doanh và quan hệ lao động dưới các hình thức khác nhau theo quy định hiện hành.</li>
<li>Là đầu mối tập hợp thông tin, ý kiến của cộng đồng doanh nghiệp, tổ chức và tham gia quá trình tham vấn với các đoàn đàm phán về kinh tế, thương mại; tham gia với cơ quan Nhà nước có thẩm quyền trong quá trình đàm phán, ký kết, gia nhập, phê chuẩn, thực thi các điều ước quốc tế có liên quan tới kinh tế, thương mại; hỗ trợ cộng đồng doanh nghiệp trong hội nhập kinh tế quốc tế, đặc biệt là các vấn đề liên quan đến thực thi các điều ước quốc tế về kinh tế, thương mại mà Việt Nam là thành viên; tham gia tổ chức các đoàn doanh nghiệp tháp tùng lãnh đạo Đảng, Nhà nước; tổ chức các Diễn đàn doanh nghiệp Việt Nam, Hội đồng doanh nghiệp Việt Nam với doanh nghiệp các nước và các hoạt động xúc tiến khác nhằm mở rộng quan hệ thương mại, đầu tư quốc tế.</li>
<li>Thực hiện vai trò của tổ chức đại diện ở Trung ương của người sử dụng lao động Việt Nam tham gia vào các thiết chế ba bên về quan hệ lao động, hướng dẫn, hỗ trợ xây dựng và liên kết tổ chức của người sử dụng lao động ở cấp ngành và địa phương; phối hợp với tổ chức đại diện người lao động và các cơ quan, đơn vị hữu quan để hỗ trợ doanh nghiệp, giới sử dụng lao động xây dựng quan hệ lao động hài hòa, ổn định và tiến bộ theo quy định hiện hành.</li>
<li>Tiến hành những hoạt động để bảo vệ quyền lợi hợp pháp của cộng đồng doanh nghiệp trong các quan hệ kinh doanh trong nước và quốc tế; tư vấn và tham gia hỗ trợ giải quyết các vướng mắc, kiến nghị của cộng đồng doanh nghiệp với các cơ quan quản lý trong quá trình kinh doanh và thực thi pháp luật.</li>
<li>Tổ chức tuyên truyền, phổ biến, tư vấn thực thi chính sách, pháp luật; phổ biến, cung cấp, hỗ trợ thông tin kinh doanh, khoa học kỹ thuật cho cộng đồng doanh nghiệp.</li>
<li>Tổ chức vận động cộng đồng doanh nghiệp nâng cao trách nhiệm xã hội, xây dựng đạo đức và văn hóa kinh doanh, bảo vệ môi trường và tham gia các hoạt động xã hội khác liên quan tới hoạt động của Phòng Thương mại và Công nghiệp Việt Nam phù hợp với quy định của pháp luật.</li>
<li>Hỗ trợ việc thành lập, phối hợp nâng cao năng lực hoạt động và liên kết hệ thống các hiệp hội doanh nghiệp trong cả nước.</li>
<li>Hợp tác với các tổ chức, đơn vị hữu quan trong nước; hợp tác với các tổ chức của cộng đồng doanh nghiệp ở nước ngoài, ký, thực hiện các thỏa thuận hợp tác quốc tế, tham gia các tổ chức quốc tế phù hợp với quy định của pháp luật.</li>
<li>Tổ chức đào tạo để phát triển, bồi dưỡng nguồn nhân lực cho các doanh nghiệp, hiệp hội doanh nghiệp, nâng cao năng lực quản lý, kinh doanh cho các doanh nhân, xây dựng đội ngũ doanh nhân năng động, hiệu quả.</li>
<li>Tiến hành các hoạt động nhằm xây dựng, quảng bá thương hiệu và nâng cao uy tín hàng hóa, dịch vụ, cộng đồng doanh nghiệp và môi trường kinh doanh tại Việt Nam.</li>
<li>Tổ chức các hoạt động hỗ trợ phát triển kinh doanh; hỗ trợ doanh nghiệp tiếp cận các nguồn lực phát triển; hỗ trợ doanh nghiệp phát triển quan hệ kinh doanh và đầu tư ở trong và ngoài nước thông qua các biện pháp như: kết nối và giới thiệu bạn hàng, cung cấp thông tin, hướng dẫn và tư vấn cho doanh nghiệp, tổ chức nghiên cứu, khảo sát thị trường, hội thảo, hội nghị, hội chợ, triển lãm, quảng cáo và các hoạt động xúc tiến thương mại và đầu tư khác ở trong nước và nước ngoài theo quy định của pháp luật.</li>
<li>Tổ chức nghiên cứu, thử nghiệm, triển khai, chuyển giao các mô hình kinh doanh mới hỗ trợ phát triển doanh nghiệp, hiệp hội doanh nghiệp; thực hiện các đề tài, nghiên cứu, điều tra… về năng lực cạnh tranh, lao động và các nội dung khác nhằm nâng cao năng lực cạnh tranh và phát triển doanh nghiệp, hiệp hội doanh nghiệp.</li>
<li>Chủ trì hoặc phối hợp tổ chức thực hiện các hoạt động tôn vinh, khen thưởng doanh nghiệp, doanh nhân, các đơn vị, cá nhân có đóng góp lớn vào sự phát triển của cộng đồng doanh nghiệp và nền kinh tế theo quy định của pháp luật.</li>
<li>Hỗ trợ đăng ký và bảo hộ quyền sở hữu trí tuệ và chuyển giao công nghệ ở Việt Nam và ở nước ngoài theo quy định.</li>
<li>Cấp giấy chứng nhận xuất xứ cho hàng hóa xuất khẩu của Việt Nam theo ủy quyền của cơ quan Nhà nước có thẩm quyền; xác nhận các trường hợp bất khả kháng và chứng nhận, xác nhận các giấy tờ cần thiết khác trong hoạt động thương mại theo yêu cầu tự nguyện của các bên trong giao dịch hoặc theo yêu cầu, ủy quyền của các cơ quan, tổ chức có thẩm quyền ở trong và ngoài nước.</li>
<li>Hỗ trợ các doanh nghiệp trong và ngoài nước giải quyết bất đồng, tranh chấp thông qua thương lượng, hòa giải hoặc trọng tài phù hợp với quy định của pháp luật.</li>
<li>Thực hiện các nhiệm vụ khác mà cơ quan Nhà nước giao hoặc ủy quyền.</li>
</ol>
</section>
</div>
<p className="mb-4 font-bold">
ĐIỀU LỆ VCCI:
</p>
<div className="pb-5 w-full">
<ImageGallery images={images} />
</div>
</div>
</main>
</div>
</div>
);
};
export default Page;
"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>
);
}
'use client'
import { useState } from 'react'
import { Swiper, SwiperSlide } from 'swiper/react'
import 'swiper/css'
export default function ImageGallery({ images }: { images: string[] }) {
const [activeIndex, setActiveIndex] = useState(0)
const [lightboxOpen, setLightboxOpen] = useState(false)
return (
<div className="w-full max-w-4xl mx-auto">
{/* Ảnh lớn */}
<div
className="w-full mb-4 overflow-hidden rounded-lg shadow-md cursor-zoom-in"
onClick={() => setLightboxOpen(true)}
>
<img
src={images[activeIndex]}
alt={`Image ${activeIndex + 1}`}
className="w-full h-full object-cover transition-transform duration-300"
/>
</div>
{/* Slider ảnh nhỏ */}
<Swiper spaceBetween={10} slidesPerView={4} className="cursor-pointer">
{images.map((img, index) => (
<SwiperSlide key={index} onClick={() => setActiveIndex(index)}>
<img
src={img}
alt={`Thumbnail ${index + 1}`}
className={`w-full object-cover rounded-lg border-2 transition-all duration-300
${activeIndex === index
? 'border-blue-500 filter brightness-100'
: 'border-transparent filter brightness-50 hover:brightness-75'
}`}
/>
</SwiperSlide>
))}
</Swiper>
{/* Lightbox */}
{lightboxOpen && (
<div
className="fixed inset-0 bg-black bg-opacity-80 flex items-center justify-center z-50 cursor-zoom-out"
onClick={() => setLightboxOpen(false)}
>
<img
src={images[activeIndex]}
alt={`Image ${activeIndex + 1}`}
className="max-h-full max-w-full object-contain"
/>
</div>
)}
</div>
)
}
"use client"
import Link from "next/link"
import { usePathname } from "next/navigation"
import { usePathname, useRouter } from "next/navigation"
import React from "react"
type Category = {
......@@ -11,25 +11,30 @@ type Category = {
const CATEGORIES: Category[] = [
{ title: "Về VCCI-HCM", href: "/gioi-thieu" },
{ title: "Chức năng và Nhiệm vụ", href: "/gioi-thieu/chuc-nang" },
{ title: "Sơ đồ Tổ chức", href: "/gioi-thieu/so-do" },
{ title: "Dịch vụ cung cấp", href: "/gioi-thieu/dich-vu" },
{ title: "Chức năng và nhiệm vụ", href: "/gioi-thieu/chuc-nang-nhiem-vu" },
{ title: "Sơ đồ tổ chức", href: "/gioi-thieu/so-do-to-chuc" },
{ title: "Dịch vụ cung cấp", href: "/gioi-thieu/dich-vu-cung-cap" },
]
const ListCategory: React.FC = () => {
const pathname = usePathname() || ""
const router = useRouter()
const isActive = (href: string) => {
// treat the base path as active for nested routes as well
if (href === "/gioi-thieu") return pathname === href || pathname.startsWith(href + "/")
if (href === "/gioi-thieu") return pathname === "/gioi-thieu"
return pathname === href
}
const handleChange = (e: React.ChangeEvent<HTMLSelectElement>) => {
router.push(e.target.value)
}
return (
<div className="border-t border-gray-200 bg-white p-2.5">
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
<div className="max-w-7xl mx-auto">
<nav aria-label="Danh mục" className="py-3">
<ul className="flex gap-8 items-center">
{/* --- Desktop view --- */}
<ul className="hidden sm:flex items-center">
{CATEGORIES.map((c) => {
const active = isActive(c.href)
return (
......@@ -37,10 +42,10 @@ const ListCategory: React.FC = () => {
<Link
href={c.href}
className={
"text-sm font-medium py-3.5 px-5 transition-colors duration-150 " +
"text-sm font-bold py-3.5 px-5 transition-colors duration-150 " +
(active
? "text-yellow-500 font-semibold decoration-yellow-300 "
: "text-gray-600 hover:text-gray-800 hover:underline")
? "text-yellow-500 font-semibold decoration-yellow-300"
: "text-gray-600 hover:text-yellow-500")
}
>
{c.title}
......@@ -49,6 +54,21 @@ const ListCategory: React.FC = () => {
)
})}
</ul>
{/* --- Mobile view (Dropdown) --- */}
<div className="sm:hidden">
<select
value={pathname}
onChange={handleChange}
className="w-full border border-gray-300 rounded-md px-3 py-2 text-sm text-gray-700 focus:outline-none focus:ring-2 focus:ring-yellow-400"
>
{CATEGORIES.map((c) => (
<option key={c.href} value={c.href}>
{c.title}
</option>
))}
</select>
</div>
</nav>
</div>
</div>
......
"use client"
import React, { useState } from 'react'
import { Checkbox } from '@/components/ui/checkbox'
import { Input } from '@/components/ui/input'
import { Button } from '@/components/ui/button'
type Category = { id: string; title: string; count: number }
const DEFAULT_CATEGORIES: Category[] = [
{ id: 'ceo', title: 'CEO', count: 4 },
{ id: 'policy', title: 'Hỏi đáp về chính sách', count: 0 },
{ id: 'biz', title: 'Tin Doanh Nghiệp', count: 9 },
{ id: 'member', title: 'Tin Hội Viên', count: 17 },
{ id: 'law', title: 'Văn bản Pháp luật sắp có hiệu lực', count: 30 }
]
export const ListFilter: React.FC<{
categories?: Category[]
onSearch?: (q: string) => void
onReset?: () => void
}> = ({ categories = DEFAULT_CATEGORIES, onSearch, onReset }) => {
const [query, setQuery] = useState('')
const [selected, setSelected] = useState<Record<string, boolean>>(() => {
const map: Record<string, boolean> = {}
categories.forEach((c) => (map[c.id] = false))
return map
})
const toggle = (id: string) => setSelected((s) => ({ ...s, [id]: !s[id] }))
return (
<aside className="p-6 bg-white border rounded-md">
<h3 className="text-lg font-semibold mb-3">Tìm kiếm</h3>
<div className="mb-4">
<Input
placeholder="Tên văn bản ..."
value={query}
className='text-black placeholder:text-gray-400 rounded-none py-2.5 px-2'
onChange={(e) => setQuery(e.target.value)}
onKeyDown={(e) => {
if (e.key === 'Enter') {
onSearch?.(query)
}
}}
/>
</div>
{/* <div className="flex flex-col gap-3 mb-6">
{categories.map((c) => (
<label key={c.id} className="flex items-center gap-3">
<Checkbox checked={!!selected[c.id]} onCheckedChange={() => toggle(c.id)} />
<div className="flex justify-between w-full items-center">
<span className="text-sm">{c.title}</span>
<span className="text-sm text-gray-400">({c.count})</span>
</div>
</label>
))}
</div> */}
<div className="flex gap-3">
<Button className="flex-1 rounded-none font-medium text-lg text-white hover:bg-muted-foreground hover:outline-1 outline-primary hover:text-primary" onClick={() => onSearch?.(query)}>
Tìm kiếm
</Button>
<Button
className="flex-1 rounded-none font-medium text-lg text-white hover:bg-muted-foreground hover:outline-1 outline-primary hover:text-primary"
onClick={() => {
setQuery('')
// restore initial map
const map: Record<string, boolean> = {}
categories.forEach((c) => (map[c.id] = false))
setSelected(map)
onReset?.()
}}
>
Bỏ tìm
</Button>
</div>
</aside>
)
}
export default ListFilter
'use client'
import React from "react";
import ListCategory from "../components/list-category";
import EventCalendar from "../components/event-calendar";
const Page = () => {
return (
<div className="min-h-screen container mx-auto pb-4">
<div className="w-full flex flex-col gap-5">
<ListCategory />
<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-7">
<h1 className="text-2xl font-bold text-[#153e8e]">Dịch vụ cung cấp</h1>
<hr className="my-5" />
<div className="flex items-center">
<img src="/gioi-thieu/dich-vu-cung-cap/1-7.webp" alt="Thông tin" className="w-12 px-2 py-2" />
<p>Tổ chức sự kiện, hội nghị, hội thảo, giao lưu thương mại, hội chợ, triển lãm</p>
</div>
<div className="flex items-center">
<img src="/gioi-thieu/dich-vu-cung-cap/3-4-150x145.webp" alt="Thông tin" className="w-12 px-2 py-2" />
<p>Đào tạo nâng cao năng lực quản trị doanh nghiệp</p>
</div>
<div className="flex items-center">
<img src="/gioi-thieu/dich-vu-cung-cap/5-1.webp" alt="Thông tin" className="w-12 px-2 py-2" />
<p>Khảo sát thị trường nước ngoài</p>
</div>
<div className="flex items-center">
<img src="/gioi-thieu/dich-vu-cung-cap/7-150x147.webp" alt="Thông tin" className="w-12 px-2 py-2" />
<p>Cho thuê văn phòng, hội trường</p>
</div>
<div className="flex items-center">
<img src="/gioi-thieu/dich-vu-cung-cap/8.webp" alt="Thông tin" className="w-12 px-2 py-2" />
<p>Quảng cáo, truyền thông</p>
</div>
<div className="flex items-center">
<img src="/gioi-thieu/dich-vu-cung-cap/2-6.webp" alt="Thông tin" className="w-12 px-2 py-2" />
<p>Tư vấn về pháp lý, quan hệ lao động, môi trường kinh doanh</p>
</div>
<div className="flex items-center">
<img src="/gioi-thieu/dich-vu-cung-cap/4-1.webp" alt="Thông tin" className="w-12 px-2 py-2" />
<p>Cấp C/O và xác nhận các chứng từ thương mại</p>
</div>
<div className="flex items-center">
<img src="/gioi-thieu/dich-vu-cung-cap/6-5.webp" alt="Thông tin" className="w-12 px-2 py-2" />
<p>Cung cấp thông tin thị trường và hồ sơ doanh nghiệp</p>
</div>
<div className="flex items-center">
<img src="/gioi-thieu/dich-vu-cung-cap/9.webp" alt="Thông tin" className="w-12 px-2 py-2" />
<p>Thu xếp visa nhập cảnh</p>
</div>
<div className="flex items-center">
<img src="/gioi-thieu/dich-vu-cung-cap/10.webp" alt="Thông tin" className="w-12 px-2 py-2" />
<p>Biên phiên dịch</p>
</div>
</main>
{/* Sidebar */}
<aside className="space-y-6">
<EventCalendar />
<img src="/home/eCarAid_web_banner_600x400.webp" alt="banner" />
</aside>
</div >
</div >
</div >
);
};
export default Page;
\ No newline at end of file
'use client'
import React from "react";
import ListCategory from "./components/list-category";
import EventCalendar from "./components/event-calendar";
import ImageGallery from "./components/image-gallery";
const Page = () => {
const images = [
'/gioi-thieu/VCCI-HCM-BROCHURE-2020_Tieng-Viet-1-scaled.webp',
'/gioi-thieu/VCCI-HCM-BROCHURE-2020_Tieng-Viet-2-scaled.webp',
'/gioi-thieu/VCCI-HCM-BROCHURE-2020_Tieng-Viet-3-scaled.webp',
'/gioi-thieu/VCCI-HCM-BROCHURE-2020_Tieng-Viet-4-scaled.webp',
]
return (
<div className="min-h-screen container mx-auto pb-4">
<div className="w-full flex flex-col gap-5">
<ListCategory />
{/* Main content */}
<main className="lg:col-span-2 bg-white border rounded-md py-10 px-5 md:px-10 xl:px-50 text-justify">
<h1 className="text-2xl font-bold text-[#153e8e]">Về VCCI-HCM</h1>
<hr className="my-5" />
<div className="flex flex-col justify-center items-center">
<p className="text-center text-[#063e8e] text-[14pt] font-bold pb-5">
GIỚI THIỆU CHUNG
</p>
<p className="pb-5">Liên đoàn Thương mại và Công nghiệp Việt Nam (VCCI) là tổ chức quốc gia tập hợp và đại diện cho cộng đồng doanh nghiệp, doanh nhân, người sử dụng lao động và các hiệp hội doanh nghiệp ở Việt Nam nhằm mục đích phát triển, bảo vệ và hỗ trợ cộng đồng doanh nghiệp, góp phần phát triển kinh tế - xã hội của đất nước, thúc đẩy các quan hệ hợp tác kinh tế, thương mại và khoa học - công nghệ với nước ngoài trên cơ sở bình đẳng và cùng có lợi, theo quy định của pháp luật.</p>
<p>Chi nhánh VCCI khu vực Thành phố Hồ Chí Minh (VCCI-HCM) là Chi nhánh lớn nhất, hoạt động trên địa bàn TP.HCM và 5 tỉnh thành phía Nam: Bình Dương, Bình Phước, Đồng Nai, Lâm Đồng, Tây Ninh.</p>
<img src="/gioi-thieu/MAPS_VCCI-HCM-Upload-1259x1536.jpg.webp" alt="map" />
<img src="/gioi-thieu/tam-nhin-1024x470.jpg.webp" alt="map" />
<p className="text-[14pt] pb-5">
<strong className="text-[#063e8e] font-sans">
BROCHURE VCCI-HCM
</strong>
</p>
<div className="pb-5">
<ImageGallery images={images} />
</div>
<p className="text-[14pt] pb-5">
<strong className="text-[#063e8e] font-sans">
VIDEO VỀ VCCI-HCM
</strong>
</p>
<iframe
width="808"
height="455"
src="https://www.youtube.com/embed/j9ao-9b6Jf0"
title="VCCI-HCM 2024 IN REVIEW"
frameBorder="0"
allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share"
referrerPolicy="strict-origin-when-cross-origin"
allowFullScreen>
</iframe>
</div>
</main>
</div>
</div>
);
};
export default Page;
\ No newline at end of file
'use client'
import React from "react";
import ListCategory from "../components/list-category";
const Page = () => {
return (
<div className="min-h-screen container mx-auto pb-4">
<div className="w-full flex flex-col gap-5">
<ListCategory />
{/* Main content */}
<main className="lg:col-span-2 bg-white border rounded-md py-10 px-5 md:px-10 xl:px-50 text-justify">
<h1 className="text-2xl font-bold text-[#153e8e]">Về VCCI-HCM</h1>
<hr className="my-5" />
<img src="/gioi-thieu/so-do-to-chuc/2025-SO-DO-TO-CHUC-01-VN.jpg.webp" alt="Sơ đồ tổ chức VCCI-HCM" />
</main>
</div >
</div >
);
};
export default Page;
\ No newline at end of file
"use client";
import React from "react";
import ReactCountryFlag from "react-country-flag";
type Region = { id: string; name: string; image?: string };
export const DEFAULT_REGIONS: Region[] = [
......@@ -40,17 +40,258 @@ export default function MapRegion({
<div className="bg-white shadow-sm rounded-md overflow-hidden p-6">
<div className="flex flex-col gap-6">
<div>
<h3 className="text-2xl font-semibold text-primary mb-0">{activeRegion?.name ?? ""}</h3>
<h3 className="text-2xl font-semibold text-primary mb-0">
{activeRegion?.name ?? ""}
</h3>
</div>
<div className="w-full flex items-center justify-center">
<div className="w-full max-w-3xl">
<div className="bg-gray-50 p-4 flex items-center justify-center">
<div className="relative">
<img
src={displayedImage}
alt={activeRegion?.name ?? "Map"}
className="w-full h-auto object-contain block mx-auto"
style={{ maxHeight: 520 }}
/>
{/* Country Flags*/}
{active == "dong-nam-a" && (
<>
<div className="absolute top-[17%] left-[29%] w-4 h-4 md:w-6 md:h-6 rounded-full">
<ReactCountryFlag
countryCode="MM"
svg
style={{
width: "100%",
height: "100%",
objectFit: "cover",
}}
className="rounded-full"
/>
</div>
<div className="absolute top-[23%] left-[40%] w-4 h-4 md:w-6 md:h-6 rounded-full">
<ReactCountryFlag
countryCode="LA"
svg
style={{
width: "100%",
height: "100%",
objectFit: "cover",
}}
className="rounded-full"
/>
</div>
<div className="absolute top-[20%] left-[43%] w-4 h-4 md:w-6 md:h-6 rounded-full">
<ReactCountryFlag
countryCode="VN"
svg
style={{
width: "100%",
height: "100%",
objectFit: "cover",
}}
className="rounded-full"
/>
</div>
<div className="absolute top-[38%] left-[43%] w-4 h-4 md:w-6 md:h-6 rounded-full">
<ReactCountryFlag
countryCode="KH"
svg
style={{
width: "100%",
height: "100%",
objectFit: "cover",
}}
className="rounded-full"
/>
</div>
<div className="absolute top-[27%] left-[68%] w-4 h-4 md:w-6 md:h-6 rounded-full">
<ReactCountryFlag
countryCode="PH"
svg
style={{
width: "100%",
height: "100%",
objectFit: "cover",
}}
className="rounded-full"
/>
</div>
<div className="absolute top-[55%] left-[40%] w-4 h-4 md:w-6 md:h-6 rounded-full">
<ReactCountryFlag
countryCode="MY"
svg
style={{
width: "100%",
height: "100%",
objectFit: "cover",
}}
className="rounded-full"
/>
</div>
<div className="absolute top-[55%] left-[57%] w-4 h-4 md:w-6 md:h-6 rounded-full">
<ReactCountryFlag
countryCode="BN"
svg
style={{
width: "100%",
height: "100%",
objectFit: "cover",
}}
className="rounded-full"
/>
</div>
<div className="absolute top-[62%] left-[47%] w-4 h-4 md:w-6 md:h-6 rounded-full">
<ReactCountryFlag
countryCode="SG"
svg
style={{
width: "100%",
height: "100%",
objectFit: "cover",
}}
className="rounded-full"
/>
</div>
<div className="absolute top-[65%] left-[57%] w-4 h-4 md:w-6 md:h-6 rounded-full">
<ReactCountryFlag
countryCode="ID"
svg
style={{
width: "100%",
height: "100%",
objectFit: "cover",
}}
className="rounded-full"
/>
</div>
<div className="absolute top-[32%] left-[36%] w-4 h-4 md:w-6 md:h-6 rounded-full">
<ReactCountryFlag
countryCode="TH"
svg
style={{
width: "100%",
height: "100%",
objectFit: "cover",
}}
className="rounded-full"
/>
</div>
</>
)}
{active == "dong-bac-a" && (
<>
<div className="absolute top-[40%] left-[60%] w-4 h-4 md:w-6 md:h-6 rounded-full">
<ReactCountryFlag
countryCode="CN"
svg
style={{
width: "100%",
height: "100%",
objectFit: "cover",
}}
className="rounded-full"
/>
</div>
<div className="absolute top-[37%] left-[77%] w-4 h-4 md:w-6 md:h-6 rounded-full">
<ReactCountryFlag
countryCode="KR"
svg
style={{
width: "100%",
height: "100%",
objectFit: "cover",
}}
className="rounded-full"
/>
</div>
<div className="absolute top-[34%] left-[87%] w-4 h-4 md:w-6 md:h-6 rounded-full">
<ReactCountryFlag
countryCode="JP"
svg
style={{
width: "100%",
height: "100%",
objectFit: "cover",
}}
className="rounded-full"
/>
</div>
<div className="absolute top-[60%] left-[73%] w-4 h-4 md:w-6 md:h-6 rounded-full">
<ReactCountryFlag
countryCode="TW"
svg
style={{
width: "100%",
height: "100%",
objectFit: "cover",
}}
className="rounded-full"
/>
</div>
</>
)}
{active == "nam-a" && (
<>
<div className="absolute top-[40%] left-[60%] w-4 h-4 md:w-6 md:h-6 rounded-full">
<ReactCountryFlag
countryCode="IN"
svg
style={{
width: "100%",
height: "100%",
objectFit: "cover",
}}
className="rounded-full"
/>
</div>
</>
)}
{active == "bac-my" && (
<>
<div className="absolute top-[60%] left-[40%] w-4 h-4 md:w-6 md:h-6 rounded-full">
<ReactCountryFlag
countryCode="US"
svg
style={{
width: "100%",
height: "100%",
objectFit: "cover",
}}
className="rounded-full"
/>
</div>
<div className="absolute top-[40%] left-[40%] w-4 h-4 md:w-6 md:h-6 rounded-full">
<ReactCountryFlag
countryCode="CA"
svg
style={{
width: "100%",
height: "100%",
objectFit: "cover",
}}
className="rounded-full"
/>
</div>
</>
)}
{active == "chau-uc" && (
<>
<div className="absolute top-[30%] left-[40%] w-4 h-4 md:w-6 md:h-6 rounded-full">
<ReactCountryFlag
countryCode="AU"
svg
style={{
width: "100%",
height: "100%",
objectFit: "cover",
}}
className="rounded-full"
/>
</div>
</>
)}
</div>
</div>
</div>
</div>
......
// Core
"use client";
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 parse from "html-react-parser";
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)
return (
<div className="min-h-screen w-full container mx-auto p-4">
<div className="w-full flex flex-col gap-5">
<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-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>
{/* Sidebar */}
<aside className="space-y-6">
<ListFilter />
<div className="bg-white border rounded-md overflow-hidden">
<div className="w-full h-56 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 Page;
......@@ -4,19 +4,21 @@ 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">
......@@ -27,8 +29,15 @@ export default function Page() {
{/* 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ải khóa đào tạo...</span>
</div>
) : (
<>
{allData?.responseData.rows.map((news) => (
<NewsContent key={news.id} news={news} />
<NewsContent key={news.id} news={news} link={`${PATHS.event}/dao-tao/${news.id}`} />
))}
<div className="w-full flex justify-center mt-4">
......@@ -40,6 +49,8 @@ export default function Page() {
onGoToNextPage={() => setPage(Math.min(Number(allData?.responseData.totalPages ?? 1), page + 1))}
/>
</div>
</>
)}
</div>
</main>
......
// Core
"use client";
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 {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 } = useGetEventsId<GetEventsIdQueryResponseType>(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={EVENT_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">
{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 */}
<aside className="space-y-6">
<ListFilter />
<div className="bg-white border rounded-md overflow-hidden">
<div className="w-full h-56 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 Page;
......@@ -5,20 +5,25 @@ 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<GetNewsResponseType>({
const { data: allData, isLoading } = useGetEvents<EventApiResponse>({
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">
......@@ -29,8 +34,15 @@ 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} />
{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">
......@@ -42,6 +54,8 @@ export default function Page() {
onGoToNextPage={() => setPage(Math.min(Number(allData?.responseData.totalPages ?? 1), page + 1))}
/>
</div>
</>
)}
</div>
</main>
......
......@@ -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,6 +27,13 @@ export default function Page() {
{/* 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ả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} />
))}
......@@ -46,6 +54,8 @@ export default function Page() {
}
/>
</div>
</>
)}
</div>
</main>
......
......@@ -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,6 +27,13 @@ export default function Page() {
{/* 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ải tin hội viên...</span>
</div>
) : (
<>
{allData?.responseData.rows.map((news) => (
<CardNews key={news.id} news={news} />
))}
......@@ -46,6 +54,8 @@ export default function Page() {
}
/>
</div>
</>
)}
</div>
</main>
......
"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>
);
}
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;
"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;
"use client";
import ListCategory from "@/components/base/list-category";
import { MEDIA_INFORMATION_CATEGORIES } from "@/constants/categories";
import Image from "next/image";
import Link from "next/link";
import { notFound, useParams } from "next/navigation";
import Calendar from "../components/calendar";
const publications = [
{
id: "huong-dan-dau-tu-2024",
title: "Cẩm nang Hướng dẫn đầu tư kinh doanh tại Việt Nam",
date: "18/10/2023",
link: "https://vcci-hcm.org.vn/wp-content/uploads/2023/10/Doing-Business-in-Vietnam-2023_Upload.pdf",
title_link: "“DOING BUSINESS IN VIETNAM 2023”",
img: "/an-pham/A-Guide-2023_Cover-725x1024.webp",
},
{
id: "connections-2022-2023",
title: "Danh bạ Hội viên CONNECTIONS 2022-2023",
date: "19/01/2023",
link: "https://vcci-hcm.org.vn/wp-content/uploads/2022/12/Danh-ba-HV-Connections_2022-2023.pdf",
title_link: "“DANH BẠ HỘI VIÊN CONNECTIONS 2022-2023”",
img: "/an-pham/Trang-bia_Connections_2022-2023-725x1024.jpg.webp",
},
{
id: "chuyen-doi-so",
title: "Chuyển đổi số – Động lực phục hồi và phát triển kinh tế",
date: "19/01/2023", // ← Sửa: 119 → 19
link: "https://vcci-hcm.org.vn/wp-content/uploads/2022/12/CHUYEN-DOI-SO-2022_Final_19.12.2022.pdf",
title_link: "“CHUYỂN ĐỔI SỐ – ĐỘNG LỰC PHỤC HỒI VÀ PHÁT TRIỂN KINH TẾ”",
img: "/an-pham/Trang-bia_Chuyen-doi-so_2022-750x1024.webp",
},
{
id: "huong-dan-dau-tu-2021",
title: "Cẩm nang Hướng dẫn đầu tư kinh doanh tại Việt Nam 2021",
date: "14/03/2022",
link: "https://vcci-hcm.org.vn/wp-content/uploads/2023/10/Doing-Business-in-Vietnam-2023_Upload.pdf",
title_link: "“DOING BUSINESS IN VIETNAM 2021”",
img: "/an-pham/doing-in-business-cover-1-1.webp",
},
{
id: "ban-tin-quy-4-2020",
title: "Bản tin Quý IV năm 2020",
date: "04/01/2021",
link: "https://vcci-hcm.org.vn/wp-content/uploads/2021/01/No3-092020_VCCI-NEWS-FINAL.pdf",
title_link: "Bản Tin Quý IV năm 2020",
img: "/an-pham/bia-ban-tin-quy-4-1.webp",
},
{
id: "ban-tin-quy-1-2020",
title: "Bản tin Quý I năm 2020",
date: "16/07/2020",
link: "https://vcci-hcm.org.vn/wp-content/uploads/2020/07/VCCI-NEWS-012020_XUAN.pdf",
title_link: "Bản tin Quý I năm 2020",
img: "/an-pham/bantintet-1.webp", // ← Sửa: iimg → img
},
];
// ĐÚNG: Không async, params là object
export default function PublicationDetail() {
const params = useParams(); // Dùng hook
const id = params.id as string; // Ép kiểu an toàn
const publication = publications.find((p) => p.id === id);
if (!publication) return notFound();
return (
<div className="bg-[#f6f6f6] min-h-screen">
<div className="max-w-[1200px] mx-auto flex flex-col gap-5 mb-[50px]">
<div className="border-[#e5e7f2] border-[1px]">
<ListCategory categories={MEDIA_INFORMATION_CATEGORIES} />
</div>
<div className="w-full flex gap-5 flex-wrap">
<div className="lg:w-[calc(65%-10px)] w-full border-[#e5e7f2] border-[1px] bg-white p-[30px] flex flex-col gap-[15px]">
<h1 className="text-[22px] font-semibold text-[#003366]">
{publication.title}
</h1>
<p className="text-[#00AED5] text-sm">{publication.date}</p>
<hr />
<p className="text-[16px] text-[#363636]">
Tải về ấn phẩm:{" "}
<Link
href={publication.link}
target="_blank"
className="text-[#0073e6] hover:text-[#e8c518]"
>
{publication.title_link}
</Link>
</p>
<div className="flex justify-center">
<Link href={publication.link} target="_blank">
<Image
src={publication.img}
alt={publication.title}
width={416}
height={566}
className="rounded-lg transition-all duration-300"
/>
</Link>
</div>
</div>
<div className="lg:w-[calc(35%-10px)] w-full">
<Calendar />
<div className="relative w-full mt-4 h-[300px] aspect-video rounded-lg overflow-hidden">
<Image
src="/banner.webp"
alt="Quảng cáo"
fill
className="object-contain"
/>
</div>
</div>
</div>
</div>
</div>
);
}
"use client";
import React, { useState, useMemo } from "react";
import {
format,
startOfMonth,
endOfMonth,
eachDayOfInterval,
isSameMonth,
isSameDay,
addMonths,
subMonths,
} from "date-fns";
import { ArrowLeft, ArrowRight, ChevronLeft, ChevronRight } from "lucide-react";
import { vi } from "date-fns/locale";
interface Event {
date: Date;
title: string;
type: "event" | "training";
description?: string;
}
export default function Calendar() {
const [currentMonth, setCurrentMonth] = useState(new Date());
const today = new Date();
// Dữ liệu mẫu
const events: Event[] = [
{
date: new Date(2025, 10, 1),
title: "Đào tạo nội bộ",
type: "training",
description: "Khóa học kỹ năng mềm",
},
{
date: new Date(2025, 10, 3),
title: "Họp cổ đông",
type: "event",
description: "Báo cáo Q3",
},
{
date: new Date(2025, 10, 13),
title: "Đào tạo kỹ thuật",
type: "training",
description: "React Advanced",
},
{
date: new Date(2025, 10, 14),
title: "Đào tạo an toàn",
type: "training",
description: "An toàn lao động",
},
{
date: new Date(2025, 10, 20),
title: "Hội thảo thuế",
type: "event",
description:
"Cập nhật luật thuế thu nhập doanh nghiệp số 67/2025/QH15...",
},
{
date: new Date(2025, 10, 28),
title: "Sự kiện nội bộ",
type: "event",
description: "Team building",
},
];
const monthStart = startOfMonth(currentMonth);
const monthEnd = endOfMonth(currentMonth);
const monthDays = eachDayOfInterval({ start: monthStart, end: monthEnd });
const firstDayOfWeek = monthStart.getDay(); // 0 = CN, 1 = T2...
const startDate = new Date(monthStart);
startDate.setDate(startDate.getDate() - firstDayOfWeek);
const days = [];
for (let i = 0; i < 42; i++) {
const day = new Date(startDate);
day.setDate(startDate.getDate() + i);
days.push(day);
}
const getEventForDay = (date: Date) =>
events.filter((e) => isSameDay(e.date, date));
const formatMonthTitle = () => {
return `THÁNG ${format(currentMonth, "M/yyyy")}`.toUpperCase();
};
return (
<>
<div className="w-full mx-auto bg-white rounded-lg p-4 ">
{/* Header */}
<div className="flex items-center justify-between mb-4 px-3">
<h2 className="text-[15px] font-bold text-[#063E8E]">
{formatMonthTitle()}
</h2>
<div className="flex gap-3">
<button
onClick={() => setCurrentMonth(subMonths(currentMonth, 1))}
className="p-2 cursor-pointer rounded-full group border-3 border-[#363636] hover:border-[#063e8e] transition"
>
<ArrowLeft className="group-hover:text-[#e8c518] text-[#363636] w-5 h-5" />
</button>
<button
onClick={() => setCurrentMonth(addMonths(currentMonth, 1))}
className="p-2 cursor-pointer rounded-full group border-3 border-[#363636] hover:border-[#063e8e] transition"
>
<ArrowRight className="group-hover:text-[#e8c518] text-[#363636] w-5 h-5" />
</button>
</div>
</div>
{/* Days of week */}
<div className="grid grid-cols-7 text-center text-sm font-medium text-gray-600 mb-1">
{["CN", "T2", "T3", "T4", "T5", "T6", "T7"].map((day) => (
<div key={day} className="py-2 text-[15px] text-[#063E8E]">
{day}
</div>
))}
</div>
{/* Calendar grid */}
<div className="grid grid-cols-7 gap-1 text-sm">
{days.map((day, idx) => {
const dayEvents = getEventForDay(day);
const isCurrentMonth = isSameMonth(day, currentMonth);
const isToday = isSameDay(day, today);
const hasEvent = dayEvents.some((e) => e.type === "event");
const hasTraining = dayEvents.some((e) => e.type === "training");
return (
<div
key={idx}
className={`
relative group aspect-square flex items-center justify-center rounded-full
${!isCurrentMonth ? "text-[#A4A4A4]" : "text-[#333333]"}
${hasEvent || hasTraining ? "text-white" : "text-[#333333]"}
${isToday ? "text-red-600 font-bold" : ""}
hover:bg-gray-50 transition
`}
>
<span className="relative z-10">{format(day, "d")}</span>
{/* Event/Training dots */}
{(hasEvent || hasTraining) && (
<div className="absolute inset-0 flex items-center justify-center pointer-events-none">
<div className="flex">
{hasEvent && (
<div className="w-10 h-10 bg-blue-600 rounded-full"></div>
)}
{hasTraining && (
<div className="w-10 h-10 bg-yellow-500 rounded-full"></div>
)}
</div>
</div>
)}
{/* Tooltip on hover */}
{dayEvents.length > 0 && (
<div
className="absolute top-full left-1/2 -translate-x-1/2 mt-2
w-64 p-3 bg-gray-900 text-white text-xs rounded-lg
shadow-xl opacity-0 pointer-events-none
group-hover:opacity-90 transition-opacity z-50"
>
<div className="space-y-2">
{dayEvents.map((event, i) => (
<div
key={i}
className="border-b border-gray-700 border-opacity-20 last:border-0 pb-2 last:pb-0"
>
<div className="flex items-center gap-2">
<div
className={`w-3 h-3 rounded-full ${
event.type === "event"
? "bg-blue-400"
: "bg-yellow-400"
}`}
></div>
<span className="font-medium">{event.title}</span>
</div>
{event.description && (
<p className="text-gray-300 mt-1 text-xs">
{event.description}
</p>
)}
</div>
))}
</div>
{/* Mũi tên nhọn HƯỚNG LÊN (chỉ vào ngày) */}
<div
className="absolute bottom-full left-1/2 -translate-x-1/2 -mb-1
w-0 h-0
border-l-8 border-l-transparent
border-r-8 border-r-transparent
border-b-8 border-b-gray-900"
></div>
</div>
)}
</div>
);
})}
</div>
{/* Legend */}
<div className="flex justify-center gap-6 mt-4 text-xs">
<div className="flex items-center gap-2">
<div className="w-3 h-3 bg-blue-600 rounded-full"></div>
<span>Sự kiện</span>
</div>
<div className="flex items-center gap-2">
<div className="w-3 h-3 bg-yellow-500 rounded-full"></div>
<span>Đào tạo</span>
</div>
</div>
</div>
</>
);
}
"use client";
import React, { useState } from "react";
import Image from "next/image";
import Link from "next/link";
import { Pagination } from "@components/base/pagination";
import { useGetNews } from "@api/endpoints/news";
import { GetNewsResponseType } from "@api/types/NewsPage.type";
const publications = [
{
id: "huong-dan-dau-tu-2024",
title: "Cẩm nang Hướng dẫn đầu tư kinh doanh tại Việt Nam",
img: "/an-pham/A-Guide-2023_Cover-725x1024.webp",
},
{
id: "connections-2022-2023",
title: "Danh bạ Hội viên CONNECTIONS 2022-2023",
img: "/an-pham/Trang-bia_Connections_2022-2023-725x1024.jpg.webp",
},
{
id: "chuyen-doi-so",
title: "Chuyển đổi số – Động lực phục hồi và phát triển kinh tế",
img: "/an-pham/Trang-bia_Chuyen-doi-so_2022-750x1024.webp",
},
{
id: "huong-dan-dau-tu-2021",
title: "Cẩm nang Hướng dẫn đầu tư kinh doanh tại Việt Nam 2021",
img: "/an-pham/doing-in-business-cover-1-1.webp",
},
{
id: "ban-tin-quy-4-2020",
title: "Bản tin Quý IV năm 2020",
img: "/an-pham/bia-ban-tin-quy-4-1.webp",
},
{
id: "ban-tin-quy-1-2020",
title: "Bản tin Quý I năm 2020",
img: "/an-pham/bantintet-1.webp",
},
];
export default function PublicationList() {
const [page, setPage] = useState(1);
const pageSize = 5;
const { data: allData } = useGetNews<GetNewsResponseType>({
pageSize: String(pageSize),
currentPage: String(page),
});
return (
<div className="lg:w-[calc(65%-10px)] w-full flex flex-col gap-[15px]">
<div className="grid grid-cols-1 sm:grid-cols-2 md:grid-cols-3 gap-8">
{publications.map((pub) => (
<Link
href={`/thong-tin-truyen-thong/an-pham/${pub.id}`}
key={pub.id}
className="flex flex-col items-center text-center h-full max-h-[342px] bg-white group"
>
<div className="w-full max-w-[260px] aspect-[3/4] overflow-hidden rounded-lg">
<Image
src={pub.img}
alt={pub.title}
width={300}
height={400}
className="object-contain w-full h-full transition-transform duration-300 group-hover:scale-105"
/>
</div>
<h3 className="mt-3 text-[15px] font-semibold text-[#124588] group-hover:text-[#E8C518] leading-snug">
{pub.title}
</h3>
</Link>
))}
</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>
);
}
"use client";
import React, { useState } from "react";
import ListCategory from "@app/dai-dien-gioi-chu/components/list-category";
import { MEDIA_INFORMATION_CATEGORIES } from "@constants/categories";
import ListCategory from "@/components/base/list-category";
import { MEDIA_INFORMATION_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 Image from "next/image";
import { useGetNews } from "@api/endpoints/news";
import { GetNewsResponseType } from "@api/types/NewsPage.type";
export default function Page() {
const [submitSearch] = useState("");
const [page, setPage] = useState(1);
import PublicationList from "./components/publicationList";
const pageSize = 5;
const { data: allData } = useGetNews<GetNewsResponseType>({
pageSize: String(pageSize),
currentPage: String(page),
filters: submitSearch ? `title @=${submitSearch}` : undefined,
});
export default function Page() {
return (
<div className="min-h-screen container mx-auto p-4">
<div className="w-full flex flex-col gap-5">
<div className="bg-[#f6f6f6]">
<div className="max-w-[1200px] m-auto flex flex-col gap-5 mb-[50px]">
<div className="border-[#e5e7f2] border-[1px]">
<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} />
))}
<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">
<div className="w-full flex gap-5 flex-wrap">
<PublicationList />
<div className="lg:w-[calc(35%-10px)] w-full">
<EventFilter />
<div className="bg-white border rounded-md overflow-hidden">
<div className="w-full h-56 relative bg-gray-100">
<div className="relative w-full mt-4 h-[300px] aspect-video rounded-lg overflow-hidden">
<Image
src="/banner.webp"
alt="Quảng cáo"
fill
className="object-cover"
className="object-contain"
/>
</div>
</div>
</aside>
</div>
</div>
</div>
......
// Core
"use client";
import Image from "next/image";
import ListCategory from "@app/dai-dien-gioi-chu/components/list-category";
import { MEDIA_INFORMATION_CATEGORIES } from "@constants/categories";
import ListFilter from "@app/dai-dien-gioi-chu/components/list-filter";
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';
// ...existing code...
const Page: React.FC = () => {
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={MEDIA_INFORMATION_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='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 */}
<aside className="space-y-6">
<ListFilter />
<div className="bg-white border rounded-md overflow-hidden">
<div className="w-full h-56 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 Page;
......@@ -5,19 +5,20 @@ 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">
......@@ -28,8 +29,21 @@ export default function Page() {
{/* 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ả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} />
<NewsContent
key={news.id}
news={news}
link={`${PATHS.mediaInformation}/chuyen-de/${news.id}`}
/>
))}
<div className="w-full flex justify-center mt-4">
......@@ -41,6 +55,8 @@ export default function Page() {
onGoToNextPage={() => setPage(Math.min(Number(allData?.responseData.totalPages ?? 1), page + 1))}
/>
</div>
</>
)}
</div>
</main>
......
// Core
"use client";
import Image from "next/image";
import ListCategory from "@app/dai-dien-gioi-chu/components/list-category";
import { MEDIA_INFORMATION_CATEGORIES } from "@constants/categories";
import ListFilter from "@app/dai-dien-gioi-chu/components/list-filter";
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';
// ...existing code...
const Page: React.FC = () => {
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={MEDIA_INFORMATION_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='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 */}
<aside className="space-y-6">
<ListFilter />
<div className="bg-white border rounded-md overflow-hidden">
<div className="w-full h-56 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 Page;
......@@ -5,19 +5,20 @@ 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";
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ô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">
......@@ -28,9 +29,16 @@ 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} />
))}
{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
......
// Core
"use client";
import Image from "next/image";
import ListCategory from "@app/dai-dien-gioi-chu/components/list-category";
import { MEDIA_INFORMATION_CATEGORIES } from "@constants/categories";
import ListFilter from "@app/dai-dien-gioi-chu/components/list-filter";
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';
// ...existing code...
const Page: React.FC = () => {
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={MEDIA_INFORMATION_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='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 */}
<aside className="space-y-6">
<ListFilter />
<div className="bg-white border rounded-md overflow-hidden">
<div className="w-full h-56 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 Page;
......@@ -4,20 +4,22 @@ 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">
......@@ -28,8 +30,21 @@ export default function Page() {
{/* 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ả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} />
<NewsContent
key={news.id}
news={news}
link={`${PATHS.mediaInformation}/thu-vien-tai-lieu/${news.id}`}
/>
))}
<div className="w-full flex justify-center mt-4">
......@@ -41,6 +56,8 @@ export default function Page() {
onGoToNextPage={() => setPage(Math.min(Number(allData?.responseData.totalPages ?? 1), page + 1))}
/>
</div>
</>
)}
</div>
</main>
......
// Core
"use client";
import Image from "next/image";
import ListCategory from "@app/dai-dien-gioi-chu/components/list-category";
import { MEDIA_INFORMATION_CATEGORIES } from "@constants/categories";
import ListFilter from "@app/dai-dien-gioi-chu/components/list-filter";
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';
// ...existing code...
const Page: React.FC = () => {
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={MEDIA_INFORMATION_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='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 */}
<aside className="space-y-6">
<ListFilter />
<div className="bg-white border rounded-md overflow-hidden">
<div className="w-full h-56 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 Page;
......@@ -5,19 +5,21 @@ 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">
......@@ -28,8 +30,21 @@ export default function Page() {
{/* 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ả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} />
<NewsContent
key={news.id}
news={news}
link={`${PATHS.mediaInformation}/tin-doanh-nghiep/${news.id}`}
/>
))}
<div className="w-full flex justify-center mt-4">
......@@ -41,6 +56,8 @@ export default function Page() {
onGoToNextPage={() => setPage(Math.min(Number(allData?.responseData.totalPages ?? 1), page + 1))}
/>
</div>
</>
)}
</div>
</main>
......
// Core
"use client";
import Image from "next/image";
import ListCategory from "@app/dai-dien-gioi-chu/components/list-category";
import { MEDIA_INFORMATION_CATEGORIES } from "@constants/categories";
import ListFilter from "@app/dai-dien-gioi-chu/components/list-filter";
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';
// ...existing code...
const Page: React.FC = () => {
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={MEDIA_INFORMATION_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='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 */}
<aside className="space-y-6">
<ListFilter />
<div className="bg-white border rounded-md overflow-hidden">
<div className="w-full h-56 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 Page;
......@@ -4,19 +4,21 @@ 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">
......@@ -27,8 +29,21 @@ export default function Page() {
{/* 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ả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} />
<NewsContent
key={news.id}
news={news}
link={`${PATHS.mediaInformation}/tin-kinh-te/${news.id}`}
/>
))}
<div className="w-full flex justify-center mt-4">
......@@ -40,6 +55,8 @@ export default function Page() {
onGoToNextPage={() => setPage(Math.min(Number(allData?.responseData.totalPages ?? 1), page + 1))}
/>
</div>
</>
)}
</div>
</main>
......
// Core
"use client";
import Image from "next/image";
import ListCategory from "@app/dai-dien-gioi-chu/components/list-category";
import { MEDIA_INFORMATION_CATEGORIES } from "@constants/categories";
import ListFilter from "@app/dai-dien-gioi-chu/components/list-filter";
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';
// ...existing code...
const Page: React.FC = () => {
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={MEDIA_INFORMATION_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='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 */}
<aside className="space-y-6">
<ListFilter />
<div className="bg-white border rounded-md overflow-hidden">
<div className="w-full h-56 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 Page;
......@@ -6,16 +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,
......@@ -29,8 +31,21 @@ export default function Page() {
{/* 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ả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} />
<NewsContent
key={news.id}
news={news}
link={`${PATHS.mediaInformation}/tin-vcci/${news.id}`}
/>
))}
<div className="w-full flex justify-center mt-4">
......@@ -42,13 +57,15 @@ export default function Page() {
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
......
......@@ -6,7 +6,7 @@ import Image from "next/image";
export default function page() {
return (
<div className="bg-[#f6f6f6]">
<div className="max-w-[1200px] m-auto flex flex-col gap-5 mb-[50px]">
<div className="container m-auto flex flex-col gap-5 mb-[50px]">
<div className="border-[#e5e7f2] border-[1px]">
<ListCategory />
</div>
......
// Core
"use client";
import Image from "next/image";
import ListCategory from "@app/dai-dien-gioi-chu/components/list-category";
import { TRADE_PROMOTION_CATEGORIES } from "@constants/categories";
import ListFilter from "@app/dai-dien-gioi-chu/components/list-filter";
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()
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={TRADE_PROMOTION_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">
{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 */}
<aside className="space-y-6">
<ListFilter />
<div className="bg-white border rounded-md overflow-hidden">
<div className="w-full h-56 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 Page;
// Core
"use client";
import Image from "next/image";
import ListCategory from "@app/dai-dien-gioi-chu/components/list-category";
import { TRADE_PROMOTION_CATEGORIES } from "@constants/categories";
import ListFilter from "@app/dai-dien-gioi-chu/components/list-filter";
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()
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={TRADE_PROMOTION_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">
{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 */}
<aside className="space-y-6">
<ListFilter />
<div className="bg-white border rounded-md overflow-hidden">
<div className="w-full h-56 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 Page;
......@@ -8,15 +8,18 @@ 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}` : undefined,
filters:'category@=Cơ hội kinh doanh'
});
return (
<div className="min-h-screen container mx-auto p-4">
......@@ -27,8 +30,17 @@ export default function Page() {
{/* 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ả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} />
<NewsContent key={news.id} news={news} link={`${PATHS.tradePromotion}/${news.id}`} />
))}
<div className="w-full flex justify-center mt-4">
......@@ -47,6 +59,8 @@ export default function Page() {
}
/>
</div>
</>
)}
</div>
</main>
......
// Core
"use client";
import Image from "next/image";
import ListCategory from "@app/dai-dien-gioi-chu/components/list-category";
import { TRADE_PROMOTION_CATEGORIES } from "@constants/categories";
import ListFilter from "@app/dai-dien-gioi-chu/components/list-filter";
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()
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={TRADE_PROMOTION_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">
{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 */}
<aside className="space-y-6">
<ListFilter />
<div className="bg-white border rounded-md overflow-hidden">
<div className="w-full h-56 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 Page;
......@@ -7,15 +7,18 @@ 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}` : undefined,
filters:'category@=Hỗ trợ kinh doanh'
});
return (
<div className="min-h-screen container mx-auto p-4">
......@@ -26,8 +29,21 @@ export default function Page() {
{/* 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ả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} />
<NewsContent
key={news.id}
news={news}
link={`${PATHS.tradePromotion}/${news.id}`}
/>
))}
<div className="w-full flex justify-center mt-4">
......@@ -46,6 +62,8 @@ export default function Page() {
}
/>
</div>
</>
)}
</div>
</main>
......
// Core
"use client";
import Image from "next/image";
import ListCategory from "@app/dai-dien-gioi-chu/components/list-category";
import { TRADE_PROMOTION_CATEGORIES } from "@constants/categories";
import ListFilter from "@app/dai-dien-gioi-chu/components/list-filter";
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()
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={TRADE_PROMOTION_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">
{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 */}
<aside className="space-y-6">
<ListFilter />
<div className="bg-white border rounded-md overflow-hidden">
<div className="w-full h-56 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 Page;
......@@ -8,15 +8,18 @@ import Image from "next/image";
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">
......@@ -27,8 +30,21 @@ export default function Page() {
{/* 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ả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} />
<NewsContent
key={news.id}
news={news}
link={`${PATHS.tradePromotion}/${news.id}`}
/>
))}
<div className="w-full flex justify-center mt-4">
......@@ -47,6 +63,8 @@ export default function Page() {
}
/>
</div>
</>
)}
</div>
</main>
......
......@@ -7,12 +7,14 @@ 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,
......@@ -26,8 +28,15 @@ export default function Page() {
{/* 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ải tin xúc tiến thương mại...</span>
</div>
) : (
<>
{allData?.responseData.rows.map((news) => (
<NewsContent key={news.id} news={news} />
<NewsContent key={news.id} news={news} link={`${PATHS.tradePromotion}/${news.id}`} />
))}
<div className="w-full flex justify-center mt-4">
......@@ -46,6 +55,8 @@ export default function Page() {
}
/>
</div>
</>
)}
</div>
</main>
......
import { NewsItem } from '@app/dai-dien-gioi-chu/lib/types/NewsPage.type';
import Links from '@links/index'
import dayjs from 'dayjs';
// 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
href={`${link}`}
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.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"
}}
/>
<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">
{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">{stripImagesAndHtml(news.description)}</div>
</div>
</div>
</a>
)
}
export default NewsContent;
\ No newline at end of file
......@@ -81,6 +81,7 @@ export const EventFilter: React.FC<{ onFilter?: (payload: FilterPayload) => void
placeholder="Tên sự kiện ..."
value={query}
onChange={(e) => setQuery(e.target.value)}
className='text-black placeholder:text-gray-400 rounded-none py-2.5 px-2'
/>
</div>
......@@ -104,8 +105,8 @@ export const EventFilter: React.FC<{ onFilter?: (payload: FilterPayload) => void
</div>
<div className="flex items-center gap-3">
<Button onClick={handleFilter} className="flex-1">Lọc sự kiện</Button>
<Button variant="outline" onClick={handleReset} className="flex-1">Bỏ lọc</Button>
<Button onClick={handleFilter} className="flex-1 rounded-none font-medium text-lg text-white hover:bg-muted-foreground hover:outline-1 outline-primary hover:text-primary">Lọc sự kiện</Button>
<Button onClick={handleReset} className="flex-1 rounded-none font-medium text-lg text-white hover:bg-muted-foreground hover:outline-1 outline-primary hover:text-primary">Bỏ lọc</Button>
</div>
</aside>
)
......
"use client"
import { usePathname } from "next/navigation"
import React from "react"
import { MenuItem } from '../menu-category'
"use client";
import { usePathname } from "next/navigation";
import React from "react";
import { MenuItem } from "../menu-category";
// Local Menu shape compatible with MenuItem
type Menu = {
id: string | number
name: string
link?: string
}
id: string | number;
name: string;
link?: string;
};
type Category = {
title: string
href: string
}
const CATEGORIES: Category[] = [
{ title: "Về VCCI-HCM", href: "/dai-dien-gioi-chu" },
{ title: "Chức năng và Nhiệm vụ", href: "/gioi-thieu/chuc-nang" },
{ title: "Sơ đồ Tổ chức", href: "/gioi-thieu/so-do" },
{ title: "Dịch vụ cung cấp", href: "/gioi-thieu/dich-vu" },
{ title: "Dịch vụ cung cấp", href: "/gioi-thieu/dich-vu" },
{ title: "Dịch vụ cung cấp", href: "/gioi-thieu/dich-vu" },
{ title: "Dịch vụ cung cấp", href: "/gioi-thieu/dich-vu" },
{ title: "Dịch vụ cung cấp", href: "/gioi-thieu/dich-vu" },
title: string;
href: string;
};
]
// Default categories removed — component now accepts `categories` via props.
const ListCategory: React.FC = () => {
const pathname = usePathname() || ""
const ListCategory: React.FC<{ categories?: Category[] }> = ({
categories = [],
}) => {
const pathname = usePathname() || "";
const isActive = (href: string) => {
// treat the base path as active for nested routes as well
if (href === "/gioi-thieu") return pathname === href || pathname.startsWith(href + "/")
return pathname === href
}
// if (href === "/gioi-thieu") return pathname === href || pathname.startsWith(href + "/")
return pathname === href;
};
return (
<div className="border-t border-gray-200 bg-white p-2.5">
<div className="w-full px-4 sm:px-6 lg:px-8">
<nav aria-label="Danh mục" className="py-3">
<ul className="flex flex-wrap gap-4 sm:gap-8 items-center max-w-full overflow-x-auto">
{CATEGORIES.map((c) => {
const menu: Menu = { id: c.href, name: c.title, link: c.href }
const active = isActive(c.href)
<div className="py-3">
<div className="flex flex-wrap items-center max-w-full overflow-x-auto">
{categories.map((c) => {
const menu: Menu = { id: c.href, name: c.title, link: c.href };
const active = isActive(c.href);
return (
<li key={c.href} className="shrink-0">
<div key={c.href} className="shrink-0">
<MenuItem menu={menu} active={active} />
</li>
)
</div>
);
})}
</ul>
</nav>
</div>
</div>
)
}
</div>
</div>
);
};
export default ListCategory
\ No newline at end of file
export default ListCategory;
"use client"
import React, { useState } from 'react'
import React, { useState, useEffect } from 'react'
import { Checkbox } from '@/components/ui/checkbox'
import { Input } from '@/components/ui/input'
import { Button } from '@/components/ui/button'
type Category = { id: string; title: string; count: number }
const DEFAULT_CATEGORIES: Category[] = [
{ id: 'ceo', title: 'CEO', count: 4 },
{ id: 'policy', title: 'Hỏi đáp về chính sách', count: 0 },
{ id: 'biz', title: 'Tin Doanh Nghiệp', count: 9 },
{ id: 'member', title: 'Tin Hội Viên', count: 17 },
{ id: 'law', title: 'Văn bản Pháp luật sắp có hiệu lực', count: 30 }
]
export const ListFilter: React.FC<{
categories?: Category[]
onSearch?: (q: string) => void
onReset?: () => void
}> = ({ categories = DEFAULT_CATEGORIES, onSearch, onReset }) => {
}> = ({ categories, onSearch, onReset }) => {
const [query, setQuery] = useState('')
const [visibleCount, setVisibleCount] = useState(5)
const [selected, setSelected] = useState<Record<string, boolean>>(() => {
const map: Record<string, boolean> = {}
categories.forEach((c) => (map[c.id] = false))
if (categories && categories.length) {
categories.forEach((c: Category) => (map[c.id] = false))
}
return map
})
// Keep selected map in sync when categories prop changes.
// Defer setSelected to avoid calling setState synchronously inside the effect.
useEffect(() => {
const timer = setTimeout(() => {
setSelected((prev) => {
const map: Record<string, boolean> = {}
if (categories && categories.length) {
categories.forEach((c: Category) => (map[c.id] = !!prev[c.id]))
}
return map
})
}, 0)
return () => clearTimeout(timer)
}, [categories])
const toggle = (id: string) => setSelected((s) => ({ ...s, [id]: !s[id] }))
return (
......@@ -36,7 +46,7 @@ export const ListFilter: React.FC<{
<Input
placeholder="Tên văn bản ..."
value={query}
className='text-black placeholder:text-gray-400'
className='text-black placeholder:text-gray-400 rounded-none py-2.5 px-2'
onChange={(e) => setQuery(e.target.value)}
onKeyDown={(e) => {
if (e.key === 'Enter') {
......@@ -46,8 +56,9 @@ export const ListFilter: React.FC<{
/>
</div>
<div className="flex flex-col gap-3 mb-6">
{categories.map((c) => (
<div className="flex flex-col gap-3">
{categories && categories.length > 0 ? (
categories.slice(0, visibleCount).map((c) => (
<label key={c.id} className="flex items-center gap-3">
<Checkbox checked={!!selected[c.id]} onCheckedChange={() => toggle(c.id)} />
<div className="flex justify-between w-full items-center">
......@@ -55,7 +66,27 @@ export const ListFilter: React.FC<{
<span className="text-sm text-gray-400">({c.count})</span>
</div>
</label>
))}
))
) : null}
<div className="mt-2 flex items-center gap-3">
{(categories?.length ?? 0) > visibleCount && (
<button
className="text-sm text-primary self-start"
onClick={() => setVisibleCount((v) => v + 5)}
>
Xem thêm
</button>
)}
{visibleCount > 5 && (
<button
className="text-sm text-gray-500 self-start"
onClick={() => setVisibleCount(5)}
>
Thu gọn
</button>
)}
</div>
</div>
<div className="flex gap-3">
......@@ -68,8 +99,11 @@ export const ListFilter: React.FC<{
setQuery('')
// restore initial map
const map: Record<string, boolean> = {}
if (categories && categories.length) {
categories.forEach((c) => (map[c.id] = false))
}
setSelected(map)
setVisibleCount(5)
onReset?.()
}}
>
......
'use client'
// Local simplified menu type instead of importing HeaderMenu
type Menu = {
id: string | number
name: string
......
......@@ -4,24 +4,47 @@ 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 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 (
......
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
......@@ -3,7 +3,7 @@ import { PATHS } from "@constants/paths";
export const TRADE_PROMOTION_CATEGORIES = [
{
title: "Hồ sơ thị trường",
href: `${PATHS.marketProfile}/`,
href: `${PATHS.marketProfile}`,
},
{
title: "Môi trường kinh doanh",
......
......@@ -7,16 +7,6 @@ export const PATHS = {
marketProfile: '/ho-so-thi-truong',
tradePromotion: '/xuc-tien-thuong-mai',
mediaInformation:'/thong-tin-truyen-thong',
recruitmentDetail: '/tuyen-dung/[id]',
newsPage: '/tin-tuc',
newsDetail: '/tin-tuc/[id]',
contact: '/lien-he',
lookup: '/tra-cuu',
training: '/dao-tao',
login: '/dang-nhap',
logout: '/dang-xuat',
students: '/sinh-vien',
studentLife: '/cuoc-song-sinh-vien',
search: '/tim-kiem',
originOfGoods: '/xuat-xu-hang-hoa',
......
// 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 }
export interface NewsDetailItem {
id: string
title: string
thumbnail: string
external_link: string
description: string
release_at: string
is_active: boolean
created_at: string
created_by: string | null
updated_at: string
updated_by: string | null
mode: 'NOW' | string
category: string
}
export interface NewsDetailResponseData {
count: number
rows: NewsDetailItem[]
totalPages: number
currentPage: number
}
export interface GetNewsDetailResponseType {
message: string
message_en: string
responseData: NewsDetailItem
status: 'success' | 'error'
timeStamp: string
violations: any | null
}
\ No newline at end of file
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment