From 2acf3fc2a176bcb811bdc424581f7fa5196427ee Mon Sep 17 00:00:00 2001 From: "lkh14011424@gmail.com" Date: Wed, 18 Sep 2024 04:22:57 +0900 Subject: [PATCH] =?UTF-8?q?[feat/#46]=20=ED=81=AC=EB=A3=A8=20=EA=B0=80?= =?UTF-8?q?=EC=9E=85,=20=ED=81=AC=EB=A3=A8=20=EC=83=9D=EC=84=B1=20API=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/api/group.ts | 50 +++++++++++----- src/components/Crew/CrewItem.tsx | 7 ++- src/components/Crew/CrewList.tsx | 4 +- src/components/Modal/CreateCrewModal.tsx | 73 +++++++++++++++++++++--- src/components/Modal/InviteCrewModal.tsx | 1 + src/components/Modal/JoinCrewModal.tsx | 58 +++++++++++++------ src/hooks/useGroupMutation.ts | 17 +++++- src/layouts/AnalysisLayout.tsx | 2 +- 8 files changed, 166 insertions(+), 46 deletions(-) diff --git a/src/api/group.ts b/src/api/group.ts index eec5675..c140593 100644 --- a/src/api/group.ts +++ b/src/api/group.ts @@ -1,5 +1,6 @@ import qs from "qs" import axiosInstance from "./axiosInstance" +import { AxiosError } from "axios" export type sort = "userCount,desc" | "createdAt,desc" @@ -10,15 +11,17 @@ export interface sortRes { } export interface group { - id: number - name: string - description: string - ownerUid: number - isHidden: boolean - joinCode: string - userCount: number - userCapacity: number - ranks: groupUserRank[] + id?: number + name?: string + description?: string + ownerUid?: number + ownerName?: string + isHidden?: boolean + joinCode?: string + userCount?: number + userCapacity?: number + hasJoined?: boolean + ranks?: groupUserRank[] } export interface groupUserRank { @@ -45,7 +48,7 @@ export interface groupsRes { export interface groupJoinReq { groupId: number - joinCode: string + joinCode?: string } export interface groupJoinRes { @@ -74,9 +77,14 @@ export const getGroup = async (id: number | undefined): Promise => { export const joinGroup = async (groupJoinReq: groupJoinReq): Promise => { try { - // eslint-disable-next-line max-len - const res = await axiosInstance.post(`groups/${groupJoinReq.groupId}/join`, { joinCode: groupJoinReq.joinCode }) - return res.data + const res = await axiosInstance.post( + `groups/${groupJoinReq.groupId}/join`, + {}, // POST 요청에 body가 없다면 빈 객체 전달 + { + params: groupJoinReq.joinCode ? { joinCode: groupJoinReq.joinCode } : {}, // query string으로 joinCode 전달 + } + ) + return res.data.data } catch (e) { throw e } @@ -86,8 +94,20 @@ export const checkGroupName = async (name: string): Promise => { try { // eslint-disable-next-line max-len const res = await axiosInstance.post(`groups/check`, { name }) - const errorMessage = res.data?.errorMessage - return errorMessage ? false : true + const errorCode = res.data?.errorCode + return !errorCode + } catch (e) { + const { response } = e as AxiosError + const data = response?.data as { errorCode: string; reason: string } + if (data.errorCode) return false + throw e + } +} + +export const createGroup = async (group: group): Promise => { + try { + const res = await axiosInstance.post(`groups`, { ...group }) + return res.data.data } catch (e) { throw e } diff --git a/src/components/Crew/CrewItem.tsx b/src/components/Crew/CrewItem.tsx index fa2fef9..f5539c5 100644 --- a/src/components/Crew/CrewItem.tsx +++ b/src/components/Crew/CrewItem.tsx @@ -31,10 +31,13 @@ const CrewItem = (props: CrewItemProps): ReactElement => { {/* detail button */} ) diff --git a/src/components/Crew/CrewList.tsx b/src/components/Crew/CrewList.tsx index 7471844..38efd1b 100644 --- a/src/components/Crew/CrewList.tsx +++ b/src/components/Crew/CrewList.tsx @@ -24,14 +24,14 @@ const CrewList = (): ReactElement => { sort: "userCount,desc", }) - const { data, isLoading, isError } = useGetGroups(params) + const { data, isLoading, isError, refetch } = useGetGroups(params) const { openModal } = useModals() const openCreateModal = (): void => { openModal(modals.createCrewModal, { onSubmit: () => { - console.log("open") + refetch() }, }) } diff --git a/src/components/Modal/CreateCrewModal.tsx b/src/components/Modal/CreateCrewModal.tsx index e513356..9354dee 100644 --- a/src/components/Modal/CreateCrewModal.tsx +++ b/src/components/Modal/CreateCrewModal.tsx @@ -3,6 +3,10 @@ import CheckedIcon from "@assets/icons/crew-checked-icon.svg?react" import UnCheckedIcon from "@assets/icons/crew-unckecked-icon.svg?react" import { useState } from "react" import { ModalProps } from "@/contexts/ModalsContext" +import { useCheckGroupName, useCreateGroup } from "@/hooks/useGroupMutation" +import { group } from "@/api" + +type TPossible = "POSSIBLE" | "IMPOSSIBLE" | "NONCHECKED" const CreateCrewModal = (props: ModalProps): React.ReactElement => { const { onClose, onSubmit } = props @@ -11,9 +15,14 @@ const CreateCrewModal = (props: ModalProps): React.ReactElement => { const [description, setDescription] = useState("") const [isHidden, setIsHidden] = useState(false) const [joinCode, setJoinCode] = useState("") + const [isPossible, setIsPossible] = useState(null) + + const checkGroupNameMutation = useCheckGroupName() + const createGroupMutation = useCreateGroup() const onChangeName = (e: React.ChangeEvent): void => { setName(e.target.value) + setIsPossible(null) } const onChangeDescription = (e: React.ChangeEvent): void => { @@ -40,6 +49,38 @@ const CreateCrewModal = (props: ModalProps): React.ReactElement => { setJoinCode("") } + const onCheckGroupName = async (): Promise => { + const _isPossible = await checkGroupNameMutation.mutateAsync(name) + setIsPossible(_isPossible ? "POSSIBLE" : "IMPOSSIBLE") + } + + const getNameCheckedMsg = (_isPossible: TPossible | null): string => { + if (_isPossible === "POSSIBLE") return "사용가능한 크루명입니다." + if (_isPossible === "IMPOSSIBLE") return "이미 사용중인 크루명이에요. 다른 크루명을 사용해주세요." + if (_isPossible === "NONCHECKED") return "중복체크를 해주세요." + return "" + } + + const canCreate = (): string | boolean => { + return name && description && ((isHidden && joinCode.length === 4) || !isHidden) + } + + const handleSubmit = (): void => { + if (isPossible === null) { + setIsPossible("NONCHECKED") + return + } + if (isPossible === "NONCHECKED") return + + let newGroup: group = { name, description } + if (isHidden) newGroup = { ...newGroup, joinCode, isHidden } + createGroupMutation.mutate(newGroup, { + onSuccess: (): void => { + if (onSubmit && typeof onSubmit === "function") onSubmit() + }, + }) + } + return (
@@ -48,22 +89,37 @@ const CreateCrewModal = (props: ModalProps): React.ReactElement => {
{"크루 만들기"}
-
+
{/* crew owner */} -
+
크루명
-
+
-
+
+ {getNameCheckedMsg(isPossible)} +
{/* crew description */} @@ -105,8 +161,11 @@ const CreateCrewModal = (props: ModalProps): React.ReactElement => { {/* button */} diff --git a/src/components/Modal/InviteCrewModal.tsx b/src/components/Modal/InviteCrewModal.tsx index e3efb18..b4e9832 100644 --- a/src/components/Modal/InviteCrewModal.tsx +++ b/src/components/Modal/InviteCrewModal.tsx @@ -26,6 +26,7 @@ const InviteCrewModal = (props: ModalProps): React.ReactElement => { > 초대 링크 복사하기 +
초대 링크가 복사되었어요.
) diff --git a/src/components/Modal/JoinCrewModal.tsx b/src/components/Modal/JoinCrewModal.tsx index 19ad761..4ec380c 100644 --- a/src/components/Modal/JoinCrewModal.tsx +++ b/src/components/Modal/JoinCrewModal.tsx @@ -3,7 +3,8 @@ import CrewJoinUserIcon from "@assets/icons/crew-join-user-icon.svg?react" import PrivateCrewIcon from "@assets/icons/crew-private-icon.svg?react" import { ReactNode, useState } from "react" import { ModalProps } from "@/contexts/ModalsContext" -import { useGetGroup } from "@/hooks/useGroupMutation" +import { useGetGroup, useJoinGroup } from "@/hooks/useGroupMutation" +import { groupJoinReq } from "@/api" const JoinCrewModal = (props: ModalProps): React.ReactElement => { const { onClose, onSubmit, id } = props @@ -12,21 +13,49 @@ const JoinCrewModal = (props: ModalProps): React.ReactElement => { const [isCodeError, setIsCodeError] = useState(false) const { data, isLoading, isError } = useGetGroup(id) + const joinGroupMutation = useJoinGroup() const onChangeJoinCode = (e: React.ChangeEvent): void => { if (isCodeError) setIsCodeError(false) if (e.target.value.length <= 4) setJoinCode(e.target.value) } + const handleSubmit = (): void => { + if (!data?.id) return + let groupJoinReq: groupJoinReq = { groupId: data.id } + if (data?.isHidden) groupJoinReq = { ...groupJoinReq, joinCode } + joinGroupMutation.mutate(groupJoinReq, { + onSuccess: (): void => { + if (onSubmit && typeof onSubmit === "function") onSubmit() + }, + onError: (e): void => { + console.log(e) + }, + }) + } + const createRank = (): ReactNode => { + if (!data?.ranks) return + if (data.ranks.length === 0) return return ( -
- {data?.ranks.map((r) => ( -
-
{`${r.rank}등`}
-
{`${r.name}`}
+
+
+
오늘 바른자세 랭킹
+
크루에 가입하면 볼 수 있어요
+
+
+
+ {data?.ranks.map((r, i) => ( +
+
{`${r.rank}등`}
+
{`${r.name}`}
+
+ ))}
- ))} +
) } @@ -57,7 +86,7 @@ const JoinCrewModal = (props: ModalProps): React.ReactElement => {
크루장
- {data?.ownerUid} + {data?.ownerName}
@@ -70,15 +99,7 @@ const JoinCrewModal = (props: ModalProps): React.ReactElement => {
{/* crew rank */} - {data?.ranks && ( -
-
-
오늘 바른자세 랭킹
-
크루에 가입하면 볼 수 있어요
-
-
{createRank()}
-
- )} + {createRank()}
) : ( // private crew @@ -101,10 +122,11 @@ const JoinCrewModal = (props: ModalProps): React.ReactElement => { {/* button */} +
1개의 크루에만 가입할 수 있어요.
)} diff --git a/src/hooks/useGroupMutation.ts b/src/hooks/useGroupMutation.ts index f5125d9..2c8095b 100644 --- a/src/hooks/useGroupMutation.ts +++ b/src/hooks/useGroupMutation.ts @@ -1,6 +1,7 @@ import { useMutation, UseMutationResult, useQuery, UseQueryResult } from "@tanstack/react-query" import { checkGroupName, + createGroup, getGroup, getGroups, group, @@ -13,7 +14,10 @@ import { export const useGetGroups = (params: groupsReq): UseQueryResult => { // eslint-disable-next-line max-len - return useQuery({ queryKey: ["groups", params], queryFn: () => getGroups(params) }) + return useQuery({ + queryKey: ["groups", params.page, params.size, params.sort], + queryFn: () => getGroups(params), + }) } export const useGetGroup = (id: number | undefined): UseQueryResult => { @@ -42,3 +46,14 @@ export const useCheckGroupName = (): UseMutationResult => { + return useMutation({ + mutationFn: (group: group) => { + return createGroup(group) + }, + onSuccess: (data) => { + console.log(data) + }, + }) +} diff --git a/src/layouts/AnalysisLayout.tsx b/src/layouts/AnalysisLayout.tsx index 19cf049..8f66e9a 100644 --- a/src/layouts/AnalysisLayout.tsx +++ b/src/layouts/AnalysisLayout.tsx @@ -2,7 +2,7 @@ import { Outlet } from "react-router-dom" export default function AnalysisLayout() { return ( -
+
)