;
- completedMessage?: string;
-};
-
-export const MeetingList = ({
- meetings,
- tabType,
- emptyStateType,
- emptyStatePath,
- showActions,
- leaveActionText,
- error,
- hasNextPage,
- sentinelRef,
- completedMessage,
-}: MeetingListProps) => {
- const router = useRouter();
-
- if (meetings.length === 0 && !error) {
- const config = EMPTY_STATE_CONFIG[emptyStateType];
- return (
-
- {config.text}
-
-
-
- );
- }
-
- return (
-
- {error && meetings.length === 0 && (
- window.location.reload()}
- />
- )}
-
- {meetings.map((meeting) => (
- console.log(leaveActionText || '모임 탈퇴', meeting.id),
- onChat: () => router.push(`/chat/${meeting.id}`),
- }
- : undefined
- }
- location={meeting.location}
- maxParticipants={meeting.maxParticipants}
- nickName={meeting.createdBy.nickName}
- participantCount={meeting.participantCount}
- profileImage={meeting.createdBy.profileImage}
- tabType={tabType}
- tags={meeting.tags}
- title={meeting.title}
- onClick={() => router.push(`/group/${meeting.id}`)}
- />
- ))}
-
- {error && meetings.length > 0 && (
- window.location.reload()}
- />
- )}
-
- {hasNextPage && !error && sentinelRef && }
-
- {!hasNextPage && meetings.length > 0 && !error && completedMessage && (
- {completedMessage}
- )}
-
- );
-};
diff --git a/src/app/schedule/_components/meetings.tsx b/src/app/schedule/_components/meetings.tsx
new file mode 100644
index 00000000..4b085ec1
--- /dev/null
+++ b/src/app/schedule/_components/meetings.tsx
@@ -0,0 +1,139 @@
+'use client';
+
+import { useRouter } from 'next/navigation';
+
+import { RefObject } from 'react';
+
+import { EmptyState } from '@/components/layout/empty-state';
+import { ErrorMessage } from '@/components/shared';
+import { Button } from '@/components/ui';
+import { GroupListItemResponse } from '@/types/service/group';
+
+import { ScheduleCard } from './card';
+import { EMPTY_STATE_CONFIG } from './constants';
+
+type TabType = 'current' | 'myPost' | 'past';
+
+const getModalType = (
+ meeting: GroupListItemResponse,
+ tabType: TabType,
+): 'pending' | 'leave' | 'delete' => {
+ if (tabType === 'myPost' || (tabType === 'current' && meeting.myMembership?.role === 'HOST')) {
+ return 'delete';
+ }
+ if (tabType === 'current' && meeting.myMembership?.status === 'PENDING') {
+ return 'pending';
+ }
+ return 'leave';
+};
+
+type MeetingsProps = {
+ meetings: GroupListItemResponse[];
+ tabType: TabType;
+ emptyStateType: TabType;
+ emptyStatePath: string;
+ showActions: boolean;
+ error?: Error | null;
+ hasNextPage?: boolean;
+ isLoading?: boolean;
+ sentinelRef?: RefObject;
+ completedMessage?: string;
+};
+
+const MIN_HEIGHT = 'min-h-[calc(100vh-156px)]';
+
+export const Meetings = ({
+ meetings,
+ tabType,
+ emptyStateType,
+ emptyStatePath,
+ showActions,
+ error,
+ hasNextPage,
+ isLoading,
+ sentinelRef,
+ completedMessage,
+}: MeetingsProps) => {
+ const router = useRouter();
+
+ const isEmpty = meetings.length === 0;
+ const hasError = !!error;
+ const isLoadingState = !!isLoading;
+ const showLoading = isEmpty && isLoadingState && !hasError;
+ const showEmptyState = isEmpty && !hasError && !isLoadingState;
+ const showErrorOnly = hasError && isEmpty;
+ const showErrorWithData = hasError && !isEmpty;
+
+ if (showLoading) {
+ return (
+
+ );
+ }
+
+ if (showEmptyState) {
+ const config = EMPTY_STATE_CONFIG[emptyStateType];
+ const handleEmptyStateClick = () => router.push(emptyStatePath);
+
+ return (
+
+ {config.text}
+
+
+
+ );
+ }
+
+ const handleRetry = () => window.location.reload();
+
+ return (
+
+ {showErrorOnly && (
+
+ )}
+
+ {meetings.map((meeting) => {
+ const groupId = String(meeting.id);
+ const myMembership = meeting.myMembership;
+ const isPending = myMembership?.status === 'PENDING';
+ const isFinished = meeting.status === 'FINISHED';
+ const isHost = myMembership?.role === 'HOST';
+ const createdBy = meeting.createdBy;
+ const shouldFetchChatRoomId = showActions && !isPending && !isFinished;
+
+ return (
+
+ );
+ })}
+
+ {showErrorWithData && (
+
+ )}
+
+ {hasNextPage && !hasError && sentinelRef && }
+
+ {!hasNextPage && !isEmpty && !hasError && completedMessage && (
+ {completedMessage}
+ )}
+
+ );
+};
diff --git a/src/app/schedule/_components/my.tsx b/src/app/schedule/_components/my.tsx
index d715ada0..27167855 100644
--- a/src/app/schedule/_components/my.tsx
+++ b/src/app/schedule/_components/my.tsx
@@ -4,26 +4,34 @@ import { API } from '@/api';
import { useInfiniteScroll } from '@/hooks/use-group/use-group-infinite-list';
import { useIntersectionObserver } from '@/hooks/use-intersection-observer';
import { INTERSECTION_OBSERVER_THRESHOLD } from '@/lib/constants/group-list';
+import { groupKeys } from '@/lib/query-key/query-key-group';
import { GroupListItemResponse } from '@/types/service/group';
-import { MeetingList } from './meeting-list';
+import { Meetings } from './meetings';
export default function My() {
- const { items, error, fetchNextPage, hasNextPage, isFetchingNextPage, completedMessage } =
- useInfiniteScroll({
- queryFn: async ({ cursor, size }) => {
- return await API.groupService.getMyGroups({
- type: 'myPost',
- cursor,
- size,
- filter: 'ALL',
- });
- },
- queryKey: ['myGroups', 'myPost'],
- pageSize: 10,
- errorMessage: '나의 모임 목록을 불러오는데 실패했습니다.',
- completedMessage: '모든 나의 모임을 불러왔습니다.',
- });
+ const {
+ items,
+ error,
+ fetchNextPage,
+ hasNextPage,
+ isFetchingNextPage,
+ isFetching,
+ completedMessage,
+ } = useInfiniteScroll({
+ queryFn: async ({ cursor, size }) => {
+ return await API.groupService.getMyGroups({
+ type: 'myPost',
+ cursor,
+ size,
+ filter: 'ALL',
+ });
+ },
+ queryKey: groupKeys.myGroupsList('myPost') as ['myGroups', 'myPost'],
+ pageSize: 10,
+ errorMessage: '나의 모임 목록을 불러오는데 실패했습니다.',
+ completedMessage: '모든 나의 모임을 불러왔습니다.',
+ });
const sentinelRef = useIntersectionObserver({
onIntersect: () => {
@@ -36,13 +44,13 @@ export default function My() {
});
return (
- ;
+ groupId?: string;
}
interface KickProps {
@@ -26,6 +27,7 @@ interface KickProps {
targetUserId: string;
targetUserName: string;
};
+ groupId?: string;
}
type Props = BaseProps | KickProps;
@@ -33,8 +35,10 @@ type Props = BaseProps | KickProps;
export const GroupModal = (props: Props) => {
const { type } = props;
- const { groupId } = useParams() as { groupId: string };
+ const params = useParams();
+ const groupId = props.groupId || (params as { groupId: string }).groupId;
const { replace } = useRouter();
+ const pathname = usePathname();
const { run } = useToast();
const { mutateAsync: attendMutate, isPending: isAttending } = useAttendGroup({ groupId });
@@ -62,7 +66,9 @@ export const GroupModal = (props: Props) => {
delete: async () => {
await deleteMutate();
await revalidateGroupAction(groupId);
- replace('/');
+ if (!pathname.startsWith('/schedule')) {
+ replace('/');
+ }
},
kick: () => kickMutate(),
};
diff --git a/src/components/shared/card/index.tsx b/src/components/shared/card/index.tsx
index 52264478..7815089a 100644
--- a/src/components/shared/card/index.tsx
+++ b/src/components/shared/card/index.tsx
@@ -137,14 +137,17 @@ const Card = ({
{shouldShowButtons && (
)}
diff --git a/src/hooks/use-group/use-group-delete/index.ts b/src/hooks/use-group/use-group-delete/index.ts
index c2c6f59a..8a346f21 100644
--- a/src/hooks/use-group/use-group-delete/index.ts
+++ b/src/hooks/use-group/use-group-delete/index.ts
@@ -13,6 +13,7 @@ export const useDeleteGroup = (params: GroupIdParams) => {
onSuccess: async () => {
queryClient.removeQueries({ queryKey: groupKeys.detail(params.groupId) });
queryClient.invalidateQueries({ queryKey: groupKeys.lists() });
+ queryClient.invalidateQueries({ queryKey: groupKeys.myGroups() });
console.log('모임 삭제 성공.');
},
onError: () => {
diff --git a/src/hooks/use-group/use-group-get-my-list/index.ts b/src/hooks/use-group/use-group-get-my-groups/index.ts
similarity index 87%
rename from src/hooks/use-group/use-group-get-my-list/index.ts
rename to src/hooks/use-group/use-group-get-my-groups/index.ts
index 930698c0..148bbb5c 100644
--- a/src/hooks/use-group/use-group-get-my-list/index.ts
+++ b/src/hooks/use-group/use-group-get-my-groups/index.ts
@@ -6,7 +6,7 @@ import { GetMyGroupsPayload } from '@/types/service/group';
export const useGetMyGroups = (payload: GetMyGroupsPayload) => {
const query = useQuery({
- queryKey: groupKeys.myList(payload),
+ queryKey: groupKeys.myGroupsList(payload.type),
queryFn: () => API.groupService.getMyGroups(payload),
});
return query;
diff --git a/src/hooks/use-group/use-group-leave/index.ts b/src/hooks/use-group/use-group-leave/index.ts
index b11f1597..2a81a859 100644
--- a/src/hooks/use-group/use-group-leave/index.ts
+++ b/src/hooks/use-group/use-group-leave/index.ts
@@ -11,6 +11,7 @@ export const useLeaveGroup = (params: GroupIdParams) => {
mutationFn: () => API.groupService.leaveGroup(params),
onSuccess: async () => {
await queryClient.invalidateQueries({ queryKey: groupKeys.detail(params.groupId) });
+ await queryClient.invalidateQueries({ queryKey: groupKeys.myGroups() });
console.log('모임 탈퇴 성공.');
},
onError: () => {
diff --git a/src/lib/query-key/query-key-group/index.ts b/src/lib/query-key/query-key-group/index.ts
index bc2f3313..0b05b0a0 100644
--- a/src/lib/query-key/query-key-group/index.ts
+++ b/src/lib/query-key/query-key-group/index.ts
@@ -3,9 +3,8 @@ export const groupKeys = {
lists: () => [...groupKeys.all, 'list'] as const,
list: (filters: { keyword?: string; cursor?: number; size: number }) =>
[...groupKeys.lists(), filters] as const,
- myLists: () => [...groupKeys.all, 'me'] as const,
- myList: (filters: { type: 'current' | 'myPost' | 'past'; cursor?: number; size: number }) =>
- [...groupKeys.myLists(), filters] as const,
+ myGroups: () => ['myGroups'] as const,
+ myGroupsList: (type: 'current' | 'myPost' | 'past') => [...groupKeys.myGroups(), type] as const,
detail: (groupId: string) => [...groupKeys.all, groupId] as const,
joinRequests: (groupId: string, status: string = 'PENDING') =>
['joinRequests', groupId, status] as const,