Skip to content
Merged
Show file tree
Hide file tree
Changes from 28 commits
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
b001f51
stlye : 데이터 피컀 λ””μžμΈ μˆ˜μ •
KongMezu Nov 12, 2025
dc661e4
api : 행사 api 연동
KongMezu Nov 12, 2025
aa595f2
Merge branch 'develop' of https://github.com/cukCS/FE into api/#114/e…
KongMezu Nov 12, 2025
2ad0b18
fix : 헀더 νŒ¨λ”©κ°’ κ³ μΉ¨
KongMezu Nov 12, 2025
ac01835
feat : api 쑰회 성곡
KongMezu Nov 13, 2025
b1fe18a
api : 행사 쑰회
KongMezu Nov 13, 2025
cc14c8e
remove : λΆˆν•„μš”ν•œ import 제거
KongMezu Nov 13, 2025
a1badcd
feat : μ˜€λ²„λ ˆμ΄ z-index μˆ˜μ •
KongMezu Nov 13, 2025
118f98c
remove: λͺ©μ—… 데이터 제거
KongMezu Nov 13, 2025
0ae7073
Merge branch 'develop' of https://github.com/cukCS/FE into api/#114/e…
KongMezu Nov 13, 2025
921aa3f
feat : 상세 νŽ˜μ΄μ§€ 쑰회 api μ—°κ²°
KongMezu Nov 13, 2025
99bab3f
feat : 전체λͺ©λ‘ 사진 쑰회
KongMezu Nov 13, 2025
e97ecf6
Merge branch 'develop' of https://github.com/cukCS/FE into api/#114/e…
KongMezu Nov 13, 2025
2119aa6
fix : instance 였λ₯˜
KongMezu Nov 13, 2025
0969058
fix : λΆˆν•„μš”ν•œ λ‘œλ”© 제거
KongMezu Nov 13, 2025
13eed95
feat : 뢁마크 μƒνƒœ 쑰회
KongMezu Nov 14, 2025
7359e55
feat : κ΄€λ ¨ 행사듀 클릭
KongMezu Nov 14, 2025
389e160
feat : event 뢁마크 동기화
KongMezu Nov 14, 2025
e730c25
feat : λ§ˆμ΄νŽ˜μ΄μ§€ 적용
KongMezu Nov 14, 2025
b2de4d1
fix : build error
KongMezu Nov 14, 2025
5c8cf5d
feat : instance μˆ˜μ €
KongMezu Nov 14, 2025
bc38265
Merge branch 'develop' of https://github.com/cukCS/FE into api/#114/e…
KongMezu Nov 14, 2025
2a5017a
Merge branch 'develop' of https://github.com/cukCS/FE into api/#114/e…
KongMezu Nov 15, 2025
0d34fb2
Merge branch 'develop' of https://github.com/cukCS/FE into api/#114/e…
KongMezu Nov 15, 2025
c46fc31
fix : 뢁마크 동기화
KongMezu Nov 15, 2025
6098283
feat : 뢁마크 동기화 μ™„λ£Œ
KongMezu Nov 15, 2025
1b116df
feat : λ§ˆμ΄νŽ˜μ΄μ§€ μ—°κ²° μ™„λ£Œ
KongMezu Nov 15, 2025
7a954e9
feat : λ§ˆμ΄νŽ˜μ΄μ§€ 상세 μ—°κ²°
KongMezu Nov 15, 2025
27e232b
feat : 이전 λˆ„λ₯΄λ©΄ λ‚ μ§œ 쿼리 κ°€μ§€κ³  λŒμ•„κ°€κΈ°
KongMezu Nov 15, 2025
dc6070d
fix : λ‚ μ§œ λŒμ•„κ°€κΈ°
KongMezu Nov 15, 2025
fcbaee2
fix : build error
KongMezu Nov 15, 2025
cdc4f5c
feat : 이벀트 μΉ΄λ“œ
KongMezu Nov 15, 2025
a28d795
feat : μ½”λ“œ λž˜λΉ— ν”Όλ“œλ°±
KongMezu Nov 15, 2025
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
11 changes: 9 additions & 2 deletions next.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,17 @@ const nextConfig: NextConfig = {
domains: [
'maps.googleapis.com',
'geulda-ai-video-bucket.s3.ap-southeast-2.amazonaws.com',
'example.com'
'example.com',
'www.bucheon.go.kr',
'www.bcf.or.kr',
],
remotePatterns: [
{ protocol: 'https', hostname: 'mblogthumb-phinf.pstatic.net' },
{ protocol: 'https', hostname: 'blogfiles.pstatic.net' },
{ protocol: 'https', hostname: 'postfiles.pstatic.net' },
],
},

webpack: (config) => {
const svgRule = config.module.rules.find(
// @ts-ignore
Expand Down
122 changes: 72 additions & 50 deletions src/pages/events/[id].tsx
Original file line number Diff line number Diff line change
@@ -1,63 +1,82 @@
'use client';

import { useEffect } from 'react';
import { Header, EventCard } from '@/shared/components';
import DateTag from '@/pages/events/components/DateTag';
import { cn } from '@/shared/lib';
import { eventData } from '@/shared/constants/events/eventsData';
import Image from 'next/image';
import { useRouter } from 'next/router';
import { useEventDetail } from '@/shared/hooks/events/useEventDetail';
import { buildNextEventList } from '@/shared/utils/buildNextEventList';
import type { RelatedEventOrEmpty } from '@/shared/types/eventtypes';

const isEmptyItem = (
item: RelatedEventOrEmpty
): item is { isEmpty: true } => 'isEmpty' in item;

const EventDetailPage = () => {
const router = useRouter();
const { id } = router.query;

const event = eventData.find((e) => e.id === Number(id));
if (!event) return null;
const eventId = Number(id);
const { data: eventDetail, isLoading, isError } = useEventDetail(eventId);

useEffect(() => {
if (!isLoading && (isError || !eventDetail)) {
router.replace('/events');
}
}, [isLoading, isError, eventDetail, router]);

if (!eventId) return null;
if (isError || !eventDetail) return null;

const { name, address, description, startDate, endDate, imageSrc } = event;
const { title, body, address, startDate, endDate, imageUrl, nextEvents } =
eventDetail;

return (
<div
className={cn(
'relative w-full min-h-[100vh] overflow-auto',
)}
>
const nextList = buildNextEventList(nextEvents);

return (
<div className={cn('relative w-full min-h-[100vh] overflow-auto')}>
<Header
title='행사λͺ…'
title="행사λͺ…"
onClick={() => router.back()}
className={cn('fixed top-0 left-0 right-0 z-50')}
/>

<main
className={cn(
'flex flex-col items-center justify-start',
'px-[2.4rem] pt-[calc(10rem+1.4rem)]'
'px-[2.4rem] pt-[calc(10rem+1.4rem)]',
)}
>
{/* 행사 κΈ°κ°„ */}
<div aria-label="행사 κΈ°κ°„" className={cn('flex justify-center w-[18.4rem] mt-[1.3rem]')}>
<DateTag startDate={startDate} endDate={endDate} />
<div
aria-label="행사 κΈ°κ°„"
className={cn('flex justify-center w-[18.4rem] mt-[1.3rem]')}
>
<DateTag startDate={startDate!} endDate={endDate!} />
</div>

{/* λŒ€ν‘œ 이미지 */}
<section
aria-label="행사 λŒ€ν‘œ 이미지"
className={cn(
'relative w-full flex justify-center max-w-[35.4rem]',
'mt-[1rem]'
'relative w-full flex justify-center max-w-[35.4rem] h-[43rem]',
'mt-[1rem]',
)}
>
{imageSrc ? (
{imageUrl ? (
<Image
src={imageSrc}
alt={`${name} 이미지`}
width={354}
height={430}
className={cn('w-full h-auto object-cover rounded-[2rem]')}
src={imageUrl}
alt={`${title} 이미지`}
fill
className={cn('object-cover rounded-[2rem]')}
/>
) : (
<div
className={cn('w-full h-[43.6rem] bg-gray-200 rounded-[2rem]')}
className={cn('w-full h-full bg-gray-200 rounded-[2rem]')}
role="img"
aria-label={`${name} 이미지가 μ œκ³΅λ˜μ§€ μ•ŠμŠ΅λ‹ˆλ‹€.`}
aria-label={`${title} 이미지가 μ œκ³΅λ˜μ§€ μ•ŠμŠ΅λ‹ˆλ‹€.`}
/>
)}
</section>
Expand All @@ -67,47 +86,50 @@ const EventDetailPage = () => {
aria-label="행사 정보"
className={cn(
'flex flex-col items-center w-full gap-[0.8rem]',
'mt-[0.8rem]'
'mt-[0.8rem]',
)}
>
<EventCard
name={name}
address={address}
description={description}
variant='gray'
size='large'
eventId={eventId}
name={title}
address={address ?? ''}
description={body ?? ''}
variant="gray"
size="large"
imageSrc={imageUrl ?? ''}
liked={eventDetail.isBookmarked ?? false}
/>

{/* κ΄€λ ¨ 행사 */}
<div
aria-label="κ΄€λ ¨ 행사 λͺ©λ‘"
className={cn(
'grid grid-cols-2 gap-[1.2rem] justify-items-center w-full max-w-[35.4rem]'
'grid grid-cols-2 gap-[1.2rem] justify-items-center w-full max-w-[35.4rem]',
)}
>
<div className={cn('w-[17rem]')}>
<EventCard
name='κ΄€λ ¨ 행사'
address=''
description=''
variant='gray'
size='small'
/>
</div>
<div className={cn('w-[17rem]')}>
<EventCard
name='κ΄€λ ¨ 행사'
address=''
description=''
variant='gray'
size='small'
/>
</div>
{nextList.map((item: RelatedEventOrEmpty, idx) => (
<div key={idx} className={cn('w-[17rem]')}>
{isEmptyItem(item) ? (
<div className="w-[17rem] h-[8rem] bg-gray-200 rounded-[1rem]" />
) : (
<EventCard
eventId={item.eventId}
name={item.title}
address=""
description=""
imageSrc={item.imageUrl}
variant="gray"
size="small"
liked={false}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

κ΄€λ ¨ ν–‰μ‚¬μ˜ 뢁마크 μƒνƒœ 확인이 ν•„μš”ν•©λ‹ˆλ‹€.

κ΄€λ ¨ ν–‰μ‚¬μ˜ liked 속성이 false둜 ν•˜λ“œμ½”λ”©λ˜μ–΄ μžˆμŠ΅λ‹ˆλ‹€. PR λͺ©ν‘œμ— "뢁마크 UI 동기화 확인 (todo)"κ°€ μ–ΈκΈ‰λ˜μ–΄ μžˆμœΌλ―€λ‘œ, μΆ”ν›„ μž‘μ—…μœΌλ‘œ μ‹€μ œ 뢁마크 μƒνƒœλ₯Ό λ°˜μ˜ν•΄μ•Ό ν•©λ‹ˆλ‹€.

ν˜„μž¬ RelatedEvent νƒ€μž…μ— isBookmarked ν•„λ“œκ°€ μ—†λŠ”μ§€ ν™•μΈν•˜μ„Έμš”:


🏁 Script executed:

#!/bin/bash
# RelatedEvent νƒ€μž… μ •μ˜ 확인
ast-grep --pattern $'interface RelatedEvent {
  $$$
}'

Length of output: 259


🏁 Script executed:

#!/bin/bash
# Check the actual code context around line 123 in [id].tsx
sed -n '115,130p' src/pages/events/[id].tsx

Length of output: 445


🏁 Script executed:

#!/bin/bash
# Search for RelatedEvent component usage and bookmark-related props
rg -n "liked\s*=" src/pages/events/[id].tsx -A 2 -B 2

Length of output: 313


🏁 Script executed:

#!/bin/bash
# Check if there's an Event type with bookmark state that should be mapped to RelatedEvent
rg -n "interface Event|type Event" src/shared/types/ -A 10

Length of output: 1937


RelatedEvent νƒ€μž…μ— isBookmarked ν•„λ“œλ₯Ό μΆ”κ°€ν•΄μ•Ό ν•©λ‹ˆλ‹€.

검증 κ²°κ³Ό:

  • RelatedEvent μΈν„°νŽ˜μ΄μŠ€μ— isBookmarked ν•„λ“œκ°€ μ‹€μ œλ‘œ μ—†μŒ (src/shared/types/eventtypes.ts:23-27)
  • 메인 ν–‰μ‚¬λŠ” eventDetail.isBookmarked둜 μ˜¬λ°”λ₯΄κ²Œ 처리 (line 100)
  • κ΄€λ ¨ ν–‰μ‚¬λŠ” liked={false}둜 ν•˜λ“œμ½”λ”©λ¨ (line 123)

ν•„μš”ν•œ μž‘μ—…:

  1. src/shared/types/eventtypes.ts의 RelatedEvent μΈν„°νŽ˜μ΄μŠ€μ— isBookmarked?: boolean ν•„λ“œ μΆ”κ°€
  2. API μ‘λ‹΅μ—μ„œ κ΄€λ ¨ ν–‰μ‚¬μ˜ 뢁마크 μƒνƒœ 데이터 포함 확인
  3. src/pages/events/[id].tsx:123의 liked={false}λ₯Ό liked={item.isBookmarked ?? false}둜 λ³€κ²½

μ΄λŠ” PR λͺ©ν‘œμΈ "뢁마크 UI 동기화 확인 (todo)"λ₯Ό μ™„λ£Œν•˜λŠ” 데 ν•„μˆ˜μž…λ‹ˆλ‹€.

πŸ€– Prompt for AI Agents
In src/pages/events/[id].tsx around line 123 and src/shared/types/eventtypes.ts
around lines 23-27: RelatedEvent is missing an isBookmarked field and the
related-event component is hardcoding liked to false; add an optional boolean
isBookmarked? field to the RelatedEvent interface in
src/shared/types/eventtypes.ts, ensure the API response for related events
includes the bookmark state (update backend/serialization or mapper as needed),
and replace the hardcoded liked={false} in src/pages/events/[id].tsx with a prop
that uses the related item’s isBookmarked value with a safe fallback (use
item.isBookmarked if present, otherwise false).

/>
)}
</div>
))}
</div>
</div>
</main>
</div>
);
};

export default EventDetailPage;
export default EventDetailPage;
37 changes: 20 additions & 17 deletions src/pages/events/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,18 +10,15 @@ import {
BottomNav,
EventCard,
} from '@/shared/components';
import { eventData } from '@/shared/constants/events/eventsData';
import { formatDateToISO, isDateWithinRange } from '@/shared/utils/date';
import { useEvents } from '@/shared/hooks/events/useEvents';
import type {EventData} from '@/shared/types/eventtypes';

export default function EventPage() {
const router = useRouter();
const [date, setDate] = useState<Date>();
const { events } = useEvents(date);

const selectedDate = formatDateToISO(date);

const filteredEvents = eventData.filter((event) =>
isDateWithinRange(selectedDate, event.startDate, event.endDate),
);
const filteredEvents = events;

const handleCardClick = (id: number) => {
router.push(`/events/${id}`);
Expand All @@ -34,47 +31,53 @@ export default function EventPage() {
)}
>
{/* 헀더 */}
<ControlBar className="fixed top-[1rem] left-0 right-0 z-50 px-[2rem]" />
<ControlBar className='fixed top-[1rem] left-0 right-0 z-50 px-[2rem]' />

{/* λ³Έλ¬Έ μ½˜ν…μΈ  */}
<main className='w-full pt-[6.3rem] flex flex-col items-center'>
{/* λ‚ μ§œ 선택 */}
<div className='w-full mt-[3.7rem] flex justify-start'>
{/* μŠ€ν¬λ¦°λ¦¬λ”κ°€ β€œλ‚ μ§œ μ„ νƒβ€μœΌλ‘œ μ½νžˆλ„λ‘ μΆ”κ°€ */}
<label htmlFor="event-date" className="sr-only">
<label htmlFor='event-date' className='sr-only'>
행사 λ‚ μ§œ 선택
</label>
<DatePicker ariaLabel="행사 λ‚ μ§œ 선택" value={date} onChange={setDate} />
<DatePicker
ariaLabel='행사 λ‚ μ§œ 선택'
value={date}
onChange={setDate}
/>
</div>

{/* ν–‰μ‚¬μΉ΄λ“œ & λΉˆν™”λ©΄ */}
{filteredEvents.length > 0 ? (
<section
aria-label="이벀트 λͺ©λ‘"
aria-label='이벀트 λͺ©λ‘'
className={cn(
'grid w-full mt-[1.4rem]',
'grid-cols-2 gap-x-[1.4rem] gap-y-[1.4rem]',
)}
>
{filteredEvents.map((event) => (
{filteredEvents.map((event: EventData) => (
<div
key={event.id}
onClick={() => handleCardClick(event.id)}
className='cursor-pointer'
className="cursor-pointer"
>
<EventCard
eventId={event.id}
name={event.name}
address={event.address}
description={event.description}
variant='gray'
size='medium'
imageSrc={event.imageSrc ?? ''}
variant="gray"
size="medium"
imageSrc={event.imageSrc}
liked={event.liked}
/>
</div>
))}
</section>
) : (
<div
<div
className='flex flex-col items-center justify-center text-center mt-[15rem]'
role='status'
aria-live='polite'
Expand Down
51 changes: 32 additions & 19 deletions src/pages/mypage/events/[id].tsx
Original file line number Diff line number Diff line change
@@ -1,18 +1,28 @@
import { useEffect } from 'react';
import { Header, EventCard, AddressCopy } from '@/shared/components';
import DateTag from '@/pages/events/components/DateTag';
import { cn } from '@/shared/lib';
import { eventData } from '@/shared/constants/events/eventsData';
import Image from 'next/image';
import { useRouter } from 'next/router';
import { useEventDetail } from '@/shared/hooks/events/useEventDetail';

const EventSavePage = () => {
const router = useRouter();
const { id } = router.query;

const event = eventData.find((e) => e.id === Number(id));
if (!event) return null;
const eventId = Number(id);
const { data: eventDetail, isLoading, isError } = useEventDetail(eventId);

const { name, address, description, startDate, endDate, imageSrc } = event;
useEffect(() => {
if (!isLoading && (isError || !eventDetail)) {
router.replace('/mypage');
}
}, [isLoading, isError, eventDetail, router]);

if (!eventId) return null;
if (isError || !eventDetail) return null;

const { title, body, address, startDate, endDate, imageUrl } = eventDetail;

return (
<div className={cn('relative w-full min-h-[100vh] overflow-auto')}>
Expand All @@ -23,8 +33,8 @@ const EventSavePage = () => {
/>

<main
role="main"
aria-label="μ €μž₯ν•œ 행사 상세 νŽ˜μ΄μ§€"
role='main'
aria-label='μ €μž₯ν•œ 행사 상세 νŽ˜μ΄μ§€'
className={cn(
'flex flex-col items-center justify-start',
'px-[2.4rem] pt-[calc(10rem+1.4rem)]',
Expand All @@ -38,21 +48,22 @@ const EventSavePage = () => {
{/* λŒ€ν‘œ 이미지 */}
<section
className={cn(
'relative w-full flex justify-center max-w-[35.4rem]',
'relative w-full flex justify-center max-w-[35.4rem] h-[43rem]',
'mt-[1rem]',
)}
>
{imageSrc ? (
{imageUrl ? (
<Image
src={imageSrc}
alt={`${name} 이미지`}
width={354}
height={430}
className={cn('w-full h-auto object-cover rounded-[2rem]')}
src={imageUrl}
alt={`${title} 이미지`}
fill
className='object-cover rounded-[2rem]'
/>
) : (
<div
className={cn('w-full h-[43.6rem] bg-gray-200 rounded-[2rem]')}
className='w-full h-full bg-gray-200 rounded-[2rem]'
role='img'
aria-label={`${title} 이미지가 μ œκ³΅λ˜μ§€ μ•ŠμŠ΅λ‹ˆλ‹€.`}
/>
)}
</section>
Expand All @@ -65,15 +76,17 @@ const EventSavePage = () => {
)}
>
<EventCard
name={name}
address={address}
description={description}
eventId={eventId}
name={title}
address={address ?? ''}
description={body ?? ''}
variant='gray'
size='large'
imageSrc={imageUrl ?? ''}
liked={eventDetail.isBookmarked ?? true}
/>

{/* μ£Όμ†Œλ³΅μ‚¬ */}
<AddressCopy variant='gray' value={address} />
<AddressCopy variant='gray' value={address ?? ''} />
</div>
</main>
</div>
Expand Down
Loading
Loading