diff --git a/frontend/apps/client/src/features/discussion/ui/DiscussionRank/index.tsx b/frontend/apps/client/src/features/discussion/ui/DiscussionRank/index.tsx index 0a00a9a2..d86235ef 100644 --- a/frontend/apps/client/src/features/discussion/ui/DiscussionRank/index.tsx +++ b/frontend/apps/client/src/features/discussion/ui/DiscussionRank/index.tsx @@ -24,7 +24,7 @@ const DiscussionRank = () => { return ( diff --git a/frontend/apps/client/src/features/shared-schedule/api/index.ts b/frontend/apps/client/src/features/shared-schedule/api/index.ts index ccc6d6d1..bdf44f22 100644 --- a/frontend/apps/client/src/features/shared-schedule/api/index.ts +++ b/frontend/apps/client/src/features/shared-schedule/api/index.ts @@ -2,7 +2,7 @@ import { MINUTE_IN_MILLISECONDS } from '@endolphin/core/utils'; import { request } from '@utils/fetch'; import type { - AttendType, + OngoingScheduleAttendType, OngoingSchedulesResponse, UpcomingScheduleDetailsResponse, UpcomingSchedulesResponse, @@ -34,7 +34,7 @@ export const schedulesApi = { getOngoingSchedules: async ( page: number, size: number, - attendType: AttendType, + attendType: OngoingScheduleAttendType, ): Promise => { const response = await request.get(ENDPOINT_PREFIX + '/ongoing', { params: { diff --git a/frontend/apps/client/src/features/shared-schedule/api/keys.ts b/frontend/apps/client/src/features/shared-schedule/api/keys.ts index 4dc047f1..ea34187d 100644 --- a/frontend/apps/client/src/features/shared-schedule/api/keys.ts +++ b/frontend/apps/client/src/features/shared-schedule/api/keys.ts @@ -1,4 +1,4 @@ -import type { AttendType } from '../model'; +import type { OngoingScheduleAttendType } from '../model'; export const sharedScheduleKey = ['shared-schedule']; @@ -8,7 +8,7 @@ export const upcomingDetailsQueryKey = (discussionId: string) => ['upcomingDetai export const ongoingQueryKey = { all: [...sharedScheduleKey, 'ongoing'], - detail: (page: number, size: number, type: AttendType) => + detail: (page: number, size: number, type: OngoingScheduleAttendType) => [...ongoingQueryKey.all, page, size, type], }; diff --git a/frontend/apps/client/src/features/shared-schedule/api/prefetch.ts b/frontend/apps/client/src/features/shared-schedule/api/prefetch.ts index 0621afff..987199e3 100644 --- a/frontend/apps/client/src/features/shared-schedule/api/prefetch.ts +++ b/frontend/apps/client/src/features/shared-schedule/api/prefetch.ts @@ -1,6 +1,7 @@ import type { QueryClient } from '@tanstack/react-query'; -import { type AttendType, FINISHED_SCHEDULE_FETCH_SIZE } from '../model'; +import type { OngoingScheduleAttendType } from '../model'; +import { FINISHED_SCHEDULE_FETCH_SIZE } from '../model'; import { sharedSchedulesQueryOptions } from './queryOptions'; export const prefetchUpcomingSchedules = async (queryClient: QueryClient) => { @@ -11,7 +12,7 @@ export const prefetchOngoingSchedules = async ( queryClient: QueryClient, page: number, size: number, - attendType: AttendType, + attendType: OngoingScheduleAttendType, ) => { await queryClient.prefetchQuery(sharedSchedulesQueryOptions.ongoing(page, size, attendType)); }; diff --git a/frontend/apps/client/src/features/shared-schedule/api/queries.ts b/frontend/apps/client/src/features/shared-schedule/api/queries.ts index 851d5d1d..f5f0c030 100644 --- a/frontend/apps/client/src/features/shared-schedule/api/queries.ts +++ b/frontend/apps/client/src/features/shared-schedule/api/queries.ts @@ -5,7 +5,10 @@ import type { UpcomingScheduleDetailsResponse, UpcomingSchedulesResponse, } from '../model'; -import type { AttendType, OngoingSchedulesResponse } from '../model/ongoingSchedules'; +import type { + OngoingScheduleAttendType, + OngoingSchedulesResponse, +} from '../model/ongoingSchedules'; import { sharedSchedulesQueryOptions } from './queryOptions'; export const useUpcomingQuery = () => useQuery( @@ -18,7 +21,11 @@ export const useUpcomingDetailsQuery = (discussionId: string) => ( ) ); -export const useOngoingQuery = (page: number, size: number, attendType: AttendType) => +export const useOngoingQuery = ( + page: number, + size: number, + attendType: OngoingScheduleAttendType, +) => useQuery( sharedSchedulesQueryOptions.ongoing(page, size, attendType), ); @@ -26,4 +33,4 @@ export const useOngoingQuery = (page: number, size: number, attendType: AttendTy export const useFinishedQuery = (page: number, size: number, year: number) => useQuery( sharedSchedulesQueryOptions.finished(page, size, year), - ); + ); \ No newline at end of file diff --git a/frontend/apps/client/src/features/shared-schedule/api/queryOptions.ts b/frontend/apps/client/src/features/shared-schedule/api/queryOptions.ts index 2f7e1df3..9c555467 100644 --- a/frontend/apps/client/src/features/shared-schedule/api/queryOptions.ts +++ b/frontend/apps/client/src/features/shared-schedule/api/queryOptions.ts @@ -1,7 +1,7 @@ import { keepPreviousData } from '@tanstack/react-query'; -import type { AttendType } from '../model'; +import type { OngoingScheduleAttendType } from '../model'; import { schedulesApi } from '.'; import { finishedQueryKey, @@ -19,7 +19,7 @@ export const sharedSchedulesQueryOptions = { queryKey: upcomingDetailsQueryKey(discussionId), queryFn: () => schedulesApi.getUpcomingScheduleDetail(discussionId), }), - ongoing: (page: number, size: number, attendtype: AttendType) => ({ + ongoing: (page: number, size: number, attendtype: OngoingScheduleAttendType) => ({ queryKey: ongoingQueryKey.detail(page, size, attendtype), queryFn: () => schedulesApi.getOngoingSchedules(page, size, attendtype), }), @@ -28,4 +28,4 @@ export const sharedSchedulesQueryOptions = { queryFn: () => schedulesApi.getFinishedSchedules(page, size, year), placeholderData: keepPreviousData, }), -}; +}; \ No newline at end of file diff --git a/frontend/apps/client/src/features/shared-schedule/model/ongoingSchedules.ts b/frontend/apps/client/src/features/shared-schedule/model/ongoingSchedules.ts index 8d2f33af..d5c92679 100644 --- a/frontend/apps/client/src/features/shared-schedule/model/ongoingSchedules.ts +++ b/frontend/apps/client/src/features/shared-schedule/model/ongoingSchedules.ts @@ -21,7 +21,7 @@ export const OngoingSchedulesResponseSchema = z.object({ ongoingDiscussions: z.array(OngoingScheduleSchema), }); -export type AttendType = 'HOST' | 'ATTENDEE' | 'ALL'; +export type OngoingScheduleAttendType = 'HOST' | 'ATTENDEE' | 'ALL'; export type OngoingSchedulesResponse = z.infer; export type OngoingSchedule = z.infer; diff --git a/frontend/apps/client/src/features/shared-schedule/ui/OngoingSchedules/OngoingScheduleList.tsx b/frontend/apps/client/src/features/shared-schedule/ui/OngoingSchedules/OngoingScheduleList.tsx index 19b592d7..e45c2b28 100644 --- a/frontend/apps/client/src/features/shared-schedule/ui/OngoingSchedules/OngoingScheduleList.tsx +++ b/frontend/apps/client/src/features/shared-schedule/ui/OngoingSchedules/OngoingScheduleList.tsx @@ -1,7 +1,9 @@ import { vars } from '@endolphin/theme'; import { Flex, Pagination, Text } from '@endolphin/ui'; import { useQueryClient } from '@tanstack/react-query'; -import { useState } from 'react'; +import { useAtom } from 'jotai'; + +import { ongoingSegmentAtomFamily } from '@/store/home'; import { prefetchOngoingSchedules } from '../../api/prefetch'; import { useOngoingQuery } from '../../api/queries'; @@ -30,16 +32,16 @@ interface OngoingScheduleListProps { segmentOption: OngoingSegmentOption; } -// TODO: useEffect 뺄 수 있으면 다른 걸로 대체 const OngoingScheduleList = ({ segmentOption }: OngoingScheduleListProps) => { const queryClient = useQueryClient(); - const [currentPage, handlePageChange] = useState(1); - const [selectedIndex, setSelectedIndex] = useState(0); - const { data, isPending } = useOngoingQuery(currentPage, PAGE_SIZE, segmentOption.value ); - if (isPending) return
pending...
; - if (!data || data.ongoingDiscussions.length === 0) - return ; + const [segmentAtom, setSegmentAtom] = useAtom(ongoingSegmentAtomFamily(segmentOption.value)); + const setPage = (page: number) => setSegmentAtom((prev) => ({ ...prev, page })); + const setListIndex = (index: number) => setSegmentAtom((prev) => ({ ...prev, listIndex: index })); + const { data, isPending } = useOngoingQuery(segmentAtom.page, PAGE_SIZE, segmentOption.value); + + if (isPending) return
pending...
; + if (!data?.ongoingDiscussions.length) return ; return (
{ > - prefetchOngoingSchedules(queryClient, page, PAGE_SIZE, segmentOption.value )} + prefetchOngoingSchedules(queryClient, page, PAGE_SIZE, segmentOption.value)} onPageChange={(page: number) => { - setSelectedIndex(0); - handlePageChange(page); + setListIndex(0); + setPage(page); }} totalPages={data.totalPages} /> - +
); }; interface ScheduleItemsProps { - schedules: NonNullable< - ReturnType['data'] - >['ongoingDiscussions']; + schedules: NonNullable['data']>['ongoingDiscussions']; selectedIndex: number; setSelectedIndex: (index: number) => void; } diff --git a/frontend/apps/client/src/features/shared-schedule/ui/OngoingSchedules/index.tsx b/frontend/apps/client/src/features/shared-schedule/ui/OngoingSchedules/index.tsx index ac227071..71aaf2fe 100644 --- a/frontend/apps/client/src/features/shared-schedule/ui/OngoingSchedules/index.tsx +++ b/frontend/apps/client/src/features/shared-schedule/ui/OngoingSchedules/index.tsx @@ -1,11 +1,14 @@ - -import { Flex, SegmentControl, Text } from '@endolphin/ui'; +import { Flex, SegmentControl, Text } from '@endolphin/ui'; import { useQueryClient } from '@tanstack/react-query'; +import { useAtom } from 'jotai'; + +import { ongoingSegmentValueAtom } from '@/store/home'; import { prefetchOngoingSchedules } from '../../api/prefetch'; import { useOngoingQuery } from '../../api/queries'; import { sharedSchedulesQueryOptions } from '../../api/queryOptions'; -import { type AttendType, ONGOING_SCHEDULE_FETCH_SIZE } from '../../model/'; +import type { OngoingScheduleAttendType } from '../../model/'; +import { ONGOING_SCHEDULE_FETCH_SIZE } from '../../model/'; import { ongoingFallbackContainerStyle } from '../Fallbacks/index.css'; import OngoingFallback from '../Fallbacks/OngoingFallback'; import { containerStyle, segmentControlStyle, titleStyle } from './index.css'; @@ -13,7 +16,7 @@ import OngoingScheduleList, { PAGE_SIZE } from './OngoingScheduleList'; export interface OngoingSegmentOption { label: string; - value: AttendType; + value: OngoingScheduleAttendType; } const segmentOptions: OngoingSegmentOption[] = [ @@ -29,12 +32,15 @@ const OngoingSchedules = () => ( justify='flex-start' width='full' > - 확정되지 않은 일정 + + 확정되지 않은 일정 + ); const Content = () => { + const [segmentValue, setSegmentValue] = useAtom(ongoingSegmentValueAtom); const queryClient = useQueryClient(); const { data, isPending } = useOngoingQuery(1, 6, 'ALL'); if (!data || isPending) return
; @@ -44,26 +50,28 @@ const Content = () => { ); return ; } - + return ( prefetchOngoingSchedules( - // TODO: segmentOption value의 타입을 제네릭으로 지정할 수 있게 구현 (as 변환 리팩토룅) - queryClient, 1, PAGE_SIZE, value as AttendType, - )} + initialValue={segmentValue} + onButtonHover={(value) => + prefetchOngoingSchedules( + queryClient, + 1, + PAGE_SIZE, + value, + )} + onValueChange={(value) => setSegmentValue(value)} segmentOptions={segmentOptions} > {segmentOptions.map((option, idx) => ( - + ))} ); }; -export default OngoingSchedules; \ No newline at end of file +export default OngoingSchedules; diff --git a/frontend/apps/client/src/pages/UpcomingScheduleDetailPage/index.tsx b/frontend/apps/client/src/pages/UpcomingScheduleDetailPage/index.tsx index cbb473e7..a0ac07ff 100644 --- a/frontend/apps/client/src/pages/UpcomingScheduleDetailPage/index.tsx +++ b/frontend/apps/client/src/pages/UpcomingScheduleDetailPage/index.tsx @@ -1,8 +1,8 @@ import { formatDateToDdayString } from '@endolphin/core/utils'; import { Chip, Flex, Text } from '@endolphin/ui'; +import { useUpcomingDetailsQuery } from '@features/shared-schedule/api/queries'; import { useParams } from '@tanstack/react-router'; -import { useUpcomingDetailsQuery } from '@/features/shared-schedule/api/queries'; import TimelineScheduleModal from '@/features/timeline-schedule/ui'; import Header from './Header'; @@ -60,4 +60,4 @@ const TopBarContent = ({ ); -export default UpcomingScheduleDetailPage; +export default UpcomingScheduleDetailPage; \ No newline at end of file diff --git a/frontend/apps/client/src/routes/_main/discussion/candidate/$id.lazy.tsx b/frontend/apps/client/src/routes/_main/discussion/candidate/$id.lazy.tsx deleted file mode 100644 index 9ce4ea69..00000000 --- a/frontend/apps/client/src/routes/_main/discussion/candidate/$id.lazy.tsx +++ /dev/null @@ -1,21 +0,0 @@ -import { createLazyFileRoute, useLocation } from '@tanstack/react-router'; - -import GlobalNavBar from '@/layout/GlobalNavBar'; -import CandidateSchedulePage from '@/pages/CandidateSchedulePage'; - -const CandidateSchedule = () => { - const { state } = useLocation(); - const { candidate } = state ?? {}; - if (!candidate) return
; - - return ( - <> - - - - ); -}; - -export const Route = createLazyFileRoute('/_main/discussion/candidate/$id')({ - component: CandidateSchedule, -}); diff --git a/frontend/apps/client/src/routes/_main/my-calendar/index.lazy.tsx b/frontend/apps/client/src/routes/_main/my-calendar/index.lazy.tsx deleted file mode 100644 index baff0985..00000000 --- a/frontend/apps/client/src/routes/_main/my-calendar/index.lazy.tsx +++ /dev/null @@ -1,17 +0,0 @@ -import { createLazyFileRoute } from '@tanstack/react-router'; - -import GlobalNavBar from '@/layout/GlobalNavBar'; -import MyCalendarPage from '@/pages/MyCalendarPage'; - -const MyCalendar = () => ( - <> - - - - - -); - -export const Route = createLazyFileRoute('/_main/my-calendar/')({ - component: MyCalendar, -}); diff --git a/frontend/apps/client/src/routes/_main/upcoming-schedule/index.lazy.tsx b/frontend/apps/client/src/routes/_main/upcoming-schedule/index.lazy.tsx deleted file mode 100644 index 102273af..00000000 --- a/frontend/apps/client/src/routes/_main/upcoming-schedule/index.lazy.tsx +++ /dev/null @@ -1,17 +0,0 @@ -import { createLazyFileRoute } from '@tanstack/react-router'; - -import GlobalNavBar from '@/layout/GlobalNavBar'; -import UpcomingSchedulePage from '@/pages/UpcomingSchedulePage'; - -const UpcomingSchedule = () => ( - <> - - - - - - -); -export const Route = createLazyFileRoute('/_main/upcoming-schedule/')({ - component: UpcomingSchedule, -}); diff --git a/frontend/apps/client/src/routes/login/index.lazy.tsx b/frontend/apps/client/src/routes/login/index.lazy.tsx deleted file mode 100644 index 22540ecf..00000000 --- a/frontend/apps/client/src/routes/login/index.lazy.tsx +++ /dev/null @@ -1,12 +0,0 @@ -import { createLazyFileRoute } from '@tanstack/react-router'; - -import LoginPage from '@/pages/LoginPage'; - -const Login = () => ( - -); - -export const Route = createLazyFileRoute('/login/')({ - component: Login, -}); - diff --git a/frontend/apps/client/src/store/home/index.ts b/frontend/apps/client/src/store/home/index.ts new file mode 100644 index 00000000..de8b17fc --- /dev/null +++ b/frontend/apps/client/src/store/home/index.ts @@ -0,0 +1,20 @@ +import { atom } from 'jotai'; +import { atomFamily } from 'jotai/utils'; + +import type { OngoingScheduleAttendType } from '@/features/shared-schedule/model'; + +export const ongoingSegmentValueAtom = atom('ALL'); + +export interface OngoingSegmentStateAtom { + listIndex: number; + page: number; +} + +export const ongoingSegmentAtomFamily = atomFamily((_: OngoingScheduleAttendType) => + atom({ + listIndex: 0, + page: 1, + } as OngoingSegmentStateAtom), +); + +export const ongoingListIndexAtom = atom(0); diff --git a/frontend/packages/ui/src/components/SegmentControl/Content.tsx b/frontend/packages/ui/src/components/SegmentControl/Content.tsx index 0d519759..dfb93c76 100644 --- a/frontend/packages/ui/src/components/SegmentControl/Content.tsx +++ b/frontend/packages/ui/src/components/SegmentControl/Content.tsx @@ -4,12 +4,16 @@ import type { PropsWithChildren } from 'react'; import { contentContainerStyle } from './index.css'; import { useSegmentControlContext } from './SegmentControlContext'; -export interface ContentProps extends PropsWithChildren { - value: string; +export interface ContentProps extends PropsWithChildren { + value: SegmentValue; className?: string; } - -const Content = ({ value, className, children }: ContentProps) => { + +const Content = ({ + value, + className, + children, +}: ContentProps) => { const { selectedValue } = useSegmentControlContext(); if (selectedValue !== value) return null; return
{children}
; diff --git a/frontend/packages/ui/src/components/SegmentControl/ControlButton.tsx b/frontend/packages/ui/src/components/SegmentControl/ControlButton.tsx index 8a4b036d..5ff154a9 100644 --- a/frontend/packages/ui/src/components/SegmentControl/ControlButton.tsx +++ b/frontend/packages/ui/src/components/SegmentControl/ControlButton.tsx @@ -2,23 +2,23 @@ import Button from '../Button'; import type { SegmentControlProps, SegmentOption } from '.'; import { useSegmentControlContext } from './SegmentControlContext'; -interface ControlButtonProps { - segmentOption: SegmentOption; - segmentControlStyle: SegmentControlProps['style']; - onButtonHover?: (value: string) => void; +interface ControlButtonProps { + segmentOption: SegmentOption; + segmentControlStyle: SegmentControlProps['style']; + onButtonHover?: SegmentControlProps['onButtonHover']; } - -const ControlButton = ({ + +const ControlButton = ({ segmentOption, segmentControlStyle, onButtonHover, -}: ControlButtonProps ) => { - const { selectedValue, handleSelect } = useSegmentControlContext(); +}: ControlButtonProps ) => { + const { selectedValue, onSelect } = useSegmentControlContext(); const { label, value } = segmentOption; return (