diff --git a/src/apis/club/entity.ts b/src/apis/club/entity.ts index 3347bebe..9e6db44e 100644 --- a/src/apis/club/entity.ts +++ b/src/apis/club/entity.ts @@ -267,3 +267,19 @@ export interface AddPreMemberResponse { studentNumber: string; name: string; } + +export interface PreMember { + preMemberId: number; + studentNumber: string; + name: string; + clubPosition: 'MANAGER' | 'MEMBER'; +} + +export interface PreMembersList { + preMembers: PreMember[]; +} + +export interface PreMemberDeleteRequest { + clubId: number; + preMemberId: number; +} diff --git a/src/apis/club/index.ts b/src/apis/club/index.ts index 8aa521a0..4e44ecbe 100644 --- a/src/apis/club/index.ts +++ b/src/apis/club/index.ts @@ -27,6 +27,7 @@ import { type AddPreMemberRequest, type AddPreMemberResponse, type PositionType, + type PreMembersList, } from './entity'; export type { Bank, ClubFeeRequest }; @@ -195,3 +196,13 @@ export const postAddPreMember = async (clubId: number, data: AddPreMemberRequest }); return response; }; + +export const getPreMembers = async (clubId: number) => { + const response = await apiClient.get(`clubs/${clubId}/pre-members`, { requiresAuth: true }); + return response; +}; + +export const deletePreMember = async (clubId: number, preMemberId: number) => { + const response = await apiClient.delete(`clubs/${clubId}/pre-members/${preMemberId}`, { requiresAuth: true }); + return response; +}; diff --git a/src/pages/Manager/ManagedMemberList/index.tsx b/src/pages/Manager/ManagedMemberList/index.tsx index 6f0c6163..55606c6e 100644 --- a/src/pages/Manager/ManagedMemberList/index.tsx +++ b/src/pages/Manager/ManagedMemberList/index.tsx @@ -1,6 +1,6 @@ import { useState, useMemo } from 'react'; import { useParams } from 'react-router-dom'; -import type { ClubMember, PositionType } from '@/apis/club/entity'; +import type { ClubMember, PositionType, PreMember } from '@/apis/club/entity'; import CheckIcon from '@/assets/svg/check.svg'; import BottomModal from '@/components/common/BottomModal'; import Card from '@/components/common/Card'; @@ -12,6 +12,8 @@ import { useAddPreMember, useChangeMemberPosition, useChangeVicePresident, + useDeletePreMember, + useGetPreMemberList, useManagedMembers, useRemoveMember, useTransferPresident, @@ -69,9 +71,16 @@ function ManagedMemberList() { onSuccess: () => showToast('부원이 추가되었습니다'), }); - const isPending = isTransferring || isChangingVP || isChangingPosition || isRemoving || isAdding; + const { preMembersList } = useGetPreMemberList(clubId); + const { mutate: deletePreMemberMutate, isPending: isDeletingPreMember } = useDeletePreMember(clubId, { + onSuccess: () => showToast('사전 등록 회원이 삭제되었습니다'), + }); + + const isPending = + isTransferring || isChangingVP || isChangingPosition || isRemoving || isAdding || isDeletingPreMember; const [selectedMember, setSelectedMember] = useState(null); + const [selectedPreMember, setSelectedPreMember] = useState(null); const { value: isActionOpen, setTrue: openAction, setFalse: closeAction } = useBooleanState(); const { value: isTransferOpen, setTrue: openTransfer, setFalse: closeTransfer } = useBooleanState(); @@ -79,6 +88,16 @@ function ManagedMemberList() { const { value: isPositionOpen, setTrue: openPosition, setFalse: closePosition } = useBooleanState(); const { value: isRemoveOpen, setTrue: openRemove, setFalse: closeRemove } = useBooleanState(); const { value: isAddOpen, setTrue: openAdd, setFalse: closeAdd } = useBooleanState(); + const { + value: isPreMemberActionOpen, + setTrue: openPreMemberAction, + setFalse: closePreMemberAction, + } = useBooleanState(); + const { + value: isPreMemberDeleteOpen, + setTrue: openPreMemberDelete, + setFalse: closePreMemberDelete, + } = useBooleanState(); const [transferTarget, setTransferTarget] = useState(null); const [vpTarget, setVPTarget] = useState(null); @@ -145,6 +164,23 @@ function ManagedMemberList() { setNewMemberName(''); }; + const handlePreMemberAction = (member: PreMember) => { + setSelectedPreMember(member); + openPreMemberAction(); + }; + + const handleOpenPreMemberDelete = () => { + closePreMemberAction(); + openPreMemberDelete(); + }; + + const handleDeletePreMember = () => { + if (!selectedPreMember) return; + deletePreMemberMutate(selectedPreMember.preMemberId); + closePreMemberDelete(); + setSelectedPreMember(null); + }; + return (
@@ -209,6 +245,35 @@ function ManagedMemberList() { ))}
))} + + {preMembersList.preMembers.length > 0 && ( +
+
사전 등록 회원
+ {preMembersList.preMembers.map((member) => ( + +
+
+ {member.name.charAt(0)} +
+
+
+ {member.name} ({member.studentNumber}) +
+
사전 등록
+
+
+ +
+ ))} +
+ )}
{/* Member Action Modal */} @@ -390,7 +455,7 @@ function ManagedMemberList() { setNewStudentNumber(e.target.value)} + onChange={(e) => setNewStudentNumber(e.target.value.replace(/\D/g, ''))} placeholder="학번을 입력해주세요" className="border-indigo-25 rounded-lg border px-3 py-2 text-sm outline-none focus:border-blue-500" /> @@ -416,6 +481,47 @@ function ManagedMemberList() { + {/* Pre-Member Action Modal */} + +
+
{selectedPreMember?.name} 관리
+ +
+
+ + {/* Pre-Member Delete Confirm Modal */} + +
+
사전 등록 삭제
+
+ 정말 {selectedPreMember?.name}님의 사전 등록을 삭제하시겠어요? +
+
+ + +
+
+
+ ); diff --git a/src/pages/Manager/hooks/useManagerQuery.ts b/src/pages/Manager/hooks/useManagerQuery.ts index 4998a787..1ac3e1f6 100644 --- a/src/pages/Manager/hooks/useManagerQuery.ts +++ b/src/pages/Manager/hooks/useManagerQuery.ts @@ -21,6 +21,8 @@ import { patchVicePresident, patchMemberPosition, deleteMember, + getPreMembers, + deletePreMember, } from '@/apis/club'; import type { AddPreMemberRequest, @@ -51,6 +53,7 @@ const managerQueryKeys = { banks: () => [...managerQueryKeys.all, 'banks'], managedClubFee: (clubId: number) => [...managerQueryKeys.all, 'managedClubFee', clubId], managedMembers: (clubId: number) => [...managerQueryKeys.all, 'managedMembers', clubId], + preMembersList: (clubId: number) => [...managerQueryKeys.all, 'preMembersList', clubId], }; export const useManagerQuery = () => { @@ -290,6 +293,28 @@ export const useAddPreMember = (clubId: number, options: MutationOptions = {}) = mutationFn: (data: AddPreMemberRequest) => postAddPreMember(clubId, data), onSuccess: () => { queryClient.invalidateQueries({ queryKey: managerQueryKeys.managedMembers(clubId) }); + queryClient.invalidateQueries({ queryKey: managerQueryKeys.preMembersList(clubId) }); + options.onSuccess?.(); + }, + }); +}; + +export const useGetPreMemberList = (clubId: number) => { + const { data: preMembersList } = useSuspenseQuery({ + queryKey: managerQueryKeys.preMembersList(clubId), + queryFn: () => getPreMembers(clubId), + }); + + return { preMembersList }; +}; + +export const useDeletePreMember = (clubId: number, options: MutationOptions = {}) => { + const queryClient = useQueryClient(); + + return useMutation({ + mutationFn: (preMemberId: number) => deletePreMember(clubId, preMemberId), + onSuccess: () => { + queryClient.invalidateQueries({ queryKey: managerQueryKeys.preMembersList(clubId) }); options.onSuccess?.(); }, });