From 6a6f536695fab228314e75f7caeab09b8bf921df Mon Sep 17 00:00:00 2001 From: Heesunee <91944542+heesunee@users.noreply.github.com> Date: Fri, 5 Sep 2025 13:11:13 +0900 Subject: [PATCH 1/5] =?UTF-8?q?fix:=20=EB=9D=BC=EB=B2=A8=20=EC=88=98?= =?UTF-8?q?=EC=A0=95=20(#339)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: CHAEEUN KIM <154000318+bongtta@users.noreply.github.com> --- src/pages/edit-profile/constants/edit-profile.ts | 6 +++--- src/pages/onboarding/constants/onboarding.ts | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/pages/edit-profile/constants/edit-profile.ts b/src/pages/edit-profile/constants/edit-profile.ts index ed25cfbe..94262786 100644 --- a/src/pages/edit-profile/constants/edit-profile.ts +++ b/src/pages/edit-profile/constants/edit-profile.ts @@ -3,14 +3,14 @@ export const PROFILE_SYNC_MATE = ['같은 팀 메이트', '상관없어요']; export const PROFILE_VIEWING_STYLE = [ { id: 1, - label: '열정 응원러', + label: '열정응원러', }, { id: 2, - label: '경기 집중러', + label: '경기집중러', }, { id: 3, - label: '직관 먹방러', + label: '직관먹방러', }, ]; diff --git a/src/pages/onboarding/constants/onboarding.ts b/src/pages/onboarding/constants/onboarding.ts index d705a8c7..16e10991 100644 --- a/src/pages/onboarding/constants/onboarding.ts +++ b/src/pages/onboarding/constants/onboarding.ts @@ -30,17 +30,17 @@ export const SYNC_MATE = ['같은 팀 메이트와 보고 싶어요', '상관없 export const VIEWING_STYLE = [ { id: 1, - label: '열정 응원러', + label: '열정응원러', icon: 'passion', }, { id: 2, - label: '경기 집중러', + label: '경기집중러', icon: 'focus', }, { id: 3, - label: '직관 먹방러', + label: '직관먹방러', icon: 'eat', }, ]; From c018d31dc8f39a48b98d189b3a733db0bcfce117 Mon Sep 17 00:00:00 2001 From: Heesunee <91944542+heesunee@users.noreply.github.com> Date: Mon, 8 Sep 2025 15:30:12 +0900 Subject: [PATCH 2/5] deploy: release v1.0.0 (#384) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * fix: (QA/3) 홈으로 페이지 이동할 때 배너 이미지 로드 전에 텍스트만 뜨는 문제 해결 (#384) * fix: 홈으로 페이지 이동할 때 배너 이미지 로드 전에 텍스트만 뜨는 문제 해결 (#384) * fix: 배너 최소 높이 설정 (#384) * fix: 회원가입 design, gaEvent 수정 및 매칭 현황 내 중복 데이터 오류 해결 (#383) * fix: sign-up 페이지 배경색 짤리는 오류 수정 (#383) * fix: match-card-view gaEvent 삭제 (#383) * fix: gaEvent 위치 수정 (#383) * fix: 캐시가 섞이면서 데이터 중복되는 에러 해결 (#383) * fix: 주석 제거 (#383) --------- Co-authored-by: heesunee --------- Co-authored-by: Yewon Kim <163109964+yeeeww@users.noreply.github.com> Co-authored-by: heesunee --- .../match/components/match-tab-pannel.tsx | 33 +++++++--- src/pages/match/match.tsx | 63 +++---------------- .../onboarding/utils/onboarding-button.ts | 1 - src/pages/sign-up/components/signup-step.tsx | 2 +- .../game-match/game-match-bottom-sheet.tsx | 7 +++ .../card/banner-card/banner-card.tsx | 20 ++++-- 6 files changed, 55 insertions(+), 71 deletions(-) diff --git a/src/pages/match/components/match-tab-pannel.tsx b/src/pages/match/components/match-tab-pannel.tsx index e50ce126..de0e7f2a 100644 --- a/src/pages/match/components/match-tab-pannel.tsx +++ b/src/pages/match/components/match-tab-pannel.tsx @@ -1,11 +1,11 @@ import { matchMutations } from '@apis/match/match-mutations'; +import { matchQueries } from '@apis/match/match-queries'; import Card from '@components/card/match-card/card'; import type { GroupCardProps, SingleCardProps } from '@components/card/match-card/types/card'; import { getColorType } from '@components/card/match-card/utils/get-color-type'; import EmptyView from '@components/ui/empty-view'; import { cn } from '@libs/cn'; import { CLICKABLE_STATUS_MAP } from '@pages/match/constants/matching'; -import type { MatchCardData } from '@pages/match/create/types/match-data-type'; import { getCardColor, getPendingToast, @@ -13,26 +13,43 @@ import { statusToCategory, } from '@pages/match/utils/match-status'; import { ROUTES } from '@routes/routes-config'; -import { useMutation } from '@tanstack/react-query'; +import { useMutation, useQuery } from '@tanstack/react-query'; import { useNavigate } from 'react-router-dom'; import { showErrorToast } from '@/shared/utils/show-error-toast'; +import { mapGroupMatchData, mapSingleMatchData } from '../hooks/mapMatchData'; type MatchableCardProps = SingleCardProps | GroupCardProps; interface MatchTabPanelProps { - cards: MatchableCardProps[]; + activeType: '1:1' | '그룹'; + statusParam: string; filter: string; - onCardClick: (card: MatchCardData) => void; + onCardClick: (card: MatchableCardProps) => void; } -const MatchTabPanel = ({ cards, filter, onCardClick }: MatchTabPanelProps) => { +const MatchTabPanel = ({ activeType, filter, statusParam, onCardClick }: MatchTabPanelProps) => { const navigate = useNavigate(); + const isSingle = activeType === '1:1'; - const patchStageMutation = useMutation(matchMutations.MATCH_STAGE()); + const { data: singleData } = useQuery({ + ...matchQueries.SINGLE_MATCH_STATUS(statusParam), + enabled: isSingle, + }); + + const { data: groupData } = useQuery({ + ...matchQueries.GROUP_MATCH_STATUS(statusParam), + enabled: !isSingle, + }); + + const cards: MatchableCardProps[] = isSingle + ? mapSingleMatchData(singleData?.results) + : mapGroupMatchData(groupData?.mates); const filteredCards = filter === '전체' ? cards : cards.filter((card) => statusToCategory(card.status) === filter); + const patchStageMutation = useMutation(matchMutations.MATCH_STAGE()); + const handleCardClick = async (card: MatchableCardProps) => { onCardClick(card); @@ -70,9 +87,7 @@ const MatchTabPanel = ({ cards, filter, onCardClick }: MatchTabPanelProps) => { key={card.id} type="button" onClick={() => handleCardClick(card)} - className={cn('w-full', { - 'cursor-pointer': isClickable(card.status), - })} + className={cn('w-full', { 'cursor-pointer': isClickable(card.status) })} aria-disabled={!isClickable(card.status)} > { @@ -16,36 +11,6 @@ const Match = () => { const { type: activeType, filter } = tabState; const statusParam = filter === '전체' ? '' : filter; - const { data: singleData } = useQuery({ - ...matchQueries.SINGLE_MATCH_STATUS(statusParam), - enabled: activeType === '1:1', - }); - - const { data: groupData } = useQuery({ - ...matchQueries.GROUP_MATCH_STATUS(statusParam), - enabled: activeType === '그룹', - }); - - useEffect(() => { - if (activeType === '1:1' && singleData?.results) { - singleData.results.forEach((card) => { - gaEvent('match_card_view', { - match_id: card.id, - match_type: 'one_to_one', - match_status: card.status, - }); - }); - } else if (activeType === '그룹' && groupData?.mates) { - groupData.mates.forEach((card) => { - gaEvent('match_card_view', { - match_id: card.id, - match_type: 'group', - match_status: card.status, - }); - }); - } - }, [singleData, groupData, activeType]); - const handleCardClick = (card: MatchCardData) => { gaEvent('match_card_click', { match_id: card.id, @@ -54,25 +19,6 @@ const Match = () => { }); }; - const contentMap = { - '1:1': ( - - ), - 그룹: ( - - ), - }; - return (
- + +
); }; diff --git a/src/pages/onboarding/utils/onboarding-button.ts b/src/pages/onboarding/utils/onboarding-button.ts index a6b727b4..525fb2d9 100644 --- a/src/pages/onboarding/utils/onboarding-button.ts +++ b/src/pages/onboarding/utils/onboarding-button.ts @@ -79,7 +79,6 @@ export const handleButtonClick = ( if (selections.GROUP_ROLE === '그룹원') { goTo?.('COMPLETE'); } else { - gaEvent('match_create_click', { match_type: 'group', role: 'creator' }); gaEvent('condition_set_completed', { match_type: 'group' }); goNext(); } diff --git a/src/pages/sign-up/components/signup-step.tsx b/src/pages/sign-up/components/signup-step.tsx index ed302811..db31fbf0 100644 --- a/src/pages/sign-up/components/signup-step.tsx +++ b/src/pages/sign-up/components/signup-step.tsx @@ -118,7 +118,7 @@ const SignupStep = () => { return (

{NICKNAME_TITLE}

diff --git a/src/shared/components/bottom-sheet/game-match/game-match-bottom-sheet.tsx b/src/shared/components/bottom-sheet/game-match/game-match-bottom-sheet.tsx index 81571666..a232285a 100644 --- a/src/shared/components/bottom-sheet/game-match/game-match-bottom-sheet.tsx +++ b/src/shared/components/bottom-sheet/game-match/game-match-bottom-sheet.tsx @@ -6,6 +6,7 @@ import GameMatchList from '@components/bottom-sheet/game-match/game-match-list'; import { formatDateWeekday } from '@components/bottom-sheet/game-match/utils/format-date-weekday'; import { TAB_TYPES, type TabType } from '@components/tab/tab/constants/tab-type'; import { MATCH_REQUEST_ERROR_MESSAGES } from '@constants/error-toast'; +import { gaEvent } from '@libs/analytics'; import { ROUTES } from '@routes/routes-config'; import { useMutation, useQueryClient } from '@tanstack/react-query'; import type { AxiosError } from 'axios'; @@ -60,6 +61,12 @@ const GameMatchBottomSheet = ({ const selectedGame = gameSchedule[selectedIdx]; if (!selectedGame) return; + const gaMatchType = activeType === TAB_TYPES.SINGLE ? 'one_to_one' : 'group'; + gaEvent('match_create_click', { + match_type: gaMatchType, + role: 'creator', + }); + createMatchMutation.mutate( { gameId: selectedGame.id, diff --git a/src/shared/components/card/banner-card/banner-card.tsx b/src/shared/components/card/banner-card/banner-card.tsx index a0f35a38..65ec5ebb 100644 --- a/src/shared/components/card/banner-card/banner-card.tsx +++ b/src/shared/components/card/banner-card/banner-card.tsx @@ -1,4 +1,5 @@ import { cn } from '@libs/cn'; +import { useState } from 'react'; import BannerImg from '/images/card_banner_img.webp'; interface CardBannerProps { @@ -9,13 +10,22 @@ interface CardBannerProps { } const BannerCard = ({ text, subText, onClick }: CardBannerProps) => { + const [imageLoaded, setImageLoaded] = useState(false); + return ( ); }; From 7bee9f177240b6b842cfc3ab9d3379e150a9d740 Mon Sep 17 00:00:00 2001 From: Heesunee <91944542+heesunee@users.noreply.github.com> Date: Mon, 8 Sep 2025 16:53:18 +0900 Subject: [PATCH 3/5] fix: hotfix design (#388) Co-authored-by: heesunee --- src/pages/sign-up/sign-up.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pages/sign-up/sign-up.tsx b/src/pages/sign-up/sign-up.tsx index 7f73aee5..2590d4bd 100644 --- a/src/pages/sign-up/sign-up.tsx +++ b/src/pages/sign-up/sign-up.tsx @@ -8,7 +8,7 @@ const SignUp = () => { const { Funnel, Step, goNext } = useFunnel(SIGNUP_STEPS, ROUTES.HOME); return ( -
+
From 404d75dd8a0630b8cc6188f3ac35cfa74cb3ace2 Mon Sep 17 00:00:00 2001 From: heesunee Date: Fri, 19 Sep 2025 14:46:51 +0900 Subject: [PATCH 4/5] =?UTF-8?q?refactor:=20=EC=A4=91=EB=B3=B5=ED=99=95?= =?UTF-8?q?=EC=9D=B8=20useMutation=EC=9C=BC=EB=A1=9C=20=EB=B3=80=EA=B2=BD?= =?UTF-8?q?=20(#393)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/shared/apis/user/user-mutations.ts | 26 +++++++++++++++++++++++++- src/shared/apis/user/user-queries.ts | 24 ------------------------ 2 files changed, 25 insertions(+), 25 deletions(-) diff --git a/src/shared/apis/user/user-mutations.ts b/src/shared/apis/user/user-mutations.ts index 7a2593d2..3164f1b7 100644 --- a/src/shared/apis/user/user-mutations.ts +++ b/src/shared/apis/user/user-mutations.ts @@ -1,10 +1,13 @@ -import { patch, post, put } from '@apis/base/http'; +import { get, patch, post, put } from '@apis/base/http'; import { END_POINT } from '@constants/api'; import { USER_KEY } from '@constants/query-key'; +import { HTTP_STATUS } from '@constants/response'; import queryClient from '@libs/query-client'; +import { NICKNAME_DUPLICATE_FAILURE_COUNT } from '@pages/sign-up/constants/validation'; import { router } from '@routes/router'; import { ROUTES } from '@routes/routes-config'; import { mutationOptions } from '@tanstack/react-query'; +import axios from 'axios'; import type { responseTypes } from '@/shared/types/base-types'; import type { postAgreementInfoRequest, @@ -68,4 +71,25 @@ export const userMutations = { mutationKey: USER_KEY.AGREEMENT(), mutationFn: ({ hasAccepted }) => post(END_POINT.AGREEMENT_INFO, { hasAccepted }), }), + + NICKNAME_CHECK: () => + mutationOptions({ + mutationFn: async ({ nickname }) => { + try { + await get(END_POINT.GET_NICKNAME_CHECK(nickname)); + return true; + } catch (e) { + if (axios.isAxiosError(e) && e.response?.status === HTTP_STATUS.CONFLICT) { + return false; + } + throw e; + } + }, + retry: (failureCount, error) => { + if (axios.isAxiosError(error) && error.response?.status === HTTP_STATUS.CONFLICT) { + return false; + } + return failureCount < NICKNAME_DUPLICATE_FAILURE_COUNT; + }, + }), }; diff --git a/src/shared/apis/user/user-queries.ts b/src/shared/apis/user/user-queries.ts index 3a549f43..1f4ace60 100644 --- a/src/shared/apis/user/user-queries.ts +++ b/src/shared/apis/user/user-queries.ts @@ -1,9 +1,7 @@ import { get } from '@apis/base/http'; import { END_POINT } from '@constants/api'; import { USER_KEY } from '@constants/query-key'; -import { HTTP_STATUS } from '@constants/response'; import { queryOptions } from '@tanstack/react-query'; -import axios from 'axios'; import type { getMatchConditionResponse, getUserInfoResponse } from '@/shared/types/user-types'; export const userQueries = { @@ -26,26 +24,4 @@ export const userQueries = { queryKey: USER_KEY.MATCH_CONDITION(), queryFn: () => get(END_POINT.MATCH_CONDITION), }), - - NICKNAME_CHECK: (nickname: string) => - queryOptions({ - queryKey: USER_KEY.NICKNAME_CHECK(nickname), - enabled: false, - queryFn: async () => { - try { - await get(END_POINT.GET_NICKNAME_CHECK(nickname)); - return true; - } catch (e) { - if (axios.isAxiosError(e) && e.response?.status === HTTP_STATUS.CONFLICT) { - return false; - } - throw e; - } - }, - retry: (failureCount, error) => { - if (axios.isAxiosError(error) && error.response?.status === HTTP_STATUS.CONFLICT) - return false; - return failureCount < 2; - }, - }), }; From 8a07a6d0ec7988cc846e39de0b5ec93018be5960 Mon Sep 17 00:00:00 2001 From: heesunee Date: Fri, 19 Sep 2025 14:47:15 +0900 Subject: [PATCH 5/5] =?UTF-8?q?refactor:=20=EC=A4=91=EB=B3=B5=ED=99=95?= =?UTF-8?q?=EC=9D=B8=20=EB=A1=9C=EC=A7=81=20mutation=EC=9C=BC=EB=A1=9C=20?= =?UTF-8?q?=EB=B3=80=EA=B2=BD=20(#393)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/pages/edit-profile/edit-profile.tsx | 7 +++---- src/pages/sign-up/components/signup-step.tsx | 9 ++++----- src/pages/sign-up/constants/validation.ts | 2 ++ 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/src/pages/edit-profile/edit-profile.tsx b/src/pages/edit-profile/edit-profile.tsx index 5b255e2e..7d8c861f 100644 --- a/src/pages/edit-profile/edit-profile.tsx +++ b/src/pages/edit-profile/edit-profile.tsx @@ -58,8 +58,7 @@ const EditProfile = () => { const nicknameVal = watch('nickname', ''); const introductionVal = watch('introduction', ''); - const { refetch: refetchNicknameCheck } = useQuery(userQueries.NICKNAME_CHECK(nicknameVal)); - + const { mutateAsync: checkNickname } = useMutation(userMutations.NICKNAME_CHECK()); const submitNickname = async () => { const ok = await trigger('nickname'); if (!ok) return; @@ -113,8 +112,8 @@ const EditProfile = () => { if (errors.nickname || nicknameVal.trim().length < 2) return; setNicknameStatus('checking'); try { - const { data } = await refetchNicknameCheck(); - setNicknameStatus(data ? 'available' : 'duplicate'); + const available = await checkNickname({ nickname: nicknameVal }); + setNicknameStatus(available ? 'available' : 'duplicate'); } catch { setNicknameStatus('idle'); } diff --git a/src/pages/sign-up/components/signup-step.tsx b/src/pages/sign-up/components/signup-step.tsx index 4bdf4af5..d24bef89 100644 --- a/src/pages/sign-up/components/signup-step.tsx +++ b/src/pages/sign-up/components/signup-step.tsx @@ -1,5 +1,4 @@ import { userMutations } from '@apis/user/user-mutations'; -import { userQueries } from '@apis/user/user-queries'; import Button from '@components/button/button/button'; import Input from '@components/input/input'; import { zodResolver } from '@hookform/resolvers/zod'; @@ -21,7 +20,7 @@ import { } from '@pages/sign-up/constants/validation'; import { type UserInfoFormValues, UserInfoSchema } from '@pages/sign-up/schema/validation-schema'; import type { NicknameStatus } from '@pages/sign-up/types/nickname-types'; -import { useMutation, useQuery } from '@tanstack/react-query'; +import { useMutation } from '@tanstack/react-query'; import { useEffect, useState } from 'react'; import { useForm } from 'react-hook-form'; import type { postUserInfoRequest } from '@/shared/types/user-types'; @@ -52,7 +51,7 @@ const SignupStep = () => { const userInfoMutation = useMutation(userMutations.USER_INFO()); const agreementInfoMutaion = useMutation(userMutations.AGREEMENT_INFO()); - const { refetch: refetchNicknameCheck } = useQuery(userQueries.NICKNAME_CHECK(nicknameValue)); + const { mutateAsync: checkNickname } = useMutation(userMutations.NICKNAME_CHECK()); const informationLength = informationValue.length ?? 0; @@ -103,8 +102,8 @@ const SignupStep = () => { if (!isNicknameValid) return; setNicknameStatus('checking'); try { - const { data } = await refetchNicknameCheck(); - setNicknameStatus(data ? 'available' : 'duplicate'); + const available = await checkNickname({ nickname: nicknameValue }); + setNicknameStatus(available ? 'available' : 'duplicate'); } catch { setNicknameStatus('idle'); } diff --git a/src/pages/sign-up/constants/validation.ts b/src/pages/sign-up/constants/validation.ts index ff3f66fc..3bccc98c 100644 --- a/src/pages/sign-up/constants/validation.ts +++ b/src/pages/sign-up/constants/validation.ts @@ -40,3 +40,5 @@ export const SIGNUP_STEPS = ['AGREEMENT', 'INFORMATION']; export const INTRODUCTION_MIN_LENGTH = 1; export const INTRODUCTION_MAX_LENGTH = 50; + +export const NICKNAME_DUPLICATE_FAILURE_COUNT = 2;