diff --git a/src/routes/_main.tsx b/src/routes/_main.tsx index c94ff30..a082075 100644 --- a/src/routes/_main.tsx +++ b/src/routes/_main.tsx @@ -1,10 +1,20 @@ -import { createFileRoute, Outlet } from "@tanstack/react-router"; +import { createFileRoute, Outlet, redirect } from "@tanstack/react-router"; import BottomTab from "./_main/components/BottomTab"; import { useState } from "react"; import { LayoutContext } from "./_main/layout-context"; import Logo from "../assets/logo/RealMatchLogo_ex.svg"; +import { useAuthStore } from "../stores/auth-store"; export const Route = createFileRoute("/_main")({ + beforeLoad: () => { + // 처음 진입 시 로그인 페이지로 + const { me } = useAuthStore.getState(); + if (!me) { + throw redirect({ + to: "/login", + }); + } + }, component: MainLayout, }); diff --git a/src/routes/_main/_business/calendar/calendar-content.tsx b/src/routes/_main/_business/calendar/calendar-content.tsx index 850b076..305d100 100644 --- a/src/routes/_main/_business/calendar/calendar-content.tsx +++ b/src/routes/_main/_business/calendar/calendar-content.tsx @@ -1,71 +1,138 @@ import { useState } from "react"; -import Tabs from "../../../../components/common/Tabs"; - +import FilterBottomSheet from "../components/FilterBottomSheet"; import WeeklyCalendar from "../components/WeeklyCalendar"; import MonthlyCalendar from "../components/MonthlyCalendar"; import CampaignCard from "../components/CampaignCard"; import SectionTitle from "../components/SectionTitle"; +import MatchingCard from "../components/MatchingCard"; +import MatchingTabSection from "../components/MatchingTabSection"; +import dropdownIcon from "../../../../assets/arrow-down.svg"; +import EmptyState from "../components/EmptyState"; export default function CalendarContent() { - const [mainTab, setMainTab] = useState<'collaboration' | 'matching'>('collaboration'); - const [activeTab, setActiveTab] = useState<'thisMonth' | 'today'>('thisMonth'); - - return ( -
+ const [mainTab, setMainTab] = useState<"collaboration" | "matching">("collaboration"); + const [activeTab, setActiveTab] = useState<"thisMonth" | "today">("thisMonth"); + const [matchingSubTab, setMatchingSubTab] = useState<"sent" | "received">("sent"); + const [isFilterOpen, setIsFilterOpen] = useState(false); + const [activeFilter, setActiveFilter] = useState("전체"); + const hasData = true; - {/* 1. 상단 협업/매칭 현황 탭 */} - setMainTab(val as 'collaboration' | 'matching')} - /> + return ( +
+ {/* 탭 네비게이션 */} +
+ + +
-
- {/* 진행 중인 협업 섹션 */} -
- -

이번주 일정

- -
+
+ {mainTab === "collaboration" ? ( + /* [A] 협업 현황 */ +
+
+ +

이번주 일정

+ +
+
+

이번달 일정

+ +
+
+
+ + | + +
+
+ + +
+
+
+ ) : ( + /* [B] 매칭 현황 */ +
+ - {/* 이번달 일정 섹션 */} -
-

이번달 일정

- -
+ {hasData ? ( +
+
+

매칭 현황

+ +
+
+ {matchingSubTab === "sent" ? ( + <> + + + + + + ) : ( + <> + + + + + + )} +
- {/* 협업 리스트 섹션 */} -
-
- - | - -
+
+ ) : ( +
+ +
+ )} -
- - - -
- -
-
- ); + setIsFilterOpen(false)} + onApply={(filter) => setActiveFilter(filter)} + currentFilter={activeFilter} + /> +
+ )} + + + ); } diff --git a/src/routes/_main/_business/components/MatchingCard.tsx b/src/routes/_main/_business/components/MatchingCard.tsx index f29280a..2ebd140 100644 --- a/src/routes/_main/_business/components/MatchingCard.tsx +++ b/src/routes/_main/_business/components/MatchingCard.tsx @@ -1,5 +1,5 @@ import chatIcon from "../../../../assets/chat-icon2.svg"; -import searchIcon from "../../../../assets/search.svg"; +import searchIcon from "../../../../assets/icon/search.svg"; interface MatchingCardProps { brand: string; diff --git a/src/routes/_main/_business/proposal/received-proposal-content.tsx b/src/routes/_main/_business/proposal/received-proposal-content.tsx new file mode 100644 index 0000000..66d3396 --- /dev/null +++ b/src/routes/_main/_business/proposal/received-proposal-content.tsx @@ -0,0 +1,142 @@ +import { useState } from "react"; + +import Header from "../../../../components/layout/Header"; +import CampaignBrandCard from "../components/CampaignBrandCard"; +import CampaignInfoGroup from "../components/CampaignInfoGroup"; + +import dropdownIcon from "../../../../assets/arrow-down.svg"; +import dropupIcon from "../../../../assets/arrow-up.svg"; +import arrowRightIcon from "../../../../assets/icon/arrow-right.svg"; +import profileIcon from "../../../../assets/logo/mini-logo.svg"; + +export default function ReceivedProposalContent() { + const [isContentOpen, setIsContentOpen] = useState(false); + + return ( +
+
+ +
+ {/* 1. 상단 섹션: 브랜드 카드 */} +
+ +
+

브랜드 제안 캠페인

+
+
+ + {/* 2. 상세 정보 섹션 */} +
+ {/* 캠페인명 */} + +
+ 비플레인 선크림 리뷰 콘텐츠 +
+
+ + {/* 캠페인 내용 */} + setIsContentOpen(prev => !prev)}> + toggle + + } + > +
+
+

설명

+
+ 안녕하세요 비플레인 입니다!
+ 크리에이터님과 이미지와 비플레인이 추구하는 가치가 잘 맞닿아 있다고 생각되어 협찬을 제안드립니다. +
+
+ + {/* 아코디언 상세 내용 */} + {isContentOpen && ( +
+
+ +
+ + + + +
+ )} +
+
+ + {/* 협찬품 / 원고료 */} +
+ +
+ 글로우 크림 1개 + arrow +
+
+ + +
+ 200,000 +
+
+
+ + {/* 제작 기간 */} + +
+
+ 2025년 1월 20일 +
+ ~ +
+ 2025년 1월 30일 +
+
+
+
+ + {/* 3. 하단 액션 버튼 (거절하기 / 제안 수락하기) */} +
+ + +
+
+
+ ); +} + +// 기존 ContentItem 재사용 +function ContentItem({ label, value }: { label: string; value: string }) { + return ( +
+

{label}

+
+ {value} + arrow +
+
+ ); +} \ No newline at end of file diff --git a/src/routes/_main/_business/proposal/route.tsx b/src/routes/_main/_business/proposal/route.tsx index 2ace6f0..3145bbd 100644 --- a/src/routes/_main/_business/proposal/route.tsx +++ b/src/routes/_main/_business/proposal/route.tsx @@ -1,5 +1,29 @@ import { createFileRoute } from '@tanstack/react-router' -import ProposalContent from "./proposal-content"; +import ProposalContent from "./sent-proposal-content"; +import ReceivedProposalContent from "./received-proposal-content"; + +// 쿼리 파라미터 타입 정의 +type ProposalSearch = { + type: 'sent' | 'received' +} + export const Route = createFileRoute('/_main/_business/proposal')({ - component: ProposalContent, + // validateSearch를 통해 쿼리 파라미터를 검증하고 타입 안정성을 확보합니다. + validateSearch: (search: Record): ProposalSearch => { + return { + type: (search.type as 'sent' | 'received') || 'sent', // 기본값은 'sent' + } + }, + component: ProposalComponent, }) + +function ProposalComponent() { + const { type } = Route.useSearch(); + + // type 값에 따라 다른 컨텐츠를 보여줍니다. + if (type === 'received') { + return ; + } + + return ; +} \ No newline at end of file diff --git a/src/routes/_main/_business/proposal/proposal-content.tsx b/src/routes/_main/_business/proposal/sent-proposal-content.tsx similarity index 100% rename from src/routes/_main/_business/proposal/proposal-content.tsx rename to src/routes/_main/_business/proposal/sent-proposal-content.tsx