diff --git a/src/assets/icon/chevron/chervon-left.svg b/src/assets/icon/chevron/chervon-left.svg index dc7b444e..eca1cf74 100644 --- a/src/assets/icon/chevron/chervon-left.svg +++ b/src/assets/icon/chevron/chervon-left.svg @@ -1,3 +1,3 @@ - + diff --git a/src/components/ads/AdDetailContent.tsx b/src/components/ads/AdDetailContent.tsx index 93c68d33..63452183 100644 --- a/src/components/ads/AdDetailContent.tsx +++ b/src/components/ads/AdDetailContent.tsx @@ -39,14 +39,8 @@ export default function AdDetailContent({ }); const trackControl = useControlModal({ - successMessage: "트래킹이 활성화되었습니다.", - errorMessage: "트래킹 활성화에 실패했습니다.", - onSuccess: refetchAds, - }); - - const stopTrackControl = useControlModal({ - successMessage: "트래킹이 중단되었습니다.", - errorMessage: "트래킹 중단에 실패했습니다.", + successMessage: "트래킹 링크가 발급되었습니다.", + errorMessage: "트래킹 링크 발급에 실패했습니다.", onSuccess: refetchAds, }); @@ -75,17 +69,19 @@ export default function AdDetailContent({ }; return ( -
-
-
- -
- {/* description */} +
+
+ {/* description */} +
+ 광고 소재

{ad.description}

+
- {/* tags */} + {/* tags */} +
+ 타겟
{tags.length > 0 ? ( tags.map((tag, idx) => ( @@ -93,7 +89,7 @@ export default function AdDetailContent({ key={idx} variant="running" size="sm" - className="border border-bg-text-sub bg-bg-surface text-text-sub font-normal px-3" + className="border border-bg-disabled bg-white text-text-sub px-3" > #{tag} @@ -104,10 +100,13 @@ export default function AdDetailContent({ )}
+
- {/* link */} -
-
+ {/* link */} +
+ 랜딩 URL +
+
{ad.landingUrl} @@ -126,35 +125,33 @@ export default function AdDetailContent({
-
-
-
+
+
handleCopy(ad.trackingUrl!) : trackControl.openModal } buttonDisabled={false} - containerClassName={`${isTrackingActive ? "bg-status-red/7 border-status-red" : "bg-chart-3/7 border-chart-3"} px-6 py-4 tablet:flex-col tablet:items-start tablet:gap-4`} - titleClassName={`${isTrackingActive ? "text-status-red" : "text-chart-3"} font-heading3`} + containerClassName="bg-status-blue/7 border-status-blue px-6 py-4" + titleClassName="text-status-blue font-heading4 -mb-1" descriptionClassName="font-caption text-text-sub" - buttonSize="big" - buttonClassName={`${ - isTrackingActive ? "bg-status-red" : "bg-chart-3" - } font-body1 tablet:w-full `} + buttonSize="small" + buttonClassName="bg-status-blue font-body2 tablet:w-full" /> +
- {/* 트래킹 활성화 */} + {/* 트래킹 링크 발급 */} } - title="트래킹을 활성화하시겠습니까?" + title="트래킹 링크를 발급하시겠습니까?" description={ <> - 실시간 광고 성과 데이터를 수집하여 + 발급된 링크를 복사하여 광고 플랫폼에 등록하면
- 분석 리포트에 즉시 반영합니다. + 트래킹을 활성화할 수 있습니다. } - buttonText="시작하기" + buttonText="발급하기" onConfirm={() => trackControl.handleConfirm(async () => { if (!ad.landingUrl) { + toast.error( + "광고에 등록된 랜딩 URL이 없어 발급이 불가능합니다.", + ); throw new Error("랜딩 URL이 없습니다."); } await createTrackingUrl(Number(orgId), ad.id, ad.landingUrl); @@ -220,27 +220,6 @@ export default function AdDetailContent({ />
- {/* 트래킹 중단 */} - - } - title="트래킹을 중단하시겠습니까?" - description="데이터 수집이 중단되어 분석 리포트에 공백이 생길 수 있습니다." - buttonText="중단하기" - onConfirm={() => - stopTrackControl.handleConfirm(async () => { - /* API 호출 */ - }) - } - isLoading={stopTrackControl.isLoading} - variant="danger" - /> - - {/* 광고 소재 중단 */} {/* status badge */} -
+
- {/* title */} -
- {name} -
- {/* platform */} -
+
{LogoMap[platform]}
+ {/* title */} +
+ {name} +
+ {/* chevron */} -
+
{isLoading ? "처리 중.." : buttonText} diff --git a/src/pages/ads/list/CampaignDetail.tsx b/src/pages/ads/list/CampaignDetail.tsx index 1e545975..02dfd716 100644 --- a/src/pages/ads/list/CampaignDetail.tsx +++ b/src/pages/ads/list/CampaignDetail.tsx @@ -1,4 +1,5 @@ -import { useParams } from "react-router-dom"; +import React from "react"; +import { useNavigate, useParams } from "react-router-dom"; import type { TPlatform } from "@/types/ads/campaign"; @@ -7,15 +8,23 @@ import { useCampaignDetail } from "@/hooks/ads/useCampaignDetail"; import { useControlModal } from "@/hooks/ads/useControlModal"; import AdListTable from "@/components/ads/AdListTable"; -import CampaignInfoCard from "@/components/ads/CampaignInfoCard"; -import PlatformCard from "@/components/ads/PlatformCard"; import Badge from "@/components/common/badge/Badge"; import ControlBox from "@/components/common/controlbox/ControlBox"; import Modal from "@/components/common/modal/Modal"; import ModalContent from "@/components/common/modal/ModalContent"; import { updateCampaignStatus } from "@/api/ads/ads"; +import LeftChevronIcon from "@/assets/icon/chevron/chervon-left.svg?react"; import WarnCircleIcon from "@/assets/icon/common/warn-circle.svg?react"; +import GoogleLogo from "@/assets/logo/social-logo/circle/google-circle.svg?react"; +import KakaoLogo from "@/assets/logo/social-logo/circle/kakao-circle.svg?react"; +import NaverLogo from "@/assets/logo/social-logo/circle/naver-circle.svg?react"; + +const LogoMap: Record = { + kakao: , + google: , + naver: , +}; export default function CampaignDetail() { const { orgId, projectId } = useParams<{ @@ -29,6 +38,8 @@ export default function CampaignDetail() { projectId ? Number(projectId) : null, ); + const navigate = useNavigate(); + const stopControl = useControlModal({ successMessage: "해당 캠페인의 모든 광고 운영이 중단되었습니다.", errorMessage: "중단 처리에 실패하였습니다.", @@ -67,46 +78,66 @@ export default function CampaignDetail() { return (
-
+
{/* header */}
-
-

{data.name}

- - {data.status === "ON_GOING" - ? "운영 중" - : data.status === "PAUSED" - ? "중단" - : "종료"} - -
- {data.description && ( -
-

- {data.description} -

+ + +
+
+
+

{data.name}

+ + {data.status === "ON_GOING" + ? "운영 중" + : data.status === "PAUSED" + ? "중단" + : "종료"} + +
+ + {data.createdAt.replaceAll("-", ".")} 등록 +
- )} -
- {/* card section */} -
- - p.toLowerCase() as TPlatform, +
+ 예산 {data.budget.toLocaleString()}원 + · +
+ {data.providers.map((provider) => { + const platform = provider.toLowerCase() as TPlatform; + return ( +
+ {LogoMap[platform] || null} +
+ ); + })} +
+
+ + {data.description && ( +
+

+ {data.description} +

+
)} - className="flex-1 min-w-[320px] w-full" - /> -
+
+
{/* ads list */}