Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
3 changes: 2 additions & 1 deletion .vscode/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -28,5 +28,6 @@
"editor.defaultFormatter": "vscode.json-language-features"
},
// (선택) Biome 로그 자세히 보기
"biome.trace.server": "verbose"
"biome.trace.server": "verbose",
"typescript.tsdk": "node_modules/typescript/lib"
}
4 changes: 2 additions & 2 deletions src/pages/chat/chat-list.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@ import Icon from '@components/icon/icon';

const ChatList = () => {
return (
<div className="h-full flex-col-center gap-[0.8rem] bg-gray-white">
<div className="h-full flex-col-center gap-[0.8rem] bg-gray-black">
<Icon name="graphic_chat_empty" width={16} height={16} />
<p className="cap_14_sb text-center text-gray-800">
<p className="cap_14_sb text-center text-gray-white">
채팅 기능은 아직 준비 중이에요!
<br />
완료된 매칭은 '매칭 현황'에서 확인하고 소통할 수 있어요.
Expand Down
2 changes: 1 addition & 1 deletion src/pages/home/components/match-list-section.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ const MatchListSection = ({

{!gameLoading && !hasGames ? (
<EmptyView
iconName="empty-2"
iconName="graphic_empty_2"
className="mt-[4rem]"
text="해당 날짜에는 진행되는 경기가 없어요!"
subText="경기가 있는 다른 날짜를 탐색해 보세요."
Expand Down
2 changes: 1 addition & 1 deletion src/pages/home/home.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ const Home = () => {
};

return (
<div className="h-full bg-gray-200 pb-[5.6rem]">
<div className="h-full bg-gray-black pb-[5.6rem]">
<TopSection />
<CalendarSection
activeType={activeType}
Expand Down
44 changes: 17 additions & 27 deletions src/pages/match/components/match-tab-pannel.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,47 +6,37 @@ 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 {
getCardColor,
getPendingToast,
isClickable,
statusToCategory,
} from '@pages/match/utils/match-status';
import { getCardColor, getPendingToast, isClickable } from '@pages/match/utils/match-status';
import { ROUTES } from '@routes/routes-config';
import { useMutation, useQuery } from '@tanstack/react-query';
import { useMemo } from 'react';
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 {
activeType: '1:1' | '그룹';
statusParam: string;
filter: string;
isCreatedTab: boolean;
onCardClick: (card: MatchableCardProps) => void;
}

const MatchTabPanel = ({ activeType, filter, statusParam, onCardClick }: MatchTabPanelProps) => {
const MatchTabPanel = ({ isCreatedTab, onCardClick }: MatchTabPanelProps) => {
const navigate = useNavigate();
const isSingle = activeType === '1:1';

const { data: singleData } = useQuery({
...matchQueries.SINGLE_MATCH_STATUS(statusParam),
enabled: isSingle,
});
const { data: singleData } = useQuery(matchQueries.SINGLE_MATCH_STATUS(''));
const { data: groupData } = useQuery(matchQueries.GROUP_MATCH_STATUS(''));

const { data: groupData } = useQuery({
...matchQueries.GROUP_MATCH_STATUS(statusParam),
enabled: !isSingle,
});
const allCards: MatchableCardProps[] = useMemo(() => {
const singles = mapSingleMatchData(singleData?.results);
const groups = mapGroupMatchData(groupData?.mates);
return [...singles, ...groups];
}, [singleData, groupData]);

const cards: MatchableCardProps[] = isSingle
? mapSingleMatchData(singleData?.results)
: mapGroupMatchData(groupData?.mates);

const filteredCards =
filter === '전체' ? cards : cards.filter((card) => statusToCategory(card.status) === filter);
const filteredCards = useMemo(
() => allCards.filter((card) => card.isCreated === isCreatedTab),
[allCards, isCreatedTab],
);

const patchStageMutation = useMutation(matchMutations.MATCH_STAGE());

Expand All @@ -73,7 +63,7 @@ const MatchTabPanel = ({ activeType, filter, statusParam, onCardClick }: MatchTa
};

return (
<div className="z-[var(--z-under-header-section)] flex-col gap-[1.2rem] px-[1.6rem]">
<div className="z-[var(--z-under-header-section)] flex-col gap-[1.2rem] px-[1.6rem] pt-[1.6rem]">
{filteredCards.length === 0 ? (
<EmptyView
iconName="empty"
Expand All @@ -84,7 +74,7 @@ const MatchTabPanel = ({ activeType, filter, statusParam, onCardClick }: MatchTa
) : (
filteredCards.map((card) => (
<button
key={card.id}
key={`${card.type}-${card.id}`}
type="button"
onClick={() => handleCardClick(card)}
className={cn('w-full', { 'cursor-pointer': isClickable(card.status) })}
Expand Down
16 changes: 14 additions & 2 deletions src/pages/match/constants/matching.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,19 @@
export const MATCH_TAB_TYPES = {
CREATED: '생성한 매칭',
REQUESTED: '요청한 매칭',
} as const;

export type MatchTabType = (typeof MATCH_TAB_TYPES)[keyof typeof MATCH_TAB_TYPES];

export const MATCH_TAB_LIST: MatchTabType[] = [MATCH_TAB_TYPES.CREATED, MATCH_TAB_TYPES.REQUESTED];

export const MATCHING_NOTICE = {
group: '동시에 진행할 수 있는 그룹 매칭은 최대 2개예요.',
single: '동시에 진행할 수 있는 1:1 매칭은 최대 3개예요.',
};

export const MATCHING_COMPLETE_MESSAGE = {
group: '모든 그룹원이 수락하면 그룹원이 돼요.',
group: '그룹장이 요청을 수락하면 그룹원이 돼요.',
single: '상대방이 요청을 승인하면 매칭이 성사돼요.',
};

Expand Down Expand Up @@ -38,7 +47,10 @@ export const MATCHING_GUIDE_MESSAGE_TITLE = (nickname: string) =>
`${nickname} 님을 위한\n맞춤 매칭이 생성되었어요!`;

export const MATCHING_GUIDE_MESSAGE_DESCRIPTION =
'딱! 맞는 메이트의 요청이 도착하면\n' + "'매칭 현황'에서 확인할 수 있어요.";
'새 요청이 도착하면\n매칭 현황에서 확인할 수 있어요.';

export const GROUP_MATCHING_CREATED_DESCRIPTION =
'매칭현황에서 요청을 확인하고\n채팅방에서 메이트와 만날 수 있어요.';
Comment on lines 49 to +53
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

띄어쓰기 불일치

Line 50의 "매칭 현황에서"와 Line 53의 "매칭현황에서" 사이에 띄어쓰기가 일관되지 않습니다. UI 일관성을 위해 통일해주세요.

제안된 수정
 export const GROUP_MATCHING_CREATED_DESCRIPTION =
-  '매칭현황에서 요청을 확인하고\n채팅방에서 메이트와 만날 수 있어요.';
+  '매칭 현황에서 요청을 확인하고\n채팅방에서 메이트와 만날 수 있어요.';
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
export const MATCHING_GUIDE_MESSAGE_DESCRIPTION =
'딱! 맞는 메이트의 요청이 도착하면\n' + "'매칭 현황'에서 확인할 수 있어요.";
'새 요청이 도착하면\n매칭 현황에서 확인할 수 있어요.';
export const GROUP_MATCHING_CREATED_DESCRIPTION =
'매칭현황에서 요청을 확인하고\n채팅방에서 메이트와 만날 수 있어요.';
export const MATCHING_GUIDE_MESSAGE_DESCRIPTION =
'새 요청이 도착하면\n매칭 현황에서 확인할 수 있어요.';
export const GROUP_MATCHING_CREATED_DESCRIPTION =
'매칭 현황에서 요청을 확인하고\n채팅방에서 메이트와 만날 수 있어요.';
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/pages/match/constants/matching.ts` around lines 49 - 53, Two message
constants have inconsistent spacing: MATCHING_GUIDE_MESSAGE_DESCRIPTION uses "매칭
현황에서" while GROUP_MATCHING_CREATED_DESCRIPTION uses "매칭현황에서"; update
GROUP_MATCHING_CREATED_DESCRIPTION to use the same spacing as
MATCHING_GUIDE_MESSAGE_DESCRIPTION ("매칭 현황에서") so both strings are consistent,
by editing the GROUP_MATCHING_CREATED_DESCRIPTION string value accordingly.


export const CLICKABLE_STATUS_MAP: Record<string, string> = {
'매칭 완료': 'success',
Expand Down
44 changes: 13 additions & 31 deletions src/pages/match/hooks/useMatchTabState.ts
Original file line number Diff line number Diff line change
@@ -1,42 +1,24 @@
import type { TabType } from '@components/tab/tab/tab-content';
import { ROUTES } from '@routes/routes-config';
import { useState } from 'react';
import { useNavigate, useSearchParams } from 'react-router-dom';
import { useSearchParams } from 'react-router-dom';
import { MATCH_TAB_TYPES, type MatchTabType } from '../constants/matching';

export const useMatchTabState = () => {
const navigate = useNavigate();
const [searchParams] = useSearchParams();
const isMatchTabType = (value: string | null): value is MatchTabType =>
value === MATCH_TAB_TYPES.CREATED || value === MATCH_TAB_TYPES.REQUESTED;

const isTabType = (value: string | null): value is TabType => {
return value === '1:1' || value === '그룹';
};
export const useMatchTabState = () => {
const [searchParams, setSearchParams] = useSearchParams();

const tabParam = searchParams.get('tab');
const filterParam = searchParams.get('filter');
const initialTab: TabType = isTabType(tabParam) ? tabParam : '1:1';
const initialFilter = filterParam || '전체';
const initialTab: MatchTabType = isMatchTabType(tabParam) ? tabParam : MATCH_TAB_TYPES.CREATED;

const [tabState, setTabState] = useState({ type: initialTab, filter: initialFilter });
const [activeTab, setActiveTab] = useState<MatchTabType>(initialTab);

const updateTabQuery = (type: TabType, filter: string) => {
const query = new URLSearchParams();
query.set('tab', type);
query.set('filter', filter);
navigate(`${ROUTES.MATCH}?${query.toString()}`, { replace: true });
const handleTabChange = (tab: MatchTabType) => {
setActiveTab(tab);
setSearchParams({ tab }, { replace: true });
};

const handleTabChange = (type: TabType) => {
setTabState({ type, filter: '전체' });
updateTabQuery(type, '전체');
};

const handleFilterChange = (filter: string) => {
setTabState((prev) => {
const next = { ...prev, filter };
updateTabQuery(next.type, filter);
return next;
});
};
const isCreatedTab = activeTab === MATCH_TAB_TYPES.CREATED;

return { tabState, handleTabChange, handleFilterChange };
return { activeTab, handleTabChange, isCreatedTab };
};
49 changes: 21 additions & 28 deletions src/pages/match/match.tsx
Original file line number Diff line number Diff line change
@@ -1,48 +1,41 @@
import FillTabList from '@components/tab/fill-tab/fill-tab-list';
import TabList from '@components/tab/tab/tab-list';
import { tabStyleMap } from '@components/tab/tab/styles/tab-style';
import TabItem from '@components/tab/tab/tab-item';
import { gaEvent } from '@libs/analytics';
import MatchTabPanel from '@pages/match/components/match-tab-pannel';
import { MATCH_TAB_LIST } from '@pages/match/constants/matching';
import { useMatchTabState } from '@pages/match/hooks/useMatchTabState';
import { fillTabItems } from '@pages/match/utils/match-status';
import type { MatchCardData } from './create/types/match-data-type';

const tabStyle = tabStyleMap.matchStatus;

const Match = () => {
const { tabState, handleTabChange, handleFilterChange } = useMatchTabState();
const { type: activeType, filter } = tabState;
const statusParam = filter === '전체' ? '' : filter;
const { activeTab, handleTabChange, isCreatedTab } = useMatchTabState();

const handleCardClick = (card: MatchCardData) => {
gaEvent('match_card_click', {
match_id: card.id,
match_type: activeType === '1:1' ? 'one_to_one' : 'group',
match_type: card.type === 'single' ? 'one_to_one' : 'group',
match_status: card.status,
});
};

return (
<div className="h-full grow flex-col">
<nav className="sticky top-0 z-[var(--z-under-header-section)] w-full bg-gray-100">
<TabList
className="px-[1.6rem]"
colorMode="match"
activeType={activeType}
onTabChange={handleTabChange}
/>
<FillTabList
className="my-[0.8rem] px-[1.6rem] py-[1.2rem]"
tabs={fillTabItems}
selected={filter}
onChange={handleFilterChange}
/>
<div className="h-full grow flex-col bg-gray-black">
<nav className="sticky top-0 z-[var(--z-under-header-section)] w-full bg-gray-black">
<ul className={`flex items-center ${tabStyle.gap}`}>
{MATCH_TAB_LIST.map((tab) => (
<TabItem
key={tab}
label={tab}
isActive={activeTab === tab}
style={tabStyle}
onClick={() => handleTabChange(tab)}
/>
))}
</ul>
</nav>

<MatchTabPanel
key={`${activeType}-${statusParam}`}
activeType={activeType as '1:1' | '그룹'}
statusParam={statusParam}
filter={filter}
onCardClick={handleCardClick}
/>
<MatchTabPanel key={activeTab} isCreatedTab={isCreatedTab} onCardClick={handleCardClick} />
</div>
);
};
Expand Down
Loading
Loading