Skip to content

Commit

Permalink
feat: 나눔 상세내역 조회, 나눔 신청자 정보 조회 API 연동 (#32)
Browse files Browse the repository at this point in the history
* feat: 나눔 상세내역 조회 API 연동

* feat: 나눔 상세내역 > 신청자 정보 조회 API 연동

* fix: 나눔 목록 조회 API type 수정
  • Loading branch information
hyeseon-han authored Feb 25, 2024
1 parent c269340 commit d68a39d
Show file tree
Hide file tree
Showing 11 changed files with 131 additions and 56 deletions.
27 changes: 27 additions & 0 deletions src/components/organisms/ShareApplicantListItem.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import Image from 'next/image';
import React from 'react';
import type { ShareApplicantData } from '@/types/share';
import { returnProfileImg } from '@/utils/returnProfileImg';

const ShareApplicantListItem: React.FC<{
idx: number;
data: ShareApplicantData;
}> = ({ idx, data }) => {
return (
<div className="flex items-center mb-[20px]">
<p className="flex justify-center items-center w-[20px] h-[20px] rounded-full bg-gray0 body2-semibold text-gray5">
{idx}
</p>
<Image
src={returnProfileImg(data.profileImage)}
width={40}
height={40}
className="w-[40px] h-[40px] aspect-square mx-[8px]"
alt="신청자 프로필"
/>
<p className="text-gray7 heading4-semibold">{data.nickname}</p>
</div>
);
};

export default ShareApplicantListItem;
35 changes: 13 additions & 22 deletions src/components/organisms/ShareDetailAuthorBottomWrapper.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,35 +9,20 @@ import {
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';

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

const MOCK_DATA_PARTICIPANTS = [
'김지수',
'김지수',
'김지수',
'김지수',
'김지수',
'김지수',
'김지수',
'김지수',
'김지수',
'김지수',
'김지수',
'김지수',
'김지수',
'김지수',
'김지수',
];

const ShareDetailAuthorBottomWrapper: React.FC<{
id: string | string[] | undefined;
curStatus: SortLabel;
onChangeStatus: React.Dispatch<React.SetStateAction<SortLabel>>;
}> = ({ curStatus, onChangeStatus }) => {
}> = ({ id, curStatus, onChangeStatus }) => {
const {
isOpen: isStatusModalOpen,
onOpen: onStatusModalOpen,
Expand All @@ -49,6 +34,8 @@ const ShareDetailAuthorBottomWrapper: React.FC<{
onClose: onParticipantsModalClose,
} = useDisclosure();

const applicants = useGetShareApplicants({ id });

return (
<>
<div className="fixed flex gap-[11px] w-full max-w-[480px] bottom-0 p-[20px] pb-[32px] bg-gray1">
Expand Down Expand Up @@ -117,9 +104,13 @@ const ShareDetailAuthorBottomWrapper: React.FC<{
maxW="lg"
>
<ModalBody>
<div className="max-h-[300px] overflow-scroll px-[20px] py-[40px]">
{MOCK_DATA_PARTICIPANTS.map((ele, idx) => (
<div key={idx}>{ele}</div>
<div className="max-h-[300px] overflow-scroll px-[20px] pt-[40px] py-[20px]">
{applicants?.map((ele, idx) => (
<ShareApplicantListItem
key={ele.nickname}
idx={idx}
data={ele}
/>
))}
</div>
<div className="pt-[20px] pb-[32px]">
Expand Down
3 changes: 2 additions & 1 deletion src/components/organisms/ShareListItem.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { DotIcon, LocationIcon } from '@/assets/icons';
import Image from 'next/image';
import Link from 'next/link';
import React from 'react';
import type { ShareData } from '@/types/share';
import dayjs from 'dayjs';

const ShareListItem: React.FC<{
Expand Down Expand Up @@ -43,7 +44,7 @@ const ShareListItem: React.FC<{
className="mx-[4px] mb-1"
/>
</span>
{`${dayjs(data.shareDate).format('MM월 DD일')} ${data.shareTime.hour} : ${data.shareTime.minute}`}
{`${dayjs(data.shareDate).format('MM월 DD일')} ${data.shareTime}`}
</p>
</div>
</div>
Expand Down
2 changes: 2 additions & 0 deletions src/hooks/queries/queryKeys.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@ export const queryKeys = {
DELETE_FRIENDSHIP: () => ['deleteFriendship'],
MY_INVITE_CODE: () => ['myInviteCode'],
ADD_FRIENDSHIP: () => ['addFriendship'],
SHARE_DETAIL: () => ['shareDetail'],
SHARE_APPLICANTS: () => ['shareApplicants'],
} as const;

export type QueryKeys = (typeof queryKeys)[keyof typeof queryKeys];
2 changes: 2 additions & 0 deletions src/hooks/queries/share/index.ts
Original file line number Diff line number Diff line change
@@ -1 +1,3 @@
export { default as useGetShares } from './useGetShares';
export { default as useGetShareDetail } from './useGetShareDetail';
export { default as useGetShareApplicants } from './useGetShareApplicants';
21 changes: 21 additions & 0 deletions src/hooks/queries/share/useGetShareApplicants.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import type { ShareApplicantData } from '@/types/share';
import { queryKeys } from '../queryKeys';
import { useBaseQuery } from '../useBaseQuery';

const useGetShareApplicants = ({
id,
}: {
id: string | string[] | undefined;
}) => {
if (typeof id !== 'string') {
return null;
}
const { data } = useBaseQuery<ShareApplicantData[]>(
queryKeys.SHARE_APPLICANTS(),
`/shares/${id}/applies`,
);

return data?.data;
};

export default useGetShareApplicants;
17 changes: 17 additions & 0 deletions src/hooks/queries/share/useGetShareDetail.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import type { ShareDetailData } from '@/types/share';
import { queryKeys } from '../queryKeys';
import { useBaseQuery } from '../useBaseQuery';

const useGetShareDetail = ({ id }: { id: string | string[] | undefined }) => {
if (typeof id !== 'string') {
return null;
}
const { data } = useBaseQuery<ShareDetailData>(
queryKeys.SHARE_DETAIL(),
`/shares/${id}`,
);

return data?.data;
};

export default useGetShareDetail;
4 changes: 3 additions & 1 deletion src/hooks/queries/share/useGetShares.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
import { type ShareStatusType, type ShareSortType } from '@/types/friendship';
import type { ShareSortType, ShareStatusType } from '@/types/friendship';

import type { ShareData } from '@/types/share';
import { queryKeys } from '../queryKeys';
import { useBaseInfiniteQuery } from '../useBaseInfiniteQuery';

Expand Down
56 changes: 30 additions & 26 deletions src/pages/share/[id].tsx
Original file line number Diff line number Diff line change
@@ -1,36 +1,38 @@
import { ClockIcon, DateIcon, LocationIcon } from '@/assets/icons';
import { ShareInfoRowItem, VerticalLabelValue } from '@/components/molecules';
import { Header, ShareDetailAuthorBottomWrapper } from '@/components/organisms';
import { useGetShareDetail } from '@/hooks/queries/share';
import { type SortLabel } from '@/types/common';
import dayjs from 'dayjs';
import { type NextPage } from 'next';
import Image from 'next/image';
import { useRouter } from 'next/router';
import React, { useEffect, useState } from 'react';

const MOCK_DATA = {
shareerName: '김지수',
image: '',
title: '사과받아갈사람선착순12345명',
ingredient: '사과',
fixedNum: 15,
useByDate: dayjs('2024-02-30'),
meetingDate: dayjs('2024-02-20'),
meetingTime: '14:00',
meetingLocation: '디지털시티역 8번출구 뜌레주르앞',
desc: '사과는역시청송사과맛있음은당연보장선착순1명사과는역시청송사과맛있음은당연보장선착순1명사과는역체시청송사과맛있음은당연보장선착순1명사과는역시청송사과맛있음은당연보장선착순1명은당연보장선착순1명사당연',
};
// const MOCK_DATA = {
// shareerName: '김지수',
// image: '',
// title: '사과받아갈사람선착순12345명',
// ingredient: '사과',
// fixedNum: 15,
// useByDate: dayjs('2024-02-30'),
// meetingDate: dayjs('2024-02-20'),
// meetingTime: '14:00',
// meetingLocation: '디지털시티역 8번출구 뜌레주르앞',
// desc: '사과는역시청송사과맛있음은당연보장선착순1명사과는역시청송사과맛있음은당연보장선착순1명사과는역체시청송사과맛있음은당연보장선착순1명사과는역시청송사과맛있음은당연보장선착순1명은당연보장선착순1명사당연',
// };

const MOCK_DATA_IS_AUTHOR: boolean = true;

const MOCK_DATA_SHARE_STATUS = { label: '나눔 신청', value: 'enroll' };

const ShareDetailPage: NextPage = () => {
const router = useRouter();
// eslint-disable-next-line no-unused-vars, @typescript-eslint/no-unused-vars
const { id } = router.query;
const [curStatus, setCurStatus] = useState<SortLabel>(MOCK_DATA_SHARE_STATUS);

const data = useGetShareDetail({ id });

useEffect(() => {
if (window) {
console.log(window.innerWidth);
Expand All @@ -41,10 +43,10 @@ const ShareDetailPage: NextPage = () => {
<>
<div className={'min-h-screen mb-[110px]'}>
<Header backgroundColor="transparent" />
{MOCK_DATA.image ? (
{data?.thumbNailImage ? (
<Image
alt="detailImage"
src={''}
src={data?.thumbNailImage}
sizes="100vw"
width={0}
height={0}
Expand All @@ -56,51 +58,53 @@ const ShareDetailPage: NextPage = () => {

<div className="relative mt-[-13px] mx-[20px] px-[16px] py-[24px] bg-white rounded-[12px]">
<p className="text-center body1-semibold text-gray7">
{MOCK_DATA_IS_AUTHOR ? '나의 나눔' : MOCK_DATA.shareerName}
{MOCK_DATA_IS_AUTHOR ? '나의 나눔' : data?.userName}
</p>
<p className="mt-[14px] mb-[20px] text-center heading2-semibold text-black">
{MOCK_DATA.title}
{data?.title}
</p>
<div className="flex p-[16px] mb-[6px] justify-between rounded-[12px] bg-gray1">
<VerticalLabelValue label="식자재" value={MOCK_DATA.ingredient} />
<VerticalLabelValue
label="식자재"
value={data?.itemName as string}
/>
<hr className="w-[1px] h-[36px] mx-[14px] bg-gray2" />
<VerticalLabelValue
label="모집인원"
value={`${MOCK_DATA.fixedNum} 명`}
value={`${data?.limitPerson} 명`}
/>
<hr className="w-[1px] h-[36px] bg-gray2" />
<VerticalLabelValue
label="소비기한"
value={`D-${MOCK_DATA.useByDate.diff(dayjs(), 'day')}`}
value={`D-${dayjs(data?.limitDate).diff(dayjs(), 'day') > 0 ? dayjs(data?.limitDate).diff(dayjs(), 'day') : 0}`}
/>
</div>
<ShareInfoRowItem
icon={DateIcon}
label="약속 날짜"
value={MOCK_DATA.meetingDate.format('YYYY.MM.DD')}
value={dayjs(data?.shareDate).format('YYYY.MM.DD')}
/>
<ShareInfoRowItem
icon={ClockIcon}
label="약속시간"
value={MOCK_DATA.meetingTime}
value={`${data?.shareTime}`}
/>
<ShareInfoRowItem
icon={LocationIcon}
label="약속 장소"
value={MOCK_DATA.meetingLocation}
value={data?.location as string}
/>
<hr className="h-0.5 my-[24px] bg-gray1" />

<div>
<p className="text-center body1-semibold text-primary2">상세설명</p>
<p className="mt-[12px] body1-medium text-gray6">
사과는역시청송사과맛있음은당연보장선착순1명사과는역시청송사과맛있음은당연보장선착순1명사과는역시청송사과맛있음은당연보장선착순1명사과는역시청송사과맛있음은당연보장선착순1명은당연보장선착순1명사당연
</p>
<p className="mt-[12px] body1-medium text-gray6">{data?.content}</p>
</div>
</div>
</div>
{MOCK_DATA_IS_AUTHOR ? (
<ShareDetailAuthorBottomWrapper
id={id}
curStatus={curStatus}
onChangeStatus={setCurStatus}
/>
Expand Down
1 change: 1 addition & 0 deletions src/pages/share/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import Header from '@/components/organisms/Header';
import Link from 'next/link';
import type { NextPage } from 'next';
import { PlusIcon } from '@/assets/icons';
import { type ShareData } from '@/types/share';
import ShareListItem from '@/components/organisms/ShareListItem';
import { SuspenseFallback } from '@/components/templates';
import { useGetShares } from '@/hooks/queries/share';
Expand Down
19 changes: 13 additions & 6 deletions src/types/share/index.d.ts
Original file line number Diff line number Diff line change
@@ -1,18 +1,25 @@
import type { ProfileEnum } from '../common';

interface ShareData {
shareId: number;
title: string;
itemName: string;
content: string;
shareTime: {
hour: number;
minute: number;
second: number;
nano: number;
};
shareTime: string;
shareDate: string;
limitDate: string;
limitPerson: number;
location: string;
status: string;
thumbNailImage: string;
}

interface ShareDetailData extends ShareData {
userName: string;
profileImage: ProfileEnum;
}

interface ShareApplicantData {
nickname: string;
profileImage: ProfileEnum;
}

0 comments on commit d68a39d

Please sign in to comment.