diff --git a/app/routes/_main.tsx b/app/routes/_main.tsx index 8d011e9..480140b 100644 --- a/app/routes/_main.tsx +++ b/app/routes/_main.tsx @@ -29,7 +29,7 @@ export default function MainLayout() { useEffect(() => { mainRef.current?.scrollTo(0, 0); - }, [location.pathname]); + }, [location.pathname, location.search]); useEffect(() => { const accessToken = tokenStorage.getAccessToken(); diff --git a/app/routes/brand-detail/components/BrandHero.tsx b/app/routes/brand-detail/components/BrandHero.tsx index 6876ed8..ccfc920 100644 --- a/app/routes/brand-detail/components/BrandHero.tsx +++ b/app/routes/brand-detail/components/BrandHero.tsx @@ -35,8 +35,8 @@ export default function BrandHero({ 뒤로가기 -
-
+
+
{logoImageUrl ? ( +
{name}
@@ -25,7 +25,7 @@ export default function BrandInfo({
-
+
{hashtags .map((tag) => (tag.startsWith("#") ? tag : `#${tag}`)) .join(" ")} diff --git a/app/routes/brand-detail/components/OngoingCampaignSection.tsx b/app/routes/brand-detail/components/OngoingCampaignSection.tsx index e58f4c0..e5bcaf7 100644 --- a/app/routes/brand-detail/components/OngoingCampaignSection.tsx +++ b/app/routes/brand-detail/components/OngoingCampaignSection.tsx @@ -4,6 +4,7 @@ import type { BrandOngoingCampaign } from "../types"; type Props = { campaigns: BrandOngoingCampaign[]; + brandLogoUrl?: string; onMore?: () => void; onCampaignClick?: (c: BrandOngoingCampaign) => void; onLikeToggle?: (id: string) => void; @@ -11,6 +12,7 @@ type Props = { export default function OngoingCampaignSection({ campaigns, + brandLogoUrl, onMore, onCampaignClick, onLikeToggle, @@ -18,46 +20,28 @@ export default function OngoingCampaignSection({ const isEmpty = campaigns.length === 0; return ( -
+
-
진행 중인 캠페인
+
진행 중인 다른 캠페인
- {onMore ? ( + {!isEmpty && onMore ? ( - ) : ( -
- )} + ) : null}
{isEmpty ? ( -
-
-
-
-
- 진행 중인 캠페인이 없어요 -
+
+
+
+
+ 진행 중인 다른 캠페인이 없어요
@@ -80,7 +64,7 @@ export default function OngoingCampaignSection({ className="text-left" > onCampaignClick?.(c)} onLikeToggle={onLikeToggle} /> diff --git a/app/routes/brand-detail/components/toCampaignItem.tsx b/app/routes/brand-detail/components/toCampaignItem.tsx index 1b7f747..f741f54 100644 --- a/app/routes/brand-detail/components/toCampaignItem.tsx +++ b/app/routes/brand-detail/components/toCampaignItem.tsx @@ -1,14 +1,14 @@ import type { CampaignItem } from "../../home/types"; import type { BrandOngoingCampaign } from "../types"; -export function toCampaignItem(campaign: BrandOngoingCampaign): CampaignItem { +export function toCampaignItem(campaign: BrandOngoingCampaign, brandLogoUrl?: string): CampaignItem { const ddayLabel = campaign.dday === 0 ? "D-DAY" : `D-${campaign.dday}`; const result: CampaignItem = { id: String(campaign.campaignId), brandName: campaign.brandName || "", - logoUrl: campaign.imageUrl || undefined, + logoUrl: brandLogoUrl || campaign.imageUrl || undefined, progressText: String(campaign.recruitQuota || 0), ddayLabel, descText: campaign.title || "", diff --git a/app/routes/campaign-detail/campaign-detail.tsx b/app/routes/campaign-detail/campaign-detail.tsx index beef326..e374488 100644 --- a/app/routes/campaign-detail/campaign-detail.tsx +++ b/app/routes/campaign-detail/campaign-detail.tsx @@ -30,11 +30,6 @@ type OngoingCampaign = NonNullable[number]; const fmtMoney = (n?: number) => Number.isFinite(n) ? `${Number(n).toLocaleString()}원` : "-"; -const formatDateOnly = (iso?: string) => { - if (!iso) return "-"; - return iso.split("T")[0]; -}; - const joinTagNames = (items?: { name: string }[]) => (items ?? []).map((x) => x.name).filter(Boolean); @@ -90,6 +85,10 @@ export default function CampaignDetailContent({ const [searchParams] = useSearchParams(); const setProposalData = useCampaignProposalStore((s) => s.setProposalData); + useEffect(() => { + window.scrollTo(0, 0); + }, [campaignId]); + const heroUrl = brandData.brandImages?.[0] ?? brandData.heroImageUrl; const [isCampaignLiked, setIsCampaignLiked] = useState(false); @@ -127,16 +126,8 @@ export default function CampaignDetailContent({ } setCampaign(res.data.result); + setIsCampaignLiked(res.data.result.like ?? false); setCampaignError(null); - - const liked = (() => { - const r: unknown = res.data.result; - if (!r || typeof r !== "object") return false; - const rec = r as Record; - return rec["isLiked"] === true; - })(); - - setIsCampaignLiked(liked); } catch { if (!alive) return; setCampaignError("캠페인 정보를 불러오지 못했어요."); @@ -155,14 +146,14 @@ export default function CampaignDetailContent({ { label: "모집인원", value: `${campaign.quota}명` }, { label: "우대사항", value: campaign.preferredSkills || "-" }, { label: "제품협찬", value: campaign.product || "-" }, - { label: "원고료", value: fmtMoney(campaign.rewardAmount) }, + { label: "원고료", value: `${fmtMoney(campaign.rewardAmount)} (VAT포함)` }, { label: "일정", value: campaign.schedule || "-" }, - { - label: "모집기간", - value: `${formatDateOnly(campaign.recruitStartDate)} ~ ${formatDateOnly( - campaign.recruitEndDate, - )}`, - }, + // { + // label: "모집기간", + // value: `${formatDateOnly(campaign.recruitStartDate)} ~ ${formatDateOnly( + // campaign.recruitEndDate, + // )}`, + // }, ]; }, [campaign]); @@ -174,10 +165,10 @@ export default function CampaignDetailContent({ { label: "설명", value: campaign.description || "-" }, { label: "개수 및 길이", value: campaign.videoSpec || "-" }, { label: "콘텐츠 형식", chips: joinTagNames(tags?.formats) }, - { label: "카테고리", chips: joinTagNames(tags?.categories) }, - { label: "톤", chips: joinTagNames(tags?.tones) }, - { label: "참여도", chips: joinTagNames(tags?.involvements) }, - { label: "사용 범위", chips: joinTagNames(tags?.usageRanges) }, + { label: "콘텐츠 종류", chips: joinTagNames(tags?.categories) }, + { label: "컨톤체 톤", chips: joinTagNames(tags?.tones) }, + { label: "콘텐츠 활용범위", chips: joinTagNames(tags?.usageRanges) }, + { label: "콘텐츠 관려도", chips: joinTagNames(tags?.involvements) }, ]; }, [campaign]); @@ -395,7 +386,7 @@ export default function CampaignDetailContent({ const campaignImage = campaign.imageUrl ?? heroUrl; return ( -
+
-
+
-
+
} @@ -439,10 +430,10 @@ export default function CampaignDetailContent({ onToggleHeart={handleToggleHeart} /> -
+
-
+
campaign
-
-
상세 설명
-
+
+
상세 설명
+
{detailRows.map((row) => (
-
+
-
+
콘텐츠
-
+
{contentRows.map((row) => ( -
-
- {row.label} -
- -
- {"value" in row && row.value ? ( -
- {row.value} -
- ) : ( -
- {(row.chips ?? []).map((c) => ( - - {c} - - ))} - {(!row.chips || row.chips.length === 0) && ( - - - )} -
- )} -
-
+ ))}
-
+
@@ -26,7 +27,7 @@ export default function CampaignActionBar({ diff --git a/app/routes/campaign-detail/route.tsx b/app/routes/campaign-detail/route.tsx index d898144..919d626 100644 --- a/app/routes/campaign-detail/route.tsx +++ b/app/routes/campaign-detail/route.tsx @@ -21,6 +21,7 @@ export default function CampaignDetailRoute() { const brandIdParam = searchParams.get("brandId"); const domainParam = searchParams.get("domain"); const campaignIdParam = searchParams.get("campaignId"); + const matchRateParam = searchParams.get("matchRate"); const campaignId = useMemo(() => { const n = campaignIdParam ? Number(campaignIdParam) : NaN; @@ -104,7 +105,14 @@ export default function CampaignDetailRoute() { brandId: String(resolvedBrandId), domain: resolvedDomain ?? undefined, }); + if (!alive) return; + + const paramRate = matchRateParam ? Number(matchRateParam) : NaN; + if (Number.isFinite(paramRate) && paramRate > 0) { + res.matchRate = paramRate; + } + setData(res); setError(null); } catch { diff --git a/app/routes/home/home-after-match.tsx b/app/routes/home/home-after-match.tsx index 54b24ec..8b30c28 100644 --- a/app/routes/home/home-after-match.tsx +++ b/app/routes/home/home-after-match.tsx @@ -231,8 +231,9 @@ export default function HomeAfterMatchPage() { const brandId = getBrandIdFromCampaign(c); if (!campaignId || !brandId) return; + const rate = c.matchRate || 0; navigate( - `/campaign?brandId=${brandId}&campaignId=${campaignId}&domain=${category}`, + `/campaign?brandId=${brandId}&campaignId=${campaignId}&domain=${category}&matchRate=${rate}`, ); }; diff --git a/app/routes/matching/campaign/campaign-content.tsx b/app/routes/matching/campaign/campaign-content.tsx index 52fe926..40d48a3 100644 --- a/app/routes/matching/campaign/campaign-content.tsx +++ b/app/routes/matching/campaign/campaign-content.tsx @@ -205,7 +205,7 @@ export default function CampaignContent() { applicants={campaign.applicants} isLiked={campaign.isLiked} onLike={() => toggleLike(campaign.id)} - onClick={() => navigate(`/campaign?campaignId=${campaign.id}&brandId=${campaign.brandId}&domain=${category.toLowerCase()}`)} + onClick={() => navigate(`/campaign?campaignId=${campaign.id}&brandId=${campaign.brandId}&domain=${category.toLowerCase()}&matchRate=${campaign.matchingRatio || campaign.matchRate || 0}`)} logoUrl={campaign.logoUrl || `/dummy-logo-${campaign.id}.png`} dDay={campaign.dDay} />