diff --git a/app/routes.ts b/app/routes.ts index 3ce45f9..abd3bc8 100644 --- a/app/routes.ts +++ b/app/routes.ts @@ -55,6 +55,7 @@ export default [ route("calendar", "routes/business/calendar/route.tsx"), route("proposal", "routes/business/proposal/route.tsx"), route("rejection", "routes/business/rejection/route.tsx"), + route("campaign/:campaignId", "routes/business/campaign/$campaignId.tsx"), ]), @@ -94,10 +95,10 @@ export default [ ]), // Campaign detail route (without main layout) - route( + /*route( "business/campaign/:campaignId", "routes/business/campaign/$campaignId.tsx", - ), + ),*/ // 404 Catch-all route("*", "routes/not-found.tsx"), diff --git a/app/routes/business/campaign/$campaignId.tsx b/app/routes/business/campaign/$campaignId.tsx index 724768e..6d76a1d 100644 --- a/app/routes/business/campaign/$campaignId.tsx +++ b/app/routes/business/campaign/$campaignId.tsx @@ -1,25 +1,30 @@ -import { useParams, useNavigate } from "react-router"; +import { useParams, useNavigate, useSearchParams } from "react-router"; import { useState, useEffect } from "react"; -import Button from "../../../components/common/Button"; import LoadingSpinner from "../../../components/common/LoadingSpinner"; - import CampaignBrandCard from "../components/CampaignBrandCard"; import CampaignInfoGroup from "../components/CampaignInfoGroup"; +import { getProposalDetail, getAppliedCampaignDetail, type ProposalDetail, type AppliedCampaignDetail } from "../proposal/api/proposal"; +import { useHideHeader } from "../../../hooks/useHideHeader"; +import { getBrandSummary, type BrandSummary } from "../proposal/api/brand"; -import { type ProposalDetail, getProposalDetail } from "../../../data/campaign"; - -import editIcon from "../../../assets/icon-edit.svg"; import dropdownIcon from "../../../assets/arrow-down.svg"; import dropupIcon from "../../../assets/arrow-up.svg"; import arrowRightIcon from "../../../assets/icon/arrow-right.svg"; -import calendarIcon from "../../../assets/icon-calender.svg"; +import chatIcon from "../../../assets/chat-icon.svg"; // 채팅 아이콘 경로 확인 필요 export default function CampaignContent() { const { campaignId } = useParams(); const navigate = useNavigate(); - const [isContentOpen, setIsContentOpen] = useState(false); + const [searchParams] = useSearchParams(); - const [data, setData] = useState(null); + const campaignType = searchParams.get("type"); + const isApplied = campaignType === "applied-campaign"; + const isReceived = campaignType === "received-campaign"; + + useHideHeader(true); + const [isContentOpen, setIsContentOpen] = useState(false); + const [data, setData] = useState(null); // ProposalDetail | AppliedCampaignDetail + const [brand, setBrand] = useState(null); useEffect(() => { console.log("1. 현재 주소창에서 가져온 ID:", campaignId); @@ -27,23 +32,46 @@ export default function CampaignContent() { const loadData = async () => { try { - const res = await getProposalDetail(campaignId); - console.log("2. 서버에서 받은 데이터:", res); + let res; + if (isApplied) { + // 지원한 캠페인 API 호출 + res = await getAppliedCampaignDetail(campaignId); + } else { + // 보낸/받은 제안 API 호출 + res = await getProposalDetail(campaignId); + } setData(res); + + if (res.brandId) { + const brandResult = await getBrandSummary(Number(res.brandId)); + setBrand(brandResult); + } } catch (err) { - console.error("3. API 호출 중 발생한 에러:", err); + console.error("데이터 호출 에러:", err); } }; loadData(); - }, [campaignId]); + }, [campaignId, isApplied]); if (!data) return ; + const getStatusLabel = (status: string) => { + const MAP: Record = { + REVIEWING: "검토 중", MATCHED: "완료", REJECTED: "거절됨", CANCELED: "취소됨", + }; + return MAP[status] || status; + }; + const formatTags = (tags: { name: string }[]) => tags.map(t => t.name).join(", "); + const proposalData = !isApplied ? (data as ProposalDetail) : null; + const appliedData = isApplied ? (data as AppliedCampaignDetail) : null; + + //const isExistingCampaign = data.campaignId !== null && data.campaignId !== undefined; + return ( -
+
{/* Header */}
@@ -61,115 +89,133 @@ export default function CampaignContent() {
-
- - -
- {/* 캠페인명 */} - } - > -
- {data.title} +
+ + {/* 상단 통합 영역: 브랜드 카드 + 캠페인 제목 + 채팅하기 */} +
+ + +
+
+ + {isApplied ? "기존 캠페인" : (proposalData?.campaignId ? "기존 캠페인" : "신규 캠페인")} + +

+ {/* 각 타입에 맞는 필드명을 조건부로 출력 */} + {appliedData ? `‘${appliedData.campaignTitle}’` : (proposalData?.campaignId ? proposalData.campaignName : proposalData?.title)} +

- - - {/* 캠페인 내용 */} - setIsContentOpen(prev => !prev)}> - toggle - - } - > -
- {/* 설명 */} -
-

- 설명 -

-
- {data.description} {/* data.description으로 변경 */} -
-
- {/* dropdown 열렸을 때 */} - {isContentOpen && ( -
-
- -
+ +
+
- - - - + {isApplied && appliedData ? ( + /* 1. 지원한 캠페인 뷰 */ +
+
+ +
+ {appliedData.campaignReason || "작성된 지원 이유가 없습니다."}
- )} +
- - - {/* 협찬품 / 원고료 */} -
- -
- 상품 ID: {data.productId} - arrow -
-
- - -
- {data.rewardAmount.toLocaleString()} -
-
+ ) : proposalData ? ( + /* 2. 제안 뷰 (proposalData가 확실히 있을 때) */ +
+ {!isReceived && ( + +
+ {proposalData.title} +
+
+ )} + + {/* 캠페인 내용 */} + setIsContentOpen(prev => !prev)}> + toggle + + } + > +
+ {/* 설명 */} +
+

+ 설명 +

+
+ {proposalData.description} +
+
- {/* 제작 기간 */} - } - > -
-
- {data.startDate.replace(/-/g, '. ')} + {/* dropdown 열렸을 때 */} + {isContentOpen && proposalData.contentTags && ( +
+
+ +
+ + + + +
+ )}
+ - ~ + {/* 협찬품 / 원고료 */} +
+ +
+ {proposalData.product || "상품 정보 없음"} + arrow +
+
-
- {data.endDate.replace(/-/g, '. ')} -
+ +
+ {proposalData.rewardAmount?.toLocaleString()} +
+
- - {/* 기타 협의 사항 */} - } - > -
- {data.refusalReason || "기타 협의 사항이 없습니다."} -
-
-
+ {/* 제작 기간 */} + +
+
+ {(proposalData.startDate || "").replace(/-/g, '. ')} +
+ ~ +
+ {(proposalData.endDate || "").replace(/-/g, '. ')} +
+
+
+
+ ) : null}
- -
- -
); } diff --git a/app/routes/business/proposal/api/proposal.ts b/app/routes/business/proposal/api/proposal.ts index 6cc0cc9..824cc47 100644 --- a/app/routes/business/proposal/api/proposal.ts +++ b/app/routes/business/proposal/api/proposal.ts @@ -13,10 +13,13 @@ export interface ProposalDetail { proposalId: number; brandId: number; creatorId: number; + campaignId: number | null; + campaignName: string | null; title: string; description: string; rewardAmount: number; productId: number; + product?: string; startDate: string | null; endDate: string | null; status: string; diff --git a/app/routes/mypage/components/profileCard/CampaignsSection.tsx b/app/routes/mypage/components/profileCard/CampaignsSection.tsx index 1166153..d764634 100644 --- a/app/routes/mypage/components/profileCard/CampaignsSection.tsx +++ b/app/routes/mypage/components/profileCard/CampaignsSection.tsx @@ -49,6 +49,12 @@ export default function CampaignsSection() { const [page, setPage] = useState(1); const [isOpen, setIsOpen] = useState(true); + const typeQueryMap: Record = { + SENT: "sent-campaign", + RECEIVED: "received-campaign", + APPLIED: "applied-campaign", + }; + useEffect(() => { let isMounted = true; const fetchData = async () => { @@ -121,40 +127,40 @@ export default function CampaignsSection() {
{pageItems.map((item, idx) => { const typeLabel = item.type ? typeLabelMap[item.type] : ""; - const statusLabel = item.status - ? statusLabelMap[item.status] - : ""; - const dateLabel = formatDate( - item.endDate ?? item.startDate ?? undefined, - ); - const rightLabel = [dateLabel, statusLabel] - .filter(Boolean) - .join(" "); + const statusLabel = item.status ? statusLabelMap[item.status] : ""; + const dateLabel = formatDate(item.endDate ?? item.startDate ?? undefined); + const rightLabel = [dateLabel, statusLabel].filter(Boolean).join(" "); const title = item.title ?? ""; const brand = item.brandName ? `${item.brandName} - ` : ""; + const idToUse = item.proposalId ?? item.campaignId ?? null; + //const campaignId = item.campaignId ?? null; + + const handleNavigate = () => { + if (!idToUse) return; + + // type에 따른 쿼리 스트링 매핑 + const typeQuery = item.type ? typeQueryMap[item.type] : "sent-campaign"; + + // proposalId + navigate(`/business/campaign/${idToUse}?type=${typeQuery}`); + }; - const campaignId = item.campaignId ?? null; return (
navigate(`/business/campaign/${campaignId}`) - : undefined - } - role={campaignId ? "button" : undefined} - tabIndex={campaignId ? 0 : -1} + onClick={idToUse ? handleNavigate : undefined} + role={idToUse ? "button" : undefined} + tabIndex={idToUse ? 0 : -1} >
{typeLabel}
- {brand} - {title} + {brand}{title}
{rightLabel}