diff --git a/.gitignore b/.gitignore index f167b61e8..878447ead 100644 --- a/.gitignore +++ b/.gitignore @@ -23,4 +23,5 @@ apps/fo/.next/**/* .qodo pnpm-lock.yaml -settings.json \ No newline at end of file +settings.json +.npmrc diff --git a/apps/cm/.env.local b/apps/cm/.env.local index 96f70cf59..4aba6b3b2 100644 --- a/apps/cm/.env.local +++ b/apps/cm/.env.local @@ -1,12 +1,13 @@ # apps/cs/src/shared/config/env.ts 참고 NEXT_PUBLIC_PORT=3000 NEXT_PUBLIC_PROXY_HOST=http://localhost:3000 -NEXT_PUBLIC_SITE_ID=CS +NEXT_PUBLIC_SITE_ID=CM NEXT_PUBLIC_SUB_DOMAIN=http://localhost:13000 NEXT_PUBLIC_SERVER_API_URL=http://dev.onasys.co.kr:8000 NEXT_PUBLIC_LINK_BACKEND_API_HOST=http://rsone.iptime.org:10014 NEXT_PUBLIC_TINY_API_KEY=t9nc4z6inquvvd5pq1vea8wcp7lb01qmqseh6egms9vpaju2 NEXT_PUBLIC_SERVER_API_URL2=http://con.mind-one.co.kr:11083 +NEXT_PUBLIC_SERVER_API_URL_RSONE=https://apigateway.apps.rsonesoft.com/ NEXT_PUBLIC_SN=https://devlion.hasuon.co.kr NEXT_PUBLIC_WR=https://devtiger.hasuon.co.kr diff --git a/apps/cm/.env.production b/apps/cm/.env.production index 070113102..52e7e135c 100644 --- a/apps/cm/.env.production +++ b/apps/cm/.env.production @@ -1,8 +1,8 @@ # apps/cs/src/shared/config/env.ts 참고 -NEXT_PUBLIC_PORT=3000 -NEXT_PUBLIC_PROXY_HOST=http://localhost:3000 +NEXT_PUBLIC_PORT=3001 +NEXT_PUBLIC_PROXY_HOST=http://localhost:3001 NEXT_PUBLIC_SITE_ID=CS -NEXT_PUBLIC_SUB_DOMAIN=http://localhost:3000 +NEXT_PUBLIC_SUB_DOMAIN=http://localhost:13000 NEXT_PUBLIC_SERVER_API_URL=http://192.168.0.102:8000 NEXT_PUBLIC_TINY_API_KEY=t9nc4z6inquvvd5pq1vea8wcp7lb01qmqseh6egms9vpaju2 diff --git a/apps/cm/app/api/proxy/[...url]/route.ts b/apps/cm/app/api/proxy/[...url]/route.ts index 761991e39..105b36fa7 100644 --- a/apps/cm/app/api/proxy/[...url]/route.ts +++ b/apps/cm/app/api/proxy/[...url]/route.ts @@ -36,9 +36,15 @@ async function handler( } try { - if (!isRefreshHandler) { - body = await request.json(); - console.log(`[Proxy] Request body:`, body); + // GET 요청이 아닐 때만 body 파싱 + if (method !== "GET" && !isRefreshHandler) { + try { + body = await request.json(); + console.log(`[Proxy] Request body:`, body); + } catch { + // body가 없을 수 있음 + body = {}; + } } const targetUrl = `${SERVER_API_URL}/${params.url.join("/")}`; @@ -47,7 +53,7 @@ async function handler( const result = await fetch(targetUrl, { method, headers, - body: JSON.stringify(body), + body: method !== "GET" ? JSON.stringify(body) : undefined, }); console.log(`[Proxy] Response status: ${result.status}`); @@ -55,12 +61,14 @@ async function handler( // 2FA 관련 엔드포인트인 경우 응답 본문도 확인 const is2FAEndpoint = params.url.includes("2fa") || params.url.includes("auth"); + // 배너 API는 인증 없이 호출 가능 + const isBannerEndpoint = params.url.includes("bnrs"); let responseData = {}; - if (is2FAEndpoint) { + if (is2FAEndpoint || isBannerEndpoint) { try { responseData = await result.json(); - console.log(`[Proxy] 2FA Response data:`, responseData); + console.log(`[Proxy] ${is2FAEndpoint ? '2FA' : 'Banner'} Response data:`, responseData); } catch { // JSON 파싱 실패 시 빈 객체 사용 console.log(`[Proxy] Failed to parse JSON response`); @@ -74,15 +82,15 @@ async function handler( console.log(`[Proxy] Tokens - accessToken: ${!!accessToken}, userId: ${!!userId}, refreshToken: ${!!refreshToken}`); console.log(`[Proxy] Available headers:`, Array.from(result.headers.keys())); - // 2FA 엔드포인트이고 실패한 경우 실제 오류 메시지 반환 - if (!result.ok && is2FAEndpoint) { - console.log(`[Proxy] 2FA endpoint failed, returning actual error`); + // 2FA 또는 배너 엔드포인트이고 실패한 경우 실제 오류 메시지 반환 + if (!result.ok && (is2FAEndpoint || isBannerEndpoint)) { + console.log(`[Proxy] ${is2FAEndpoint ? '2FA' : 'Banner'} endpoint failed, returning actual error`); return NextResponse.json(responseData, { status: result.status }); } - // 2FA 엔드포인트이고 성공한 경우 (토큰이 헤더에 없을 수 있음) - if (result.ok && is2FAEndpoint) { - console.log(`[Proxy] 2FA endpoint succeeded, returning response data`); + // 2FA 또는 배너 엔드포인트이고 성공한 경우 (토큰이 헤더에 없을 수 있음) + if (result.ok && (is2FAEndpoint || isBannerEndpoint)) { + console.log(`[Proxy] ${is2FAEndpoint ? '2FA' : 'Banner'} endpoint succeeded, returning response data`); return NextResponse.json(responseData, { status: result.status }); } @@ -143,3 +151,11 @@ export async function PUT( const params = await props.params; return handler(request, params, "PUT"); } + +export async function GET( + request: Request, + props: { params: Promise<{ url: string[] }> }, +) { + const params = await props.params; + return handler(request, params, "GET"); +} diff --git a/apps/cm/middleware.ts b/apps/cm/middleware.ts index 564cab5e8..1cc23b8c2 100644 --- a/apps/cm/middleware.ts +++ b/apps/cm/middleware.ts @@ -14,8 +14,8 @@ const serverApiUrl = const serverApiUrl2 = process.env.NEXT_PUBLIC_SERVER_API_URL2 ?? serverApiUrl; - // const serverApiUrl3 = - // process.env.NEXT_PUBLIC_SERVER_API_URL3 ?? serverApiUrl; + const serverApiUrl3 = + process.env.NEXT_PUBLIC_SERVER_API_URL3 ?? serverApiUrl; // const serverApiUrl4 = // process.env.NEXT_PUBLIC_SERVER_API_URL4 ?? serverApiUrl; @@ -110,11 +110,12 @@ export function middleware(req: NextRequest) { }); } - if (pathname.startsWith("/ic-service")) { + if (pathname.startsWith("/server/ic-service")) { const requestHeaders = new Headers(req.headers); const token = req.cookies.get(CLAIM_NAME)?.value; const userId = req.cookies.get(AUTH_USER_ID)?.value; - const newUrl = `${serverApiUrl5}${pathname}`; + + const newUrl = `${serverApiUrl3}${pathname.replace("/server/", "")}${searchParams ? `?${searchParams}` : ""}`; if (token) requestHeaders.set(CLAIM_NAME, token); if (userId) requestHeaders.set(AUTH_USER_ID, userId); @@ -128,11 +129,12 @@ export function middleware(req: NextRequest) { }); } - if (pathname.startsWith("/dc-service")) { + if (pathname.startsWith("/server/dc-service")) { const requestHeaders = new Headers(req.headers); const token = req.cookies.get(CLAIM_NAME)?.value; const userId = req.cookies.get(AUTH_USER_ID)?.value; - const newUrl = `${serverApiUrl5}${pathname}`; + + const newUrl = `${serverApiUrl3}${pathname.replace("/server/", "")}${searchParams ? `?${searchParams}` : ""}`; if (token) requestHeaders.set(CLAIM_NAME, token); if (userId) requestHeaders.set(AUTH_USER_ID, userId); diff --git a/apps/cm/src/pages/dashboard/api/banner.ts b/apps/cm/src/pages/dashboard/api/banner.ts index d27552047..ee9a32dd7 100644 --- a/apps/cm/src/pages/dashboard/api/banner.ts +++ b/apps/cm/src/pages/dashboard/api/banner.ts @@ -22,13 +22,27 @@ export const getBannerList = async (siteId: string) => { }; 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 + try { + const response = await client.get( + `${ENDPOINT.ATTACH_SERVICE.IMAGES}/atflCd/${atflCd}`, + { + responseType: "blob", + }, + ); + const blob = response.data; + const url = URL.createObjectURL(blob); + console.log("[getImageUrlByAtflCd] 이미지 URL 생성 성공:", url); + return url; + } catch (error: unknown) { + const err = error as + | { status?: number; message?: string; response?: { status?: number } } + | undefined; + console.error("[getImageUrlByAtflCd] 이미지 URL 조회 실패:", { + atflCd, + error: err, + status: err?.status ?? err?.response?.status, + message: err instanceof Error ? err.message : String(err), + }); + throw error; + } +}; diff --git a/apps/cm/src/pages/dashboard/api/support.ts b/apps/cm/src/pages/dashboard/api/support.ts new file mode 100644 index 000000000..b0513d28f --- /dev/null +++ b/apps/cm/src/pages/dashboard/api/support.ts @@ -0,0 +1,44 @@ +import { ENDPOINT } from "@/shared/config"; + +export interface Support { + total: number; + ongoing: number; + han: number; + youngsang: number; + geum: number; + nakdong: number; + type: string; +} + +export const getSupportList = async () => { + const url = `/api/proxy${ENDPOINT.IC_SERVICE.GROUPING_STATISTICS}`; + console.log("[getSupportList] API 호출 시작 (프록시, 인증 없음):", url); + try { + const response = await fetch(url, { + method: "GET", + headers: { + "Content-Type": "application/json", + accept: "application/json", + }, + }); + + if (!response.ok) { + throw new Error(`HTTP error! status: ${response.status}`); + } + + const data = await response.json(); + console.log("[getSupportList] API 응답 성공:", data); + return data; + } catch (error: unknown) { + const err = error as + | { status?: number; message?: string; response?: { status?: number } } + | undefined; + console.error("[getBannerList] API 호출 실패:", { + url, + error: err, + status: err?.status ?? err?.response?.status, + message: err instanceof Error ? err.message : String(err), + }); + throw error; + } +}; 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 0234b4a4f..00014cfa7 100644 --- a/apps/cm/src/pages/dashboard/lib/korea-basin-map.tsx +++ b/apps/cm/src/pages/dashboard/lib/korea-basin-map.tsx @@ -2,12 +2,12 @@ /** * 유역 지도 */ -import React, { useEffect, useRef, useState } from "react"; +import React, { useEffect, useRef } from "react"; import { useStore } from "zustand"; import styles from "./korea-basin-map.module.css"; // 추가 import { basinStore } from "../api/fo-service"; import { basinKeyMap,MANUAL_LABEL_POS } from "../model/map-data"; -import { BASIN_DATA, BASIN_IDS,svcInfo } from "../model/map-data"; +import { BASIN_DATA, BASIN_IDS } from "../model/map-data"; // import { operFcltStore } from "../api/fo-service"; // import {Button} from "@common/components"; @@ -120,17 +120,17 @@ export function KoreaBasinMap() { // console.log(`Found ${gs.length} basin groups`); // 각 유역 그룹의 패스 정보 확인 - gs.forEach((g, index) => { - const paths = g.querySelectorAll('path'); - // console.log(`Group ${index} (${(g as SVGGElement & { dataset: { key: string } }).dataset.key}):`, { - // element: g, - // pathCount: paths.length, - // paths: Array.from(paths).map(p => ({ - // className: p.className.baseVal, - // d: p.getAttribute('d')?.substring(0, 50) + '...' - // })) - // }); - }); + // gs.forEach((g, index) => { + // const paths = g.querySelectorAll('path'); + // // console.log(`Group ${index} (${(g as SVGGElement & { dataset: { key: string } }).dataset.key}):`, { + // // element: g, + // // pathCount: paths.length, + // // paths: Array.from(paths).map(p => ({ + // // className: p.className.baseVal, + // // d: p.getAttribute('d')?.substring(0, 50) + '...' + // // })) + // // }); + // }); // 2) 함수들 먼저 정의 const clear = () => { diff --git a/apps/cm/src/pages/dashboard/model/support-columns.tsx b/apps/cm/src/pages/dashboard/model/support-columns.tsx new file mode 100644 index 000000000..813b09051 --- /dev/null +++ b/apps/cm/src/pages/dashboard/model/support-columns.tsx @@ -0,0 +1,49 @@ +import type { ColumnDef } from "@tanstack/react-table"; +import type { Support } from "../api/support"; + +const typeLabelMap: Record = { + waterQuality: "수질", + flood: "침수", + technical: "기술지원", +}; + +const supportTableColumns: ColumnDef[] = [ + { + accessorKey: "type", + header: "구분", + cell: ({ getValue }) => { + const value = getValue() as string; + return typeLabelMap[value] || value; + }, + }, + { + accessorKey: "total", + header: "합계", + }, + { + accessorKey: "han", + header: "한강", + }, + { + accessorKey: "nakdong", + header: "낙동강", + }, + { + accessorKey: "geum", + header: "금강", + }, + { + accessorKey: "youngsang", + header: "영산강", + }, + { + accessorKey: "ongoing", + header: "진행중", + }, + { + accessorKey: "completed", + header: "환료", + }, +]; + +export { supportTableColumns }; diff --git a/apps/cm/src/pages/dashboard/ui/banner-list.tsx b/apps/cm/src/pages/dashboard/ui/banner-list.tsx index d6c3ef040..a755facd4 100644 --- a/apps/cm/src/pages/dashboard/ui/banner-list.tsx +++ b/apps/cm/src/pages/dashboard/ui/banner-list.tsx @@ -3,104 +3,184 @@ import { useRef } from "react"; import { useQuery } from "@tanstack/react-query"; import { ENDPOINT } from "@/shared/config"; -import { getImageUrlByAtflCd, getBannerList } from "../api/banner"; +import { getImageUrlByAtflCd, getBannerList, type Banner } from "../api/banner"; import { getCookie } from "@/shared/lib"; import { - Carousel, - CarouselContent, - CarouselItem, - CarouselNext, - CarouselPrevious, - Autoplay, - } from "@common/components"; + Carousel, + CarouselContent, + CarouselItem, + CarouselNext, + CarouselPrevious, + Autoplay, +} 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) => ( - - - - )) - ) : ( - -
-
배너가 없습니다
-
-
- )} -
- - -
-
- ); +type BannerImageProps = { + atflCd: string; + lnkgUrlAddr: string; }; -export default BannerList; \ No newline at end of file +const BannerList: React.FC = () => { + const siteId = getCookie("site-id-key") ?? ""; + + const BannerImage = ({ atflCd, 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, + error: bannerError, + isLoading: isLoadingBanners, + } = useQuery({ + queryKey: [ENDPOINT.CMS_SERVICE.BANNERS, siteId], + queryFn: () => getBannerList(siteId), + enabled: !!siteId, + retry: false, + }); + + const plugin = useRef(Autoplay({ delay: 2000, stopOnInteraction: true })); + + // MAIN 타입 배너만 필터링 + const mainBanners = + bannerList?.filter((banner) => banner.bnrTypeCd === "BNRMAIN") ?? []; + + // 에러 상태 판별 헬퍼 함수 + const getErrorStatus = (e: unknown): number | undefined => { + if (!e || typeof e !== "object") return undefined; + const maybe = e as { status?: number; response?: { status?: number } }; + return maybe.status ?? maybe.response?.status; + }; + + // 401 에러 처리 + if (bannerError) { + const is401Error = + (bannerError instanceof Error && bannerError.message.includes("401")) || + getErrorStatus(bannerError) === 401; + + if (is401Error) { + console.error("[BannerList] 401 인증 오류:", bannerError); + return ( +
+
+
+ 인증이 필요합니다. 로그인해주세요. +
+
+
+ ); + } + + console.error("[BannerList] 배너 조회 오류:", bannerError); + return ( +
+
+
+ 배너를 불러올 수 없습니다 +
+
+
+ ); + } + + // 로딩 중 + if (isLoadingBanners) { + return ( +
+
+
배너 로딩 중...
+
+
+ ); + } + + // siteId가 없을 때 + if (!siteId) { + return ( +
+
+
+ 사이트 ID가 없습니다 +
+
+
+ ); + } + + return ( +
+ + + {mainBanners.length > 0 ? ( + mainBanners.map((banner) => ( + + + + )) + ) : ( + +
+
+ 배너가 없습니다 +
+
+
+ )} +
+ + +
+
+ ); +}; + +export default BannerList; diff --git a/apps/cm/src/pages/dashboard/ui/dashboard-page.tsx b/apps/cm/src/pages/dashboard/ui/dashboard-page.tsx index f8d41ed22..3d0285ef6 100644 --- a/apps/cm/src/pages/dashboard/ui/dashboard-page.tsx +++ b/apps/cm/src/pages/dashboard/ui/dashboard-page.tsx @@ -25,6 +25,7 @@ import ReuseChart from "./reuse-chart"; import ReuseStatus from "./reuse-status"; import NoticeList from "./notice-list"; import BannerList from "./banner-list"; +import SupportList from "./support-list"; // Chart.js 등록 ChartJS.register( @@ -173,13 +174,8 @@ function DashboardPage() { {/* 두번째 라인 */}
-
-
-

지원현황

-
-
- -
+
+
@@ -193,7 +189,7 @@ function DashboardPage() {
{/* 배너 영역 */} -
+
diff --git a/apps/cm/src/pages/dashboard/ui/notice-list.tsx b/apps/cm/src/pages/dashboard/ui/notice-list.tsx index f6c21a4db..feb0f4f80 100644 --- a/apps/cm/src/pages/dashboard/ui/notice-list.tsx +++ b/apps/cm/src/pages/dashboard/ui/notice-list.tsx @@ -9,10 +9,14 @@ import { useDataTable } from "@/shared/lib"; import { noticePostTableColumns, } from "../model/table-columns"; +import { Button } from "@common/components"; const NoticeList: React.FC = () => { const router = useRouter(); + // #region agent log + fetch('http://127.0.0.1:7242/ingest/fdf9af6c-c6f9-41cb-8b7f-640be19ecbac',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({location:'notice-list.tsx:16',message:'Router initialized',data:{routerExists:!!router},timestamp:Date.now(),sessionId:'debug-session',runId:'run1',hypothesisId:'E'})}).catch(()=>{}); + // #endregion const noticePostQuery = useQuery({ queryKey: [ENDPOINT.BOARD_SERVICE.POSTS], queryFn: () => @@ -28,6 +32,25 @@ const NoticeList: React.FC = () => { router.push(`/bbs/bbsnotice/${row.bbsId}/${row.pstNo}`); }; + const handleMoreClick = () => { + // #region agent log + fetch('http://127.0.0.1:7242/ingest/fdf9af6c-c6f9-41cb-8b7f-640be19ecbac',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({location:'notice-list.tsx:32',message:'Button click handler called',data:{targetPath:'/cms/bbsnotice/7'},timestamp:Date.now(),sessionId:'debug-session',runId:'run1',hypothesisId:'A'})}).catch(()=>{}); + // #endregion + try { + // #region agent log + fetch('http://127.0.0.1:7242/ingest/fdf9af6c-c6f9-41cb-8b7f-640be19ecbac',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({location:'notice-list.tsx:35',message:'Before router.push',data:{routerExists:!!router,path:'/cms/bbsnotice/7'},timestamp:Date.now(),sessionId:'debug-session',runId:'run1',hypothesisId:'B'})}).catch(()=>{}); + // #endregion + router.push('/cms/bbsnotice/7'); + // #region agent log + fetch('http://127.0.0.1:7242/ingest/fdf9af6c-c6f9-41cb-8b7f-640be19ecbac',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({location:'notice-list.tsx:43',message:'After router.push',data:{navigationCalled:true},timestamp:Date.now(),sessionId:'debug-session',runId:'run1',hypothesisId:'B'})}).catch(()=>{}); + // #endregion + } catch (error) { + // #region agent log + fetch('http://127.0.0.1:7242/ingest/fdf9af6c-c6f9-41cb-8b7f-640be19ecbac',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({location:'notice-list.tsx:41',message:'Router.push error',data:{error:error instanceof Error?error.message:String(error)},timestamp:Date.now(),sessionId:'debug-session',runId:'run1',hypothesisId:'B'})}).catch(()=>{}); + // #endregion + } + }; + const { tableComponent: noticePostTableComponent } = useDataTable({ query: noticePostQuery, tableOptions: { @@ -44,6 +67,7 @@ const NoticeList: React.FC = () => {

공지사항

+
{noticePostTableComponent}
diff --git a/apps/cm/src/pages/dashboard/ui/support-list.tsx b/apps/cm/src/pages/dashboard/ui/support-list.tsx new file mode 100644 index 000000000..7567d5862 --- /dev/null +++ b/apps/cm/src/pages/dashboard/ui/support-list.tsx @@ -0,0 +1,37 @@ +"use client"; + +import { useQuery } from "@tanstack/react-query"; +import { getCoreRowModel } from "@tanstack/react-table"; +import { ENDPOINT } from "@/shared/config"; +import { useDataTable } from "@/shared/lib"; +import { supportTableColumns } from "../model/support-columns"; +import { getSupportList } from "../api/support"; + +const SupportList: React.FC = () => { + const supportQuery = useQuery({ + queryKey: [ENDPOINT.BOARD_SERVICE.POSTS], + queryFn: () => getSupportList(), + }); + + const { tableComponent: supportTableComponent } = useDataTable({ + query: supportQuery, + tableOptions: { + columns: supportTableColumns, + getCoreRowModel: getCoreRowModel(), + }, + dataTableOptions: { + variant: "line", + }, + }); + + return ( +
+
+

지원현황

+
+ {supportTableComponent} +
+ ); +}; + +export default SupportList; diff --git a/apps/cm/src/shared/config/api.ts b/apps/cm/src/shared/config/api.ts index 45096e25e..241188bee 100644 --- a/apps/cm/src/shared/config/api.ts +++ b/apps/cm/src/shared/config/api.ts @@ -44,7 +44,7 @@ export const ENDPOINT = { ORGANIZATIONS: "/cms-service/api/v1/ognzs", USER_ORGANIZATION: "/cms-service/api/v1/user-ognz", IMAGES: "/cms-service/api/v1/images", - BANNERS: "/cms-service/api/v1/bnrs", + BANNERS: "/cms-service/api/v1/bnrs/user", TEMPLATE: "/cms-service/api/v1/tmplt", TERMS: "/cms-service/api/v1/trms", LWPO_CODE: "/cms-service/api/v1/lwpo-cds", @@ -82,10 +82,10 @@ export const ENDPOINT = { REUSE_STATUS: "/operation-service/api/v1/dashBoard/main/reuse/status", }, IC_SERVICE: { - GROUPING_STATISTICS: "/ic-service/api/v1/nti/grouping-statistics", + GROUPING_STATISTICS: "/ic-service/api/v1/support-status-summary", }, DC_SERVICE: { - DATA_STATUS: "/dc-service/api/v1/dc/dataStatus", + DATA_STATUS: "/dc-service/api/v1/dataStatus", }, }; diff --git a/apps/cm/src/shared/ui/page-layout.tsx b/apps/cm/src/shared/ui/page-layout.tsx index d792d0ff7..5355531aa 100644 --- a/apps/cm/src/shared/ui/page-layout.tsx +++ b/apps/cm/src/shared/ui/page-layout.tsx @@ -1,4 +1,3 @@ -import { PageTitle } from "@common/components"; import { useMenuStore } from "../lib/use-menu-store"; function PageLayout({ children }: { children: React.ReactNode }) { @@ -8,11 +7,7 @@ function PageLayout({ children }: { children: React.ReactNode }) { ?.split(">") .map((path) => path.trim()); - return ( -
- {children} -
- ); + return
{children}
; } export { PageLayout }; diff --git a/apps/dc/package.json b/apps/dc/package.json index ba2b422ed..747cdf91e 100644 --- a/apps/dc/package.json +++ b/apps/dc/package.json @@ -24,6 +24,7 @@ "js-cookie": "^3.0.5", "lucide-react": "^0.474.0", "next": "15.3.8", + "picocolors": "^1.1.1", "react-chartjs-2": "^5.3.0", "react-dnd": "^16.0.1", "react-dropzone": "^14.3.8", diff --git a/package.json b/package.json index 0b5f5fe9a..cc043dd86 100644 --- a/package.json +++ b/package.json @@ -45,7 +45,10 @@ "@types/react": "^19.2.7", "@types/react-dom": "^19.2.3", "react": "^19.2.1", - "react-dom": "^19.2.1" + "react-dom": "^19.2.1", + + "lodash.isequal": "^4.5.0", + "source-map": "^0.7.4" }, "onlyBuiltDependencies": [ "esbuild", diff --git a/packages/common/components/src/ui/footer/index.tsx b/packages/common/components/src/ui/footer/index.tsx index d1b0cd830..747cb26c3 100644 --- a/packages/common/components/src/ui/footer/index.tsx +++ b/packages/common/components/src/ui/footer/index.tsx @@ -70,6 +70,10 @@ export function Footer() { queryFn: () => getBannerList(siteId), }); +// MAIN 타입 배너만 필터링 + const mainBanners = bannerList?.filter((banner) => banner.bnrTypeCd === "BNRFTER") ?? []; + + const plugin = useRef(Autoplay({ delay: 2000, stopOnInteraction: true })); return ( @@ -95,8 +99,8 @@ export function Footer() { className="relative my-8 h-24" > - {bannerList && bannerList.length > 0 ? ( - bannerList.map((banner) => ( + {mainBanners && mainBanners.length > 0 ? ( + mainBanners.map((banner) => ( =6.0.0'} fast-glob@3.3.1: @@ -5336,9 +5341,6 @@ packages: lodash.size@4.2.0: resolution: {integrity: sha512-wbu3SF1XC5ijqm0piNxw59yCbuUf2kaShumYBLWUrcCvwh6C8odz6SY/wGVzCWTQTFL/1Ygbvqg2eLtspUVVAQ==} - lodash.sortby@4.7.0: - resolution: {integrity: sha512-HDWXG8isMntAyRF5vZ7xKuEvOhT4AhlRt/3czTSjvGUxjYCBVRQY48ViDHyfYz9VIoBkW4TMGQNapx+l3RUwdA==} - lodash.throttle@4.1.1: resolution: {integrity: sha512-wIkUCfVKpVsWo3JSZlc+8MB5it+2AN5W8J7YVMST30UrvcQNZ1Okbj+rbVniijTWE6FGYy4XJq/rHkas8qJMLQ==} @@ -6276,14 +6278,9 @@ packages: source-map-support@0.5.21: resolution: {integrity: sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==} - source-map@0.6.1: - resolution: {integrity: sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==} - engines: {node: '>=0.10.0'} - - source-map@0.8.0-beta.0: - resolution: {integrity: sha512-2ymg6oRBpebeZi9UUNsgQ89bhx01TcTkmNTGnNO88imTmbSgy4nfujrgVEFKWpMTEGA11EDkTt7mqObTPdigIA==} - engines: {node: '>= 8'} - deprecated: The work that was done in this beta branch won't be included in future versions + source-map@0.7.6: + resolution: {integrity: sha512-i5uvt8C3ikiWeNZSVZNWcfZPItFQOsYTUAOkcUPGd8DqDy1uOUikjt5dG+uRlwyvR108Fb9DOd4GvXfT0N2/uQ==} + engines: {node: '>= 12'} ssf@0.11.2: resolution: {integrity: sha512-+idbmIXoYET47hH+d7dfm2epdOMUDjqcB4648sTZ+t2JwoyBFL/insLfB/racrDmsKB3diwsDA696pZMieAC5g==} @@ -6481,9 +6478,6 @@ packages: resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==} engines: {node: '>=8.0'} - tr46@1.0.1: - resolution: {integrity: sha512-dTpowEjclQ7Kgx5SdBkqRzVhERQXov8/l9Ft9dVM9fmg0W0KQSVaXX9T4i6twCPNtYiZM53lpSSUAwJbFPOHxA==} - tree-kill@1.2.2: resolution: {integrity: sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==} hasBin: true @@ -6778,9 +6772,6 @@ packages: web-worker@1.5.0: resolution: {integrity: sha512-RiMReJrTAiA+mBjGONMnjVDP2u3p9R1vkcGz6gDIrOMT3oGuYwX2WRMYI9ipkphSuE5XKEhydbhNEJh4NY9mlw==} - webidl-conversions@4.0.2: - resolution: {integrity: sha512-YQ+BmxuTgd6UXZW3+ICGfyqRyHXVlD5GtQr5+qjiNW7bF0cqrzX500HVXPBOvgXb5YnzDd+h0zqyv61KUD7+Sg==} - webpack-sources@3.3.3: resolution: {integrity: sha512-yd1RBzSGanHkitROoPFd6qsrxt+oFhg/129YzheDGqeustzX0vTZJZsSsQjVQC4yzBQ56K55XU8gaNCtIzOnTg==} engines: {node: '>=10.13.0'} @@ -6798,9 +6789,6 @@ packages: webpack-cli: optional: true - whatwg-url@7.1.0: - resolution: {integrity: sha512-WUu7Rg1DroM7oQvGWfOiAK21n74Gg+T4elXEQYkOhtyLeWiJFoOGLXPKI/9gzIie9CtwVLm8wtw6YJdKyxSjeg==} - which-boxed-primitive@1.1.1: resolution: {integrity: sha512-TbX3mj8n0odCBFVlY8AxkqcHASw3L60jIuF8jFP78az3C2YhmGvqbHBpAjTRH2/xqYunrJ9g1jSyjCjpoWzIAA==} engines: {node: '>= 0.4'} @@ -10090,7 +10078,7 @@ snapshots: estraverse: 5.3.0 esutils: 2.0.3 optionalDependencies: - source-map: 0.6.1 + source-map: 0.7.6 eslint-config-next@15.1.7(eslint@9.39.1(jiti@1.21.7))(typescript@5.9.3): dependencies: @@ -10504,7 +10492,7 @@ snapshots: fast-deep-equal@3.1.3: {} - fast-equals@5.3.3: {} + fast-equals@5.3.4: {} fast-glob@3.3.1: dependencies: @@ -11133,8 +11121,6 @@ snapshots: lodash.size@4.2.0: {} - lodash.sortby@4.7.0: {} - lodash.throttle@4.1.1: {} lodash.uniqby@4.5.0: @@ -11734,7 +11720,7 @@ snapshots: react-smooth@4.0.4(react-dom@19.2.3(react@19.2.3))(react@19.2.3): dependencies: - fast-equals: 5.3.3 + fast-equals: 5.3.4 prop-types: 15.8.1 react: 19.2.3 react-dom: 19.2.3(react@19.2.3) @@ -11789,7 +11775,7 @@ snapshots: dependencies: ast-types: 0.16.1 esprima: 4.0.1 - source-map: 0.6.1 + source-map: 0.7.6 tiny-invariant: 1.3.3 tslib: 2.8.1 @@ -12070,13 +12056,9 @@ snapshots: source-map-support@0.5.21: dependencies: buffer-from: 1.1.2 - source-map: 0.6.1 + source-map: 0.7.6 - source-map@0.6.1: {} - - source-map@0.8.0-beta.0: - dependencies: - whatwg-url: 7.1.0 + source-map@0.7.6: {} ssf@0.11.2: dependencies: @@ -12324,10 +12306,6 @@ snapshots: dependencies: is-number: 7.0.0 - tr46@1.0.1: - dependencies: - punycode: 2.3.1 - tree-kill@1.2.2: {} ts-api-utils@2.1.0(typescript@5.6.3): @@ -12378,7 +12356,7 @@ snapshots: postcss-load-config: 6.0.1(jiti@2.6.1)(postcss@8.5.6) resolve-from: 5.0.0 rollup: 4.53.3 - source-map: 0.8.0-beta.0 + source-map: 0.7.6 sucrase: 3.35.1 tinyexec: 0.3.2 tinyglobby: 0.2.15 @@ -12405,7 +12383,7 @@ snapshots: postcss-load-config: 6.0.1(jiti@2.6.1)(postcss@8.5.6) resolve-from: 5.0.0 rollup: 4.53.3 - source-map: 0.8.0-beta.0 + source-map: 0.7.6 sucrase: 3.35.1 tinyexec: 0.3.2 tinyglobby: 0.2.15 @@ -12663,8 +12641,6 @@ snapshots: web-worker@1.5.0: {} - webidl-conversions@4.0.2: {} - webpack-sources@3.3.3: {} webpack-virtual-modules@0.6.2: {} @@ -12701,12 +12677,6 @@ snapshots: - esbuild - uglify-js - whatwg-url@7.1.0: - dependencies: - lodash.sortby: 4.7.0 - tr46: 1.0.1 - webidl-conversions: 4.0.2 - which-boxed-primitive@1.1.1: dependencies: is-bigint: 1.1.0