From f2bc3443945ff6636f76beb0258bc8166e7614a8 Mon Sep 17 00:00:00 2001 From: Jio Date: Mon, 2 Feb 2026 14:33:05 +0900 Subject: [PATCH 1/8] chore: main merge and fix confilict --- .../business/components/CampaignCard.tsx | 1 + app/routes/business/proposal/api/proposal.ts | 19 +++++++++++++++++++ 2 files changed, 20 insertions(+) diff --git a/app/routes/business/components/CampaignCard.tsx b/app/routes/business/components/CampaignCard.tsx index 48de4b8..03dbffd 100644 --- a/app/routes/business/components/CampaignCard.tsx +++ b/app/routes/business/components/CampaignCard.tsx @@ -23,6 +23,7 @@ export default function CampaignCard({ showButton = true, campaignId = 1, }: CampaignCardProps) { + const navigate = useNavigate(); // 로고 컴포넌트 diff --git a/app/routes/business/proposal/api/proposal.ts b/app/routes/business/proposal/api/proposal.ts index cfca5e8..a7f13cd 100644 --- a/app/routes/business/proposal/api/proposal.ts +++ b/app/routes/business/proposal/api/proposal.ts @@ -33,7 +33,11 @@ export interface ProposalDetail { export const getProposalDetail = async (proposalId: string): Promise => { const BASE_URL = "https://api.realmatch.co.kr"; +<<<<<<< HEAD +======= + +>>>>>>> d83ca21 (feat: 보낸 제안 - 제안 보기 연동중) // 1. tokenStorage 유틸을 사용하여 안전하게 토큰을 가져옵니다. const token = tokenStorage.getAccessToken(); @@ -54,6 +58,7 @@ export const getProposalDetail = async (proposalId: string): Promise>>>>>> d83ca21 (feat: 보낸 제안 - 제안 보기 연동중) } throw error; } @@ -83,9 +96,15 @@ export const getBrandDetail = async (brandId: number | string): Promise>>>>>> d83ca21 (feat: 보낸 제안 - 제안 보기 연동중) console.error("브랜드 상세 조회 실패:", error); throw error; } From 1e06e844e05d9ee621beb9390425e2581bd8f45b Mon Sep 17 00:00:00 2001 From: Jio Date: Mon, 2 Feb 2026 13:42:04 +0900 Subject: [PATCH 2/8] =?UTF-8?q?feat:=20=EB=A7=A4=EC=B9=AD=20=ED=98=84?= =?UTF-8?q?=ED=99=A9=20=EC=97=B0=EB=8F=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/data/campaign.ts | 61 ++++++- app/routes/business/calendar/api/calendar.ts | 49 +++++- .../business/calendar/calendar-content.tsx | 149 ++++++++++-------- 3 files changed, 190 insertions(+), 69 deletions(-) diff --git a/app/data/campaign.ts b/app/data/campaign.ts index 21f69f8..c27e480 100644 --- a/app/data/campaign.ts +++ b/app/data/campaign.ts @@ -130,4 +130,63 @@ export const getProposalDetail = async (id: string): Promise => console.log("API 전체 응답:", response.data); return response.data.result; -}; \ No newline at end of file +}; + +export interface CampaignCollaboration { + campaignId: number; + proposalId: string | null; + brandName: string; + thumbnailUrl: string; + title: string; + status: "NONE" | "REVIEWING" | "MATCHED" | "REJECTED"; + startDate: string; + endDate: string; + type: "APPLIED" | "SENT" | "RECEIVED"; +} +// MatchingCard 연동을 위한 전용 더미 데이터 +export const MATCHING_DUMMY_DATA: CampaignCollaboration[] = [ + { + campaignId: 1, + proposalId: "p1", + brandName: "라운드랩", + thumbnailUrl: "", + title: "자작나무 수분크림 체험단", + status: "MATCHED", + startDate: "2025-12-23", + endDate: "2025-12-30", + type: "SENT", + }, + { + campaignId: 2, + proposalId: "p2", + brandName: "비플레인", + thumbnailUrl: "", + title: "'글로우업' 선크림 신제품 홍보", + status: "REVIEWING", + startDate: "2025-12-23", + endDate: "2025-12-30", + type: "RECEIVED", + }, + { + campaignId: 3, + proposalId: "p3", + brandName: "그레이스유", + thumbnailUrl: "", + title: "봄 신상 코디 콘텐츠 제작", + status: "REVIEWING", + startDate: "2025-12-23", + endDate: "2025-12-30", + type: "RECEIVED", + }, + { + campaignId: 4, + proposalId: "p4", + brandName: "이즈앤트리", + thumbnailUrl: "", + title: "비타민C 세럼 리뷰 캠페인", + status: "REJECTED", + startDate: "2025-12-23", + endDate: "2025-12-30", + type: "SENT", + }, +]; \ No newline at end of file diff --git a/app/routes/business/calendar/api/calendar.ts b/app/routes/business/calendar/api/calendar.ts index 9f4ab67..4ae43b3 100644 --- a/app/routes/business/calendar/api/calendar.ts +++ b/app/routes/business/calendar/api/calendar.ts @@ -32,4 +32,51 @@ export const getMyCollaborations = async (params?: { { params } ); return response.data.result; -}; \ No newline at end of file +}; + +export const MATCHING_DUMMY_DATA: CampaignCollaboration[] = [ + { + campaignId: 1, + proposalId: "p1", + brandName: "라운드랩", + thumbnailUrl: "", + title: "자작나무 수분크림 체험단", + status: "MATCHED", + startDate: "2025-12-23", + endDate: "2025-12-30", + type: "SENT", + }, + { + campaignId: 2, + proposalId: "p2", + brandName: "비플레인", + thumbnailUrl: "", + title: "'글로우업' 선크림 신제품 홍보", + status: "REVIEWING", + startDate: "2025-12-23", + endDate: "2025-12-30", + type: "RECEIVED", + }, + { + campaignId: 3, + proposalId: "p3", + brandName: "그레이스유", + thumbnailUrl: "", + title: "봄 신상 코디 콘텐츠 제작", + status: "REVIEWING", + startDate: "2025-12-23", + endDate: "2025-12-30", + type: "RECEIVED", + }, + { + campaignId: 4, + proposalId: "p4", + brandName: "이즈앤트리", + thumbnailUrl: "", + title: "비타민C 세럼 리뷰 캠페인", + status: "REJECTED", + startDate: "2025-12-23", + endDate: "2025-12-30", + type: "SENT", + }, +]; diff --git a/app/routes/business/calendar/calendar-content.tsx b/app/routes/business/calendar/calendar-content.tsx index d1f8722..e492543 100644 --- a/app/routes/business/calendar/calendar-content.tsx +++ b/app/routes/business/calendar/calendar-content.tsx @@ -11,6 +11,7 @@ import MatchingCard from "../components/MatchingCard"; import MatchingTabSection from "../components/MatchingTabSection"; import dropdownIcon from "../../../assets/arrow-down.svg"; import EmptyState from "../components/EmptyState"; +import { MATCHING_DUMMY_DATA } from "../calendar/api/calendar"; export default function CalendarContent() { @@ -23,27 +24,68 @@ export default function CalendarContent() { const hasData = true; - // API 데이터 상태 const [campaigns, setCampaigns] = useState([]); const [isLoading, setIsLoading] = useState(true); - // 데이터 로드 + useEffect(() => { const fetchCampaigns = async () => { try { setIsLoading(true); - // 협업 현황 조회를 위해 전체 데이터를 가져옵니다. const data = await getMyCollaborations(); - setCampaigns(data); + + if (data && data.length > 0) { + setCampaigns(data); + } else { + // 실제 데이터가 없으면 우리가 만든 MATCHING_DUMMY_DATA를 사용 + setCampaigns(MATCHING_DUMMY_DATA); + } } catch (error) { - console.error("캠페인 로드 실패:", error); + console.error("데이터 로드 실패, 더미 사용:", error); + setCampaigns(MATCHING_DUMMY_DATA); } finally { setIsLoading(false); } }; - fetchCampaigns(); }, []); + // [상태 변환 헬퍼 함수] + const getStatusLabel = (status: CampaignCollaboration["status"]): "매칭" | "검토 중" | "거절" => { + switch (status) { + case "MATCHED": + return "매칭"; + case "REVIEWING": + case "NONE": // NONE 상태도 '검토 중'으로 처리하거나 기획에 맞게 할당 + return "검토 중"; + case "REJECTED": + return "거절"; + default: + // API에서 예상치 못한 값이 올 경우 기본값으로 "검토 중"을 반환하여 에러 방지 + return "검토 중"; + } + }; + + const getActionLabel = (status: CampaignCollaboration["status"]) => { + return status === "REJECTED" ? "거절 사유 보기" : "제안 보기"; + }; + + // [매칭 현황 필터링 로직] + // 1. 탭에 따른 필터링 (보낸 제안: SENT/APPLIED, 받은 제안: RECEIVED) + // 2. 우측 상단 드롭다운 필터(activeFilter) 적용 + const matchingList = campaigns.filter((item) => { + const isCorrectSubTab = + matchingSubTab === "sent" + ? (item.type === "SENT" || item.type === "APPLIED") + : item.type === "RECEIVED"; + + if (!isCorrectSubTab) return false; + + if (activeFilter === "전체") return true; + return getStatusLabel(item.status) === activeFilter; + }); + + + // [필터링 로직] const todayStr = new Date().toISOString().split('T')[0]; const currentMonthStr = todayStr.substring(0, 7); // "2026-02" @@ -90,7 +132,7 @@ export default function CalendarContent() { {mainTab === "collaboration" ? ( /* [A] 협업 현황 */
- {/* 주간 캘린더 연동 */} + {/* 주간 캘린더 연동 */}

이번주 일정

@@ -147,68 +189,41 @@ export default function CalendarContent() {
- {hasData ? ( -
-
-

매칭 현황

- -
-
- {matchingSubTab === "sent" ? ( - <> - handleCardClick("sent")} // 4. 핸들러 연결 - /> - handleCardClick("sent")} - /> - handleCardClick("sent")} - /> - handleCardClick("sent")} - /> - - ) : ( - <> - handleCardClick("received")} // 4. 핸들러 연결 - /> - handleCardClick("received")} - /> - handleCardClick("received")} - /> - handleCardClick("received")} - /> - - )} -
- +
+
+

매칭 현황

+
- ) : ( -
- + +
+ {isLoading ? ( +

로딩 중...

+ ) : matchingList.length > 0 ? ( // filteredList에서 matchingList로 변경 + matchingList.map((item) => ( + handleCardClick(matchingSubTab)} + /> + )) + ) : ( +
+ +
+ )}
- )} +
Date: Mon, 2 Feb 2026 23:07:31 +0900 Subject: [PATCH 3/8] =?UTF-8?q?feat:=20=EC=BA=98=EB=A6=B0=EB=8D=94?= =?UTF-8?q?=EB=B7=B0,=20=EB=B0=9B=EC=9D=80=20=EC=A0=9C=EC=95=88=20?= =?UTF-8?q?=EC=97=B0=EB=8F=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/routes/business/calendar/api/calendar.ts | 16 +- .../business/calendar/calendar-content.tsx | 7 +- .../business/components/MonthlyCalendar.tsx | 167 ++++++++++-------- app/routes/business/proposal/api/proposal.ts | 25 +-- .../proposal/sent-proposal-content.tsx | 2 +- 5 files changed, 113 insertions(+), 104 deletions(-) diff --git a/app/routes/business/calendar/api/calendar.ts b/app/routes/business/calendar/api/calendar.ts index 4ae43b3..d8fbf52 100644 --- a/app/routes/business/calendar/api/calendar.ts +++ b/app/routes/business/calendar/api/calendar.ts @@ -42,8 +42,8 @@ export const MATCHING_DUMMY_DATA: CampaignCollaboration[] = [ thumbnailUrl: "", title: "자작나무 수분크림 체험단", status: "MATCHED", - startDate: "2025-12-23", - endDate: "2025-12-30", + startDate: "2026-02-02", + endDate: "2026-02-07", type: "SENT", }, { @@ -53,8 +53,8 @@ export const MATCHING_DUMMY_DATA: CampaignCollaboration[] = [ thumbnailUrl: "", title: "'글로우업' 선크림 신제품 홍보", status: "REVIEWING", - startDate: "2025-12-23", - endDate: "2025-12-30", + startDate: "2026-02-04", + endDate: "2025-02-05", type: "RECEIVED", }, { @@ -64,8 +64,8 @@ export const MATCHING_DUMMY_DATA: CampaignCollaboration[] = [ thumbnailUrl: "", title: "봄 신상 코디 콘텐츠 제작", status: "REVIEWING", - startDate: "2025-12-23", - endDate: "2025-12-30", + startDate: "2026-02-23", + endDate: "2026-02-24", type: "RECEIVED", }, { @@ -75,8 +75,8 @@ export const MATCHING_DUMMY_DATA: CampaignCollaboration[] = [ thumbnailUrl: "", title: "비타민C 세럼 리뷰 캠페인", status: "REJECTED", - startDate: "2025-12-23", - endDate: "2025-12-30", + startDate: "2026-02-01", + endDate: "2025-02-04", type: "SENT", }, ]; diff --git a/app/routes/business/calendar/calendar-content.tsx b/app/routes/business/calendar/calendar-content.tsx index e492543..6414301 100644 --- a/app/routes/business/calendar/calendar-content.tsx +++ b/app/routes/business/calendar/calendar-content.tsx @@ -88,7 +88,8 @@ export default function CalendarContent() { // [필터링 로직] const todayStr = new Date().toISOString().split('T')[0]; - const currentMonthStr = todayStr.substring(0, 7); // "2026-02" + const currentMonthStr = todayStr.substring(0, 7); + const calendarEvents = campaigns.filter(item => item.status === "MATCHED"); const filteredList = campaigns.filter((item) => { if (activeTab === "today") { @@ -136,11 +137,11 @@ export default function CalendarContent() {

이번주 일정

- +

이번달 일정

- +
{/* 하단 리스트 섹션 */} diff --git a/app/routes/business/components/MonthlyCalendar.tsx b/app/routes/business/components/MonthlyCalendar.tsx index b036301..38f3396 100644 --- a/app/routes/business/components/MonthlyCalendar.tsx +++ b/app/routes/business/components/MonthlyCalendar.tsx @@ -10,100 +10,125 @@ interface MonthlyCalendarProps { const WEEK_DAYS = ["일", "월", "화", "수", "목", "금", "토"]; export default function MonthlyCalendar({ events }: MonthlyCalendarProps) { - // 1. 현재 표시할 날짜 상태 (누락되었던 부분 추가) const [currentDate, setCurrentDate] = useState(new Date()); - // 2. 해당 월의 정보 계산 - const { year, month, allSlots } = useMemo(() => { + // 1. MATCHED 상태인 이벤트만 필터링 + const matchedEvents = useMemo(() => + events.filter(event => event.status === "MATCHED"), + [events] + ); + + const { year, month, weeks } = useMemo(() => { const y = currentDate.getFullYear(); - const m = currentDate.getMonth(); // 0 ~ 11 - - const firstDay = new Date(y, m, 1).getDay(); - const lastDate = new Date(y, m + 1, 0).getDate(); - - const emptyDays = Array(firstDay).fill(null); - const days = Array.from({ length: lastDate }, (_, i) => i + 1); - - return { year: y, month: m + 1, allSlots: [...emptyDays, ...days] }; + const m = currentDate.getMonth(); + const firstDayOfMonth = new Date(y, m, 1).getDay(); + const lastDateOfMonth = new Date(y, m + 1, 0).getDate(); + + const weeksArr: (number | null)[][] = []; + let currentWeek: (number | null)[] = Array(firstDayOfMonth).fill(null); + + for (let i = 1; i <= lastDateOfMonth; i++) { + currentWeek.push(i); + if (currentWeek.length === 7) { + weeksArr.push(currentWeek); + currentWeek = []; + } + } + if (currentWeek.length > 0) { + while (currentWeek.length < 7) currentWeek.push(null); + weeksArr.push(currentWeek); + } + + return { year: y, month: m + 1, weeks: weeksArr }; }, [currentDate]); - // 월 이동 핸들러 const handlePrevMonth = () => setCurrentDate(new Date(year, month - 2, 1)); const handleNextMonth = () => setCurrentDate(new Date(year, month, 1)); - // 3. 특정 날짜에 이벤트가 있는지 확인하는 함수 - const getEventsForDay = (day: number | null) => { - if (!day) return []; - // YYYY-MM-DD 형식으로 비교 - const targetStr = `${year}-${String(month).padStart(2, "0")}-${String(day).padStart(2, "0")}`; - - return events.filter(event => { - return event.startDate <= targetStr && event.endDate >= targetStr; - }); - }; + // 날짜 문자열 비교를 위한 헬퍼 함수 + const getTime = (dateStr: string) => new Date(dateStr).setHours(0, 0, 0, 0); return ( -
- {/* 상단 월 이동 */} +
+ {/* 상단 컨트롤 */}
- - - {year}년 {String(month).padStart(2, "0")}월 - - + + {year}년 {String(month).padStart(2, "0")}월 +
-
+
{/* 요일 헤더 */} -
- {WEEK_DAYS.map((day) => ( -
{day}
- ))} +
+ {WEEK_DAYS.map(day =>
{day}
)}
- {/* 날짜 그리드 */} -
- {allSlots.map((day, index) => { - const dayEvents = getEventsForDay(day); - const isToday = day === new Date().getDate() && month === (new Date().getMonth() + 1) && year === new Date().getFullYear(); - - return ( -
- {/* 날짜 표시 */} -
+ {/* 주차별 렌더링 */} + {weeks.map((week, weekIdx) => ( +
+ {/* 배경 날짜 숫자 */} + {week.map((day, dayIdx) => { + const isToday = day === new Date().getDate() && month === (new Date().getMonth() + 1) && year === new Date().getFullYear(); + return ( +
{day && ( - + {day} )}
+ ); + })} + + {/* 이벤트 바 레이어 */} +
+ {matchedEvents.map((event, eventIdx) => { + const weekDates = week.filter((d) => d !== null) as number[]; + if (weekDates.length === 0) return null; + + const weekStart = new Date(year, month - 1, weekDates[0]).setHours(0, 0, 0, 0); + const weekEnd = new Date(year, month - 1, weekDates[weekDates.length - 1]).setHours(23, 59, 59, 999); + const eventStart = getTime(event.startDate); + const eventEnd = getTime(event.endDate); - {/* 해당 날짜의 일정 바 (이벤트가 있을 때만 표시) */} -
- {dayEvents.slice(0, 2).map((event, idx) => ( -
+ if (eventEnd < weekStart || eventStart > weekEnd) return null; + + const startIdx = week.findIndex((d) => d !== null && getTime(`${year}-${month}-${d}`) >= eventStart); + const actualStartIdx = startIdx === -1 ? 0 : startIdx; + + const reversedIdx = [...week].reverse().findIndex((d) => d !== null && getTime(`${year}-${month}-${d}`) <= eventEnd); + const endIdx = reversedIdx === -1 ? -1 : week.length - 1 - reversedIdx; + const actualEndIdx = endIdx === -1 ? 6 : endIdx; + + const colStart = actualStartIdx + 1; + const colSpan = actualEndIdx - actualStartIdx + 1; + + const isRealStart = eventStart >= weekStart && eventStart <= weekEnd; + const isRealEnd = eventEnd >= weekStart && eventEnd <= weekEnd; + + return ( +
+ {event.brandName} -
- ))} - {dayEvents.length > 2 && ( - +{dayEvents.length - 2} - )} -
-
- ); - })} -
+ +
+ ); + })} +
+
+ ))}
); diff --git a/app/routes/business/proposal/api/proposal.ts b/app/routes/business/proposal/api/proposal.ts index a7f13cd..5b188a9 100644 --- a/app/routes/business/proposal/api/proposal.ts +++ b/app/routes/business/proposal/api/proposal.ts @@ -33,11 +33,6 @@ export interface ProposalDetail { export const getProposalDetail = async (proposalId: string): Promise => { const BASE_URL = "https://api.realmatch.co.kr"; -<<<<<<< HEAD - -======= - ->>>>>>> d83ca21 (feat: 보낸 제안 - 제안 보기 연동중) // 1. tokenStorage 유틸을 사용하여 안전하게 토큰을 가져옵니다. const token = tokenStorage.getAccessToken(); @@ -58,7 +53,7 @@ export const getProposalDetail = async (proposalId: string): Promise>>>>>> d83ca21 (feat: 보낸 제안 - 제안 보기 연동중) + } throw error; } @@ -96,15 +84,10 @@ export const getBrandDetail = async (brandId: number | string): Promise>>>>>> d83ca21 (feat: 보낸 제안 - 제안 보기 연동중) + } catch (error) { + console.error("브랜드 상세 조회 실패:", error); throw error; } diff --git a/app/routes/business/proposal/sent-proposal-content.tsx b/app/routes/business/proposal/sent-proposal-content.tsx index 9ad2c7b..6b0cc33 100644 --- a/app/routes/business/proposal/sent-proposal-content.tsx +++ b/app/routes/business/proposal/sent-proposal-content.tsx @@ -22,7 +22,7 @@ export default function ProposalContent() { const [brand, setBrand] = useState(null); const [isLoading, setIsLoading] = useState(true); - const proposalId = searchParams.get("proposalId") || "1"; + const proposalId = searchParams.get("proposalId") || "29"; useEffect(() => { const fetchData = async () => { From 288c6d8c2b2da158a43f49d56f174b2dc61a9e4a Mon Sep 17 00:00:00 2001 From: Jio Date: Tue, 3 Feb 2026 03:35:56 +0900 Subject: [PATCH 4/8] =?UTF-8?q?feat:=20=EC=A0=9C=EC=95=88=20=EC=83=81?= =?UTF-8?q?=EC=84=B8=20=EC=A1=B0=ED=9A=8C=20=EC=97=B0=EB=8F=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../business/calendar/calendar-content.tsx | 19 ++-- .../proposal/received-proposal-content.tsx | 89 ++++++++++++++----- 2 files changed, 78 insertions(+), 30 deletions(-) diff --git a/app/routes/business/calendar/calendar-content.tsx b/app/routes/business/calendar/calendar-content.tsx index 6414301..24675c7 100644 --- a/app/routes/business/calendar/calendar-content.tsx +++ b/app/routes/business/calendar/calendar-content.tsx @@ -22,7 +22,7 @@ export default function CalendarContent() { const [isFilterOpen, setIsFilterOpen] = useState(false); const [activeFilter, setActiveFilter] = useState("전체"); - const hasData = true; + //const hasData = true; const [campaigns, setCampaigns] = useState([]); const [isLoading, setIsLoading] = useState(true); @@ -88,7 +88,7 @@ export default function CalendarContent() { // [필터링 로직] const todayStr = new Date().toISOString().split('T')[0]; - const currentMonthStr = todayStr.substring(0, 7); + const currentMonthStr = todayStr.substring(0, 7); const calendarEvents = campaigns.filter(item => item.status === "MATCHED"); const filteredList = campaigns.filter((item) => { @@ -99,8 +99,12 @@ export default function CalendarContent() { return item.startDate.includes(currentMonthStr) || item.endDate.includes(currentMonthStr); }); - const handleCardClick = (type: "sent" | "received") => { - navigate(`/business/proposal?type=${type}`); + const handleCardClick = (item: CampaignCollaboration) => { + if (item.status === "REJECTED") { + navigate(`/rejection?id=${item.campaignId || item.proposalId}`); + } else { + navigate(`/business/proposal?type=${matchingSubTab}`); + } }; return ( @@ -205,15 +209,16 @@ export default function CalendarContent() {
{isLoading ? (

로딩 중...

- ) : matchingList.length > 0 ? ( // filteredList에서 matchingList로 변경 + ) : matchingList.length > 0 ? ( matchingList.map((item) => ( handleCardClick(matchingSubTab)} + // [수정된 부분] item 객체 자체를 넘겨서 상태에 따라 분기 처리 + onClick={() => handleCardClick(item)} /> )) ) : ( diff --git a/app/routes/business/proposal/received-proposal-content.tsx b/app/routes/business/proposal/received-proposal-content.tsx index 450eede..4a8a143 100644 --- a/app/routes/business/proposal/received-proposal-content.tsx +++ b/app/routes/business/proposal/received-proposal-content.tsx @@ -1,9 +1,13 @@ -import { useState } from "react"; +import { useState, useEffect } from "react"; +import { useSearchParams } from "react-router-dom"; import RealmatchHeader from "../../../components/common/RealmatchHeader"; import CampaignBrandCard from "../components/CampaignBrandCard"; import CampaignInfoGroup from "../components/CampaignInfoGroup"; import Modal from "../../../components/common/Modal"; +import { getProposalDetail, getBrandDetail, type ProposalDetail } from "./api/proposal"; +import type { BrandDetail } from "../../../data/brand"; + import dropdownIcon from "../../../assets/arrow-down.svg"; import dropupIcon from "../../../assets/arrow-up.svg"; import arrowRightIcon from "../../../assets/icon/arrow-right.svg"; @@ -12,33 +16,74 @@ import checkIcon from "../../../assets/icon/icon-check-circle.svg"; import closeIcon from "../../../assets/icon/icon-close.svg"; export default function ReceivedProposalContent() { + const [searchParams] = useSearchParams(); + const proposalId = searchParams.get("id"); + + const [proposal, setProposal] = useState(null); + const [brand, setBrand] = useState(null); + const [isLoading, setIsLoading] = useState(true); const [modalType, setModalType] = useState<"none" | "confirm" | "success">("none"); const [isContentOpen, setIsContentOpen] = useState(false); + // 1. 데이터 패칭 로직 + useEffect(() => { + const fetchData = async () => { + if (!proposalId) return; + try { + setIsLoading(true); + // 1. 제안 상세 정보 가져오기 + const proposalResult = await getProposalDetail(proposalId); + setProposal(proposalResult); + + // 2. 제안 정보에 있는 brandId로 브랜드 상세 정보 가져오기 + if (proposalResult.brandId) { + const brandResult = await getBrandDetail(proposalResult.brandId); + setBrand(brandResult); + } + } catch (error) { + console.error("데이터 로드 실패:", error); + } finally { + setIsLoading(false); + } + }; + fetchData(); + }, [proposalId]) + const handleAcceptClick = () => setModalType("confirm"); const handleConfirm = () => setModalType("success"); const closeModal = () => setModalType("none"); + // 태그 배열을 문자열로 변환하는 헬퍼 함수 + const formatTags = (tags: { name: string }[]) => tags.map(t => t.name).join(", "); + + // 날짜 포맷 변경 함수 (2026-02-01 -> 2026. 02. 01) + const formatDate = (dateStr: string) => (dateStr || "").replace(/-/g, ". "); + + if (isLoading) return
로딩 중...
; + if (!proposal) return
데이터를 찾을 수 없습니다.
; + return (
- {/* 배경색 및 간격을 CampaignContent와 동일하게 유지 */}
- {/* 1. 상단 섹션: 브랜드 카드 및 타이틀 */} - {/* CampaignContent와 동일하게 상단만 흰색 배경 유지하거나 구분감 부여 */}
- +
-

브랜드 제안 캠페인

+

{proposal.title}

- {/* 2. 상세 정보 섹션: 여기서부터 회색 배경(bluegray-1) */} + {/* 2. 상세 정보 섹션 */}
- 비플레인 선크림 리뷰 콘텐츠 + {proposal.title}
@@ -47,7 +92,7 @@ export default function ReceivedProposalContent() { label="캠페인 내용" right={ } > @@ -55,37 +100,36 @@ export default function ReceivedProposalContent() {

설명

- 안녕하세요 비플레인 입니다!
- 크리에이터님과 이미지가 잘 맞아 협찬을 제안드립니다. + {proposal.description}
{isContentOpen && (
- +
- - - - + + + +
)}
- {/* 협찬품 / 원고료: 높이 36px 통일 */} + {/* 협찬품 / 원고료 */}
- 글로우 크림 1개 + 협찬품 확인 arrow
- 200,000 + {proposal.rewardAmount.toLocaleString()}
@@ -95,11 +139,11 @@ export default function ReceivedProposalContent() {
- 2025-01-20 + {formatDate(proposal.startDate)}
~
- 2025-01-30 + {formatDate(proposal.endDate)}
@@ -121,7 +165,7 @@ export default function ReceivedProposalContent() {
- {/* 모달 내용은 기존 디자인 유지 (글로벌 규격 적용) */} + {/* 모달 */} {modalType === "confirm" && (
+ ); +} + + +export default function MatchingTabSection({ subTab, setSubTab, receivedCount }: Props) { const [isSearching, setIsSearching] = useState(false); const [query, setQuery] = useState(""); @@ -41,24 +58,33 @@ export default function MatchingTabSection({ subTab, setSubTab }: Props) { return (
-
- +
+ setSubTab("sent")} + /> + + setSubTab("applied")} + />