Skip to content

Commit

Permalink
Merge pull request #40 from eunji-0623/feature-황은지
Browse files Browse the repository at this point in the history
Feat: 내 체험 관련 세부 사항 적용
  • Loading branch information
eunji-0623 authored Jul 17, 2024
2 parents 06b6de2 + ef862ab commit 7ea67d2
Show file tree
Hide file tree
Showing 10 changed files with 160 additions and 60 deletions.
2 changes: 1 addition & 1 deletion components/MyActivity/Card.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import useDeleteActivity from '@/hooks/myActivity/useDeleteActivity';
function PopoverButton({ children, onClick }: PopoverButtonProps) {
return (
<button
className="px-[46px] py-[18px] w-[160px] text-[18px] font-[500]"
className="px-[46px] py-[18px] w-auto text-[18px] font-[500]"
onClick={onClick}
>
{children}
Expand Down
26 changes: 9 additions & 17 deletions components/MyActivity/Register/TimeDropdown.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,6 @@ import useClickOutside from '@/hooks/useClickOutside';
import Image from 'next/image';
import { useState } from 'react';
import { TimeDropdownProps } from './TimeSlot.types';
import { useRecoilState, useRecoilValue } from 'recoil';
import { endTimeState, startTimeState } from '@/states/registerState';

function generateTimeOptions(startTime = 0, type: 'start' | 'end') {
const times = [];
Expand All @@ -15,24 +13,17 @@ function generateTimeOptions(startTime = 0, type: 'start' | 'end') {
return times;
}

function TimeDropdown({ type, index }: TimeDropdownProps) {
function TimeDropdown({
type,
handleChange,
startTime,
selectedTime,
}: TimeDropdownProps) {
const [isOpen, setIsOpen] = useState(false);
const [selectedTime, setSelectedTime] = useState<string>('00:00');
const [startTime, setStartTime] = useRecoilState(startTimeState);
const [endTime, setEndTime] = useRecoilState(endTimeState);

const handleSelectTime = (time: string) => {
setSelectedTime(time);
setIsOpen(false);
if (type === 'start') {
const updatedStartTime = [...startTime];
updatedStartTime[index] = time;
setStartTime(updatedStartTime);
} else {
const updatedEndTime = [...endTime];
updatedEndTime[index] = time;
setEndTime(updatedEndTime);
}
handleChange(type, time);
};

const handleClickDropdown = () => {
Expand All @@ -44,9 +35,10 @@ function TimeDropdown({ type, index }: TimeDropdownProps) {
};

const dropdownRef = useClickOutside<HTMLDivElement>(closeDropdown);

let timeLimit = 0;
if (type === 'end') {
timeLimit = Number(startTime[index].substring(0, 2)) + 1;
timeLimit = Number(startTime.substring(0, 2)) + 1;
}
const times = generateTimeOptions(timeLimit, type);

Expand Down
52 changes: 49 additions & 3 deletions components/MyActivity/Register/TimeSlot.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,19 @@ import TimeDropdown from './TimeDropdown';
import { TimeSlotGroupProps } from './TimeSlot.types';
import { useState } from 'react';
import { useRecoilState } from 'recoil';
import { timeSlotCountState } from '@/states/registerState';
import {
startTimeState,
endTimeState,
timeSlotCountState,
} from '@/states/registerState';

function autoSelectedTime(time: string) {
const tempTime = Number(time.substring(0, 2)) + 1;
if (tempTime < 10) {
return `0${tempTime}:00`;
}
return `${tempTime}:00`;
}

function TimeSlotGroup({
isDefault = false,
Expand All @@ -13,14 +25,48 @@ function TimeSlotGroup({
id = -1,
index,
}: TimeSlotGroupProps) {
const [selectedStartTime, setSelectedStartTime] = useState<string>('00:00');
const [selectedEndTime, setSelectedEndTime] = useState<string>('00:00');
const [startTime, setStartTime] = useRecoilState(startTimeState);
const [endTime, setEndTime] = useRecoilState(endTimeState);

const handleChange = (type: string, time: string) => {
if (type === 'start') {
setSelectedStartTime(time);
setSelectedEndTime(autoSelectedTime(time));
const updatedStartTime = [...startTime];
updatedStartTime[index] = time;
setStartTime(updatedStartTime);

const updatedEndTime = [...endTime];
updatedEndTime[index] = autoSelectedTime(time);
setEndTime(updatedEndTime);
} else {
setSelectedEndTime(time);
const updatedEndTime = [...endTime];
updatedEndTime[index] = time;
setEndTime(updatedEndTime);
}
};

return (
<div className="flex items-center t:justify-between m:justify-between gap-[20px] t:gap-[4px] m:gap-[4px]">
<div className="flex items-center gap-[20px] t:gap-[4px] m:gap-[4px]">
<DateInput index={index} />
<div className="flex gap-[12px] items-center t:gap-[4px] m:gap-[4px]">
<TimeDropdown type="start" index={index} />
<TimeDropdown
type="start"
handleChange={handleChange}
startTime={startTime[index]}
selectedTime={selectedStartTime}
/>
<p className="text-[20px] font-[700] t:hidden m:hidden">~</p>
<TimeDropdown type="end" index={index} />
<TimeDropdown
type="end"
handleChange={handleChange}
startTime={startTime[index]}
selectedTime={selectedEndTime}
/>
</div>
</div>
<div className="flex items-center">
Expand Down
4 changes: 3 additions & 1 deletion components/MyActivity/Register/TimeSlot.types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,5 +8,7 @@ export interface TimeSlotGroupProps {

export interface TimeDropdownProps {
type: 'start' | 'end';
index: number;
handleChange: (type: string, time: string) => void;
startTime: string;
selectedTime: string;
}
10 changes: 5 additions & 5 deletions components/MyActivity/Register/UploadImage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -51,14 +51,14 @@ function UploadImage({

return (
<div>
<label className="text-[24px] font-[700] text-var-black block mb-[24px]">
{label}
</label>

<label className="text-[24px] font-[700] text-var-black">{label}</label>
{label === '소개 이미지' ? (
<span className="text-var-gray7">{`\u00A0\u00A0\u00A0\u00A0*이미지는 최대 4개까지 등록 가능합니다.`}</span>
) : null}
<div
className={`grid ${singleImage ? 'grid-rows-1 grid-cols-4 t:grid-cols-2 m:grid-cols-2' : 'grid-rows-2 grid-cols-4 t:grid-rows-3 t:grid-cols-2 m:grid-rows-3 m:grid-cols-2'} gap-[24px]`}
>
<div>
<div className="mt-[24px]">
<label
htmlFor={`upload-${label}`}
className={`cursor-pointer ${selectedImages.length === maxImages ? 'pointer-events-none' : ''}`}
Expand Down
22 changes: 16 additions & 6 deletions components/Popup/AddressPopup.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import React, { useEffect, useState } from 'react';
import DaumPostcode from 'react-daum-postcode';
import { useRecoilState } from 'recoil';
import { AddressPopupProps } from './Popup.types';
import { CloseButtonBold } from '../Button/Button';

const style = {
width: '400px',
Expand All @@ -27,12 +28,21 @@ const AddressPopup = ({ closePopup }: AddressPopupProps) => {

return (
<div className="flex items-center justify-center bg-black bg-opacity-70 fixed inset-0 z-50">
<DaumPostcode
style={style}
autoClose
onComplete={handleComplete}
onClose={handleClose}
/>
<div className="flex flex-col items-end bg-var-gray2">
<div className="flex items-center h-[30px]">
<CloseButtonBold
onClick={() => {
handleClose('FORCE_CLOSE');
}}
/>
</div>
<DaumPostcode
style={style}
autoClose
onComplete={handleComplete}
onClose={handleClose}
/>
</div>
</div>
);
};
Expand Down
5 changes: 4 additions & 1 deletion hooks/myActivity/useMyActivityList.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ export const useMyActivityList = () => {

const { data, fetchNextPage, hasNextPage, isLoading, isFetchingNextPage } =
useInfiniteQuery<getMyActivityListResponse>({
queryKey: ['myActivityList'],
queryKey: ['myActivityListInfinite'],
queryFn: ({ pageParam = undefined }) => {
const size = pageParam === undefined ? INITIAL_SIZE : REFETCH_SIZE;
return getMyActivityList({
Expand All @@ -29,6 +29,8 @@ export const useMyActivityList = () => {
enabled: loginState.isLoggedIn,
});

const totalCount = data?.pages[0].totalCount;

const myActivityList = useMemo(() => {
if (data) {
return data.pages.flatMap((page) => page.activities);
Expand All @@ -37,6 +39,7 @@ export const useMyActivityList = () => {

return {
myActivityList,
totalCount,
fetchNextPage,
hasNextPage,
isLoading,
Expand Down
22 changes: 21 additions & 1 deletion hooks/myActivity/useRegisterActivity.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,12 @@ import {
} from '@/pages/api/activities/apiactivities.types';
import { postActivity } from '@/pages/api/activities/apiactivities';
import { useRouter } from 'next/router';
import { usePopup } from '../usePopup';
import { AxiosError } from 'axios';

export default function useRegisterActivity() {
const router = useRouter();
const { openPopup } = usePopup();

const postActivityMutation: UseMutationResult<
postActivityResponse,
Expand All @@ -16,7 +19,24 @@ export default function useRegisterActivity() {
> = useMutation({
mutationFn: postActivity,
onSuccess: () => {
router.push('/myActivity');
openPopup({
popupType: 'alert',
content: '체험 등록이 완료되었습니다.',
btnName: ['확인'],
callBackFnc: () => {
router.push('/myactivity');
},
});
},
onError: (err: AxiosError) => {
if (err.response) {
const errorMessage = (err.response.data as { message: string }).message;
openPopup({
popupType: 'alert',
content: errorMessage,
btnName: ['확인'],
});
}
},
});

Expand Down
63 changes: 40 additions & 23 deletions pages/myactivity/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,13 @@ import Card from '@/components/MyActivity/Card';
import SidenNavigation from '@/components/SideNavigation/SideNavigation';
import { useInView } from 'react-intersection-observer';
import { useMyActivityList } from '@/hooks/myActivity/useMyActivityList';
import Image from 'next/image';

function MyActivity() {
const router = useRouter();
const { ref, inView } = useInView();
const { fetchNextPage, myActivityList, hasNextPage } = useMyActivityList();
const { fetchNextPage, myActivityList, totalCount, hasNextPage } =
useMyActivityList();

useEffect(() => {
if (inView && hasNextPage) {
Expand All @@ -22,37 +24,52 @@ function MyActivity() {
};

return (
<div className="flex gap-[20px] py-[72px] m:gap-0 m:pl-[20px]">
<div className="flex gap-[20px] py-[72px] min-h-[1080px] m:gap-0 m:px-[20px]">
<div className="m:hidden">
<SidenNavigation />
</div>
<div className="flex flex-col gap-[20px] t:w-full m:w-full flex-shrink">
<div className="flex flex-col gap-[20px] w-full">
<div className="flex justify-between">
<h1 className="text-[32px] font-[700]">내 체험 관리</h1>
<PrimaryButton size="small" style="dark" onClick={handleClickAdd}>
체험 등록하기
</PrimaryButton>
</div>
<div className="flex flex-col gap-[20px] overflow-visible">
{myActivityList?.map((activity) => {
return (
<Card
key={activity.id}
activityId={activity.id}
activityImage={activity.bannerImageUrl}
rating={activity.rating}
reviewCount={activity.reviewCount}
title={activity.title}
price={activity.price}
/>
);
})}
{hasNextPage && (
<div className="text-[35px] font-bold text-center" ref={ref}>
...
</div>
)}
</div>
{totalCount !== 0 ? (
<div className="flex flex-col gap-[20px] overflow-visible">
{myActivityList?.map((activity) => {
return (
<Card
key={activity.id}
activityId={activity.id}
activityImage={activity.bannerImageUrl}
rating={activity.rating}
reviewCount={activity.reviewCount}
title={activity.title}
price={activity.price}
/>
);
})}
{hasNextPage && (
<div className="text-[35px] font-bold text-center" ref={ref}>
...
</div>
)}
</div>
) : (
<div className="flex flex-col justify-center items-center mt-[50px]">
<Image
src="/icon/empty_reservation.svg"
alt="예약 내역 없음"
width={240}
height={240}
className="t:w-[200px] t:h-[200px] m:w-[200px] m:h-[200px]"
/>
<p className="text-[24px] font-[500] text-var-gray7">
아직 등록한 체험이 없어요
</p>
</div>
)}
</div>
</div>
);
Expand Down
14 changes: 12 additions & 2 deletions pages/myactivity/register.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import {
UploadDetailImage,
} from '@/components/MyActivity/Register/UploadImage';
import { validation } from '@/components/MyActivity/Register/validation';
import { useRecoilValue } from 'recoil';
import { useRecoilValue, useResetRecoilState } from 'recoil';
import { KategoriedDropState } from '@/states/KategorieDropState';
import {
addressState,
Expand All @@ -25,6 +25,7 @@ import { formatDate } from '@/utils/formatDate';
import useActivityImage from '@/hooks/myActivity/useActivityImage';
import SidenNavigation from '@/components/SideNavigation/SideNavigation';
import AddressInput from '@/components/MyActivity/Register/AddressInput';
import { useEffect } from 'react';

function RegisterActivity() {
const {
Expand All @@ -45,6 +46,9 @@ function RegisterActivity() {
const { postActivityMutation } = useRegisterActivity();
const { postActivityImageMutation } = useActivityImage();

const resetKategorie = useResetRecoilState(KategoriedDropState);
const resetAddress = useResetRecoilState(addressState);

const formatSchedules = () =>
Array.from({ length: timeSlotCount }, (_, i) => ({
endTime: endTime[i],
Expand Down Expand Up @@ -100,11 +104,17 @@ function RegisterActivity() {
selectedKateogorie.name !== '' &&
address !== '' &&
bannerImage.length !== 0 &&
detailImage.length !== 0 &&
isTimeFieldValid()
);
};

useEffect(() => {
return () => {
resetKategorie();
resetAddress();
};
}, []);

return (
<div className="flex gap-[20px] py-[72px] m:gap-0 w-full">
<div className="m:hidden">
Expand Down

0 comments on commit 7ea67d2

Please sign in to comment.