@@ -11,7 +11,7 @@ import HistoryRow from "./components/HistoryRow";
1111import SponsorableProductSection from "./components/SponsorableProductSection" ;
1212
1313import { tokenStorage } from "../../lib/token" ;
14- import { toggleBrandLike } from "../matching/api/matching" ;
14+ import { toggleBrandLike , toggleCampaignLike } from "../matching/api/matching" ;
1515import { useCampaignProposalStore } from "../../stores/campaign-proposal" ;
1616
1717import { apiClient } from "../../api/axios" ;
@@ -143,7 +143,7 @@ const getNumberField = (
143143 const rec = obj as Record < string , unknown > ;
144144 for ( const k of keys ) {
145145 const v = rec [ k ] ;
146- if ( typeof v === "number" && Number . isFinite ( v ) && v > 0 ) return v ;
146+ if ( typeof v === "number" && Number . isFinite ( v ) && v >= 0 ) return v ;
147147 }
148148 return null ;
149149} ;
@@ -172,12 +172,14 @@ export default function BrandDetailContent({ data }: Props) {
172172 const navigate = useNavigate ( ) ;
173173 const [ searchParams ] = useSearchParams ( ) ;
174174 const brandId = Number ( searchParams . get ( "brandId" ) ) ;
175- const validBrandId = Number . isFinite ( brandId ) && brandId > 0 ;
175+ const validBrandId = Number . isFinite ( brandId ) && brandId >= 0 ;
176176
177177 const setProposalData = useCampaignProposalStore (
178178 ( state ) => state . setProposalData ,
179179 ) ;
180180
181+ const isHardcodedBeauty = brandId === 0 && searchParams . get ( "domain" ) === "beauty" ;
182+
181183 const baseOngoingCampaigns = useMemo < OngoingCampaign [ ] > (
182184 ( ) => data . ongoingCampaigns ?? [ ] ,
183185 [ data . ongoingCampaigns ] ,
@@ -187,19 +189,21 @@ export default function BrandDetailContent({ data }: Props) {
187189 Record < number , boolean >
188190 > ( { } ) ;
189191
192+
190193 const ongoingCampaigns = useMemo < OngoingCampaign [ ] > ( ( ) => {
191- if ( baseOngoingCampaigns . length === 0 ) return [ ] ;
192194 const overrides = ongoingLikeOverrides ;
193195
194- return baseOngoingCampaigns . map ( ( c ) => {
196+ const real = baseOngoingCampaigns . map ( ( c ) => {
195197 const cid = getCampaignIdFromOngoing ( c ) ;
196- if ( ! cid ) return c ;
198+ if ( cid === null ) return c ;
197199
198200 if ( Object . prototype . hasOwnProperty . call ( overrides , cid ) ) {
199201 return { ...( c as object ) , isLiked : overrides [ cid ] } as OngoingCampaign ;
200202 }
201203 return c ;
202204 } ) ;
205+
206+ return real ;
203207 } , [ baseOngoingCampaigns , ongoingLikeOverrides ] ) ;
204208
205209 const ongoingLikeInFlight = useRef < Set < number > > ( new Set ( ) ) ;
@@ -208,13 +212,17 @@ export default function BrandDetailContent({ data }: Props) {
208212 ProductMiniCardItem [ ]
209213 > ( [ ] ) ;
210214
211- const sponsorProducts = useMemo < ProductMiniCardItem [ ] > (
212- ( ) => ( validBrandId ? sponsorProductsRaw : [ ] ) ,
213- [ validBrandId , sponsorProductsRaw ] ,
214- ) ;
215+ const sponsorProducts = useMemo < ProductMiniCardItem [ ] > ( ( ) => {
216+ if ( ! validBrandId ) return [ ] ;
217+ // brandId=0일 때는 data.products를 직접 사용
218+ if ( brandId === 0 ) return data . products ?? [ ] ;
219+ return sponsorProductsRaw ;
220+ } , [ validBrandId , brandId , data . products , sponsorProductsRaw ] ) ;
215221
216222 useEffect ( ( ) => {
217223 if ( ! validBrandId ) return ;
224+ // brandId=0일 때는 API 호출 건너뛰기 (data.products 사용)
225+ //if (brandId === 0) return;
218226
219227 let alive = true ;
220228
@@ -278,16 +286,53 @@ export default function BrandDetailContent({ data }: Props) {
278286
279287 const domain = searchParams . get ( "domain" ) ;
280288
281- setProposalData ( {
282- brandId,
283- campaignId : 0 ,
284- domain : domain || "beauty" ,
285- brandName : data . name ,
286- products : sponsorProducts . map ( ( p ) => ( {
287- id : String ( p . productId ) ,
288- name : p . productName ,
289- } ) ) ,
290- } ) ;
289+ // brandId=0일 때는 광고 캠페인 정보 포함
290+ if ( brandId === 0 ) {
291+ setProposalData ( {
292+ brandId,
293+ campaignId : 0 ,
294+ domain : domain || "beauty" ,
295+ brandName : data . name ,
296+ campaignTitle : "'리얼이 캐릭터 크림' 론칭 리뷰" ,
297+ campaignDescription : "'리얼이 캐릭터 크림'\n겟레디윗미 영상에서 자연스럽게 노출" ,
298+ rewardAmount : 200000 ,
299+ product : "리얼이 캐릭터 크림 1개" ,
300+ startDate : "2025-01-05" ,
301+ endDate : "2025-01-22" ,
302+ contentTags : {
303+ formats : [ { id : 3 , name : "인스타 릴스" } ] ,
304+ categories : [
305+ { id : 6 , name : "리뷰" } ,
306+ { id : 7 , name : "겟레디윗미" } ,
307+ ] ,
308+ tones : [
309+ { id : 16 , name : "일상적인" } ,
310+ { id : 17 , name : "수다적인" } ,
311+ ] ,
312+ usageRanges : [
313+ { id : 24 , name : "크리에이터 1차활용" } ,
314+ { id : 25 , name : "브랜드 2차활용" } ,
315+ ] ,
316+ involvements : [ { id : 20 , name : "가이드만 제공" } ] ,
317+ } ,
318+ products : sponsorProducts . map ( ( p ) => ( {
319+ id : String ( p . productId ) ,
320+ name : p . productName ,
321+ } ) ) ,
322+ } ) ;
323+ } else {
324+ // 일반 브랜드는 기존 로직 유지
325+ setProposalData ( {
326+ brandId,
327+ campaignId : 0 ,
328+ domain : domain || "beauty" ,
329+ brandName : data . name ,
330+ products : sponsorProducts . map ( ( p ) => ( {
331+ id : String ( p . productId ) ,
332+ name : p . productName ,
333+ } ) ) ,
334+ } ) ;
335+ }
291336
292337 navigate ( "/matching/suggest" ) ;
293338 } ;
@@ -306,7 +351,7 @@ export default function BrandDetailContent({ data }: Props) {
306351
307352 const handleSponsorableProductClick = ( productId : number ) => {
308353 if ( ! validBrandId ) return ;
309- if ( ! Number . isFinite ( productId ) || productId <= 0 ) return ;
354+ if ( ! Number . isFinite ( productId ) ) return ;
310355
311356 navigate (
312357 `/products/sponsorable/detail?brandId=${ brandId } &productId=${ productId } ` ,
@@ -335,8 +380,8 @@ export default function BrandDetailContent({ data }: Props) {
335380 } ;
336381
337382 const goOngoingCampaignDetail = ( c : OngoingCampaign ) => {
338- const cid = getCampaignIdFromOngoing ( c ) ;
339- if ( ! cid ) return ;
383+ const cid = getCampaignIdFromOngoing ( c ) ?? ( c . campaignId === 0 ? 0 : null ) ;
384+ if ( cid === null ) return ;
340385
341386 const domainParam = searchParams . get ( "domain" ) ;
342387 const domain =
@@ -346,11 +391,11 @@ export default function BrandDetailContent({ data }: Props) {
346391
347392 const brandIdNum = validBrandId
348393 ? brandId
349- : Number . isFinite ( Number ( data . id ) ) && Number ( data . id ) > 0
394+ : Number . isFinite ( Number ( data . id ) ) && Number ( data . id ) >= 0
350395 ? Number ( data . id )
351396 : null ;
352397
353- if ( ! brandIdNum ) return ;
398+ if ( brandIdNum === null ) return ;
354399
355400 navigate (
356401 `/campaign?brandId=${ brandIdNum } &campaignId=${ cid } &domain=${ domain } ` ,
@@ -365,7 +410,7 @@ export default function BrandDetailContent({ data }: Props) {
365410 }
366411
367412 const clickedId = Number ( id ) ;
368- if ( ! Number . isFinite ( clickedId ) || clickedId <= 0 ) return ;
413+ if ( ! Number . isFinite ( clickedId ) || clickedId < 0 ) return ;
369414
370415 const currentItem = ongoingCampaigns . find ( ( c ) => {
371416 const cid = getCampaignIdFromOngoing ( c ) ;
@@ -374,7 +419,7 @@ export default function BrandDetailContent({ data }: Props) {
374419 if ( ! currentItem ) return ;
375420
376421 const cid = getCampaignIdFromOngoing ( currentItem ) ;
377- if ( ! cid ) return ;
422+ if ( cid === null ) return ;
378423
379424 if ( ongoingLikeInFlight . current . has ( cid ) ) return ;
380425 ongoingLikeInFlight . current . add ( cid ) ;
@@ -385,14 +430,24 @@ export default function BrandDetailContent({ data }: Props) {
385430
386431 setOngoingLikeOverrides ( ( m ) => ( { ...m , [ cid ] : next } ) ) ;
387432
388- ongoingLikeInFlight . current . delete ( cid ) ;
433+ try {
434+ await toggleCampaignLike ( cid ) ;
435+ } catch ( error ) {
436+ console . error ( "Failed to toggle campaign like:" , error ) ;
437+ setOngoingLikeOverrides ( ( m ) => ( { ...m , [ cid ] : prev } ) ) ;
438+ } finally {
439+ ongoingLikeInFlight . current . delete ( cid ) ;
440+ }
389441 } ;
390442
391443 const PAGE_SIZE = 4 ;
392444 const GROUP_SIZE = 4 ;
393445
394- const histories = data . histories ?? [ ] ;
395- const hasNext = ! ! data . historiesHasNext ;
446+ const histories = useMemo ( ( ) => {
447+ return data . histories ?? [ ] ;
448+ } , [ data . histories ] ) ;
449+
450+ const hasNext = isHardcodedBeauty ? false : ! ! data . historiesHasNext ;
396451
397452 const [ page , setPage ] = useState ( 1 ) ;
398453
@@ -457,8 +512,9 @@ export default function BrandDetailContent({ data }: Props) {
457512 < BrandInfo
458513 name = { data . name }
459514 matchRate = { data . matchRate }
460- hashtags = { ( data . hashtags ?? [ ] ) . slice ( 0 , 2 ) }
515+ hashtags = { data . hashtags ?? [ ] }
461516 description = { data . description }
517+ isAd = { brandId === 0 }
462518 />
463519
464520 < div className = "mt-3.5" >
@@ -486,6 +542,7 @@ export default function BrandDetailContent({ data }: Props) {
486542
487543 < section >
488544 { ( tagSections ?? [ ] ) . map ( ( sec , idx ) => {
545+
489546 const showTitle = showSectionTitle ;
490547
491548 return (
0 commit comments