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

[#523] [모임 상세] 모임 수정 페이지 #527

Merged
merged 21 commits into from
Apr 22, 2024
Merged
Show file tree
Hide file tree
Changes from 19 commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
e5f5ab2
chore: DatePicker, TextArea 컴포넌트 use client 지시문 작성
hanyugeon Apr 15, 2024
3d21c66
refactor: InputLength 컴포넌트 수정
hanyugeon Apr 15, 2024
c07cec7
feat: 독서모임 수정 페이지 마크업
hanyugeon Apr 15, 2024
2e3904d
fix: ChakraUI theme에서 input[type="date"] 속성 제거
hanyugeon Apr 16, 2024
3dbd717
fix: DatePicker 컴포넌트 gap 속성 추가
hanyugeon Apr 16, 2024
54616e9
feat: 모임 수정 페이지 폼 기능 구현
hanyugeon Apr 16, 2024
8f46d59
refactor: 독서 모임 수정 페이지 개선
hanyugeon Apr 16, 2024
5db5c83
chore: BookGroupTitleForm을 BookGroupEditTitleForm로 수정
hanyugeon Apr 16, 2024
b58a72d
refactor: TextArea 컴포넌트 defaultValue props 타입 정의
hanyugeon Apr 16, 2024
9a4534b
fix: 독서모임 수정 폼 수정(defaultValue 추가)
hanyugeon Apr 16, 2024
eabf937
feat: useBookGroupEditCurrentInfo 쿼리 작성
hanyugeon Apr 16, 2024
469bdd4
feat: 모임정보 수정 기능 구현
hanyugeon Apr 16, 2024
c2d1055
fix: next.config에 images.unuptimized:true 옵션 추가
hanyugeon Apr 16, 2024
a125040
chore: 불필요한 코드 제거
hanyugeon Apr 16, 2024
3314a6b
fix: 빌드에러 수정
hanyugeon Apr 16, 2024
59d8916
fix: next.config 내부 images.unoptimized 옵션 제거
hanyugeon Apr 17, 2024
7732d10
feat: 독서 모임 수정 reqBody 타입 작성
hanyugeon Apr 17, 2024
aba6109
refactor: formContext 타입 정의
hanyugeon Apr 17, 2024
cf5724d
feat: 그룹 정보 수정 mutation 쿼리 훅 작성
hanyugeon Apr 21, 2024
2dc9955
feat: 모임수정 타입명 수정, isOwner 타입 추가
hanyugeon Apr 22, 2024
e456f70
feat: 페이지 접근 권한 추가 (로그인 & 모임 장)
hanyugeon Apr 22, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
120 changes: 87 additions & 33 deletions src/app/group/[groupId]/edit/page.tsx
Original file line number Diff line number Diff line change
@@ -1,43 +1,97 @@
'use client';

import GroupAPI from '@/apis/group';
import { APIGroupDetail } from '@/types/group';
import AuthRequired from '@/ui/AuthRequired';
import TopNavigation from '@/ui/common/TopNavigation';
import EditGroupForm from '@/ui/Group/EditGroupForm';
import { VStack } from '@chakra-ui/react';
import { useCallback, useEffect, useState } from 'react';

const GroupEditPage = ({
import { useRouter } from 'next/navigation';
import { FormProvider, SubmitHandler, useForm } from 'react-hook-form';

import type { APIGroupDetail, BookGroupEdit } from '@/types/group';
import {
useBookGroupEditCurrentInfo,
useBookGroupInfoMutation,
} from '@/queries/group/useBookGroupQuery';

import BookGroupEditTopNavigation from '@/v1/bookGroup/edit/BookGroupEditTopNavigation';
import BookGroupEditTitleForm from '@/v1/bookGroup/edit/BookGroupEditTitleForm';
import BookGroupEditIntroduceForm from '@/v1/bookGroup/edit/BookGroupEditIntroduceForm';
import BookGroupEditDateForm from '@/v1/bookGroup/edit/BookGroupEditDateForm';
import { isAxiosErrorWithCustomCode } from '@/utils/helpers';
import { SERVICE_ERROR_MESSAGE } from '@/constants';
import useToast from '@/v1/base/Toast/useToast';

const BookGroupEditPage = ({
params: { groupId },
}: {
params: { groupId: number };
params: { groupId: APIGroupDetail['bookGroupId'] };
}) => {
const [group, setGroup] = useState<APIGroupDetail>();

const getGroup = useCallback(async () => {
try {
const { data } = await GroupAPI.getGroupDetailInfo({
bookGroupId: groupId,
});
setGroup(data);
} catch (error) {
console.error(error);
}
}, [groupId]);

useEffect(() => {
getGroup();
}, [getGroup]);
const { show: showToast } = useToast();

const router = useRouter();

const { data: bookGroupData } = useBookGroupEditCurrentInfo(groupId);
const { title, description, maxMemberCount, startDate, endDate } =
bookGroupData;

const bookGroupEdit = useBookGroupInfoMutation(groupId);

const methods = useForm<BookGroupEdit>({
mode: 'all',
defaultValues: {
title: title,
introduce: description,
maxMemberCount: maxMemberCount ? maxMemberCount : 9999,
startDate: startDate,
endDate: endDate,
},
});

const handleFormSubmit: SubmitHandler<BookGroupEdit> = async ({
title,
introduce,
maxMemberCount,
endDate,
}) => {
bookGroupEdit.mutate(
{ title, introduce, maxMemberCount, endDate },
{
onSuccess: () => {
router.push(`/group/${groupId}`);

showToast({ type: 'success', message: '모임 정보 수정 완료 🎉' });
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

p3;

모임 정보를 수정했어요! 이런 메세지는 어떨까용? 좀더.. 구어체로..😀

return;
},
onError: error => {
if (isAxiosErrorWithCustomCode(error)) {
const { code } = error.response.data;
const message = SERVICE_ERROR_MESSAGE[code];

showToast({ type: 'error', message });
return;
}

showToast({
type: 'error',
message: '모임 정보 수정을 실패했어요 🥲',
});
},
}
);
};

return (
<AuthRequired>
<VStack justify="center" align="center">
<TopNavigation pageTitle="모임 수정" />
{group && <EditGroupForm group={group} />}
</VStack>
</AuthRequired>
<>
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

p3;

불필요한 fragment가 눈에 띄어서.. 코멘트 남겨용..😊

<FormProvider {...methods}>
<BookGroupEditTopNavigation onSubmit={handleFormSubmit} />

<form
className="mt-[2.5rem] flex flex-col gap-[3.2rem]"
onSubmit={methods.handleSubmit(handleFormSubmit)}
>
<BookGroupEditTitleForm />
<BookGroupEditIntroduceForm />
<BookGroupEditDateForm />
</form>
</FormProvider>
</>
);
};

export default GroupEditPage;
export default BookGroupEditPage;
50 changes: 43 additions & 7 deletions src/queries/group/useBookGroupQuery.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,17 @@
import { UseQueryOptions } from '@tanstack/react-query';
import {
useMutation,
useQueryClient,
UseQueryOptions,
} from '@tanstack/react-query';

import { APIGroupDetail, BookGroupDetail } from '@/types/group';
import { isExpired } from '@/utils/date';
import GroupAPI from '@/apis/group';
import type {
APIGroupDetail,
BookGroupDetail,
BookGroupEdit,
} from '@/types/group';
import useQueryWithSuspense from '@/hooks/useQueryWithSuspense';
import groupAPI from '@/apis/group';
import { isExpired } from '@/utils/date';

import bookGroupKeys from './key';

Expand All @@ -26,9 +34,9 @@ export const useBookGroupQuery = <TData = APIGroupDetail>(
useQueryWithSuspense(
bookGroupKeys.detail(groupId),
() =>
GroupAPI.getGroupDetailInfo({ bookGroupId: groupId }).then(
({ data }) => data
),
groupAPI
.getGroupDetailInfo({ bookGroupId: groupId })
.then(({ data }) => data),
options
);

Expand Down Expand Up @@ -56,3 +64,31 @@ export const useBookGroupJoinInfo = (groupId: APIGroupDetail['bookGroupId']) =>
question: data.joinQuestion,
}),
});

export const useBookGroupEditCurrentInfo = (
groupId: APIGroupDetail['bookGroupId']
) =>
useBookGroupQuery(groupId, {
select: data => ({
title: data.title,
description: data.introduce,
maxMemberCount: data.maxMemberCount,
startDate: data.startDate,
endDate: data.endDate,
}),
});

export const useBookGroupInfoMutation = (
bookGroupId: APIGroupDetail['bookGroupId']
) => {
const queryClient = useQueryClient();

return useMutation({
mutationFn: (group: Omit<BookGroupEdit, 'startDate'>) =>
groupAPI.updateGroupInfo({ bookGroupId, group }).then(({ data }) => data),
onSuccess: () =>
queryClient.invalidateQueries({
queryKey: bookGroupKeys.detail(bookGroupId),
}),
});
};
18 changes: 0 additions & 18 deletions src/styles/theme.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -107,24 +107,6 @@ const theme: ThemeOverride = extendTheme({
buttonSizes,
colors,
scheme,
styles: {
global: {
'input[type="date"]': {
position: 'relative',
},
'input[type="date"]::-webkit-inner-spin-button, input[type="date"]::-webkit-calendar-picker-indicator':
{
position: 'absolute',
left: 0,
top: 0,
width: '100%',
height: '100%',
background: 'transparent',
color: 'transparent',
cursor: 'pointer',
},
},
},
shadows,
});

Expand Down
8 changes: 8 additions & 0 deletions src/types/group.ts
Original file line number Diff line number Diff line change
Expand Up @@ -95,3 +95,11 @@ export type BookGroupComment = {
createdAt: APIGroupComment['createdAt'];
content: APIGroupComment['contents'];
};

export type BookGroupEdit = {
title: APIGroupDetail['title'];
introduce: APIGroupDetail['introduce'];
maxMemberCount: APIGroupDetail['maxMemberCount'];
startDate: APIGroupDetail['startDate'];
endDate: APIGroupDetail['endDate'];
};
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

p5;

patch 요청에 쓰이는 타입이라면.. APIPatchBookGroupRequest? APIUpdateBookGroup APIEditBookGroup이런 네이밍도 괜찮을 것 같은데.. 어떠신가용.. 🙄

4 changes: 3 additions & 1 deletion src/v1/base/DatePicker.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
'use client';

import {
ChangeEventHandler,
forwardRef,
Expand Down Expand Up @@ -51,7 +53,7 @@ const DatePicker = (

return (
<label
className={`relative flex h-[3rem] max-w-[14rem] items-center justify-between bg-transparent ${disabledClasses}`}
className={`relative flex h-[3rem] max-w-[14rem] items-center justify-between gap-[0.5rem] bg-transparent ${disabledClasses}`}
htmlFor={name}
>
<div className="flex h-full min-w-0 flex-grow items-center">
Expand Down
8 changes: 4 additions & 4 deletions src/v1/base/InputLength.tsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
type InputLengthProps = {
currentLength: number;
isError: boolean;
maxLength: number;
currentLength?: number;
isError?: boolean;
maxLength?: number;
};

const InputLength = ({
currentLength,
isError,
isError = false,
maxLength,
}: InputLengthProps) => {
const textColor = isError ? 'text-warning-800 ' : 'text-main-900';
Expand Down
10 changes: 7 additions & 3 deletions src/v1/base/TextArea.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
'use client';

import {
ChangeEventHandler,
ForwardedRef,
Expand All @@ -16,6 +18,7 @@ import InputLength from './InputLength';
interface BaseTextAreaProps
extends TextareaHTMLAttributes<HTMLTextAreaElement> {
error?: boolean;
defaultValue?: string;
}
interface TextAreaProps extends BaseTextAreaProps {
count?: boolean;
Expand All @@ -24,6 +27,7 @@ interface TextAreaProps extends BaseTextAreaProps {
const _TextArea = (
{
maxLength = 500,
defaultValue,
count = false,
error = false,
onChange,
Expand All @@ -32,7 +36,7 @@ const _TextArea = (
}: PropsWithChildren<TextAreaProps>,
ref: ForwardedRef<HTMLTextAreaElement>
) => {
const [value, setValue] = useState('');
const [value, setValue] = useState(defaultValue || '');

const handleChange: ChangeEventHandler<HTMLTextAreaElement> = e => {
setValue(e.target.value);
Expand Down Expand Up @@ -68,13 +72,13 @@ const TextArea = Object.assign(forwardRef(_TextArea), {
Error: ErrorMessage,
});

const ErrorMeesageType = (<ErrorMessage />).type;
const ErrorMessageType = (<ErrorMessage />).type;

const getErrorChildren = (children: ReactNode) => {
const childrenArray = Children.toArray(children);

return childrenArray.find(
child => isValidElement(child) && child.type === ErrorMeesageType
child => isValidElement(child) && child.type === ErrorMessageType
);
};

Expand Down
50 changes: 50 additions & 0 deletions src/v1/bookGroup/edit/BookGroupEditDateForm.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import { useFormContext } from 'react-hook-form';

import type { BookGroupEdit } from '@/types/group';

import DatePicker from '@/v1/base/DatePicker';
import ErrorMessage from '@/v1/base/ErrorMessage';

type EditDateFormTypes = Pick<BookGroupEdit, 'startDate' | 'endDate'>;

const BookGroupEditDateForm = () => {
const {
register,
formState: { errors, defaultValues },
} = useFormContext<EditDateFormTypes>();

return (
<>
<section className="flex justify-between">
<div>
<h2 className="text-md text-black-500">모임 시작일</h2>
<p className="text-xs text-placeholder">
모임 시작일은 수정할 수 없어요
</p>
</div>
<DatePicker disabled={true} {...register('startDate')} />
</section>
<section className="flex flex-col gap-[0.5rem]">
<div className="flex justify-between">
<h2 className="text-md text-black-700">모임 종료일</h2>
<DatePicker
{...register('endDate', {
required: { value: true, message: '종료일을 입력해주세요' },
min: {
value: defaultValues?.startDate as string,
message: '종료일은 시작일보다 늦어야 해요',
},
})}
/>
</div>
<div>
{errors.endDate && (
<ErrorMessage>{errors.endDate.message}</ErrorMessage>
)}
</div>
</section>
</>
);
};

export default BookGroupEditDateForm;
Loading
Loading