From 8b3c9c42f5fe25d79e440bed612b6a37bacfba77 Mon Sep 17 00:00:00 2001 From: YermIm Date: Thu, 12 Mar 2026 19:13:25 +0900 Subject: [PATCH 01/13] =?UTF-8?q?feat:=20=EC=A1=B0=EC=A7=81=20=EC=A0=84?= =?UTF-8?q?=EC=B2=B4=20=EC=A1=B0=ED=9A=8C/=EC=BA=A0=ED=8E=98=EC=9D=B8=20?= =?UTF-8?q?=EB=AA=A9=EB=A1=9D=20=EC=A1=B0=ED=9A=8C=20API=20=EC=97=B0?= =?UTF-8?q?=EB=8F=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/api/ads/ads.ts | 13 ++++++++++ src/api/common/common.ts | 5 ++++ src/pages/ads/list/AdsListPage.tsx | 40 +++++++++++++++++++++++++++++- 3 files changed, 57 insertions(+), 1 deletion(-) create mode 100644 src/api/ads/ads.ts create mode 100644 src/api/common/common.ts diff --git a/src/api/ads/ads.ts b/src/api/ads/ads.ts new file mode 100644 index 00000000..602f7113 --- /dev/null +++ b/src/api/ads/ads.ts @@ -0,0 +1,13 @@ +import type { ICampaign } from "@/types/ads/campaign"; + +import type { IApiResult } from "../common/common"; + +import { axiosInstance } from "@/lib/axiosInstance"; + +export const getCampaignList = async (orgId: number): Promise => { + const { data } = await axiosInstance.get>( + `/api/project/${orgId}`, + ); + + return data.data; +}; diff --git a/src/api/common/common.ts b/src/api/common/common.ts new file mode 100644 index 00000000..ce469537 --- /dev/null +++ b/src/api/common/common.ts @@ -0,0 +1,5 @@ +export interface IApiResult { + status: number; + message: string; + data: T; +} diff --git a/src/pages/ads/list/AdsListPage.tsx b/src/pages/ads/list/AdsListPage.tsx index e5e83584..8693697b 100644 --- a/src/pages/ads/list/AdsListPage.tsx +++ b/src/pages/ads/list/AdsListPage.tsx @@ -1,15 +1,53 @@ -import { useState } from "react"; +import { useEffect, useState } from "react"; import { useNavigate } from "react-router-dom"; import { toast } from "sonner"; +import type { ICampaign } from "@/types/ads/campaign"; + import CampaignTable from "@/components/ads/CampaignTable"; import ControlBox from "@/components/common/controlbox/ControlBox"; import Modal from "@/components/common/modal/Modal"; import ModalContent from "@/components/common/modal/ModalContent"; +import { getCampaignList } from "@/api/ads/ads"; +import { getMyWorkspaces } from "@/api/workspace/org"; import WarningIcon from "@/assets/icon/workspace/message-circle-warning.svg?react"; export default function AdsListPage() { + const [campaigns, setCampaigns] = useState([]); + const [isLoading, setIsLoading] = useState(true); + + useEffect(() => { + const initData = async () => { + try { + setIsLoading(true); + const workspaces = await getMyWorkspaces(); + + if (workspaces && workspaces.length > 0) { + // 워크스페이스 ID 임시 지정 -> 추후 선택한 워크스페이스 api 연결 예정 + const TemporaryOrg = workspaces[2] || workspaces[0]; + const orgId = TemporaryOrg.orgId; + + console.log("orgId: ", orgId); + + // 캠페인 목록 API 호출 + const campaignData = await getCampaignList(orgId); + console.log("campaign data: ", campaignData); + + setCampaigns(campaignData); + } else { + toast.error("조직이 없습니다."); + } + } catch (error) { + console.log(error); + toast.error("데이터를 불러오는 중 오류가 발생하였습니다."); + } finally { + setIsLoading(false); + } + }; + initData(); + }, []); + const navigate = useNavigate(); const [stopAllOpen, setStopAllOpen] = useState(false); From d3d8387c89aac88570474b86d92b436f959bd9f2 Mon Sep 17 00:00:00 2001 From: YermIm Date: Mon, 16 Mar 2026 03:03:22 +0900 Subject: [PATCH 02/13] =?UTF-8?q?feat:=20=EC=BA=A0=ED=8E=98=EC=9D=B8=20?= =?UTF-8?q?=EB=AA=A9=EB=A1=9D=20interface=20=EC=88=98=EC=A0=95=20=EB=B0=8F?= =?UTF-8?q?=20=EB=8D=B0=EC=9D=B4=ED=84=B0=20=EC=97=B0=EB=8F=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/api/ads/ads.ts | 16 ++++++++---- src/components/ads/CampaignRow.tsx | 38 +++++++++++++++------------- src/components/ads/CampaignTable.tsx | 32 +++++++++++++++-------- src/pages/ads/list/AdsListPage.tsx | 12 ++++++--- src/types/ads/campaign.ts | 35 +++++++++++++------------ 5 files changed, 81 insertions(+), 52 deletions(-) diff --git a/src/api/ads/ads.ts b/src/api/ads/ads.ts index 602f7113..6f70a182 100644 --- a/src/api/ads/ads.ts +++ b/src/api/ads/ads.ts @@ -5,9 +5,15 @@ import type { IApiResult } from "../common/common"; import { axiosInstance } from "@/lib/axiosInstance"; export const getCampaignList = async (orgId: number): Promise => { - const { data } = await axiosInstance.get>( - `/api/project/${orgId}`, - ); - - return data.data; + const { data } = await axiosInstance.get< + IApiResult<{ projects: ICampaign[] }> + >(`/api/project/${orgId}`); + return data.data.projects; }; + +// export const getCampaignDetail = async (projectId: number): Promise => { +// const { data } = await axiosInstance.get>( +// `/api/project/detail/${projectId}` +// ); +// return data.data; +// }; diff --git a/src/components/ads/CampaignRow.tsx b/src/components/ads/CampaignRow.tsx index 256fdc5d..edec907b 100644 --- a/src/components/ads/CampaignRow.tsx +++ b/src/components/ads/CampaignRow.tsx @@ -10,11 +10,11 @@ import KakaoLogo from "@/assets/icon/ads/kakao-circle.svg?react"; import NaverLogo from "@/assets/icon/ads/naver-circle.svg?react"; interface ICampaignRowProps { - platforms: TPlatform[]; + projectId: number; name: string; + providers: TPlatform[]; status: TCampaignStatus; - statusText: string; - progress: number; + budgetUsageRate: number; onClick?: () => void; } @@ -25,11 +25,11 @@ const LogoMap: Record = { }; export default function CampaignRow({ - platforms, + projectId, name, + providers, status, - statusText, - progress, + budgetUsageRate, onClick, }: ICampaignRowProps) { return ( @@ -47,14 +47,18 @@ export default function CampaignRow({ > {/* 플랫폼 */}
- {platforms.map((p, idx) => ( -
- {LogoMap[p]} -
- ))} + {providers && providers.length > 0 ? ( + providers.map((p, idx) => ( +
+ {LogoMap[p]} +
+ )) + ) : ( +
미연결
+ )}
{/* 캠페인 명 */} @@ -63,15 +67,15 @@ export default function CampaignRow({ {/* 동기화 상태 */} -
+ {/*
{statusText} -
+
*/} {/* 예산 소진 현황 */}
- +
); diff --git a/src/components/ads/CampaignTable.tsx b/src/components/ads/CampaignTable.tsx index d39e0857..ad75fd88 100644 --- a/src/components/ads/CampaignTable.tsx +++ b/src/components/ads/CampaignTable.tsx @@ -1,12 +1,18 @@ +import type { ICampaign } from "@/types/ads/campaign"; + import CampaignRow from "./CampaignRow"; -import { MOCK_CAMPAIGNS } from "@/pages/ads/list/campaign.mock"; +//import { MOCK_CAMPAIGNS } from "@/pages/ads/list/campaign.mock"; interface ICampaignTableProps { + campaigns: ICampaign[]; onRowClick?: (id: number) => void; } -export default function CampaignTable({ onRowClick }: ICampaignTableProps) { +export default function CampaignTable({ + campaigns, + onRowClick, +}: ICampaignTableProps) { return (
@@ -14,7 +20,7 @@ export default function CampaignTable({ onRowClick }: ICampaignTableProps) {
플랫폼
캠페인 명
-
동기화 상태
+ {/*
동기화 상태
*/}
예산 소진 현황
@@ -22,13 +28,19 @@ export default function CampaignTable({ onRowClick }: ICampaignTableProps) { {/* Row */}
    - {MOCK_CAMPAIGNS.map((campaign) => ( - onRowClick?.(campaign.id)} - /> - ))} + {campaigns && campaigns.length > 0 ? ( + campaigns.map((project) => ( + onRowClick?.(project.projectId)} + /> + )) + ) : ( +
    + 현재 캠페인이 없습니다. +
    + )}
diff --git a/src/pages/ads/list/AdsListPage.tsx b/src/pages/ads/list/AdsListPage.tsx index 8693697b..16f23ba3 100644 --- a/src/pages/ads/list/AdsListPage.tsx +++ b/src/pages/ads/list/AdsListPage.tsx @@ -16,6 +16,7 @@ import WarningIcon from "@/assets/icon/workspace/message-circle-warning.svg?reac export default function AdsListPage() { const [campaigns, setCampaigns] = useState([]); const [isLoading, setIsLoading] = useState(true); + const [currentOrgId, setCurrentOrgId] = useState(null); //orgId useEffect(() => { const initData = async () => { @@ -25,14 +26,14 @@ export default function AdsListPage() { if (workspaces && workspaces.length > 0) { // 워크스페이스 ID 임시 지정 -> 추후 선택한 워크스페이스 api 연결 예정 - const TemporaryOrg = workspaces[2] || workspaces[0]; + const TemporaryOrg = workspaces[0]; const orgId = TemporaryOrg.orgId; - console.log("orgId: ", orgId); + setCurrentOrgId(orgId); // 캠페인 목록 API 호출 const campaignData = await getCampaignList(orgId); - console.log("campaign data: ", campaignData); + console.log(campaignData); setCampaigns(campaignData); } else { @@ -99,7 +100,10 @@ export default function AdsListPage() { {/* 테이블 */}
- handleCampaignClick(id)} /> + handleCampaignClick(id)} + />
{/* 하단 배너 */}
diff --git a/src/types/ads/campaign.ts b/src/types/ads/campaign.ts index cc6bc651..03454eff 100644 --- a/src/types/ads/campaign.ts +++ b/src/types/ads/campaign.ts @@ -1,6 +1,24 @@ export type TPlatform = "kakao" | "google" | "naver"; -export type TCampaignStatus = "inactive" | "syncing" | "success"; +export type TCampaignStatus = "ON_GOING" | "PAUSED" | "OVER"; +// Campaign List +export interface ICampaign { + projectId: number; + name: string; + providers: TPlatform[]; + status: TCampaignStatus; + description?: string; + budgetUsageRate: number; +} + +// Campaign Detail +export interface ICampaignDetail extends ICampaign { + budget: number; + createAt: string; + ads: IAd[]; +} + +// Ad List export interface IAd { id: number; name: string; @@ -11,18 +29,3 @@ export interface IAd { tags: string[]; link: string; } - -export interface ICampaign { - id: number; - name: string; - platforms: TPlatform[]; - status: TCampaignStatus; - statusText: string; - progress: number; - runStatus: "running" | "stopped"; - runStatusText: string; - budget: string; - startDate: string; - description?: string; - ads: IAd[]; -} From baf64cab3c589f3a1b35fd1b66bd9315da0020ce Mon Sep 17 00:00:00 2001 From: YermIm Date: Mon, 16 Mar 2026 03:22:48 +0900 Subject: [PATCH 03/13] =?UTF-8?q?fix:=20=ED=85=8C=EC=9D=B4=EB=B8=94=20?= =?UTF-8?q?=EC=86=8D=EC=84=B1=20=EB=B9=84=EC=9C=A8=20=EC=A1=B0=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/ads/CampaignRow.tsx | 3 +-- src/components/ads/CampaignTable.tsx | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/src/components/ads/CampaignRow.tsx b/src/components/ads/CampaignRow.tsx index edec907b..d4ae47c1 100644 --- a/src/components/ads/CampaignRow.tsx +++ b/src/components/ads/CampaignRow.tsx @@ -2,7 +2,6 @@ import type { ReactNode } from "react"; import type { TCampaignStatus, TPlatform } from "@/types/ads/campaign"; -import Badge from "../common/badge/Badge"; import ProgressBar from "../common/progressbar/ProgressBar"; import GoogleLogo from "@/assets/icon/ads/google-circle.svg?react"; @@ -62,7 +61,7 @@ export default function CampaignRow({
{/* 캠페인 명 */} -
+
{name}
diff --git a/src/components/ads/CampaignTable.tsx b/src/components/ads/CampaignTable.tsx index ad75fd88..1390476e 100644 --- a/src/components/ads/CampaignTable.tsx +++ b/src/components/ads/CampaignTable.tsx @@ -19,7 +19,7 @@ export default function CampaignTable({ {/* Table Header */}
플랫폼
-
캠페인 명
+
캠페인 명
{/*
동기화 상태
*/}
예산 소진 현황 From 3099c08990781e80cc1048e196061e0cda04ab27 Mon Sep 17 00:00:00 2001 From: YermIm Date: Mon, 16 Mar 2026 04:24:35 +0900 Subject: [PATCH 04/13] =?UTF-8?q?feat:=20=EC=BA=A0=ED=8E=98=EC=9D=B8=20?= =?UTF-8?q?=EC=9A=B4=EC=98=81=20=EC=83=81=ED=83=9C=EB=B3=84=20=EC=B2=98?= =?UTF-8?q?=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/ads/CampaignRow.tsx | 14 +++++++++++--- src/components/ads/CampaignTable.tsx | 16 +++++++++------- 2 files changed, 20 insertions(+), 10 deletions(-) diff --git a/src/components/ads/CampaignRow.tsx b/src/components/ads/CampaignRow.tsx index d4ae47c1..65485fc7 100644 --- a/src/components/ads/CampaignRow.tsx +++ b/src/components/ads/CampaignRow.tsx @@ -31,9 +31,13 @@ export default function CampaignRow({ budgetUsageRate, onClick, }: ICampaignRowProps) { + const isPaused = status == "PAUSED"; return (
  • -
    {name}
    +
    + {name} +
  • {/* 동기화 상태 */} @@ -73,7 +81,7 @@ export default function CampaignRow({
    */} {/* 예산 소진 현황 */} -
    +
    diff --git a/src/components/ads/CampaignTable.tsx b/src/components/ads/CampaignTable.tsx index 1390476e..1fc479dd 100644 --- a/src/components/ads/CampaignTable.tsx +++ b/src/components/ads/CampaignTable.tsx @@ -29,13 +29,15 @@ export default function CampaignTable({ {/* Row */}
      {campaigns && campaigns.length > 0 ? ( - campaigns.map((project) => ( - onRowClick?.(project.projectId)} - /> - )) + campaigns + .filter((project) => project.status !== "OVER") + .map((project) => ( + onRowClick?.(project.projectId)} + /> + )) ) : (
      현재 캠페인이 없습니다. From 9c243c3fcaf10dee62818fe2fe75955a4bebc7c6 Mon Sep 17 00:00:00 2001 From: YermIm Date: Mon, 16 Mar 2026 05:44:38 +0900 Subject: [PATCH 05/13] =?UTF-8?q?feat:=20=EC=A0=84=EC=B2=B4=20=EC=BA=A0?= =?UTF-8?q?=ED=8E=98=EC=9D=B8=20=EC=A4=91=EB=8B=A8/=EC=9E=AC=EA=B0=9C=20AP?= =?UTF-8?q?I=20=EC=97=B0=EB=8F=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/api/ads/ads.ts | 14 +++--- src/pages/ads/list/AdsListPage.tsx | 70 ++++++++++++++++++++++-------- 2 files changed, 60 insertions(+), 24 deletions(-) diff --git a/src/api/ads/ads.ts b/src/api/ads/ads.ts index 6f70a182..2142748d 100644 --- a/src/api/ads/ads.ts +++ b/src/api/ads/ads.ts @@ -11,9 +11,11 @@ export const getCampaignList = async (orgId: number): Promise => { return data.data.projects; }; -// export const getCampaignDetail = async (projectId: number): Promise => { -// const { data } = await axiosInstance.get>( -// `/api/project/detail/${projectId}` -// ); -// return data.data; -// }; +export const updateAllCampaignStatus = async ( + orgId: number, + status: "ON_GOING" | "PAUSED", +): Promise => { + await axiosInstance.patch(`/api/project/${orgId}/status`, null, { + params: { status }, + }); +}; diff --git a/src/pages/ads/list/AdsListPage.tsx b/src/pages/ads/list/AdsListPage.tsx index 16f23ba3..50b73d82 100644 --- a/src/pages/ads/list/AdsListPage.tsx +++ b/src/pages/ads/list/AdsListPage.tsx @@ -9,7 +9,7 @@ import ControlBox from "@/components/common/controlbox/ControlBox"; import Modal from "@/components/common/modal/Modal"; import ModalContent from "@/components/common/modal/ModalContent"; -import { getCampaignList } from "@/api/ads/ads"; +import { getCampaignList, updateAllCampaignStatus } from "@/api/ads/ads"; import { getMyWorkspaces } from "@/api/workspace/org"; import WarningIcon from "@/assets/icon/workspace/message-circle-warning.svg?react"; @@ -33,14 +33,12 @@ export default function AdsListPage() { // 캠페인 목록 API 호출 const campaignData = await getCampaignList(orgId); - console.log(campaignData); setCampaigns(campaignData); } else { toast.error("조직이 없습니다."); } - } catch (error) { - console.log(error); + } catch { toast.error("데이터를 불러오는 중 오류가 발생하였습니다."); } finally { setIsLoading(false); @@ -65,28 +63,49 @@ export default function AdsListPage() { navigate("/ads/campaignGroup"); }; - const onStopAll = () => { + const onStopAll = async () => { + if (!currentOrgId) return; setIsStopping(true); try { + await updateAllCampaignStatus(currentOrgId, "PAUSED"); + + setCampaigns((prev) => + prev.map((c) => + c.status === "ON_GOING" ? { ...c, status: "PAUSED" } : c, + ), + ); toast.success("전체 캠페인의 모든 광고 노출이 중단되었습니다."); setStopAllOpen(false); + } catch { + toast.error("중단 처리에 실패하였습니다."); } finally { setIsStopping(false); } }; - const onResumeAll = () => { + const onResumeAll = async () => { + if (!currentOrgId) return; setIsResuming(true); try { + await updateAllCampaignStatus(currentOrgId, "ON_GOING"); + setCampaigns((prev) => + prev.map((c) => + c.status === "PAUSED" ? { ...c, status: "ON_GOING" } : c, + ), + ); toast.success("전체 캠페인의 광고 노출이 재개되었습니다."); setResumeOpen(false); + } catch { + toast.error("재개 처리에 실패하였습니다."); } finally { setIsResuming(false); } }; + const hasActiveCampaign = campaigns.some((c) => c.status === "ON_GOING"); + return (
      @@ -119,18 +138,33 @@ export default function AdsListPage() { buttonSize="big" buttonClassName="font-body1" /> - setStopAllOpen(true)} - buttonDisabled={isStopping} - containerClassName="bg-status-red/7 border-status-red px-6 py-4 min-w-[650px] shrink-0" - titleClassName="text-status-red font-heading3" - descriptionClassName="font-body2 text-text-sub" - buttonSize="big" - buttonClassName="font-body1 bg-status-red" - /> + {hasActiveCampaign ? ( + setStopAllOpen(true)} + buttonDisabled={isStopping} + containerClassName="bg-status-red/7 border-status-red px-6 py-4 min-w-[650px] shrink-0" + titleClassName="text-status-red font-heading3" + descriptionClassName="font-body2 text-text-sub" + buttonSize="big" + buttonClassName="font-body1 bg-status-red" + /> + ) : ( + setResumeOpen(true)} + buttonDisabled={isResuming} + containerClassName="bg-status-blue/7 border-status-blue px-6 py-4 min-w-[650px] shrink-0" + titleClassName="text-status-blue font-heading3" + descriptionClassName="font-body2 text-text-sub" + buttonSize="big" + buttonClassName="font-body1 bg-status-blue" + /> + )}
    From 5ed6a7c4545a24f55e80ab5fb087a305ae859657 Mon Sep 17 00:00:00 2001 From: YermIm Date: Mon, 16 Mar 2026 06:19:04 +0900 Subject: [PATCH 06/13] =?UTF-8?q?fix:=20CampaignRow=20=EC=82=AC=EC=9A=A9?= =?UTF-8?q?=ED=95=98=EC=A7=80=20=EC=95=8A=EB=8A=94=20=EB=B3=80=EC=88=98=20?= =?UTF-8?q?=EC=A0=9C=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/ads/CampaignRow.tsx | 1 - 1 file changed, 1 deletion(-) diff --git a/src/components/ads/CampaignRow.tsx b/src/components/ads/CampaignRow.tsx index 65485fc7..3b8f6290 100644 --- a/src/components/ads/CampaignRow.tsx +++ b/src/components/ads/CampaignRow.tsx @@ -24,7 +24,6 @@ const LogoMap: Record = { }; export default function CampaignRow({ - projectId, name, providers, status, From 29f29d33510e4754bed0cae8fb34a0ddc3a1aa17 Mon Sep 17 00:00:00 2001 From: YermIm Date: Mon, 16 Mar 2026 06:29:19 +0900 Subject: [PATCH 07/13] =?UTF-8?q?fix:=20=EB=A1=9C=EB=94=A9=20=EC=B2=98?= =?UTF-8?q?=EB=A6=AC=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/pages/ads/list/AdsListPage.tsx | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/pages/ads/list/AdsListPage.tsx b/src/pages/ads/list/AdsListPage.tsx index 50b73d82..9f640172 100644 --- a/src/pages/ads/list/AdsListPage.tsx +++ b/src/pages/ads/list/AdsListPage.tsx @@ -106,6 +106,16 @@ export default function AdsListPage() { const hasActiveCampaign = campaigns.some((c) => c.status === "ON_GOING"); + if (isLoading) { + return ( +
    +

    + 데이터를 불러오는 중입니다... +

    +
    + ); + } + return (
    From b6c1d72e3c829005eee6dee8b57cb943c7671b35 Mon Sep 17 00:00:00 2001 From: YermIm Date: Mon, 16 Mar 2026 06:53:50 +0900 Subject: [PATCH 08/13] =?UTF-8?q?fix:=20=EC=9D=B8=ED=84=B0=ED=8E=98?= =?UTF-8?q?=EC=9D=B4=EC=8A=A4=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/pages/ads/list/CampaignDetail.tsx | 22 ++++++--- src/pages/ads/list/campaign.mock.ts | 65 +++++++++------------------ src/types/ads/campaign.ts | 2 +- 3 files changed, 38 insertions(+), 51 deletions(-) diff --git a/src/pages/ads/list/CampaignDetail.tsx b/src/pages/ads/list/CampaignDetail.tsx index 0ab34b62..f83358c3 100644 --- a/src/pages/ads/list/CampaignDetail.tsx +++ b/src/pages/ads/list/CampaignDetail.tsx @@ -1,6 +1,8 @@ import { useState } from "react"; import { toast } from "sonner"; +import type { ICampaignDetail } from "@/types/ads/campaign"; + import AdListTable from "@/components/ads/AdListTable"; import CampaignInfoCard from "@/components/ads/CampaignInfoCard"; import PlatformCard from "@/components/ads/PlatformCard"; @@ -14,7 +16,10 @@ import { MOCK_CAMPAIGNS } from "./campaign.mock"; import WarningIcon from "@/assets/icon/workspace/message-circle-warning.svg?react"; export default function CampaignDetail() { - const data = MOCK_CAMPAIGNS[0]; + const data = MOCK_CAMPAIGNS[0] as ICampaignDetail; + + // const isPaused = data.status === "PAUSED"; + // const isOngoing = data.status === "ON_GOING"; const [stopOpen, setStopOpen] = useState(false); const [isStopping, setIsStopping] = useState(false); @@ -50,8 +55,15 @@ export default function CampaignDetail() {

    {data.name}

    - - {data.runStatusText} + + {data.status === "ON_GOING" + ? "운영 중" + : data.status === "PAUSED" + ? "중단" + : "종료"}
    @@ -64,8 +76,8 @@ export default function CampaignDetail() { {/* card section */}
    Date: Mon, 16 Mar 2026 07:27:39 +0900 Subject: [PATCH 09/13] =?UTF-8?q?fix:=20=EC=BA=A0=ED=8E=98=EC=9D=B8=20?= =?UTF-8?q?=EB=AA=A9=EB=A1=9D=20=ED=95=84=ED=84=B0=EB=A7=81=20=EB=B2=84?= =?UTF-8?q?=EA=B7=B8=20=EC=88=98=EC=A0=95=20=EB=B0=8F=20HTML=20=EA=B5=AC?= =?UTF-8?q?=EC=A1=B0=20=EA=B0=9C=EC=84=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/ads/CampaignTable.tsx | 28 +++++++++++++++------------- 1 file changed, 15 insertions(+), 13 deletions(-) diff --git a/src/components/ads/CampaignTable.tsx b/src/components/ads/CampaignTable.tsx index 1fc479dd..512469cf 100644 --- a/src/components/ads/CampaignTable.tsx +++ b/src/components/ads/CampaignTable.tsx @@ -13,6 +13,10 @@ export default function CampaignTable({ campaigns, onRowClick, }: ICampaignTableProps) { + const visibleCampaigns = campaigns.filter( + (project) => project.status !== "OVER", + ); + return (
    @@ -28,20 +32,18 @@ export default function CampaignTable({ {/* Row */}
      - {campaigns && campaigns.length > 0 ? ( - campaigns - .filter((project) => project.status !== "OVER") - .map((project) => ( - onRowClick?.(project.projectId)} - /> - )) + {visibleCampaigns.length > 0 ? ( + visibleCampaigns.map((project) => ( + onRowClick?.(project.projectId)} + /> + )) ) : ( -
      - 현재 캠페인이 없습니다. -
      +
    • + 현재 표시할 캠페인이 없습니다. +
    • )}
    From 028ffcbba406c5a70f4b5ac5d24d0843a6003367 Mon Sep 17 00:00:00 2001 From: YermIm Date: Mon, 16 Mar 2026 21:12:00 +0900 Subject: [PATCH 10/13] =?UTF-8?q?feat:=20=EC=BA=A0=ED=8E=98=EC=9D=B8=20?= =?UTF-8?q?=EC=97=86=EC=9D=84=20=EC=8B=9C=20=EC=A0=84=EC=B2=B4=20=EC=BA=A0?= =?UTF-8?q?=ED=8E=98=EC=9D=B8=20controlBox=20=EC=88=A8=EA=B9=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/pages/ads/list/AdsListPage.tsx | 57 ++++++++++++++++-------------- 1 file changed, 30 insertions(+), 27 deletions(-) diff --git a/src/pages/ads/list/AdsListPage.tsx b/src/pages/ads/list/AdsListPage.tsx index 9f640172..b05199a5 100644 --- a/src/pages/ads/list/AdsListPage.tsx +++ b/src/pages/ads/list/AdsListPage.tsx @@ -104,6 +104,8 @@ export default function AdsListPage() { } }; + const hasCampaigns = campaigns.length > 0; + const hasActiveCampaign = campaigns.some((c) => c.status === "ON_GOING"); if (isLoading) { @@ -148,33 +150,34 @@ export default function AdsListPage() { buttonSize="big" buttonClassName="font-body1" /> - {hasActiveCampaign ? ( - setStopAllOpen(true)} - buttonDisabled={isStopping} - containerClassName="bg-status-red/7 border-status-red px-6 py-4 min-w-[650px] shrink-0" - titleClassName="text-status-red font-heading3" - descriptionClassName="font-body2 text-text-sub" - buttonSize="big" - buttonClassName="font-body1 bg-status-red" - /> - ) : ( - setResumeOpen(true)} - buttonDisabled={isResuming} - containerClassName="bg-status-blue/7 border-status-blue px-6 py-4 min-w-[650px] shrink-0" - titleClassName="text-status-blue font-heading3" - descriptionClassName="font-body2 text-text-sub" - buttonSize="big" - buttonClassName="font-body1 bg-status-blue" - /> - )} + {hasCampaigns && + (hasActiveCampaign ? ( + setStopAllOpen(true)} + buttonDisabled={isStopping} + containerClassName="bg-status-red/7 border-status-red px-6 py-4 min-w-[650px] shrink-0" + titleClassName="text-status-red font-heading3" + descriptionClassName="font-body2 text-text-sub" + buttonSize="big" + buttonClassName="font-body1 bg-status-red" + /> + ) : ( + setResumeOpen(true)} + buttonDisabled={isResuming} + containerClassName="bg-status-blue/7 border-status-blue px-6 py-4 min-w-[650px] shrink-0" + titleClassName="text-status-blue font-heading3" + descriptionClassName="font-body2 text-text-sub" + buttonSize="big" + buttonClassName="font-body1 bg-status-blue" + /> + ))}
    From 6ba9e5cf14c555a051a4f10b29d00d0b97aa4359 Mon Sep 17 00:00:00 2001 From: YermIm Date: Mon, 16 Mar 2026 21:25:31 +0900 Subject: [PATCH 11/13] =?UTF-8?q?fix:=20IApiResult=20=EC=82=AD=EC=A0=9C=20?= =?UTF-8?q?=EB=B0=8F=20=20ICommonResponse=20=EA=B5=90=EC=B2=B4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/api/ads/ads.ts | 5 ++--- src/api/common/common.ts | 5 ----- 2 files changed, 2 insertions(+), 8 deletions(-) delete mode 100644 src/api/common/common.ts diff --git a/src/api/ads/ads.ts b/src/api/ads/ads.ts index 2142748d..92171319 100644 --- a/src/api/ads/ads.ts +++ b/src/api/ads/ads.ts @@ -1,12 +1,11 @@ import type { ICampaign } from "@/types/ads/campaign"; - -import type { IApiResult } from "../common/common"; +import type { ICommonResponse } from "@/types/common/common"; import { axiosInstance } from "@/lib/axiosInstance"; export const getCampaignList = async (orgId: number): Promise => { const { data } = await axiosInstance.get< - IApiResult<{ projects: ICampaign[] }> + ICommonResponse<{ projects: ICampaign[] }> >(`/api/project/${orgId}`); return data.data.projects; }; diff --git a/src/api/common/common.ts b/src/api/common/common.ts deleted file mode 100644 index ce469537..00000000 --- a/src/api/common/common.ts +++ /dev/null @@ -1,5 +0,0 @@ -export interface IApiResult { - status: number; - message: string; - data: T; -} From de8904565d9ec662337297d6c4f181480e56e1cb Mon Sep 17 00:00:00 2001 From: YermIm Date: Mon, 16 Mar 2026 22:00:56 +0900 Subject: [PATCH 12/13] =?UTF-8?q?fix:=20provider=20=EC=86=8C=EB=AC=B8?= =?UTF-8?q?=EC=9E=90=20=EB=B3=80=ED=99=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/ads/CampaignRow.tsx | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/components/ads/CampaignRow.tsx b/src/components/ads/CampaignRow.tsx index 3b8f6290..e5e92362 100644 --- a/src/components/ads/CampaignRow.tsx +++ b/src/components/ads/CampaignRow.tsx @@ -30,6 +30,8 @@ export default function CampaignRow({ budgetUsageRate, onClick, }: ICampaignRowProps) { + console.log({ providers }); + const isPaused = status == "PAUSED"; return (
  • - {LogoMap[p]} + {LogoMap[p.toLowerCase() as TPlatform]}
  • )) ) : ( From b5d4f9def684513dffd594ff4dd680deb4e09cdb Mon Sep 17 00:00:00 2001 From: YermIm Date: Mon, 16 Mar 2026 22:19:47 +0900 Subject: [PATCH 13/13] =?UTF-8?q?fix:=20=ED=95=84=EB=93=9C=20=EB=B9=84?= =?UTF-8?q?=EC=9C=A8=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/ads/CampaignRow.tsx | 6 ++---- src/components/ads/CampaignTable.tsx | 4 ++-- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/src/components/ads/CampaignRow.tsx b/src/components/ads/CampaignRow.tsx index e5e92362..2ef66c70 100644 --- a/src/components/ads/CampaignRow.tsx +++ b/src/components/ads/CampaignRow.tsx @@ -30,8 +30,6 @@ export default function CampaignRow({ budgetUsageRate, onClick, }: ICampaignRowProps) { - console.log({ providers }); - const isPaused = status == "PAUSED"; return (
  • {/* 플랫폼 */} -
    +
    {providers && providers.length > 0 ? ( providers.map((p, idx) => (
    */} {/* 예산 소진 현황 */} -
    +
  • diff --git a/src/components/ads/CampaignTable.tsx b/src/components/ads/CampaignTable.tsx index 512469cf..8b6d6549 100644 --- a/src/components/ads/CampaignTable.tsx +++ b/src/components/ads/CampaignTable.tsx @@ -22,10 +22,10 @@ export default function CampaignTable({
    {/* Table Header */}
    -
    플랫폼
    +
    플랫폼
    캠페인 명
    {/*
    동기화 상태
    */} -
    +
    예산 소진 현황