diff --git a/frontend/apps/client/src/features/discussion/api/index.ts b/frontend/apps/client/src/features/discussion/api/index.ts index 5039f164..2d7acf08 100644 --- a/frontend/apps/client/src/features/discussion/api/index.ts +++ b/frontend/apps/client/src/features/discussion/api/index.ts @@ -24,6 +24,18 @@ export const discussionApi = { return response; }, + putDiscussion: async ( + discussionId: string, body: DiscussionRequest, + ): Promise => { + const response = await request.put(`/api/v1/discussion/${discussionId}`, { body }); + return response; + }, + + deleteDiscussion: async (discussionId: string): Promise => { + const response = await request.delete(`/api/v1/discussion/${discussionId}`); + return response; + }, + getIsHost: async (id: string): Promise => { const response = await request.get(`/api/v1/discussion/${id}/role`); return response; diff --git a/frontend/apps/client/src/features/discussion/api/mutations.ts b/frontend/apps/client/src/features/discussion/api/mutations.ts index d8afe6c2..876c9119 100644 --- a/frontend/apps/client/src/features/discussion/api/mutations.ts +++ b/frontend/apps/client/src/features/discussion/api/mutations.ts @@ -1,7 +1,11 @@ import { useMutation, useQueryClient } from '@tanstack/react-query'; -import { useNavigate } from '@tanstack/react-router'; +import { useNavigate, useParams } from '@tanstack/react-router'; -import { sharedScheduleKey } from '@/features/shared-schedule/api/keys'; +import { + ongoingQueryKey, + sharedScheduleKey, + upcomingQueryKey, +} from '@/features/shared-schedule/api/keys'; import type { DiscussionConfirmRequest, DiscussionRequest } from '../model'; import { candidateApi, discussionApi } from '.'; @@ -10,20 +14,60 @@ import { discussionKeys } from './keys'; export const useDiscussionMutation = () => { const queryClient = useQueryClient(); - + const navigate = useNavigate(); + const { mutate } = useMutation({ - mutationFn: ({ body }: { - body: DiscussionRequest; - callback: (id: string) => void; - }) => discussionApi.postDiscussion(body), - onSuccess: ({ id }, { callback }) => { - callback?.(id.toString()); - queryClient.invalidateQueries({ - queryKey: discussionKeys.all, + mutationFn: (body: DiscussionRequest) => discussionApi.postDiscussion(body), + onSuccess: ({ id }) => { + queryClient.invalidateQueries({ queryKey: upcomingQueryKey }); + queryClient.invalidateQueries({ queryKey: ongoingQueryKey.all }); + queryClient.invalidateQueries({ queryKey: discussionKeys.all }); + navigate({ + to: '/discussion/$id', + params: { id: String(id) }, }); }, }); - + + return { mutate }; +}; + +export const useDiscussionEditMutation = () => { + const { id } = useParams({ from: '/_main/discussion/edit/$id' }); + const queryClient = useQueryClient(); + const navigate = useNavigate(); + + const { mutate } = useMutation({ + mutationFn: (body: DiscussionRequest) => discussionApi.putDiscussion(id, body), + onSuccess: (_) => { + queryClient.invalidateQueries({ queryKey: upcomingQueryKey }); + queryClient.invalidateQueries({ queryKey: ongoingQueryKey.all }); + queryClient.invalidateQueries({ queryKey: discussionKeys.detail(id) }); + navigate({ + to: '/discussion/$id', + params: { id }, + }); + }, + }); + + return { mutate }; +}; + +export const useDiscussionDeleteMutation = (callback: () => void) => { + const { id } = useParams({ from: '/_main/discussion/edit/$id' }); + const queryClient = useQueryClient(); + const navigate = useNavigate(); + + const { mutate } = useMutation({ + mutationFn: () => discussionApi.deleteDiscussion(id), + onSuccess: (_) => { + queryClient.invalidateQueries({ queryKey: upcomingQueryKey }); + queryClient.invalidateQueries({ queryKey: ongoingQueryKey.all }); + callback(); + navigate({ to: '/home' }); + }, + }); + return { mutate }; }; diff --git a/frontend/apps/client/src/features/discussion/api/queries.ts b/frontend/apps/client/src/features/discussion/api/queries.ts index 84a49ddc..885ffdd7 100644 --- a/frontend/apps/client/src/features/discussion/api/queries.ts +++ b/frontend/apps/client/src/features/discussion/api/queries.ts @@ -60,10 +60,10 @@ export const discussionHostQuery = (discussionId: string) => ({ }); export const useDiscussionQuery = (discussionId: string) => { - const { data: discussion, isLoading } + const { data: discussion, isPending } = useQuery(discussionQuery(discussionId)); - return { discussion, isLoading }; + return { discussion, isPending }; }; export const useDiscussionCalendarQuery = ( diff --git a/frontend/apps/client/src/features/discussion/ui/DiscussionEditButton/index.tsx b/frontend/apps/client/src/features/discussion/ui/DiscussionEditButton/index.tsx new file mode 100644 index 00000000..10b81141 --- /dev/null +++ b/frontend/apps/client/src/features/discussion/ui/DiscussionEditButton/index.tsx @@ -0,0 +1,26 @@ +import { Button, Icon } from '@endolphin/ui'; +import { Link, useParams } from '@tanstack/react-router'; + +import { useDiscussionHostQuery } from '../../api/queries'; + +const DiscussionEditButton = () => { + const { id } = useParams({ from: '/_main/discussion/$id' }); + + const { isHost, isPending } = useDiscussionHostQuery(id); + if (isPending || !isHost) return null; + + return ( + + ); +}; + +export default DiscussionEditButton; \ No newline at end of file diff --git a/frontend/apps/client/src/features/discussion/ui/DiscussionForm/EnrollButton.tsx b/frontend/apps/client/src/features/discussion/ui/DiscussionForm/EnrollButton.tsx new file mode 100644 index 00000000..5c2e14a0 --- /dev/null +++ b/frontend/apps/client/src/features/discussion/ui/DiscussionForm/EnrollButton.tsx @@ -0,0 +1,92 @@ +import { clsx } from '@endolphin/core/utils'; +import { Button, Flex } from '@endolphin/ui'; + +import { useGlobalModal } from '@/store/global/modal'; + +import { + useDiscussionDeleteMutation, + useDiscussionEditMutation, + useDiscussionMutation, +} from '../../api/mutations'; +import { useFormContext } from './FormContext'; +import { buttonStyle } from './index.css'; + +export const AddButton = () => { + const { name, formState, isValidForm } = useFormContext(); + const { mutate } = useDiscussionMutation(); + + const handleClickEnrollButton = () => { + mutate(formState); + }; + + return ( + + ); +}; + +export const EditButton = () => { + const { name, formState, isValidForm } = useFormContext(); + const { mutate } = useDiscussionEditMutation(); + + const handleClickEnrollButton = () => { + mutate(formState); + }; + + return ( + + ); +}; + +export const DeleteButton = () => { + const { createModal, onModalClose } = useGlobalModal(); + const { mutate } = useDiscussionDeleteMutation(onModalClose); + + const handleClickDeleteDiscussion = () => { + createModal({ + subTitle: '경고', + type: 'error', + title: '정말 삭제할까요?', + description: '삭제하면 다시 되돌릴 수 없어요.', + children: ( + + + + ), + }); + }; + + return ( + + ); +}; \ No newline at end of file diff --git a/frontend/apps/client/src/features/discussion/ui/DiscussionForm/FormButton.tsx b/frontend/apps/client/src/features/discussion/ui/DiscussionForm/FormButton.tsx index 955f3046..738e6787 100644 --- a/frontend/apps/client/src/features/discussion/ui/DiscussionForm/FormButton.tsx +++ b/frontend/apps/client/src/features/discussion/ui/DiscussionForm/FormButton.tsx @@ -1,83 +1,21 @@ -import { clsx } from '@endolphin/core/utils'; -import { Button, Flex } from '@endolphin/ui'; -import { type QueryClient, useQueryClient } from '@tanstack/react-query'; -import { useNavigate } from '@tanstack/react-router'; +import { Flex } from '@endolphin/ui'; -import { ongoingQueryKey, upcomingQueryKey } from '@/features/shared-schedule/api/keys'; - -import { useDiscussionMutation } from '../../api/mutations'; -import { useFormContext } from './FormContext'; -import { buttonStyle } from './index.css'; +import { AddButton, DeleteButton, EditButton } from './EnrollButton'; import type { FormType } from './type'; -const FormButton = ({ type }: { type: FormType }) => { - const handleClickDeleteDiscussion = () => { - /* Do something */ - }; - return type === 'add' ? ( - +const FormButton = ({ type }: { type: FormType }) => ( + type === 'add' ? ( + ) : ( - - + + - ); -}; - -const EnrollButton = ({ type }: { type: FormType }) => { - const queryClient = useQueryClient(); - const { name, formState, isValidForm } = useFormContext(); - const navigate = useNavigate(); - const { mutate } = useDiscussionMutation(); - - const typeMap: Record> = { - add: { - text: '생성하기', - navigate: '/discussion/create/$id', - }, - edit: { - text: '수정하기', - navigate: '/discussion/$id', - }, - }; - - const handleClickEnrollButton = () => { - const callback = (id: string) => { - invalidateScheduleCaches(queryClient); - navigate({ - to: typeMap[type].navigate, - params: { id }, - }); - }; - mutate({ body: formState, callback }); - }; - - return ( - - ); -}; - -const invalidateScheduleCaches = (queryClient: QueryClient) => { - queryClient.invalidateQueries({ queryKey: upcomingQueryKey }); - queryClient.invalidateQueries({ queryKey: ongoingQueryKey.all }); -}; + ) +); -export default FormButton; +export default FormButton; \ No newline at end of file diff --git a/frontend/apps/client/src/features/discussion/ui/DiscussionForm/index.tsx b/frontend/apps/client/src/features/discussion/ui/DiscussionForm/index.tsx index 16c110dd..e5d7ffcc 100644 --- a/frontend/apps/client/src/features/discussion/ui/DiscussionForm/index.tsx +++ b/frontend/apps/client/src/features/discussion/ui/DiscussionForm/index.tsx @@ -15,7 +15,8 @@ import MeetingTitle from './MeetingTitle'; import type { FormType } from './type'; const DiscussionForm = ( - { type, initialValues }: { type: FormType; initialValues?: DiscussionRequest }, + { type, initialValues }: + { type: FormType; initialValues?: DiscussionRequest }, ) => { const SEVEN_DAYS = 7 * 24 * 60 * 60 * 1000; const today = new Date(); diff --git a/frontend/apps/client/src/features/discussion/ui/DiscussionTitle/index.tsx b/frontend/apps/client/src/features/discussion/ui/DiscussionTitle/index.tsx index ebc2c9d8..fb1fd275 100644 --- a/frontend/apps/client/src/features/discussion/ui/DiscussionTitle/index.tsx +++ b/frontend/apps/client/src/features/discussion/ui/DiscussionTitle/index.tsx @@ -3,30 +3,37 @@ import { Flex, Text } from '@endolphin/ui'; import { useParams } from '@tanstack/react-router'; import { useDiscussionQuery } from '../../api/queries'; +import DiscussionEditButton from '../DiscussionEditButton'; import { DiscussionBadges } from './DiscussionBadges'; import { titleStyle } from './index.css'; const DiscussionTitle = () => { const params: { id: string } = useParams({ from: '/_main/discussion/$id' }); - const { discussion, isLoading } = useDiscussionQuery(params.id); + const { discussion, isPending } = useDiscussionQuery(params.id); return ( - {!isLoading && ( - <> - - {discussion?.title} - {' '} - 일정 조율 결과 - - - - )} + + {!isPending && ( + <> + + {discussion?.title} + {' '} + 일정 조율 결과 + + + + )} + + ); }; diff --git a/frontend/apps/client/src/pages/DiscussionPage/DiscussionEditPage/index.tsx b/frontend/apps/client/src/pages/DiscussionPage/DiscussionEditPage/index.tsx index 52689678..1334bf05 100644 --- a/frontend/apps/client/src/pages/DiscussionPage/DiscussionEditPage/index.tsx +++ b/frontend/apps/client/src/pages/DiscussionPage/DiscussionEditPage/index.tsx @@ -1,33 +1,42 @@ import { Flex } from '@endolphin/ui'; +import { useParams } from '@tanstack/react-router'; +import { useDiscussionQuery } from '@/features/discussion/api/queries'; import DiscussionCreateTitle from '@/features/discussion/ui/DiscussionCreateTitle'; import DiscussionForm from '@/features/discussion/ui/DiscussionForm'; import { discussionContainerStyle } from '../index.css'; -const DiscussionEditPage = () => ( - - - - -); +const DiscussionEditPage = () => { + const { id } = useParams({ from: '/_main/discussion/edit/$id' }); + const { discussion, isPending } = useDiscussionQuery(id); + + if (isPending || !discussion) return null; + + return ( + + + + + ); +}; export default DiscussionEditPage; \ No newline at end of file