Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
"@iconify/react": "^6.0.2",
"@tanstack/react-query": "^5.90.16",
"@tanstack/react-router": "^1.145.7",
"axios": "^1.13.4",
"clsx": "^2.1.1",
"iconify": "^1.4.0",
"react": "^19.2.0",
Expand Down
34 changes: 34 additions & 0 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

21 changes: 21 additions & 0 deletions src/api/axois.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import axios from "axios";

export const axiosInstance = axios.create({
// 1. baseURL을 "/"로 수정
baseURL: "/",
headers: {
"Content-Type": "application/json",
},
});

// 2. 인증 토큰 자동 삽입
/*
axiosInstance.interceptors.request.use((config) => {
const token = localStorage.getItem("accessToken");

if (token) {
config.headers.Authorization = `Bearer ${token}`;
}
return config;
});
*/
52 changes: 29 additions & 23 deletions src/components/layout/Header.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
import { useNavigate } from "@tanstack/react-router";
import arrowLeftIcon from "../../assets/icon/arrow-left.svg";

interface HeaderProps {
title?: string;
showBack?: boolean;
Expand All @@ -11,43 +14,46 @@ export default function Header({
onBackClick,
rightElement,
}: HeaderProps) {
const navigate = useNavigate();

const handleBack = () => {
if (onBackClick) {
onBackClick();
} else {
// 기본 동작: 이전 페이지로 이동
navigate({ to: ".." });
}
};
return (
<header className="sticky top-0 z-50 flex items-center justify-between w-full h-[107px] px-4.5 bg-white border-b border-text-gray5 safe-area-top">
{/* 왼쪽: 뒤로가기 버튼 */}
<div className="flex items-center">
<header className="sticky top-0 z-50 flex items-center justify-between w-full h-[60px] px-[16px] py-[10px] bg-white border-b border-[var(--color-text-gray5)]">
<div className="flex items-center w-8">
{showBack && (
<button
onClick={onBackClick}
className="flex items-center justify-center w-8 h-8 text-text-black hover:bg-bluegray-1 rounded-lg transition-colors"
onClick={handleBack} // 수정된 부분
className="flex items-center justify-start active:opacity-50 transition-opacity"
aria-label="뒤로가기"
>
<svg
xmlns="http://www.w3.org/2000/svg"
fill="none"
viewBox="0 0 24 24"
strokeWidth={2}
stroke="currentColor"
className="w-5 h-5"
>
<path
strokeLinecap="round"
strokeLinejoin="round"
d="M15.75 19.5L8.25 12l7.5-7.5"
/>
</svg>
{/* assets의 화살표 아이콘 사용 + 왼쪽으로 회전 */}
<img
src={arrowLeftIcon}
alt="back"
className="w-6 h-6 brightness-0"
/>
</button>
)}
</div>

{/* 중앙: 타이틀 */}
{title && (
<h1 className="absolute left-1/2 transform -translate-x-1/2 text-title1 text-text-black">
<h1 className="absolute left-1/2 transform -translate-x-1/2 text-title1 text-[var(--color-text-black)] whitespace-nowrap">
{title}
</h1>
)}

{/* 오른쪽: 커스텀 엘리먼트 */}
<div className="flex items-center">{rightElement}</div>
{/* 오른쪽: 커스텀 엘리먼트 영역 */}
<div className="flex items-center justify-end w-8">
{rightElement}
</div>
</header>
);
}
}
12 changes: 12 additions & 0 deletions src/globals.css
Original file line number Diff line number Diff line change
Expand Up @@ -221,6 +221,18 @@ html {
input[type="password"]::-webkit-input-placeholder {
letter-spacing: normal;
}

.text-date-separator {
font-family: var(--font-family-pretendard);
font-size: 18px;
font-weight: 600;
line-height: 160%;
letter-spacing: -0.02em;
/* -2% */
vertical-align: middle;
}


}

@layer utilities {
Expand Down
16 changes: 16 additions & 0 deletions src/hooks/queries/useCampaign.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import { useMutation } from "@tanstack/react-query";
import { postCampaignRequest } from "../../routes/_main/_business/campaign/api/campaign";

export const useCreateCampaignMutation = () => {
return useMutation({
mutationFn: postCampaignRequest,
onSuccess: (data) => {
alert("캠페인 제안이 완료되었습니다!");
console.log("성공:", data);
},
onError: (error) => {
alert("제안에 실패했습니다.");
console.error("에러:", error);
},
});
};
40 changes: 20 additions & 20 deletions src/routes/_main/_business/calendar/calendar-content.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import { useState } from "react";

import FilterBottomSheet from "../components/FilterBottomSheet";
import WeeklyCalendar from "../components/WeeklyCalendar";
import MonthlyCalendar from "../components/MonthlyCalendar";
Expand All @@ -25,8 +24,9 @@ export default function CalendarContent() {
<div className="flex w-full bg-bg-w border-b border-text-gray5">
<button
onClick={() => setMainTab("collaboration")}
className={`flex-1 py-4 text-[16px] font-bold relative transition-colors ${mainTab === "collaboration" ? "text-core-1" : "text-text-gray3"
}`}
className={`flex-1 py-4 text-[16px] font-bold relative transition-colors ${
mainTab === "collaboration" ? "text-core-1" : "text-text-gray3"
}`}
>
협업 현황
{mainTab === "collaboration" && (
Expand All @@ -35,8 +35,9 @@ export default function CalendarContent() {
</button>
<button
onClick={() => setMainTab("matching")}
className={`flex-1 py-4 text-[16px] font-bold relative transition-colors ${mainTab === "matching" ? "text-core-1" : "text-text-gray3"
}`}
className={`flex-1 py-4 text-[16px] font-bold relative transition-colors ${
mainTab === "matching" ? "text-core-1" : "text-text-gray3"
}`}
>
매칭 현황
{mainTab === "matching" && (
Expand Down Expand Up @@ -87,40 +88,39 @@ export default function CalendarContent() {

{hasData ? (
<div className="flex flex-col gap-4 px-4 flex-1">
<div className="flex items-center justify-between mb-1">
<div className="flex items-center justify-between mb-1 mt-4">
<h2 className="text-title1 font-bold text-text-black">매칭 현황</h2>
<button
onClick={() => setIsFilterOpen(true)}
className="flex items-center gap-1 px-3 py-1 border border-text-gray4 rounded-full bg-white active:bg-bluegray-2 transition-colors"
className="flex items-center gap-1 px-3 py-1 border border-text-gray4 rounded-full bg-white transition-colors"
>
<span className="text-callout1 text-text-gray2">{activeFilter}</span>
<img src={dropdownIcon} alt="open filter" />
</button>
</div>

<div className="flex flex-col gap-4">
{/* matchingSubTab을 type 프롭스로 그대로 전달 */}
{matchingSubTab === "sent" ? (
<>
<MatchingCard brand="라운드랩" status="매칭" date="12.23.25" actionLabel="제안 보기" />
<MatchingCard brand="비플레인" status="검토 중" date="12.23.25" actionLabel="제안 보기" />
<MatchingCard brand="땡큐파머" status="검토 중" date="12.23.25" actionLabel="제안 보기" />
<MatchingCard brand="이즈트리" status="거절" date="12.23.25" actionLabel="거절 사유 보기" />
<MatchingCard brand="라운드랩" status="매칭" date="12.23.25" actionLabel="제안 보기" type={matchingSubTab} />
<MatchingCard brand="비플레인" status="검토 중" date="12.23.25" actionLabel="제안 보기" type={matchingSubTab} />
<MatchingCard brand="땡큐파머" status="검토 중" date="12.23.25" actionLabel="제안 보기" type={matchingSubTab} />
<MatchingCard brand="이즈트리" status="거절" date="12.23.25" actionLabel="거절 사유 보기" type={matchingSubTab} />
</>
) : (
<>
<MatchingCard brand="라운드랩" status="매칭" date="12.23.25" actionLabel="제안 보기" />
<MatchingCard brand="비플레인" status="검토 중" date="12.23.25" actionLabel="제안 보기" />
<MatchingCard brand="그레이스유" status="검토 중" date="12.23.25" actionLabel="제안 보기" />
<MatchingCard brand="이즈트리" status="거절" date="12.23.25" actionLabel="거절 사유 보기" />
<MatchingCard brand="라운드랩" status="매칭" date="12.23.25" actionLabel="제안 보기" type={matchingSubTab} />
<MatchingCard brand="비플레인" status="검토 중" date="12.23.25" actionLabel="제안 보기" type={matchingSubTab} />
<MatchingCard brand="그레이스유" status="검토 중" date="12.23.25" actionLabel="제안 보기" type={matchingSubTab} />
<MatchingCard brand="이즈트리" status="거절" date="12.23.25" actionLabel="거절 사유 보기" type={matchingSubTab} />
</>
)}
</div>

</div>
) : (
<div className="flex-1 flex flex-col items-center justify-center">
<EmptyState
message={`${(matchingSubTab as string) === "sent" ? "보낸" : "받은"} 제안이 없어요`}
/>
<EmptyState message={`${matchingSubTab === "sent" ? "보낸" : "받은"} 제안이 없어요`} />
</div>
)}

Expand All @@ -135,4 +135,4 @@ export default function CalendarContent() {
</main>
</div>
);
}
}
22 changes: 22 additions & 0 deletions src/routes/_main/_business/campaign/api/campaign.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import { axiosInstance } from "../../../../../api/axois";

export interface CampaignRequest {
brandId: number;
campaignId: number | null;
campaignName: string;
description: string;
formats: { id: string }[];
categories: { id: string; customValue?: string }[];
tones: { id: string }[];
involvements: { id: string }[];
usageRanges: { id: string }[];
rewardAmount: number;
productId: number;
startDate: string;
endDate: string;
}

export const postCampaignRequest = async (data: CampaignRequest) => {
const response = await axiosInstance.post("/api/v1/campaigns/request", data);
return response.data;
};
Loading