diff --git a/package.json b/package.json index 197d9cf0..5d6d18c9 100644 --- a/package.json +++ b/package.json @@ -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", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 9ba8a5c7..70379e31 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -19,10 +19,17 @@ importers: version: 5.90.20(react@19.2.4) '@tanstack/react-router': specifier: ^1.145.7 +<<<<<<< HEAD + version: 1.150.0(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + axios: + specifier: ^1.13.4 + version: 1.13.4 +======= version: 1.157.16(react-dom@19.2.4(react@19.2.4))(react@19.2.4) clsx: specifier: ^2.1.1 version: 2.1.1 +>>>>>>> main iconify: specifier: ^1.4.0 version: 1.4.0 @@ -1471,6 +1478,9 @@ packages: resolution: {integrity: sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==} engines: {node: '>= 0.4'} + axios@1.13.4: + resolution: {integrity: sha512-1wVkUaAO6WyaYtCkcYCOx12ZgpGf9Zif+qXa4n+oYzK558YryKqiL6UWwd5DqiH3VRW0GYhTZQ/vlgJrCoNQlg==} + babel-dead-code-elimination@1.0.12: resolution: {integrity: sha512-GERT7L2TiYcYDtYk1IpD+ASAYXjKbLTDPhBtYj7X1NuRMDTMtAx9kyBenub1Ev41lo91OHCKdmP+egTDmfQ7Ig==} @@ -1842,6 +1852,15 @@ packages: flatted@3.3.3: resolution: {integrity: sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg==} + follow-redirects@1.15.11: + resolution: {integrity: sha512-deG2P0JfjrTxl50XGCDyfI97ZGVCxIpfKYmfyrQ54n5FO/0gfIES8C/Psl6kWVDolizcaaxZJnTS0QSMxvnsBQ==} + engines: {node: '>=4.0'} + peerDependencies: + debug: '*' + peerDependenciesMeta: + debug: + optional: true + for-each@0.3.5: resolution: {integrity: sha512-dKx12eRCVIzqCxFGplyFKJMPvLEWgmNtUrpTiJIR5u97zEhRG8ySrtboPHZXx7daLxQVrl643cTzbab2tkQjxg==} engines: {node: '>= 0.4'} @@ -2479,6 +2498,9 @@ packages: resolution: {integrity: sha512-mQUvGU6aUFQ+rNvTIAcZuWGRT9a6f6Yrg9bHs4ImKF+HZCEK+plBvnAZYSIQztknZF2qnzNtr6F8s0+IuptdlQ==} engines: {node: ^14.13.1 || >=16.0.0} + proxy-from-env@1.1.0: + resolution: {integrity: sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==} + psl@1.15.0: resolution: {integrity: sha512-JZd3gMVBAVQkSs6HdNZo9Sdo0LNcQeMNP3CozBJb3JYC/QUYZTnKxP+f8oWRX4rHP5EurWxqAHTSwUCjlNKa1w==} @@ -4577,6 +4599,14 @@ snapshots: dependencies: possible-typed-array-names: 1.1.0 + axios@1.13.4: + dependencies: + follow-redirects: 1.15.11 + form-data: 4.0.5 + proxy-from-env: 1.1.0 + transitivePeerDependencies: + - debug + babel-dead-code-elimination@1.0.12: dependencies: '@babel/core': 7.28.6 @@ -5048,6 +5078,8 @@ snapshots: flatted@3.3.3: {} + follow-redirects@1.15.11: {} + for-each@0.3.5: dependencies: is-callable: 1.2.7 @@ -5656,6 +5688,8 @@ snapshots: pretty-bytes@6.1.1: {} + proxy-from-env@1.1.0: {} + psl@1.15.0: dependencies: punycode: 2.3.1 diff --git a/src/api/axois.ts b/src/api/axois.ts new file mode 100644 index 00000000..1bbe22a6 --- /dev/null +++ b/src/api/axois.ts @@ -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; +}); +*/ \ No newline at end of file diff --git a/src/components/layout/Header.tsx b/src/components/layout/Header.tsx index 81cad4b5..f2f11d53 100644 --- a/src/components/layout/Header.tsx +++ b/src/components/layout/Header.tsx @@ -1,3 +1,6 @@ +import { useNavigate } from "@tanstack/react-router"; +import arrowLeftIcon from "../../assets/icon/arrow-left.svg"; + interface HeaderProps { title?: string; showBack?: boolean; @@ -11,43 +14,46 @@ export default function Header({ onBackClick, rightElement, }: HeaderProps) { + const navigate = useNavigate(); + + const handleBack = () => { + if (onBackClick) { + onBackClick(); + } else { + // 기본 동작: 이전 페이지로 이동 + navigate({ to: ".." }); + } + }; return ( -
- {/* 왼쪽: 뒤로가기 버튼 */} -
+
+
{showBack && ( )}
{/* 중앙: 타이틀 */} {title && ( -

+

{title}

)} - {/* 오른쪽: 커스텀 엘리먼트 */} -
{rightElement}
+ {/* 오른쪽: 커스텀 엘리먼트 영역 */} +
+ {rightElement} +
); -} +} \ No newline at end of file diff --git a/src/globals.css b/src/globals.css index bf6281a1..76f4d461 100644 --- a/src/globals.css +++ b/src/globals.css @@ -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 { diff --git a/src/hooks/queries/useCampaign.ts b/src/hooks/queries/useCampaign.ts new file mode 100644 index 00000000..41210b63 --- /dev/null +++ b/src/hooks/queries/useCampaign.ts @@ -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); + }, + }); +}; \ No newline at end of file diff --git a/src/routes/_main/_business/calendar/calendar-content.tsx b/src/routes/_main/_business/calendar/calendar-content.tsx index 305d100a..d3f88f80 100644 --- a/src/routes/_main/_business/calendar/calendar-content.tsx +++ b/src/routes/_main/_business/calendar/calendar-content.tsx @@ -1,5 +1,4 @@ import { useState } from "react"; - import FilterBottomSheet from "../components/FilterBottomSheet"; import WeeklyCalendar from "../components/WeeklyCalendar"; import MonthlyCalendar from "../components/MonthlyCalendar"; @@ -25,8 +24,9 @@ export default function CalendarContent() {
+
+ {/* matchingSubTab을 type 프롭스로 그대로 전달 */} {matchingSubTab === "sent" ? ( <> - - - - + + + + ) : ( <> - - - - + + + + )}
-
) : (
- +
)} @@ -135,4 +135,4 @@ export default function CalendarContent() { ); -} +} \ No newline at end of file diff --git a/src/routes/_main/_business/campaign/api/campaign.ts b/src/routes/_main/_business/campaign/api/campaign.ts new file mode 100644 index 00000000..550822a5 --- /dev/null +++ b/src/routes/_main/_business/campaign/api/campaign.ts @@ -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; +}; \ No newline at end of file diff --git a/src/routes/_main/_business/campaign/campaign-content.tsx b/src/routes/_main/_business/campaign/campaign-content.tsx index 4472600b..970ab53a 100644 --- a/src/routes/_main/_business/campaign/campaign-content.tsx +++ b/src/routes/_main/_business/campaign/campaign-content.tsx @@ -1,4 +1,5 @@ import { useState } from "react"; +//import { useCreateCampaignMutation } from "../../../../hooks/queries/useCampaign"; import Header from "../../../../components/layout/Header"; import CampaignBrandCard from "../components/CampaignBrandCard"; @@ -13,56 +14,72 @@ import calendarIcon from "../../../../assets/icon-calender.svg"; export default function CampaignContent() { const [isContentOpen, setIsContentOpen] = useState(false); + const [campaignData, setCampaignData] = useState({ + brandId: 1, + campaignId: null, + campaignName: "비플레인 클렌징 및 세럼 리뷰 콘텐츠", + description: "안녕하세요 크리에이터 비비 입니다! 비플레인의 가치가 제 채널에서 소개하는 뷰티 콘텐츠와 잘 맞닿아 있다고 생각되어 협찬을 제안드립니다.", + rewardAmount: 200000, + startDate: "2025-01-20", + endDate: "2025-01-30", + formats: [{ id: "32000000-0000-0000-0000-000000000000" }], + categories: [{ id: "31310000-0000-0000-0000-000000000000", customValue: "성분 분석 리뷰" }], + tones: [ + { id: "31360000-0000-0000-0000-000000000000" }, + { id: "31330000-0000-0000-0000-000000000000" } + ], + involvements: [{ id: "32320000-0000-0000-0000-000000000000" }], + usageRanges: [{ id: "32350000-0000-0000-0000-000000000000" }], + productId: 5 + }); + return ( -
- {/* Header */} -
+ // 전체 컨테이너를 흰색 배경으로 설정 +
+
-
+ {/* 기타 협의 사항까지만 회색 배경을 씌움 */} +
{/* 캠페인명 */} } + right={edit} > -
- 비플레인 클렌징 및 세럼 리뷰 콘텐츠 -
+ setCampaignData({ ...campaignData, campaignName: e.target.value })} + className="w-full h-[36px] px-4 py-[10px] bg-[var(--color-bg-w)] border border-[var(--color-text-gray5)] rounded-[6px] text-callout1 text-[var(--color-text-gray1)] focus:outline-none focus:border-blue-400 box-border" + />
{/* 캠페인 내용 */} setIsContentOpen(prev => !prev)}> - toggle + } >
- {/* 설명 */}
-

- 설명 -

-
- 안녕하세요 크리에이터 비비 입니다! 비플레인의 가치가 제 채널에서 - 소개하는 뷰티 콘텐츠와 잘 맞닿아 있다고 생각되어 협찬을 제안드립니다. -
+

설명

+