Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: 나눔 상태 변경, 나눔 신청, 나눔 신청 취소 API 연동 #41

Merged
merged 8 commits into from
Mar 1, 2024
1 change: 1 addition & 0 deletions .eslintrc.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,5 +17,6 @@ module.exports = {
'prettier/prettier': ['error', { endOfLine: 'auto' }],
'@typescript-eslint/explicit-function-return-type': 'off',
'@typescript-eslint/no-floating-promises': 'off',
'@typescript-eslint/no-misused-promises': 'off',
},
};
2 changes: 1 addition & 1 deletion src/components/atoms/RadioButtonField.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ const RadioButtonField: React.FC<{
checked: boolean;
}> = ({ label, onClick, checked }) => {
return (
<button onClick={onClick} className="flex flex-1 w-screen px-[20px] py-[24px] justify-between">
<button onClick={onClick} className="flex flex-1 w-full px-[20px] py-[24px] justify-between">
<p className="heading3-semibold">{label}</p>
<Radio checked={checked} />
</button>
Expand Down
35 changes: 35 additions & 0 deletions src/components/atoms/ShareStatusBadge.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import React, { useMemo } from 'react';

import type { ShareStatusType } from '@/types/friendship';

const ShareStatusBadge: React.FC<{ status: ShareStatusType }> = ({ status }) => {
const text = useMemo(() => {
switch (status) {
case 'SHARE_START':
return '나눔 신청';
case 'SHARE_IN_PROGRESS':
return '나눔 중';
case 'SHARE_COMPLETE':
return '나눔 완료';
default:
return '';
}
}, [status]);

const className = useMemo(() => {
switch (status) {
case 'SHARE_START':
return 'bg-[#DCF3ED] text-primary2';
case 'SHARE_IN_PROGRESS':
return 'bg-[#FFEBE6] text-point3';
case 'SHARE_COMPLETE':
return 'bg-gray0 text-gray4';
default:
return '';
}
}, [status]);

return <div className={`px-[8px] py-[4px] rounded-[6px] body2-medium ${className} `}>{text}</div>;
};

export default ShareStatusBadge;
1 change: 1 addition & 0 deletions src/components/atoms/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,3 +18,4 @@ export { default as MiniButton } from './MiniButton';
export { default as ExclamationAlertSpan } from './ExclamationAlertSpan';
export { default as Lottie } from './Lottie';
export { default as CheckBox } from './CheckBox';
export { default as ShareStatusBadge } from './ShareStatusBadge';
52 changes: 38 additions & 14 deletions src/components/organisms/ShareDetailAuthorBottomWrapper.tsx
Original file line number Diff line number Diff line change
@@ -1,22 +1,30 @@
import { Button, RadioButtonField } from '@/components/atoms';
import { Modal, ModalBody, ModalContent, ModalOverlay, useDisclosure } from '@chakra-ui/react';
import React, { useEffect, useState } from 'react';
import { useGetShareApplicants, usePutShareStatus } from '@/hooks/queries/share';

import { Button, RadioButtonField } from '@/components/atoms';
import React from 'react';
import { type SortLabel } from '@/types/common';
import { useGetShareApplicants } from '@/hooks/queries/share';
import ShareApplicantListItem from './ShareApplicantListItem';
import type { ShareStatusType } from '@/types/friendship';

export interface ShareStatusKeyValue {
label: string;
value: ShareStatusType;
}

const SHARE_STATUSES = [
{ label: '나눔 신청', value: 'enroll' },
{ label: '나눔 중', value: 'proceeding' },
{ label: '나눔 완료', value: 'complete' },
const SHARE_STATUSES: ShareStatusKeyValue[] = [
{ label: '나눔 신청', value: 'SHARE_START' },
{ label: '나눔 중', value: 'SHARE_IN_PROGRESS' },
{ label: '나눔 완료', value: 'SHARE_COMPLETE' },
];

const ShareDetailAuthorBottomWrapper: React.FC<{
id: string | string[] | undefined;
curStatus: SortLabel;
onChangeStatus: React.Dispatch<React.SetStateAction<SortLabel>>;
}> = ({ id, curStatus, onChangeStatus }) => {
refetch: () => void;
curStatus: ShareStatusType;
}> = ({ id, refetch, curStatus }) => {
const [selectedStatus, setSelectedStatus] = useState<ShareStatusKeyValue>(
SHARE_STATUSES.find((ele) => ele.value === curStatus) as ShareStatusKeyValue,
);
const { isOpen: isStatusModalOpen, onOpen: onStatusModalOpen, onClose: onStatusModalClose } = useDisclosure();
const {
isOpen: isParticipantsModalOpen,
Expand All @@ -25,6 +33,22 @@ const ShareDetailAuthorBottomWrapper: React.FC<{
} = useDisclosure();

const applicants = useGetShareApplicants({ id });
const modifyShareStatus = usePutShareStatus({
id: Number(id),
status: selectedStatus.value as ShareStatusType,
onSuccessParam: refetch,
});

const onModifyShareStatus = () => {
onStatusModalClose();
modifyShareStatus.mutate({});
};

useEffect(() => {
const initialStatus = SHARE_STATUSES.find((ele) => ele.value === curStatus);
console.log(initialStatus);
setSelectedStatus(initialStatus as { label: string; value: ShareStatusType });
}, []);

return (
<>
Expand Down Expand Up @@ -53,13 +77,13 @@ const ShareDetailAuthorBottomWrapper: React.FC<{
key={ele.value}
label={ele.label}
onClick={() => {
onChangeStatus(ele);
setSelectedStatus(ele);
}}
checked={ele.value === curStatus.value}
checked={ele.value === selectedStatus.value}
/>
))}
<div className="px-[20px] pb-[32px]">
<Button className="block w-full bg-primary2" text={'선택 완료'} onClick={onStatusModalClose} />
<Button className="block w-full bg-primary2" text={'선택 완료'} onClick={onModifyShareStatus} />
</div>
</ModalBody>
</ModalContent>
Expand Down
61 changes: 61 additions & 0 deletions src/components/organisms/ShareDetailFriendBottomWrapper.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
import { useApplyShare, useDeleteApplyShare } from '@/hooks/queries/share';

import React from 'react';
import type { ShareStatusType } from '@/types/friendship';
import useToast from '@/hooks/useToast';

const APPLY_SUCCESS_MESSAGE = '나눔 신청이 완료되었습니다.';
const APPLY_DELETE_SUCCESS_MESSAGE = '나눔 신청이 취소되었습니다.';

const ShareDetailFriendBottomWrapper: React.FC<{
id: string | string[] | undefined;
isApplied: boolean;
curStatus: ShareStatusType;
refetch: () => void;
}> = ({ id, isApplied, curStatus, refetch }) => {
const { showToast } = useToast();
const applyShare = useApplyShare({
onSuccess: () => {
showToast(APPLY_SUCCESS_MESSAGE, 'success');
refetch();
},
});
const deleteShare = useDeleteApplyShare({
id: Number(id),
onSuccess: () => {
showToast(APPLY_DELETE_SUCCESS_MESSAGE, 'success');
refetch();
},
});

const onApply = () => {
applyShare.mutate({ shareId: Number(id) });
};

const onApplyCancel = () => {
deleteShare.mutate({});
};

if (curStatus === 'SHARE_COMPLETE') {
return (
<div className="fixed w-full max-w-[480px] bottom-0 p-[20px] pb-[32px] z-300 bg-gray1">
<p className="w-full text-center py-[16px] rounded-[12px] text-gray0 bg-gray3 heading4-semibold">
나눔 신청 종료
</p>
</div>
);
}

return (
<div className="fixed w-full max-w-[480px] bottom-0 p-[20px] pb-[32px] z-300 bg-gray1">
<button
onClick={isApplied ? onApplyCancel : onApply}
className="w-full text-center py-[16px] rounded-[12px] text-white bg-primary2 heading4-semibold"
>
{isApplied ? '나눔 신청 완료' : '나눔 신청'}
</button>
</div>
);
};

export default ShareDetailFriendBottomWrapper;
1 change: 1 addition & 0 deletions src/components/organisms/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,3 +15,4 @@ export { default as FriendListItem } from './FriendListItem';
export { default as BulletNoticeBox } from './BulletNoticeBox';
export { default as SelectFridgeModal } from './SelectFridgeModal';
export { default as SelectFridgeBoard } from './SelectFridgeBoard';
export { default as ShareDetailFriendBottomWrapper } from './ShareDetailFriendBottomWrapper';
3 changes: 3 additions & 0 deletions src/hooks/queries/queryKeys.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,9 @@ export const queryKeys = {
SHARE_APPLICANTS: () => ['shareApplicants'],
UPLOAD: () => ['upload'],
ADD_SHARE: () => ['addShare'],
MODIFY_SHARE_STATUS: () => ['modify_share_status'],
APPLY_SHARE: () => ['apply_share'],
DELETE_APPLY_SHARE: () => ['delete_share'],
} as const;

export type QueryKeys = (typeof queryKeys)[keyof typeof queryKeys];
3 changes: 3 additions & 0 deletions src/hooks/queries/share/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,6 @@ export { default as useGetShareDetail } from './useGetShareDetail';
export { default as useGetShareApplicants } from './useGetShareApplicants';
export { default as usePostUpload } from './usePostUpload';
export { default as usePostShare } from './usePostShare';
export { default as usePutShareStatus } from './usePutShareStatus';
export { default as useApplyShare } from './useApplyShare';
export { default as useDeleteApplyShare } from './useDeleteApplyShare';
7 changes: 7 additions & 0 deletions src/hooks/queries/share/useApplyShare.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import { queryKeys } from '../queryKeys';
import { useBaseMutation } from '../useBaseMutation';

const useApplyShare = ({ onSuccess }: { onSuccess: () => void }) => {
return useBaseMutation<{ shareId: number }>(queryKeys.APPLY_SHARE(), `/shares/applies`, onSuccess);
};
export default useApplyShare;
7 changes: 7 additions & 0 deletions src/hooks/queries/share/useDeleteApplyShare.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import { queryKeys } from '../queryKeys';
import { useBaseMutation } from '../useBaseMutation';

const useDeleteApplyShare = ({ id, onSuccess }: { id: number; onSuccess: () => void }) => {
return useBaseMutation(queryKeys.DELETE_APPLY_SHARE(), `/shares/applies/${id}`, onSuccess, 'DELETE');
};
export default useDeleteApplyShare;
4 changes: 2 additions & 2 deletions src/hooks/queries/share/useGetShareDetail.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,9 @@ const useGetShareDetail = ({ id }: { id: string | string[] | undefined }) => {
if (typeof id !== 'string') {
return null;
}
const { data } = useBaseQuery<ShareDetailData>(queryKeys.SHARE_DETAIL(), `/shares/${id}`);
const { data, refetch } = useBaseQuery<ShareDetailData>(queryKeys.SHARE_DETAIL(), `/shares/${id}`);

return data?.data;
return { data, refetch };
};

export default useGetShareDetail;
26 changes: 26 additions & 0 deletions src/hooks/queries/share/usePutShareStatus.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import type { ShareStatusType } from '@/types/friendship';
import { queryClient } from '@/pages/_app';
import { queryKeys } from '../queryKeys';
import { useBaseMutation } from '../useBaseMutation';

const usePutShareStatus = ({
id,
status,
onSuccessParam,
}: {
id: number;
status: ShareStatusType;
onSuccessParam: () => void;
}) => {
const onSuccess = () => {
onSuccessParam();
void queryClient.invalidateQueries();
};
return useBaseMutation(
queryKeys.MODIFY_SHARE_STATUS(),
`/shares/${id}/status?updateShareStatusRequest=${status}`,
onSuccess,
'PUT',
);
};
export default usePutShareStatus;
Loading
Loading