Commit 74a04b88 authored by Vũ Đình Nguyên's avatar Vũ Đình Nguyên

update layout

parent 6c996526
import { ResponseType } from '@lib/types/common'
export type EventStatus = { export type EventStatus = {
id: string; id: string;
name: string; name: string;
...@@ -22,7 +23,68 @@ export type EventOrganization = { ...@@ -22,7 +23,68 @@ export type EventOrganization = {
add_info: unknown | null; add_info: unknown | null;
organization: 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 = { export type EventItem = {
id: string; id: string;
name: string; name: string;
......
"use client"; "use client";
import React, { useState } from "react"; import React, { useState } from "react";
import { useRouter } from 'next/navigation' import { useRouter } from "next/navigation";
import { Menu, X, Facebook, Linkedin, Twitter, Youtube } from "lucide-react"; import { Menu, X, Facebook, Linkedin, Twitter, Youtube } from "lucide-react";
import logo from "@/assets/VCCI-HCM-logo-VN-2025.png"; import logo from "@/assets/VCCI-HCM-logo-VN-2025.png";
import Image from "next/image"; import Image from "next/image";
...@@ -8,33 +8,33 @@ import MenuItem from "./MenuItem"; ...@@ -8,33 +8,33 @@ import MenuItem from "./MenuItem";
import Link from "next/link"; import Link from "next/link";
function Header() { function Header() {
const [toggleMenu, setToggleMenu] = useState<boolean>(false); const [toggleMenu, setToggleMenu] = useState<boolean>(false);
const router = useRouter() const router = useRouter();
return ( return (
<> <>
<div className="sticky top-0 w-full h-[56px] hidden lg:flex items-center justify-center bg-[#063e8e]"> <div className="sticky top-0 w-full h-14 hidden lg:flex items-center justify-center bg-[#063e8e]">
<div className="container w-full px-4 flex items-center justify-between"> <div className="container w-full px-4 flex items-center justify-between">
<div className="flex items-center gap-3"> <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]"> <div className="w-[130px] h-9 bg-[#e8c518] flex items-center justify-center border-4 rounded-sm border-[#647792]">
<a <Link
className="font-[600] text-[14px] text-[#063E8E] hover:text-white transition" className="font-semibold text-[14px] text-primary hover:text-white transition"
href="#" href="https://vccihcm.vn/dang-ky"
> >
Đăng Ký Hội Viên Đăng Ký Hội Viên
</a> </Link>
</div> </div>
<a <Link
className="px-3 py-2 text-[14px] text-white hover:opacity-80" className="px-3 py-2 text-[14px] text-white hover:opacity-80"
href="#" href="/site-map"
> >
Sitemap Sitemap
</a> </Link>
<a <Link
className="px-3 py-2 text-[14px] text-white hover:opacity-80" className="px-3 py-2 text-[14px] text-white hover:opacity-80"
href="#" href="https://vccihcm.vn/lien-he"
> >
Liên hệ Liên hệ
</a> </Link>
</div> </div>
<div className="flex items-center gap-8"> <div className="flex items-center gap-8">
...@@ -43,10 +43,11 @@ function Header() { ...@@ -43,10 +43,11 @@ function Header() {
type="text" type="text"
placeholder="Tìm kiếm" placeholder="Tìm kiếm"
onKeyDown={(e) => { onKeyDown={(e) => {
if (e.key === 'Enter') { if (e.key === "Enter") {
const value = (e.currentTarget as HTMLInputElement).value || '' const value =
const encoded = encodeURIComponent(value) (e.currentTarget as HTMLInputElement).value || "";
router.push(`/search?q=${encoded}`) const encoded = encodeURIComponent(value);
router.push(`/search?q=${encoded}`);
} }
}} }}
/> />
...@@ -92,7 +93,6 @@ function Header() { ...@@ -92,7 +93,6 @@ function Header() {
{ title: "Sơ Đồ Tổ Chức", link: "so-do-to-chuc" }, { title: "Sơ Đồ Tổ Chức", link: "so-do-to-chuc" },
{ title: "Dịch Vụ Cung Cấp", link: "dich-vu-cung-cap" }, { title: "Dịch Vụ Cung Cấp", link: "dich-vu-cung-cap" },
]} ]}
/> />
<MenuItem <MenuItem
title="Hội viên" title="Hội viên"
...@@ -106,7 +106,6 @@ function Header() { ...@@ -106,7 +106,6 @@ function Header() {
{ title: "Kết Nối Hội Viên", link: "ket-noi-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" }, { title: "Tin Hội Viên", link: "tin-hoi-vien" },
]} ]}
/> />
<MenuItem <MenuItem
title="Hoạt động" title="Hoạt động"
...@@ -207,7 +206,6 @@ function Header() { ...@@ -207,7 +206,6 @@ function Header() {
{ title: "Cơ Hội Kinh Doanh", link: "co-hoi-kinh-doanh" }, { title: "Cơ Hội Kinh Doanh", link: "co-hoi-kinh-doanh" },
{ title: "Hỗ Trợ Kinh Doanh", link: "ho-tro-kinh-doanh" }, { title: "Hỗ Trợ Kinh Doanh", link: "ho-tro-kinh-doanh" },
]} ]}
/> />
<MenuItem <MenuItem
title="Thông tin truyền thông" title="Thông tin truyền thông"
...@@ -224,7 +222,6 @@ function Header() { ...@@ -224,7 +222,6 @@ function Header() {
{ title: "Ấn Phẩm", link: "an-pham" }, { title: "Ấn Phẩm", link: "an-pham" },
{ title: "Thư Viện Tài Liệu", link: "thu-vien-tai-lieu" }, { title: "Thư Viện Tài Liệu", link: "thu-vien-tai-lieu" },
]} ]}
/> />
</nav> </nav>
...@@ -240,7 +237,8 @@ function Header() { ...@@ -240,7 +237,8 @@ function Header() {
{/* Mobile Menu */} {/* Mobile Menu */}
<div <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"
}`} }`}
> >
{[ {[
......
...@@ -9,12 +9,13 @@ import Image from "next/image"; ...@@ -9,12 +9,13 @@ import Image from "next/image";
import { useGetNews } from "@api/endpoints/news"; import { useGetNews } from "@api/endpoints/news";
import { GetNewsResponseType } from "@api/types/NewsPage.type"; import { GetNewsResponseType } from "@api/types/NewsPage.type";
import { PATHS } from "@constants/paths"; import { PATHS } from "@constants/paths";
import { Spinner } from "@components/ui/spinner";
export default function Page() { export default function Page() {
const [submitSearch] = useState(""); const [submitSearch] = useState("");
const [page, setPage] = useState(1); const [page, setPage] = useState(1);
const pageSize = 5; const pageSize = 5;
const { data: allData } = useGetNews<GetNewsResponseType>({ const { data: allData,isLoading } = useGetNews<GetNewsResponseType>({
pageSize: String(pageSize), pageSize: String(pageSize),
currentPage: String(page), currentPage: String(page),
// filters: submitSearch ? `title @=${submitSearch}` : undefined, // filters: submitSearch ? `title @=${submitSearch}` : undefined,
...@@ -29,6 +30,13 @@ export default function Page() { ...@@ -29,6 +30,13 @@ export default function Page() {
{/* Main content */} {/* Main content */}
<main className="lg:col-span-2 bg-background "> <main className="lg:col-span-2 bg-background ">
<div className="pb-5 overflow-hidden"> <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) => ( {allData?.responseData.rows.map((news) => (
<NewsContent key={news.id} news={news} link={`${PATHS.ownerRepresentatives}/chu-de/${news.id}`}/> <NewsContent key={news.id} news={news} link={`${PATHS.ownerRepresentatives}/chu-de/${news.id}`}/>
))} ))}
...@@ -49,6 +57,8 @@ export default function Page() { ...@@ -49,6 +57,8 @@ export default function Page() {
} }
/> />
</div> </div>
</>
)}
</div> </div>
</main> </main>
......
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
import { NewsItem } from '@app/dai-dien-gioi-chu/lib/types/NewsPage.type'; import { NewsItem } from '@app/dai-dien-gioi-chu/lib/types/NewsPage.type';
import Links from '@links/index' import Links from '@links/index'
import dayjs from 'dayjs'; import dayjs from 'dayjs';
import {EventItem} from "@api/types/event";
// Helper: remove <img> tags and extract plain text from HTML // Helper: remove <img> tags and extract plain text from HTML
const stripImagesAndHtml = (html?: string) => { const stripImagesAndHtml = (html?: string) => {
if (!html) return '' if (!html) return ''
...@@ -19,7 +19,7 @@ const stripImagesAndHtml = (html?: string) => { ...@@ -19,7 +19,7 @@ const stripImagesAndHtml = (html?: string) => {
} }
return withoutImgs.replace(/<[^>]*>/g, '') return withoutImgs.replace(/<[^>]*>/g, '')
} }
function NewsContent({ news ,link}: { news: NewsItem ,link:string}) { function NewsContent({ news ,link,event}: { news?: NewsItem ,link:string,event?:EventItem}) {
return ( return (
<a <a
...@@ -27,8 +27,8 @@ function NewsContent({ news ,link}: { news: NewsItem ,link:string}) { ...@@ -27,8 +27,8 @@ function NewsContent({ news ,link}: { news: NewsItem ,link:string}) {
className="flex flex-col hover:no-underline sm:flex-row gap-2 mb-6 bg-white rounded-lg shadow-sm p-4 border items-start min-w-0" 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 <img
src={`${Links.imageEndpoint}${news.thumbnail}`} src={`${Links.imageEndpoint}${news?news.thumbnail:event?.image}`}
alt={news.title} alt={news?news.title:event?.name}
className="w-full sm:w-56 md:w-64 h-40 md:h-36 object-cover shrink-0" className="w-full sm:w-56 md:w-64 h-40 md:h-36 object-cover shrink-0"
onError={(e) => { onError={(e) => {
e.currentTarget.src = "/img-error.png" e.currentTarget.src = "/img-error.png"
...@@ -38,13 +38,13 @@ function NewsContent({ news ,link}: { news: NewsItem ,link:string}) { ...@@ -38,13 +38,13 @@ function NewsContent({ news ,link}: { news: NewsItem ,link:string}) {
<div className="flex-1 min-w-0 pl-0 sm:pl-4"> <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"> <p className="text-primary font-semibold text-base md:text-lg hover:underline line-clamp-2 wrap-break-word">
{news.title} {news?.title}{event?.name}
</p> </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 text-[#777] line-clamp-3">
<div className="text-sm prose tiptap">{stripImagesAndHtml(news.description)}</div> <div className="text-sm prose tiptap">{stripImagesAndHtml(news?news.description:event?.description)}</div>
</div> </div>
</div> </div>
</a> </a>
......
...@@ -9,12 +9,13 @@ import Image from "next/image"; ...@@ -9,12 +9,13 @@ import Image from "next/image";
import { useGetNews } from "@api/endpoints/news"; import { useGetNews } from "@api/endpoints/news";
import { GetNewsResponseType } from "@api/types/NewsPage.type"; import { GetNewsResponseType } from "@api/types/NewsPage.type";
import { PATHS } from "@constants/paths"; import { PATHS } from "@constants/paths";
import { Spinner } from "@components/ui/spinner";
export default function Page() { export default function Page() {
const [submitSearch] = useState(""); const [submitSearch] = useState("");
const [page, setPage] = useState(1); const [page, setPage] = useState(1);
const pageSize = 5; const pageSize = 5;
const { data: allData } = useGetNews<GetNewsResponseType>({ const { data: allData, isLoading } = useGetNews<GetNewsResponseType>({
pageSize: String(pageSize), pageSize: String(pageSize),
currentPage: String(page), currentPage: String(page),
filters: submitSearch ? `title @=${submitSearch}` : undefined, filters: submitSearch ? `title @=${submitSearch}` : undefined,
...@@ -28,6 +29,13 @@ export default function Page() { ...@@ -28,6 +29,13 @@ export default function Page() {
{/* Main content */} {/* Main content */}
<main className="lg:col-span-2 bg-background "> <main className="lg:col-span-2 bg-background ">
<div className="pb-5 overflow-hidden"> <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) => ( {allData?.responseData.rows.map((news) => (
<NewsContent key={news.id} news={news} link={`${PATHS.ownerRepresentatives}/tap-huan-nsdld/${news.id}`} /> <NewsContent key={news.id} news={news} link={`${PATHS.ownerRepresentatives}/tap-huan-nsdld/${news.id}`} />
))} ))}
...@@ -48,6 +56,8 @@ export default function Page() { ...@@ -48,6 +56,8 @@ export default function Page() {
} }
/> />
</div> </div>
</>
)}
</div> </div>
</main> </main>
......
...@@ -9,12 +9,13 @@ import Image from "next/image"; ...@@ -9,12 +9,13 @@ import Image from "next/image";
import { useGetNews } from "@api/endpoints/news"; import { useGetNews } from "@api/endpoints/news";
import { GetNewsResponseType } from "@api/types/NewsPage.type"; import { GetNewsResponseType } from "@api/types/NewsPage.type";
import { PATHS } from "@constants/paths"; import { PATHS } from "@constants/paths";
import { Spinner } from "@components/ui/spinner";
export default function Page() { export default function Page() {
const [submitSearch,setsubmitSearch] = useState(""); const [submitSearch,setsubmitSearch] = useState("");
const [page, setPage] = useState(1); const [page, setPage] = useState(1);
const pageSize = 5; const pageSize = 5;
const { data: allData } = useGetNews<GetNewsResponseType>({ const { data: allData, isLoading } = useGetNews<GetNewsResponseType>({
pageSize: String(pageSize), pageSize: String(pageSize),
currentPage: String(page), currentPage: String(page),
filters: submitSearch ? `title @=${submitSearch},category@=Tin liên quan` : 'category@=Tin liên quan', filters: submitSearch ? `title @=${submitSearch},category@=Tin liên quan` : 'category@=Tin liên quan',
...@@ -28,6 +29,13 @@ export default function Page() { ...@@ -28,6 +29,13 @@ export default function Page() {
{/* Main content */} {/* Main content */}
<main className="lg:col-span-2 bg-background "> <main className="lg:col-span-2 bg-background ">
<div className="pb-5 overflow-hidden"> <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) => ( {allData?.responseData.rows.map((news) => (
<NewsContent key={news.id} news={news} link={`${PATHS.ownerRepresentatives}/tin-lien-quan/${news.id}`} /> <NewsContent key={news.id} news={news} link={`${PATHS.ownerRepresentatives}/tin-lien-quan/${news.id}`} />
))} ))}
...@@ -41,6 +49,8 @@ export default function Page() { ...@@ -41,6 +49,8 @@ export default function Page() {
onGoToNextPage={() => setPage(Math.min(Number(allData?.responseData.totalPages ?? 1), page + 1))} onGoToNextPage={() => setPage(Math.min(Number(allData?.responseData.totalPages ?? 1), page + 1))}
/> />
</div> </div>
</>
)}
</div> </div>
</main> </main>
......
...@@ -9,12 +9,13 @@ import Image from "next/image"; ...@@ -9,12 +9,13 @@ import Image from "next/image";
import { useGetNews } from "@api/endpoints/news"; import { useGetNews } from "@api/endpoints/news";
import { GetNewsResponseType } from "@api/types/NewsPage.type"; import { GetNewsResponseType } from "@api/types/NewsPage.type";
import { PATHS } from "@constants/paths"; import { PATHS } from "@constants/paths";
import { Spinner } from "@components/ui/spinner";
export default function Page() { export default function Page() {
const [submitSearch] = useState(""); const [submitSearch] = useState("");
const [page, setPage] = useState(1); const [page, setPage] = useState(1);
const pageSize = 5; const pageSize = 5;
const { data: allData } = useGetNews<GetNewsResponseType>({ const { data: allData, isLoading } = useGetNews<GetNewsResponseType>({
pageSize: String(pageSize), pageSize: String(pageSize),
currentPage: String(page), currentPage: String(page),
filters: submitSearch ? `title @=${submitSearch}` : 'category @=Đào tạo', filters: submitSearch ? `title @=${submitSearch}` : 'category @=Đào tạo',
...@@ -28,6 +29,13 @@ export default function Page() { ...@@ -28,6 +29,13 @@ export default function Page() {
{/* Main content */} {/* Main content */}
<main className="lg:col-span-2 bg-background "> <main className="lg:col-span-2 bg-background ">
<div className="pb-5 overflow-hidden"> <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) => ( {allData?.responseData.rows.map((news) => (
<NewsContent key={news.id} news={news} link={`${PATHS.event}/dao-tao/${news.id}`} /> <NewsContent key={news.id} news={news} link={`${PATHS.event}/dao-tao/${news.id}`} />
))} ))}
...@@ -41,6 +49,8 @@ export default function Page() { ...@@ -41,6 +49,8 @@ export default function Page() {
onGoToNextPage={() => setPage(Math.min(Number(allData?.responseData.totalPages ?? 1), page + 1))} onGoToNextPage={() => setPage(Math.min(Number(allData?.responseData.totalPages ?? 1), page + 1))}
/> />
</div> </div>
</>
)}
</div> </div>
</main> </main>
......
...@@ -4,14 +4,16 @@ import Image from "next/image"; ...@@ -4,14 +4,16 @@ import Image from "next/image";
import ListCategory from "@app/dai-dien-gioi-chu/components/list-category"; import ListCategory from "@app/dai-dien-gioi-chu/components/list-category";
import { EVENT_CATEGORIES } from "@constants/categories"; import { EVENT_CATEGORIES } from "@constants/categories";
import ListFilter from "@app/dai-dien-gioi-chu/components/list-filter"; import ListFilter from "@app/dai-dien-gioi-chu/components/list-filter";
import { useGetNewsId } from '@/api/endpoints/news'; import {useGetEventsId} from '@/api/endpoints/event';
import parse from "html-react-parser"; import parse from "html-react-parser";
import { useParams } from 'next/navigation' import { useParams } from 'next/navigation'
import { GetNewsDetailResponseType } from '@lib/types/news-detail-response-data'; import { GetNewsDetailResponseType } from '@lib/types/news-detail-response-data';
import {GetEventsIdQueryResponseType} from '@api/types/event';
import { Spinner } from "@components/ui/spinner";
// ...existing code... // ...existing code...
const Page: React.FC = () => { const Page: React.FC = () => {
const { id } = useParams() const { id } = useParams()
const { data, isLoading } = useGetNewsId<GetNewsDetailResponseType>(id as string) const { data, isLoading } = useGetEventsId<GetEventsIdQueryResponseType>(id as string)
return ( return (
<div className="min-h-screen w-full container mx-auto p-4"> <div className="min-h-screen w-full container mx-auto p-4">
<div className="w-full flex flex-col gap-5"> <div className="w-full flex flex-col gap-5">
...@@ -20,11 +22,20 @@ const Page: React.FC = () => { ...@@ -20,11 +22,20 @@ const Page: React.FC = () => {
<div className="grid grid-cols-1 lg:grid-cols-3 gap-6"> <div className="grid grid-cols-1 lg:grid-cols-3 gap-6">
{/* Main content */} {/* Main content */}
<main className="lg:col-span-2 bg-white border rounded-md p-6"> <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'> <div className='pb-5 text-primary text-2xl leading-normal font-medium'>
{data?.responseData?.title} {data?.responseData?.name}
</div> </div>
<hr className="py-2"/> <hr className="py-2"/>
<div className="p-7.5 prose tiptap overflow-hidden">{parse(data?.responseData?.description ?? '')}</div> <div className="p-7.5 prose tiptap overflow-hidden">{parse(data?.responseData?.description ?? '')}</div>
</>
)}
</main> </main>
{/* Sidebar */} {/* Sidebar */}
......
...@@ -7,18 +7,23 @@ import NewsContent from "@app/dai-dien-gioi-chu/components/card-news"; ...@@ -7,18 +7,23 @@ import NewsContent from "@app/dai-dien-gioi-chu/components/card-news";
// ...existing code... // ...existing code...
import { Pagination } from "@components/base/pagination"; import { Pagination } from "@components/base/pagination";
import Image from "next/image"; import Image from "next/image";
import { useGetEvents } from '@api/endpoints/event'
import { EventApiResponse } from '@api/types/event'
import { useGetNews } from "@api/endpoints/news"; import { useGetNews } from "@api/endpoints/news";
import { GetNewsResponseType } from "@api/types/NewsPage.type"; import { GetNewsResponseType } from "@api/types/NewsPage.type";
import { PATHS } from "@constants/paths"; import { PATHS } from "@constants/paths";
import { Spinner } from "@components/ui/spinner";
export default function Page() { export default function Page() {
const [submitSearch] = useState(""); const [submitSearch] = useState("");
const [page, setPage] = useState(1); const [page, setPage] = useState(1);
const pageSize = 5; const pageSize = 5;
const { data: allData } = useGetNews<GetNewsResponseType>({ const { data: allData, isLoading } = useGetEvents<EventApiResponse>({
pageSize: String(pageSize), pageSize: String(pageSize),
currentPage: String(page), currentPage: String(page),
filters: submitSearch ? `title @=${submitSearch}` : 'category @=Sự kiện', sortField: 'start_time',
sortOrder: 'ASC',
filters: submitSearch ? `title @=${submitSearch},start_time>${new Date()}` : `start_time>${new Date()}`,
}); });
return ( return (
<div className="min-h-screen container mx-auto p-4"> <div className="min-h-screen container mx-auto p-4">
...@@ -29,8 +34,15 @@ export default function Page() { ...@@ -29,8 +34,15 @@ export default function Page() {
{/* Main content */} {/* Main content */}
<main className="lg:col-span-2 bg-background "> <main className="lg:col-span-2 bg-background ">
<div className="pb-5 overflow-hidden"> <div className="pb-5 overflow-hidden">
{allData?.responseData.rows.map((news) => ( {isLoading ? (
<NewsContent key={news.id} news={news} link={`${PATHS.event}/su-kien/${news.id}`} /> <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"> <div className="w-full flex justify-center mt-4">
...@@ -42,6 +54,8 @@ export default function Page() { ...@@ -42,6 +54,8 @@ export default function Page() {
onGoToNextPage={() => setPage(Math.min(Number(allData?.responseData.totalPages ?? 1), page + 1))} onGoToNextPage={() => setPage(Math.min(Number(allData?.responseData.totalPages ?? 1), page + 1))}
/> />
</div> </div>
</>
)}
</div> </div>
</main> </main>
......
...@@ -7,6 +7,7 @@ import { Pagination } from "@components/base/pagination"; ...@@ -7,6 +7,7 @@ import { Pagination } from "@components/base/pagination";
import Image from "next/image"; import Image from "next/image";
import { useGetNews } from "@api/endpoints/news"; import { useGetNews } from "@api/endpoints/news";
import { GetNewsResponseType } from "@api/types/NewsPage.type"; import { GetNewsResponseType } from "@api/types/NewsPage.type";
import { Spinner } from "@components/ui/spinner";
export default function Page() { export default function Page() {
const [submitSearch] = useState(""); const [submitSearch] = useState("");
...@@ -26,6 +27,13 @@ export default function Page() { ...@@ -26,6 +27,13 @@ export default function Page() {
{/* Main content */} {/* Main content */}
<main className="lg:col-span-2 bg-background"> <main className="lg:col-span-2 bg-background">
<div className="pb-5 overflow-hidden"> <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) => ( {allData?.responseData.rows.map((news) => (
<CardNews key={news.id} news={news} /> <CardNews key={news.id} news={news} />
))} ))}
...@@ -46,6 +54,8 @@ export default function Page() { ...@@ -46,6 +54,8 @@ export default function Page() {
} }
/> />
</div> </div>
</>
)}
</div> </div>
</main> </main>
......
...@@ -7,6 +7,7 @@ import { Pagination } from '@components/base/pagination' ...@@ -7,6 +7,7 @@ import { Pagination } from '@components/base/pagination'
import Image from "next/image"; import Image from "next/image";
import { useGetNews } from '@api/endpoints/news' import { useGetNews } from '@api/endpoints/news'
import { GetNewsResponseType } from '@api/types/NewsPage.type' import { GetNewsResponseType } from '@api/types/NewsPage.type'
import { Spinner } from "@components/ui/spinner";
export default function Page() { export default function Page() {
const [submitSearch] = useState('') const [submitSearch] = useState('')
...@@ -26,6 +27,13 @@ export default function Page() { ...@@ -26,6 +27,13 @@ export default function Page() {
{/* Main content */} {/* Main content */}
<main className="lg:col-span-2 bg-background"> <main className="lg:col-span-2 bg-background">
<div className='pb-5 overflow-hidden'> <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) => ( {allData?.responseData.rows.map((news) => (
<CardNews key={news.id} news={news} /> <CardNews key={news.id} news={news} />
))} ))}
...@@ -46,6 +54,8 @@ export default function Page() { ...@@ -46,6 +54,8 @@ export default function Page() {
} }
/> />
</div> </div>
</>
)}
</div> </div>
</main> </main>
......
...@@ -8,6 +8,7 @@ import { Pagination } from "@components/base/pagination"; ...@@ -8,6 +8,7 @@ import { Pagination } from "@components/base/pagination";
import Image from "next/image"; import Image from "next/image";
import { useGetNews } from "@api/endpoints/news"; import { useGetNews } from "@api/endpoints/news";
import { GetNewsResponseType } from "@api/types/NewsPage.type"; import { GetNewsResponseType } from "@api/types/NewsPage.type";
import { Spinner } from "@components/ui/spinner";
import { PATHS } from "@constants/paths"; import { PATHS } from "@constants/paths";
import { useSearchParams } from 'next/navigation' import { useSearchParams } from 'next/navigation'
...@@ -16,7 +17,7 @@ function SearchContent() { ...@@ -16,7 +17,7 @@ function SearchContent() {
const searchParams = useSearchParams() const searchParams = useSearchParams()
const query = searchParams.get('q') // const query = searchParams.get('q') //
const pageSize = 5; const pageSize = 5;
const { data: allData } = useGetNews<GetNewsResponseType>({ const { data: allData, isLoading } = useGetNews<GetNewsResponseType>({
pageSize: String(pageSize), pageSize: String(pageSize),
currentPage: String(page), currentPage: String(page),
filters: query ? `title @=${query}` : undefined, filters: query ? `title @=${query}` : undefined,
...@@ -40,6 +41,13 @@ function SearchContent() { ...@@ -40,6 +41,13 @@ function SearchContent() {
{/* Main content */} {/* Main content */}
<main className="lg:col-span-2 bg-background "> <main className="lg:col-span-2 bg-background ">
<div className="pb-5 overflow-hidden"> <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) => ( {allData?.responseData.rows.map((news) => (
<NewsContent <NewsContent
key={news.id} key={news.id}
...@@ -64,6 +72,8 @@ function SearchContent() { ...@@ -64,6 +72,8 @@ function SearchContent() {
} }
/> />
</div> </div>
</>
)}
</div> </div>
</main> </main>
......
...@@ -10,14 +10,15 @@ import Image from "next/image"; ...@@ -10,14 +10,15 @@ import Image from "next/image";
import { useGetNews } from "@api/endpoints/news"; import { useGetNews } from "@api/endpoints/news";
import { GetNewsResponseType } from "@api/types/NewsPage.type"; import { GetNewsResponseType } from "@api/types/NewsPage.type";
import { PATHS } from "@constants/paths"; import { PATHS } from "@constants/paths";
import { Spinner } from "@components/ui/spinner";
export default function Page() { export default function Page() {
const [submitSearch] = useState(""); const [submitSearch] = useState("");
const [page, setPage] = useState(1); const [page, setPage] = useState(1);
const pageSize = 5; const pageSize = 5;
const { data: allData } = useGetNews<GetNewsResponseType>({ const { data: allData, isLoading } = useGetNews<GetNewsResponseType>({
pageSize: String(pageSize), pageSize: String(pageSize),
currentPage: String(page), currentPage: String(page),
filters: submitSearch ? `title @=${submitSearch}` : 'category @=Chuyên đề', filters: submitSearch ? `title @=${submitSearch},category @=Chuyên đề` : 'category @=Chuyên đề',
}); });
return ( return (
<div className="min-h-screen container mx-auto p-4"> <div className="min-h-screen container mx-auto p-4">
...@@ -28,16 +29,22 @@ export default function Page() { ...@@ -28,16 +29,22 @@ export default function Page() {
{/* Main content */} {/* Main content */}
<main className="lg:col-span-2 bg-background "> <main className="lg:col-span-2 bg-background ">
<div className="pb-5 overflow-hidden"> <div className="pb-5 overflow-hidden">
{allData?.responseData.rows.length === 0 ? ( {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> <p className="text-center py-4">Không có dữ liệu</p>
) : ( ) : (
allData?.responseData.rows.map((news) => ( <>
{allData?.responseData.rows.map((news) => (
<NewsContent <NewsContent
key={news.id} key={news.id}
news={news} news={news}
link={`${PATHS.mediaInformation}/chuyen-de/${news.id}`} link={`${PATHS.mediaInformation}/chuyen-de/${news.id}`}
/> />
)))} ))}
<div className="w-full flex justify-center mt-4"> <div className="w-full flex justify-center mt-4">
<Pagination <Pagination
...@@ -48,6 +55,8 @@ export default function Page() { ...@@ -48,6 +55,8 @@ export default function Page() {
onGoToNextPage={() => setPage(Math.min(Number(allData?.responseData.totalPages ?? 1), page + 1))} onGoToNextPage={() => setPage(Math.min(Number(allData?.responseData.totalPages ?? 1), page + 1))}
/> />
</div> </div>
</>
)}
</div> </div>
</main> </main>
......
...@@ -15,10 +15,10 @@ export default function Page() { ...@@ -15,10 +15,10 @@ export default function Page() {
const [page, setPage] = useState(1); const [page, setPage] = useState(1);
const pageSize = 5; const pageSize = 5;
const { data: allData } = useGetNews<GetNewsResponseType>({ const { data: allData,isLoading } = useGetNews<GetNewsResponseType>({
pageSize: String(pageSize), pageSize: String(pageSize),
currentPage: String(page), currentPage: String(page),
filters: submitSearch ? `title @=${submitSearch}` : 'category @=Thông tin chính sách và pháp luật', 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 ( return (
<div className="min-h-screen container mx-auto p-4"> <div className="min-h-screen container mx-auto p-4">
......
...@@ -10,15 +10,16 @@ import Image from "next/image"; ...@@ -10,15 +10,16 @@ import Image from "next/image";
import { useGetNews } from "@api/endpoints/news"; import { useGetNews } from "@api/endpoints/news";
import { GetNewsResponseType } from "@api/types/NewsPage.type"; import { GetNewsResponseType } from "@api/types/NewsPage.type";
import { PATHS } from "@constants/paths"; import { PATHS } from "@constants/paths";
import { Spinner } from "@components/ui/spinner";
export default function Page() { export default function Page() {
const [submitSearch] = useState(""); const [submitSearch] = useState("");
const [page, setPage] = useState(1); const [page, setPage] = useState(1);
const pageSize = 5; const pageSize = 5;
const { data: allData } = useGetNews<GetNewsResponseType>({ const { data: allData, isLoading } = useGetNews<GetNewsResponseType>({
pageSize: String(pageSize), pageSize: String(pageSize),
currentPage: String(page), currentPage: String(page),
filters: submitSearch ? `title @=${submitSearch}` : 'category @=Thư viện tài liệu', filters: submitSearch ? `title @=${submitSearch},category @=Thư viện tài liệu'` : 'category @=Thư viện tài liệu',
}); });
return ( return (
<div className="min-h-screen container mx-auto p-4"> <div className="min-h-screen container mx-auto p-4">
...@@ -29,16 +30,22 @@ export default function Page() { ...@@ -29,16 +30,22 @@ export default function Page() {
{/* Main content */} {/* Main content */}
<main className="lg:col-span-2 bg-background "> <main className="lg:col-span-2 bg-background ">
<div className="pb-5 overflow-hidden"> <div className="pb-5 overflow-hidden">
{allData?.responseData.rows.length === 0 ? ( {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> <p className="text-center py-4">Không có dữ liệu</p>
) : ( ) : (
allData?.responseData.rows.map((news) => ( <>
{allData?.responseData.rows.map((news) => (
<NewsContent <NewsContent
key={news.id} key={news.id}
news={news} news={news}
link={`${PATHS.mediaInformation}/thu-vien-tai-lieu/${news.id}`} link={`${PATHS.mediaInformation}/thu-vien-tai-lieu/${news.id}`}
/> />
)))} ))}
<div className="w-full flex justify-center mt-4"> <div className="w-full flex justify-center mt-4">
<Pagination <Pagination
...@@ -49,6 +56,8 @@ export default function Page() { ...@@ -49,6 +56,8 @@ export default function Page() {
onGoToNextPage={() => setPage(Math.min(Number(allData?.responseData.totalPages ?? 1), page + 1))} onGoToNextPage={() => setPage(Math.min(Number(allData?.responseData.totalPages ?? 1), page + 1))}
/> />
</div> </div>
</>
)}
</div> </div>
</main> </main>
......
...@@ -10,15 +10,16 @@ import Image from "next/image"; ...@@ -10,15 +10,16 @@ import Image from "next/image";
import { useGetNews } from "@api/endpoints/news"; import { useGetNews } from "@api/endpoints/news";
import { GetNewsResponseType } from "@api/types/NewsPage.type"; import { GetNewsResponseType } from "@api/types/NewsPage.type";
import { PATHS } from "@constants/paths"; import { PATHS } from "@constants/paths";
import { Spinner } from "@components/ui/spinner";
export default function Page() { export default function Page() {
const [submitSearch] = useState(""); const [submitSearch] = useState("");
const [page, setPage] = useState(1); const [page, setPage] = useState(1);
const pageSize = 5; const pageSize = 5;
const { data: allData } = useGetNews<GetNewsResponseType>({ const { data: allData, isLoading } = useGetNews<GetNewsResponseType>({
pageSize: String(pageSize), pageSize: String(pageSize),
currentPage: String(page), currentPage: String(page),
filters: submitSearch ? `title @=${submitSearch}` : 'category @=Tin doanh nghiệp', filters: submitSearch ? `title @=${submitSearch},category @=Tin doanh nghiệp` : 'category @=Tin doanh nghiệp',
}); });
return ( return (
<div className="min-h-screen container mx-auto p-4"> <div className="min-h-screen container mx-auto p-4">
...@@ -29,16 +30,22 @@ export default function Page() { ...@@ -29,16 +30,22 @@ export default function Page() {
{/* Main content */} {/* Main content */}
<main className="lg:col-span-2 bg-background "> <main className="lg:col-span-2 bg-background ">
<div className="pb-5 overflow-hidden"> <div className="pb-5 overflow-hidden">
{allData?.responseData.rows.length === 0 ? ( {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> <p className="text-center py-4">Không có dữ liệu</p>
) : ( ) : (
allData?.responseData.rows.map((news) => ( <>
{allData?.responseData.rows.map((news) => (
<NewsContent <NewsContent
key={news.id} key={news.id}
news={news} news={news}
link={`${PATHS.mediaInformation}/tin-doanh-nghiep/${news.id}`} link={`${PATHS.mediaInformation}/tin-doanh-nghiep/${news.id}`}
/> />
)))} ))}
<div className="w-full flex justify-center mt-4"> <div className="w-full flex justify-center mt-4">
<Pagination <Pagination
...@@ -49,6 +56,8 @@ export default function Page() { ...@@ -49,6 +56,8 @@ export default function Page() {
onGoToNextPage={() => setPage(Math.min(Number(allData?.responseData.totalPages ?? 1), page + 1))} onGoToNextPage={() => setPage(Math.min(Number(allData?.responseData.totalPages ?? 1), page + 1))}
/> />
</div> </div>
</>
)}
</div> </div>
</main> </main>
......
...@@ -9,15 +9,16 @@ import Image from "next/image"; ...@@ -9,15 +9,16 @@ import Image from "next/image";
import { useGetNews } from "@api/endpoints/news"; import { useGetNews } from "@api/endpoints/news";
import { GetNewsResponseType } from "@api/types/NewsPage.type"; import { GetNewsResponseType } from "@api/types/NewsPage.type";
import { PATHS } from "@constants/paths"; import { PATHS } from "@constants/paths";
import { Spinner } from "@components/ui/spinner";
export default function Page() { export default function Page() {
const [submitSearch] = useState(""); const [submitSearch] = useState("");
const [page, setPage] = useState(1); const [page, setPage] = useState(1);
const pageSize = 5; const pageSize = 5;
const { data: allData } = useGetNews<GetNewsResponseType>({ const { data: allData, isLoading } = useGetNews<GetNewsResponseType>({
pageSize: String(pageSize), pageSize: String(pageSize),
currentPage: String(page), currentPage: String(page),
filters: submitSearch ? `title @=${submitSearch}` : 'category @=Tin kinh tế', filters: submitSearch ? `title @=${submitSearch},category @=Tin kinh tế` : 'category @=Tin kinh tế',
}); });
return ( return (
<div className="min-h-screen container mx-auto p-4"> <div className="min-h-screen container mx-auto p-4">
...@@ -28,16 +29,22 @@ export default function Page() { ...@@ -28,16 +29,22 @@ export default function Page() {
{/* Main content */} {/* Main content */}
<main className="lg:col-span-2 bg-background "> <main className="lg:col-span-2 bg-background ">
<div className="pb-5 overflow-hidden"> <div className="pb-5 overflow-hidden">
{allData?.responseData.rows.length === 0 ? ( {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> <p className="text-center py-4">Không có dữ liệu</p>
) : ( ) : (
allData?.responseData.rows.map((news) => ( <>
{allData?.responseData.rows.map((news) => (
<NewsContent <NewsContent
key={news.id} key={news.id}
news={news} news={news}
link={`${PATHS.mediaInformation}/tin-kinh-te/${news.id}`} link={`${PATHS.mediaInformation}/tin-kinh-te/${news.id}`}
/> />
)))} ))}
<div className="w-full flex justify-center mt-4"> <div className="w-full flex justify-center mt-4">
<Pagination <Pagination
...@@ -48,6 +55,8 @@ export default function Page() { ...@@ -48,6 +55,8 @@ export default function Page() {
onGoToNextPage={() => setPage(Math.min(Number(allData?.responseData.totalPages ?? 1), page + 1))} onGoToNextPage={() => setPage(Math.min(Number(allData?.responseData.totalPages ?? 1), page + 1))}
/> />
</div> </div>
</>
)}
</div> </div>
</main> </main>
......
...@@ -11,12 +11,13 @@ import Image from "next/image"; ...@@ -11,12 +11,13 @@ import Image from "next/image";
import { useGetNews } from "@api/endpoints/news"; import { useGetNews } from "@api/endpoints/news";
import { PATHS } from "@constants/paths"; import { PATHS } from "@constants/paths";
import { GetNewsResponseType } from "@api/types/NewsPage.type"; import { GetNewsResponseType } from "@api/types/NewsPage.type";
import { Spinner } from "@components/ui/spinner";
export default function Page() { export default function Page() {
const [submitSearch, setSubmitSearch] = useState(""); const [submitSearch, setSubmitSearch] = useState("");
const [page, setPage] = useState(1); const [page, setPage] = useState(1);
const pageSize = 5; const pageSize = 5;
const { data: allData } = useGetNews<GetNewsResponseType>({ const { data: allData, isLoading } = useGetNews<GetNewsResponseType>({
pageSize: String(pageSize), pageSize: String(pageSize),
currentPage: String(page), currentPage: String(page),
filters: submitSearch ? `title @=${submitSearch}` : undefined, filters: submitSearch ? `title @=${submitSearch}` : undefined,
...@@ -30,17 +31,22 @@ export default function Page() { ...@@ -30,17 +31,22 @@ export default function Page() {
{/* Main content */} {/* Main content */}
<main className="lg:col-span-2 bg-background "> <main className="lg:col-span-2 bg-background ">
<div className="pb-5 overflow-hidden"> <div className="pb-5 overflow-hidden">
{allData?.responseData.rows.length === 0 ? ( {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> <p className="text-center py-4">Không có dữ liệu</p>
) : ( ) : (
allData?.responseData.rows.map((news) => ( <>
{allData?.responseData.rows.map((news) => (
<NewsContent <NewsContent
key={news.id} key={news.id}
news={news} news={news}
link={`${PATHS.mediaInformation}/tin-vcci/${news.id}`} link={`${PATHS.mediaInformation}/tin-vcci/${news.id}`}
/> />
))) ))}
}
<div className="w-full flex justify-center mt-4"> <div className="w-full flex justify-center mt-4">
<Pagination <Pagination
...@@ -51,6 +57,8 @@ export default function Page() { ...@@ -51,6 +57,8 @@ export default function Page() {
onGoToNextPage={() => setPage(Math.min(Number(allData?.responseData.totalPages ?? 1), page + 1))} onGoToNextPage={() => setPage(Math.min(Number(allData?.responseData.totalPages ?? 1), page + 1))}
/> />
</div> </div>
</>
)}
</div> </div>
</main> </main>
......
...@@ -8,6 +8,7 @@ import { useGetNewsId } from '@/api/endpoints/news'; ...@@ -8,6 +8,7 @@ import { useGetNewsId } from '@/api/endpoints/news';
import parse from "html-react-parser"; import parse from "html-react-parser";
import { useParams } from 'next/navigation' import { useParams } from 'next/navigation'
import { GetNewsDetailResponseType } from '@lib/types/news-detail-response-data'; import { GetNewsDetailResponseType } from '@lib/types/news-detail-response-data';
import { Spinner } from "@components/ui/spinner";
// ...existing code... // ...existing code...
const Page: React.FC = () => { const Page: React.FC = () => {
const { id } = useParams() const { id } = useParams()
...@@ -20,11 +21,20 @@ const Page: React.FC = () => { ...@@ -20,11 +21,20 @@ const Page: React.FC = () => {
<div className="grid grid-cols-1 lg:grid-cols-3 gap-6"> <div className="grid grid-cols-1 lg:grid-cols-3 gap-6">
{/* Main content */} {/* Main content */}
<main className="lg:col-span-2 bg-white border rounded-md p-6"> <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'> <div className='pb-5 text-primary text-2xl leading-normal font-medium'>
{data?.responseData?.title} {data?.responseData?.title}
</div> </div>
<hr className="py-2"/> <hr className="py-2"/>
<div className="p-7.5 prose tiptap overflow-hidden">{parse(data?.responseData?.description ?? '')}</div> <div className="p-7.5 prose tiptap overflow-hidden">{parse(data?.responseData?.description ?? '')}</div>
</>
)}
</main> </main>
{/* Sidebar */} {/* Sidebar */}
......
...@@ -8,6 +8,7 @@ import { useGetNewsId } from '@/api/endpoints/news'; ...@@ -8,6 +8,7 @@ import { useGetNewsId } from '@/api/endpoints/news';
import parse from "html-react-parser"; import parse from "html-react-parser";
import { useParams } from 'next/navigation' import { useParams } from 'next/navigation'
import { GetNewsDetailResponseType } from '@lib/types/news-detail-response-data'; import { GetNewsDetailResponseType } from '@lib/types/news-detail-response-data';
import { Spinner } from "@components/ui/spinner";
// ...existing code... // ...existing code...
const Page: React.FC = () => { const Page: React.FC = () => {
const { id } = useParams() const { id } = useParams()
...@@ -20,11 +21,22 @@ const Page: React.FC = () => { ...@@ -20,11 +21,22 @@ const Page: React.FC = () => {
<div className="grid grid-cols-1 lg:grid-cols-3 gap-6"> <div className="grid grid-cols-1 lg:grid-cols-3 gap-6">
{/* Main content */} {/* Main content */}
<main className="lg:col-span-2 bg-white border rounded-md p-6"> <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'> <div className='pb-5 text-primary text-2xl leading-normal font-medium'>
{data?.responseData?.title} {data?.responseData?.title}
</div> </div>
<hr className="py-2"/> <hr className="py-2"/>
<div className="p-7.5 prose tiptap overflow-hidden">{parse(data?.responseData?.description ?? '')}</div> <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> </main>
{/* Sidebar */} {/* Sidebar */}
......
...@@ -9,12 +9,13 @@ import Image from "next/image"; ...@@ -9,12 +9,13 @@ import Image from "next/image";
import { useGetNews } from "@api/endpoints/news"; import { useGetNews } from "@api/endpoints/news";
import { GetNewsResponseType } from "@api/types/NewsPage.type"; import { GetNewsResponseType } from "@api/types/NewsPage.type";
import { PATHS } from "@constants/paths"; import { PATHS } from "@constants/paths";
import { Spinner } from "@components/ui/spinner";
export default function Page() { export default function Page() {
const [submitSearch] = useState(""); const [submitSearch] = useState("");
const [page, setPage] = useState(1); const [page, setPage] = useState(1);
const pageSize = 5; const pageSize = 5;
const { data: allData } = useGetNews<GetNewsResponseType>({ const { data: allData, isLoading } = useGetNews<GetNewsResponseType>({
pageSize: String(pageSize), pageSize: String(pageSize),
currentPage: String(page), currentPage: String(page),
// filters: submitSearch ? `title @=${submitSearch}` : undefined, // filters: submitSearch ? `title @=${submitSearch}` : undefined,
...@@ -29,6 +30,15 @@ export default function Page() { ...@@ -29,6 +30,15 @@ export default function Page() {
{/* Main content */} {/* Main content */}
<main className="lg:col-span-2 bg-background "> <main className="lg:col-span-2 bg-background ">
<div className="pb-5 overflow-hidden"> <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) => ( {allData?.responseData.rows.map((news) => (
<NewsContent key={news.id} news={news} link={`${PATHS.tradePromotion}/${news.id}`} /> <NewsContent key={news.id} news={news} link={`${PATHS.tradePromotion}/${news.id}`} />
))} ))}
...@@ -49,6 +59,8 @@ export default function Page() { ...@@ -49,6 +59,8 @@ export default function Page() {
} }
/> />
</div> </div>
</>
)}
</div> </div>
</main> </main>
......
...@@ -8,6 +8,7 @@ import { useGetNewsId } from '@/api/endpoints/news'; ...@@ -8,6 +8,7 @@ import { useGetNewsId } from '@/api/endpoints/news';
import parse from "html-react-parser"; import parse from "html-react-parser";
import { useParams } from 'next/navigation' import { useParams } from 'next/navigation'
import { GetNewsDetailResponseType } from '@lib/types/news-detail-response-data'; import { GetNewsDetailResponseType } from '@lib/types/news-detail-response-data';
import { Spinner } from "@components/ui/spinner";
// ...existing code... // ...existing code...
const Page: React.FC = () => { const Page: React.FC = () => {
const { id } = useParams() const { id } = useParams()
...@@ -20,11 +21,22 @@ const Page: React.FC = () => { ...@@ -20,11 +21,22 @@ const Page: React.FC = () => {
<div className="grid grid-cols-1 lg:grid-cols-3 gap-6"> <div className="grid grid-cols-1 lg:grid-cols-3 gap-6">
{/* Main content */} {/* Main content */}
<main className="lg:col-span-2 bg-white border rounded-md p-6"> <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'> <div className='pb-5 text-primary text-2xl leading-normal font-medium'>
{data?.responseData?.title} {data?.responseData?.title}
</div> </div>
<hr className="py-2"/> <hr className="py-2"/>
<div className="p-7.5 prose tiptap overflow-hidden">{parse(data?.responseData?.description ?? '')}</div> <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> </main>
{/* Sidebar */} {/* Sidebar */}
......
...@@ -8,12 +8,13 @@ import Image from "next/image"; ...@@ -8,12 +8,13 @@ import Image from "next/image";
import { useGetNews } from "@api/endpoints/news"; import { useGetNews } from "@api/endpoints/news";
import { GetNewsResponseType } from "@api/types/NewsPage.type"; import { GetNewsResponseType } from "@api/types/NewsPage.type";
import { PATHS } from "@constants/paths"; import { PATHS } from "@constants/paths";
import { Spinner } from "@components/ui/spinner";
export default function Page() { export default function Page() {
const [submitSearch] = useState(""); const [submitSearch] = useState("");
const [page, setPage] = useState(1); const [page, setPage] = useState(1);
const pageSize = 5; const pageSize = 5;
const { data: allData } = useGetNews<GetNewsResponseType>({ const { data: allData, isLoading } = useGetNews<GetNewsResponseType>({
pageSize: String(pageSize), pageSize: String(pageSize),
currentPage: String(page), currentPage: String(page),
// filters: submitSearch ? `title @=${submitSearch}` : undefined, // filters: submitSearch ? `title @=${submitSearch}` : undefined,
...@@ -28,6 +29,15 @@ export default function Page() { ...@@ -28,6 +29,15 @@ export default function Page() {
{/* Main content */} {/* Main content */}
<main className="lg:col-span-2 bg-background "> <main className="lg:col-span-2 bg-background ">
<div className="pb-5 overflow-hidden"> <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) => ( {allData?.responseData.rows.map((news) => (
<NewsContent <NewsContent
key={news.id} key={news.id}
...@@ -52,6 +62,8 @@ export default function Page() { ...@@ -52,6 +62,8 @@ export default function Page() {
} }
/> />
</div> </div>
</>
)}
</div> </div>
</main> </main>
......
...@@ -8,6 +8,7 @@ import { useGetNewsId } from '@/api/endpoints/news'; ...@@ -8,6 +8,7 @@ import { useGetNewsId } from '@/api/endpoints/news';
import parse from "html-react-parser"; import parse from "html-react-parser";
import { useParams } from 'next/navigation' import { useParams } from 'next/navigation'
import { GetNewsDetailResponseType } from '@lib/types/news-detail-response-data'; import { GetNewsDetailResponseType } from '@lib/types/news-detail-response-data';
import { Spinner } from "@components/ui/spinner";
// ...existing code... // ...existing code...
const Page: React.FC = () => { const Page: React.FC = () => {
const { id } = useParams() const { id } = useParams()
...@@ -20,11 +21,22 @@ const Page: React.FC = () => { ...@@ -20,11 +21,22 @@ const Page: React.FC = () => {
<div className="grid grid-cols-1 lg:grid-cols-3 gap-6"> <div className="grid grid-cols-1 lg:grid-cols-3 gap-6">
{/* Main content */} {/* Main content */}
<main className="lg:col-span-2 bg-white border rounded-md p-6"> <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'> <div className='pb-5 text-primary text-2xl leading-normal font-medium'>
{data?.responseData?.title} {data?.responseData?.title}
</div> </div>
<hr className="py-2"/> <hr className="py-2"/>
<div className="p-7.5 prose tiptap overflow-hidden">{parse(data?.responseData?.description ?? '')}</div> <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> </main>
{/* Sidebar */} {/* Sidebar */}
......
...@@ -9,12 +9,13 @@ import EventCalendar from "@app/dai-dien-gioi-chu/components/event-calendar"; ...@@ -9,12 +9,13 @@ import EventCalendar from "@app/dai-dien-gioi-chu/components/event-calendar";
import { useGetNews } from "@api/endpoints/news"; import { useGetNews } from "@api/endpoints/news";
import { GetNewsResponseType } from "@api/types/NewsPage.type"; import { GetNewsResponseType } from "@api/types/NewsPage.type";
import { PATHS } from "@constants/paths"; import { PATHS } from "@constants/paths";
import { Spinner } from "@components/ui/spinner";
export default function Page() { export default function Page() {
const [submitSearch] = useState(""); const [submitSearch] = useState("");
const [page, setPage] = useState(1); const [page, setPage] = useState(1);
const pageSize = 5; const pageSize = 5;
const { data: allData } = useGetNews<GetNewsResponseType>({ const { data: allData, isLoading } = useGetNews<GetNewsResponseType>({
pageSize: String(pageSize), pageSize: String(pageSize),
currentPage: String(page), currentPage: String(page),
// filters: submitSearch ? `title @=${submitSearch}` : undefined, // filters: submitSearch ? `title @=${submitSearch}` : undefined,
...@@ -29,6 +30,15 @@ export default function Page() { ...@@ -29,6 +30,15 @@ export default function Page() {
{/* Main content */} {/* Main content */}
<main className="lg:col-span-2 bg-background "> <main className="lg:col-span-2 bg-background ">
<div className="pb-5 overflow-hidden"> <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) => ( {allData?.responseData.rows.map((news) => (
<NewsContent <NewsContent
key={news.id} key={news.id}
...@@ -53,6 +63,8 @@ export default function Page() { ...@@ -53,6 +63,8 @@ export default function Page() {
} }
/> />
</div> </div>
</>
)}
</div> </div>
</main> </main>
......
...@@ -8,12 +8,13 @@ import Image from "next/image"; ...@@ -8,12 +8,13 @@ import Image from "next/image";
import { useGetNews } from "@api/endpoints/news"; import { useGetNews } from "@api/endpoints/news";
import { GetNewsResponseType } from "@api/types/NewsPage.type"; import { GetNewsResponseType } from "@api/types/NewsPage.type";
import { PATHS } from "@constants/paths"; import { PATHS } from "@constants/paths";
import { Spinner } from "@components/ui/spinner";
export default function Page() { export default function Page() {
const [submitSearch] = useState(""); const [submitSearch] = useState("");
const [page, setPage] = useState(1); const [page, setPage] = useState(1);
const pageSize = 5; const pageSize = 5;
const { data: allData } = useGetNews<GetNewsResponseType>({ const { data: allData, isLoading } = useGetNews<GetNewsResponseType>({
pageSize: String(pageSize), pageSize: String(pageSize),
currentPage: String(page), currentPage: String(page),
filters: submitSearch ? `title @=${submitSearch}` : undefined, filters: submitSearch ? `title @=${submitSearch}` : undefined,
...@@ -27,6 +28,13 @@ export default function Page() { ...@@ -27,6 +28,13 @@ export default function Page() {
{/* Main content */} {/* Main content */}
<main className="lg:col-span-2 bg-background "> <main className="lg:col-span-2 bg-background ">
<div className="pb-5 overflow-hidden"> <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) => ( {allData?.responseData.rows.map((news) => (
<NewsContent key={news.id} news={news} link={`${PATHS.tradePromotion}/${news.id}`} /> <NewsContent key={news.id} news={news} link={`${PATHS.tradePromotion}/${news.id}`} />
))} ))}
...@@ -47,6 +55,8 @@ export default function Page() { ...@@ -47,6 +55,8 @@ export default function Page() {
} }
/> />
</div> </div>
</>
)}
</div> </div>
</main> </main>
......
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
// 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 }
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