diff --git a/apps/cm/package.json b/apps/cm/package.json index cd6d5d253..9d4e1da8f 100644 --- a/apps/cm/package.json +++ b/apps/cm/package.json @@ -34,7 +34,7 @@ "dayjs": "^1.11.13", "js-cookie": "^3.0.5", "lucide-react": "^0.474.0", - "next": "15.3.6", + "next": "15.3.8", "next-qrcode": "^2.5.1", "next-themes": "^0.4.6", "react-chartjs-2": "^5.3.0", diff --git a/apps/cm/src/entities/post/api/post-service.ts b/apps/cm/src/entities/post/api/post-service.ts index b7016b780..30370458b 100644 --- a/apps/cm/src/entities/post/api/post-service.ts +++ b/apps/cm/src/entities/post/api/post-service.ts @@ -11,9 +11,11 @@ import { type MyPostList, type PostParams, type MyPostParams, + type NoticePostParams, type PostFormData, type PostUpdateFormData, type PostService, + type NoticePostList, } from "../model/post-interface"; export class PostApiService implements PostService { @@ -28,6 +30,17 @@ export class PostApiService implements PostService { return response.data; } + + public async getNoticePostList( + params?: NoticePostParams, + ): Promise { + const response = await client.get( + `${ENDPOINT.BOARD_SERVICE.POSTS}/recentPosts`, + { params }, + ); + return response.data; + } + public async getMyPostList( params?: MyPostParams, ): Promise { diff --git a/apps/cm/src/entities/post/index.ts b/apps/cm/src/entities/post/index.ts index e0241e2f4..e7482a879 100644 --- a/apps/cm/src/entities/post/index.ts +++ b/apps/cm/src/entities/post/index.ts @@ -4,11 +4,13 @@ export { postUpdateFormSchema, type Post, type MyPost, + type NoticePost, type MovePost, type DeletePost, type PostList, type PostParams, type MyPostParams, + type NoticePostParams, type PostFormData, type PostUpdateFormData, type PostService, diff --git a/apps/cm/src/entities/post/model/post-interface.ts b/apps/cm/src/entities/post/model/post-interface.ts index 1be87dadd..a8b23462f 100644 --- a/apps/cm/src/entities/post/model/post-interface.ts +++ b/apps/cm/src/entities/post/model/post-interface.ts @@ -12,6 +12,20 @@ export interface PostParams extends PageableParams { ansUseYn?: string; } +export interface NoticePostParams extends PageableParams { + keywordType?: string; + keyword?: string; + bbsId?: number | null; + delYn?: string; + frDateYmd?: string; + toDateYmd?: string; +} + +export interface NoticePost extends Post { + bbsNm: string; + bbsTypeCd: string; +} + export interface MyPostParams extends PageableParams { keywordType?: string; keyword?: string; @@ -99,6 +113,7 @@ export const postUpdateFormSchema = postFormSchema.omit({ export type PostList = PageableList; export type MyPostList = PageableList; +export type NoticePostList = PageableList; export type PostFormData = z.infer; export type PostUpdateFormData = z.infer; diff --git a/apps/cm/src/pages/dashboard/api/banner.ts b/apps/cm/src/pages/dashboard/api/banner.ts new file mode 100644 index 000000000..d27552047 --- /dev/null +++ b/apps/cm/src/pages/dashboard/api/banner.ts @@ -0,0 +1,34 @@ +import { client } from "@/shared/api"; +import { ENDPOINT } from "@/shared/config"; + +export interface Banner { + bnrSn: number; + bnrTypeCd: string; + bnrTypeNm: string; + bnrTtl: string; + lnkgUrlAddr: string; + useYn: string; + atflCd: string; + mdfcnDt: string; + mdfr: string; + mdfrNm: string; +} + +export const getBannerList = async (siteId: string) => { + const response = await client.get( + `${ENDPOINT.CMS_SERVICE.BANNERS}/${siteId}`, + ); + return response.data; +}; + +export const getImageUrlByAtflCd = async (atflCd: string): Promise => { + if (!atflCd) return ""; + const response = await client.get( + `${ENDPOINT.ATTACH_SERVICE.IMAGES}/atflCd/${atflCd}`, + { + responseType: "blob", + }, + ); + const blob = response.data; + return URL.createObjectURL(blob); +}; \ No newline at end of file diff --git a/apps/cm/src/pages/dashboard/lib/korea-basin-map.tsx b/apps/cm/src/pages/dashboard/lib/korea-basin-map.tsx index eacf1a2e1..989839187 100644 --- a/apps/cm/src/pages/dashboard/lib/korea-basin-map.tsx +++ b/apps/cm/src/pages/dashboard/lib/korea-basin-map.tsx @@ -348,85 +348,79 @@ export function KoreaBasinMap() { // 스타일은 필요에 따라 tailwind, css 등으로 커스터마이즈하세요. return ( -
-
-
-

유역별 정보

-
-
-
-
-
-
- -
+
+
+
+
+
+ -
+
); } diff --git a/apps/cm/src/pages/dashboard/model/table-columns.tsx b/apps/cm/src/pages/dashboard/model/table-columns.tsx index aac2b2868..64d654dcf 100644 --- a/apps/cm/src/pages/dashboard/model/table-columns.tsx +++ b/apps/cm/src/pages/dashboard/model/table-columns.tsx @@ -1,9 +1,9 @@ import dayjs from "dayjs"; import type { ColumnDef } from "@tanstack/react-table"; import { Badge } from "@common/components"; -import type { Post, MyPost } from "@/entities/post"; +import type { MyPost, NoticePost } from "@/entities/post"; -const noticePostTableColumns: ColumnDef[] = [ +const noticePostTableColumns: ColumnDef[] = [ { header: "번호", cell: ({ row, table }) => { diff --git a/apps/cm/src/pages/dashboard/ui/banner-list.tsx b/apps/cm/src/pages/dashboard/ui/banner-list.tsx new file mode 100644 index 000000000..2bfaba565 --- /dev/null +++ b/apps/cm/src/pages/dashboard/ui/banner-list.tsx @@ -0,0 +1,106 @@ +"use client"; + +import { useRef } from "react"; +import { useQuery } from "@tanstack/react-query"; +import Autoplay from "embla-carousel-autoplay"; +import { ENDPOINT } from "@/shared/config"; +import { getImageUrlByAtflCd, getBannerList } from "../api/banner"; +import { getCookie } from "@/shared/lib"; + +import { + Carousel, + CarouselContent, + CarouselItem, + CarouselNext, + CarouselPrevious, + } from "@common/components"; + + + type BannerImageProps = { + atflCd: string; + bnrSn: number; + lnkgUrlAddr: string; + }; + +const BannerList: React.FC = () => { + const siteId = getCookie("site-id-key") ?? ""; + + const BannerImage = ({ atflCd, bnrSn, lnkgUrlAddr }: BannerImageProps) => { + const { data: imageUrl, isLoading } = useQuery({ + queryKey: [ENDPOINT.ATTACH_SERVICE.IMAGES, atflCd], + queryFn: () => getImageUrlByAtflCd(atflCd), + enabled: !!atflCd, + }); + + const handleClick = () => { + if (lnkgUrlAddr) { + window.open(lnkgUrlAddr, '_blank'); + } + }; + + if (isLoading) { + return ( +
+
로딩 중...
+
+ ); + } + + if (!imageUrl) { + return ( +
+
이미지 없음
+
+ ); + } + + return ( +
+ 배너 이미지 +
+ ); + }; + const { data: bannerList } = useQuery({ + queryKey: [ENDPOINT.CMS_SERVICE.BANNERS, siteId], + queryFn: () => getBannerList(siteId), + }); + + const plugin = useRef(Autoplay({ delay: 2000, stopOnInteraction: true })); + + return ( +
+ + + {bannerList && bannerList.length > 0 ? ( + bannerList.map((banner) => ( + + + + )) + ) : ( + +
+
배너가 없습니다
+
+
+ )} +
+ + +
+
+ ); +}; + +export default BannerList; \ No newline at end of file diff --git a/apps/cm/src/pages/dashboard/ui/basIn-select.tsx b/apps/cm/src/pages/dashboard/ui/basIn-select.tsx index 78ab08637..7fd7e1f42 100644 --- a/apps/cm/src/pages/dashboard/ui/basIn-select.tsx +++ b/apps/cm/src/pages/dashboard/ui/basIn-select.tsx @@ -2,6 +2,7 @@ import React from "react"; import { + cn, Select, SelectContent, SelectItem, @@ -12,14 +13,19 @@ import { interface BasinSelectProps { selectedBasin: string; onValueChange: (value: string) => void; + className?: string; } -const BasinSelect: React.FC = ({ selectedBasin, onValueChange }) => { +const BasinSelect: React.FC = ({ + selectedBasin, + onValueChange, + className + }) => { const basins = ["한강", "금강", "낙동강", "영산강"]; return (