diff --git a/apps/audience/src/entities/festival/api/festival.ts b/apps/audience/src/entities/festival/api/festival.ts index 09b5bd07..82192b35 100644 --- a/apps/audience/src/entities/festival/api/festival.ts +++ b/apps/audience/src/entities/festival/api/festival.ts @@ -7,23 +7,15 @@ import type { WishListResponseData, } from '@shared/types/festival'; import type { - AllFestivalsResponse, - NicknameResponse, + FestivalsResponse, UpcomingFestivalResponse, - UpcomingFestivalsResponse, } from '@shared/types/home-response'; export const getAllFestivals = (params: PageSizeParams = {}) => - get( - END_POINT.GET_ALL_FESTIVALS, - params, - ); + get(END_POINT.GET_ALL_FESTIVALS, params); -export const getPlannedFestivals = (params: PageSizeParams = {}) => - get( - END_POINT.GET_PLANNED_FESTIVALS, - params, - ); +export const getWishlists = (params: PageSizeParams = {}) => + get(END_POINT.GET_WISHLISTS, params); export const getUpcomingFestival = (params: PageSizeParams = {}) => get( @@ -36,6 +28,3 @@ export const putWishList = (festivalId: number, body: WishListRequest) => END_POINT.PUT_WISH_LIST(festivalId), body, ); - -export const getUserNickname = () => - get(END_POINT.GET_NICKNAME); diff --git a/apps/audience/src/entities/festival/model/query-options.ts b/apps/audience/src/entities/festival/model/query-options.ts index 8f8409f8..6d4c9655 100644 --- a/apps/audience/src/entities/festival/model/query-options.ts +++ b/apps/audience/src/entities/festival/model/query-options.ts @@ -6,9 +6,8 @@ import { USERS_QUERY_KEY } from '@shared/constants/query-key'; import { getAllFestivals, - getPlannedFestivals, getUpcomingFestival, - getUserNickname, + getWishlists, } from '../api/festival'; export const FESTIVAL_QUERY_OPTIONS = { @@ -20,16 +19,11 @@ export const FESTIVAL_QUERY_OPTIONS = { PLANNED_FESTIVALS: (params: PageSizeParams = { page: 0, size: 20 }) => queryOptions({ queryKey: [...USERS_QUERY_KEY.HOME_FESTIVALS_PLANNED(), params], - queryFn: () => getPlannedFestivals(params), + queryFn: () => getWishlists(params), }), UPCOMING_FESTIVAL: (params: PageSizeParams = {}) => queryOptions({ queryKey: [...USERS_QUERY_KEY.HOME_FESTIVAL_UPCOMING(), params], queryFn: () => getUpcomingFestival(params), }), - NICKNAME: () => - queryOptions({ - queryKey: [...USERS_QUERY_KEY.NICKNAME()], - queryFn: () => getUserNickname(), - }), } as const; diff --git a/apps/audience/src/features/my-events/query.ts b/apps/audience/src/features/my-events/query.ts deleted file mode 100644 index 3e77dabb..00000000 --- a/apps/audience/src/features/my-events/query.ts +++ /dev/null @@ -1,19 +0,0 @@ -import { queryOptions } from '@tanstack/react-query'; - -import { get } from '@amp/apis'; -import type { PageSizeParams } from '@amp/shared/types'; - -import { END_POINT } from '@shared/constants/end-point'; -import { USERS_QUERY_KEY } from '@shared/constants/query-key'; -import type { MyEventsResponse } from '@shared/types/my-events-response'; - -export const getMyEvents = (params: PageSizeParams = {}) => - get(END_POINT.GET_MY_FESTIVALS_ALL, params); - -export const MY_EVENTS_QUERY_OPTIONS = { - LIST: (params: PageSizeParams = {}) => - queryOptions({ - queryKey: [...USERS_QUERY_KEY.MY_FESTIVALS_ALL(), params], - queryFn: () => getMyEvents(params), - }), -} as const; diff --git a/apps/audience/src/pages/home/home.tsx b/apps/audience/src/pages/home/home.tsx index 567c73e0..40148149 100644 --- a/apps/audience/src/pages/home/home.tsx +++ b/apps/audience/src/pages/home/home.tsx @@ -1,29 +1,38 @@ import { useQuery } from '@tanstack/react-query'; +import { useNavigate } from 'react-router'; import { HomeBanner } from '@amp/compositions'; import FestivalSection from '@widgets/home/components/festival-section/festival-section'; -import { FESTIVAL_QUERY_OPTIONS } from '@entities/festival/model/query-options'; +import { USER_QUERY_OPTIONS } from '@entities/user/model/query-options'; + +import { ROUTE } from '@shared/constants/path'; import useHomeFestivals from './model/use-home-festivals'; import { page } from './home.css'; const HomePage = () => { + const navigate = useNavigate(); + const { data } = useQuery({ - ...FESTIVAL_QUERY_OPTIONS.NICKNAME(), + ...USER_QUERY_OPTIONS.NICKNAME(), }); const nickname = data?.nickname; const { allFestivals, - upcomingFestivals, + plannedFestivals, bannerFestival, selectedTab, setSelectedTab, } = useHomeFestivals(); + const handleCardClick = (festivalId: number) => { + navigate(ROUTE.noticeList(festivalId)); + }; + return (
{bannerFestival ? ( @@ -43,7 +52,8 @@ const HomePage = () => { selectedTab={selectedTab} onTabChange={setSelectedTab} allFestivals={allFestivals} - upcomingFestivals={upcomingFestivals} + plannedFestivals={plannedFestivals} + onCardClick={handleCardClick} />
); diff --git a/apps/audience/src/pages/home/model/use-home-festivals.ts b/apps/audience/src/pages/home/model/use-home-festivals.ts index 84fcd007..75aab92e 100644 --- a/apps/audience/src/pages/home/model/use-home-festivals.ts +++ b/apps/audience/src/pages/home/model/use-home-festivals.ts @@ -28,7 +28,7 @@ const useHomeFestivals = () => { return { allFestivals: allFestivalsData?.festivals ?? [], - upcomingFestivals: plannedFestivalsData?.festivals ?? [], + plannedFestivals: plannedFestivalsData?.festivals ?? [], bannerFestival: upcomingFestivalData ? { festivalId: upcomingFestivalData.festivalId, @@ -36,7 +36,7 @@ const useHomeFestivals = () => { mainImageUrl: upcomingFestivalData.mainImageUrl, location: upcomingFestivalData.location, period: `${upcomingFestivalData.startDate} ~ ${upcomingFestivalData.endDate}`, - dDay: upcomingFestivalData.dday, + dDay: upcomingFestivalData.dDay, } : undefined, selectedTab, diff --git a/apps/audience/src/pages/my-events/my-events.tsx b/apps/audience/src/pages/my-events/my-events.tsx index e230ea81..5cbf5037 100644 --- a/apps/audience/src/pages/my-events/my-events.tsx +++ b/apps/audience/src/pages/my-events/my-events.tsx @@ -1,30 +1,34 @@ import { useQuery } from '@tanstack/react-query'; +import { useNavigate } from 'react-router'; import { EmptyView } from '@amp/ads-ui'; import { Loading } from '@amp/compositions'; -import FestivalList from '@widgets/my-events/festival-list'; +import FestivalCard from '@widgets/home/components/festival-card/festival-card'; -import { MY_EVENTS_QUERY_OPTIONS } from '@features/my-events/query'; import { MY_PAGE_QUERY_OPTIONS } from '@features/mypage/apis/query'; +import { ROUTE } from '@shared/constants/path'; + import * as styles from './my-events.css'; const MyEventsPage = () => { - const { data: viewedData } = useQuery( + const navigate = useNavigate(); + const { data: viewedData, isPending } = useQuery( MY_PAGE_QUERY_OPTIONS.VIEWED_FESTIVALS(), ); - const { data, isPending } = useQuery( - MY_EVENTS_QUERY_OPTIONS.LIST({ page: 0, size: 20 }), - ); const festivals = viewedData?.festivals ?? []; + const handleCardClick = (festivalId: number) => { + navigate(ROUTE.noticeList(festivalId)); + }; + if (isPending) { return ; } - if (!data || festivals.length === 0) { + if (!viewedData || festivals.length === 0) { return (
@@ -37,7 +41,16 @@ const MyEventsPage = () => { return (
- + {festivals.map((festival) => { + return ( + + ); + })}
); diff --git a/apps/audience/src/shared/constants/end-point.ts b/apps/audience/src/shared/constants/end-point.ts index 306e2443..5f578a86 100644 --- a/apps/audience/src/shared/constants/end-point.ts +++ b/apps/audience/src/shared/constants/end-point.ts @@ -21,9 +21,7 @@ export const END_POINT = { POST_FCM_TOKEN: '/notifications/fcm-token', // FCM 토큰 기기 동기화 // WishList - //TODO: 하나로 합치기 - GET_MY_FESTIVALS_ALL: '/wishlists', // 홈 화면 관람 예정 공연 리스트 - GET_PLANNED_FESTIVALS: '/wishlists', // 홈 화면 관람 예정 공연 리스트 + GET_WISHLISTS: '/wishlists', // 홈 화면 관람 예정 공연 리스트 GET_VIEWED_FESTIVALS: '/wishlists/all', // 마이페이지 관람 공연 전체 조회 GET_UPCOMING_FESTIVAL: '/wishlists/recent', // 가장 임박한 관람 예정 공연 조회 PUT_WISH_LIST: (festivalId: number) => `/wishlists/${festivalId}`, // 관람 예정 공연 등록 / 해제 diff --git a/apps/audience/src/shared/constants/path.ts b/apps/audience/src/shared/constants/path.ts index 72133f40..ae25a18a 100644 --- a/apps/audience/src/shared/constants/path.ts +++ b/apps/audience/src/shared/constants/path.ts @@ -12,3 +12,11 @@ export const ROUTE_PATH = { NOTICE_EDIT: '/events/:eventId/notices/:noticeId/edit', NOT_FOUND: '/not-found', } as const; + +export const ROUTE = { + noticeList: (eventId: number) => `/events/${eventId}/notices`, + noticeDetails: (eventId: number, noticeId: number) => + `/events/${eventId}/notices/${noticeId}`, + noticeEdit: (eventId: number, noticeId: number) => + `/events/${eventId}/notices/${noticeId}/edit`, +}; diff --git a/apps/audience/src/shared/types/festival.ts b/apps/audience/src/shared/types/festival.ts index 973648a3..f71b7925 100644 --- a/apps/audience/src/shared/types/festival.ts +++ b/apps/audience/src/shared/types/festival.ts @@ -1,3 +1,24 @@ +export type FestivalStatus = '관람 중' | '관람 예정' | '관람 완료'; + +export interface Festival { + festivalId: number; + title: string; + mainImageUrl: string; + period: string; + status: FestivalStatus; + wishList: boolean; + dDay?: number; +} + +export interface PaginationResponse { + currentPage: number; + totalPages: number; + totalElements: number; + size: number; + hasNext: boolean; + hasPrevious: boolean; +} + export interface WishListRequest { wishList: boolean; } diff --git a/apps/audience/src/shared/types/home-response.ts b/apps/audience/src/shared/types/home-response.ts index b41ba005..1f600c20 100644 --- a/apps/audience/src/shared/types/home-response.ts +++ b/apps/audience/src/shared/types/home-response.ts @@ -1,40 +1,7 @@ -export interface AllFestivalItem { - festivalId: number; - title: string; - mainImageUrl: string; - location: string; - period: string; - wishList: boolean; - dDay: number; -} +import type { Festival, PaginationResponse } from './festival'; -export interface UpcomingFestivalItem { - festivalId: number; - title: string; - mainImageUrl: string; - location: string; - period: string; - status: string; - wishList: boolean; - dDay: number; -} - -export interface PaginationResponse { - currentPage: number; - totalPages: number; - totalElements: number; - size: number; - hasNext: boolean; - hasPrevious: boolean; -} - -export interface AllFestivalsResponse { - festivals: AllFestivalItem[]; - pagination: PaginationResponse; -} - -export interface UpcomingFestivalsResponse { - festivals: UpcomingFestivalItem[]; +export interface FestivalsResponse { + festivals: Festival[]; pagination: PaginationResponse; } @@ -45,7 +12,7 @@ export interface UpcomingFestivalResponse { location: string; startDate: string; endDate: string; - dday: number; + dDay: number; } export interface NicknameResponse { diff --git a/apps/audience/src/shared/types/my-events-response.ts b/apps/audience/src/shared/types/my-events-response.ts deleted file mode 100644 index 5979d910..00000000 --- a/apps/audience/src/shared/types/my-events-response.ts +++ /dev/null @@ -1,22 +0,0 @@ -export interface MyEventsFestival { - festivalId: number; - title: string; - mainImageUrl: string; - period: string; - status: string; - wishList: boolean; -} - -export interface MyEventsPagination { - currentPage: number; - totalPages: number; - totalElements: number; - size: number; - hasNext: boolean; - hasPrevious: boolean; -} - -export interface MyEventsResponse { - festivals: MyEventsFestival[]; - pagination: MyEventsPagination; -} diff --git a/apps/audience/src/shared/types/viewed-festival.ts b/apps/audience/src/shared/types/viewed-festival.ts index 7ee849fc..0aa2e5c3 100644 --- a/apps/audience/src/shared/types/viewed-festival.ts +++ b/apps/audience/src/shared/types/viewed-festival.ts @@ -1,22 +1,5 @@ -export interface ViewedFestival { - festivalId: number; - title: string; - mainImageUrl: string; - period: string; - status: '관람 중' | '관람 예정' | '관람 완료'; - wishList: boolean; -} - -export interface Pagination { - currentPage: number; - totalPages: number; - totalElements: number; - size: number; - hasNext: boolean; - hasPrevious: boolean; -} - +import type { Festival, PaginationResponse } from './festival'; export interface ViewedFestivalsResponse { - festivals: ViewedFestival[]; - pagination: Pagination; + festivals: Festival[]; + pagination: PaginationResponse; } diff --git a/apps/audience/src/widgets/home/components/festival-card/festival-card.tsx b/apps/audience/src/widgets/home/components/festival-card/festival-card.tsx index c8c1bcaf..9d74813d 100644 --- a/apps/audience/src/widgets/home/components/festival-card/festival-card.tsx +++ b/apps/audience/src/widgets/home/components/festival-card/festival-card.tsx @@ -1,43 +1,96 @@ +import type { KeyboardEvent } from 'react'; + import { CardFestival } from '@amp/ads-ui'; import { FestivalStatusGroup } from '@amp/compositions'; import { formatDday } from '@amp/shared/utils'; import { useToggleWishListMutation } from '@features/usecase/toggle-wishlist/use-toggle-wishlist-mutation'; -import type { - AllFestivalItem, - UpcomingFestivalItem, -} from '@shared/types/home-response'; +import type { Festival } from '@shared/types/festival'; import FlagButton from '../flag-button/flag-button'; interface FestivalCardProps { - festival: AllFestivalItem | UpcomingFestivalItem; - onClick: () => void; + festival: Festival; + onCardClick: (festivalId: number) => void; + showWishList?: boolean; + showStatus?: boolean; } -const FestivalCard = ({ festival, onClick }: FestivalCardProps) => { - const { festivalId, title, period, mainImageUrl, wishList, dDay } = festival; +const STATUS_BY_TEXT = { + '관람 중': 'current', + '관람 완료': 'completed', + '관람 예정': 'upcoming', +} as const; + +const FestivalCard = ({ + festival, + showWishList = true, + showStatus = true, + onCardClick, +}: FestivalCardProps) => { + const { + festivalId, + title, + period, + mainImageUrl, + wishList = false, + status, + dDay, + } = festival; + const { toggleWishList, isTogglePending } = useToggleWishListMutation( festivalId, wishList, ); + const chipStatus = + STATUS_BY_TEXT[status as keyof typeof STATUS_BY_TEXT] ?? 'completed'; + + const handleCardClick = () => { + onCardClick(festivalId); + }; + + const handleKeyDown = (event: KeyboardEvent) => { + if (event.currentTarget !== event.target) { + return; + } + + if (event.key === 'Enter' || event.key === ' ') { + if (event.key === ' ') { + event.preventDefault(); + } + handleCardClick(); + } + }; + return ( - + - + - - - + {showWishList && ( + + + + )} ); }; diff --git a/apps/audience/src/widgets/home/components/festival-section/festival-section.tsx b/apps/audience/src/widgets/home/components/festival-section/festival-section.tsx index 497aef82..23525ef7 100644 --- a/apps/audience/src/widgets/home/components/festival-section/festival-section.tsx +++ b/apps/audience/src/widgets/home/components/festival-section/festival-section.tsx @@ -1,5 +1,3 @@ -import { useNavigate } from 'react-router'; - import { EmptyView } from '@amp/ads-ui'; import { @@ -8,11 +6,7 @@ import { type TabValue, } from '@widgets/home/constants/home-tabs'; -import { ROUTE_PATH } from '@shared/constants/path'; -import type { - AllFestivalItem, - UpcomingFestivalItem, -} from '@shared/types/home-response'; +import type { Festival } from '@shared/types/festival'; import FestivalCard from '../festival-card/festival-card'; import HomeFestivalTabs from '../home-festival-tabs/home-festival-tabs'; @@ -22,24 +16,20 @@ import * as styles from './festival-section.css'; interface FestivalSectionProps { selectedTab: TabValue; onTabChange: (value: TabValue) => void; - allFestivals: AllFestivalItem[]; - upcomingFestivals: UpcomingFestivalItem[]; + allFestivals: Festival[]; + plannedFestivals: Festival[]; + onCardClick: (festivalId: number) => void; } const FestivalSection = ({ selectedTab, onTabChange, allFestivals, - upcomingFestivals, + plannedFestivals, + onCardClick, }: FestivalSectionProps) => { - const navigate = useNavigate(); - - const handleMoveToFestival = (festivalId: number) => () => { - navigate(ROUTE_PATH.NOTICE_LIST.replace(':eventId', String(festivalId))); - }; - const targetFestivals = - selectedTab === TAB_ALL ? allFestivals : upcomingFestivals; + selectedTab === TAB_ALL ? allFestivals : plannedFestivals; const emptyConfig = { [TAB_ALL]: { @@ -47,7 +37,7 @@ const FestivalSection = ({ text: '등록한 공연이 아직 없어요.', }, [TAB_UPCOMING]: { - isEmpty: upcomingFestivals.length === 0, + isEmpty: plannedFestivals.length === 0, text: '관람 예정인 공연이 없어요.', }, } as const; @@ -67,7 +57,8 @@ const FestivalSection = ({
  • ))} diff --git a/apps/audience/src/widgets/my-events/festival-list.tsx b/apps/audience/src/widgets/my-events/festival-list.tsx deleted file mode 100644 index 3d02459d..00000000 --- a/apps/audience/src/widgets/my-events/festival-list.tsx +++ /dev/null @@ -1,68 +0,0 @@ -import type { ReactElement } from 'react'; - -import { CardFestival, Chip } from '@amp/ads-ui'; - -type MyEventsStatus = '관람 중' | '관람 예정' | '관람 완료'; - -import type { MyEventsFestival } from '@shared/types/my-events-response'; - -const STATUS_CHIP: Record = { - '관람 중': ( - - 관람 중 - - ), - '관람 예정': ( - - 관람 예정 - - ), - '관람 완료': ( - - 관람 완료 - - ), -}; - -const getStatusChip = (status: MyEventsStatus) => { - return STATUS_CHIP[status]; -}; - -interface FestivalListProps { - festivals: MyEventsFestival[]; -} - -const toMyEventsStatus = (status: string): MyEventsStatus | null => { - if (status === '관람 중' || status === '진행 중') { - return '관람 중'; - } - if (status === '관람 예정' || status === '진행 예정') { - return '관람 예정'; - } - return null; -}; - -const FestivalList = ({ festivals }: FestivalListProps) => { - return ( - <> - {festivals.map((festival) => ( - - - - - {(() => { - const mapped = toMyEventsStatus(festival.status); - return mapped ? getStatusChip(mapped) : null; - })()} - - - - ))} - - ); -}; - -export default FestivalList;