Skip to content
Merged
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: 3 additions & 0 deletions src/assets/filter/filterDown.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
3 changes: 3 additions & 0 deletions src/assets/filter/filterUp.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
4 changes: 4 additions & 0 deletions src/assets/searchBar/delete.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
3 changes: 3 additions & 0 deletions src/assets/searchBar/tabDelete.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
80 changes: 80 additions & 0 deletions src/components/common/Filter.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
import { colors, typography } from '@/styles/global/global';
import styled from '@emotion/styled';
import { useState } from 'react';
import { IconButton } from './IconButton';
import downImg from '../../assets/filter/filterDown.svg';
import upImg from '../../assets/filter/filterUp.svg';

interface FilterProps {
filters: string[];
selectedFilter: string;
setSelectedFilter: (filter: string) => void;
}

export const Filter = ({ filters, selectedFilter, setSelectedFilter }: FilterProps) => {
const [isOpenModal, setIsOpenModal] = useState(false);

const handleFilterClick = (filter: string) => {
setIsOpenModal(false);
setSelectedFilter(filter);
};

const handleModalClick = () => {
if (!isOpenModal) {
setIsOpenModal(true);
}
};
Comment on lines +22 to +26
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

모달 닫기 기능이 누락되었습니다.

현재 handleModalClick은 모달을 열기만 하고 닫는 기능이 없습니다. 사용자가 컨테이너를 다시 클릭했을 때 모달이 닫혀야 합니다.

다음과 같이 수정하세요:

-const handleModalClick = () => {
-  if (!isOpenModal) {
-    setIsOpenModal(true);
-  }
-};
+const handleModalClick = () => {
+  setIsOpenModal(!isOpenModal);
+};
📝 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
const handleModalClick = () => {
if (!isOpenModal) {
setIsOpenModal(true);
}
};
const handleModalClick = () => {
setIsOpenModal(!isOpenModal);
};
🤖 Prompt for AI Agents
In src/components/common/Filter.tsx between lines 22 and 26, the
handleModalClick function only opens the modal but does not close it when the
container is clicked again. Modify handleModalClick to toggle the modal state by
setting isOpenModal to its opposite value, so clicking the container will open
the modal if closed and close it if open.


return (
<Container onClick={handleModalClick}>
<Text>{selectedFilter}</Text>
<IconButton src={isOpenModal ? upImg : downImg} />
{isOpenModal ? (
<Modal>
{filters.map(filter =>
filter === selectedFilter ? (
<SelectedText onClick={() => handleFilterClick(filter)}>{filter}</SelectedText>
) : (
<Text onClick={() => handleFilterClick(filter)}>{filter}</Text>
),
)}
Comment on lines +34 to +40
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

React key prop이 누락되었습니다.

map 함수에서 렌더링되는 각 요소에 고유한 key prop이 필요합니다.

다음과 같이 수정하세요:

{filters.map(filter =>
  filter === selectedFilter ? (
-    <SelectedText onClick={() => handleFilterClick(filter)}>{filter}</SelectedText>
+    <SelectedText key={filter} onClick={() => handleFilterClick(filter)}>{filter}</SelectedText>
  ) : (
-    <Text onClick={() => handleFilterClick(filter)}>{filter}</Text>
+    <Text key={filter} onClick={() => handleFilterClick(filter)}>{filter}</Text>
  ),
)}
📝 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
{filters.map(filter =>
filter === selectedFilter ? (
<SelectedText onClick={() => handleFilterClick(filter)}>{filter}</SelectedText>
) : (
<Text onClick={() => handleFilterClick(filter)}>{filter}</Text>
),
)}
{filters.map(filter =>
filter === selectedFilter ? (
<SelectedText key={filter} onClick={() => handleFilterClick(filter)}>
{filter}
</SelectedText>
) : (
<Text key={filter} onClick={() => handleFilterClick(filter)}>
{filter}
</Text>
),
)}
🧰 Tools
🪛 Biome (1.9.4)

[error] 36-36: Missing key property for this element in iterable.

The order of the items may change, and having a key can help React identify which item was moved.
Check the React documentation.

(lint/correctness/useJsxKeyInIterable)


[error] 38-38: Missing key property for this element in iterable.

The order of the items may change, and having a key can help React identify which item was moved.
Check the React documentation.

(lint/correctness/useJsxKeyInIterable)

🤖 Prompt for AI Agents
In src/components/common/Filter.tsx around lines 34 to 40, the elements rendered
inside the map function lack a unique key prop, which is required by React for
efficient rendering. Add a key prop to both the SelectedText and Text
components, using a unique identifier such as the filter value itself, to ensure
each element has a stable and unique key.

</Modal>
) : (
<></>
)}
</Container>
);
};

const Container = styled.div`
display: flex;
position: relative;
justify-content: center;
align-items: center;
width: 85px;
`;

const Text = styled.p`
color: ${colors.grey[200]};
font-size: ${typography.fontSize.sm};
font-weight: ${typography.fontWeight.regular};
`;

const SelectedText = styled.p`
color: ${colors.white};
font-size: ${typography.fontSize.sm};
font-weight: ${typography.fontWeight.regular};
`;

const Modal = styled.div`
display: flex;
flex-direction: column;
gap: 16px;
padding: 12px 20px;
position: absolute;
top: 30px;
left: -20px;
border: solid 1px ${colors.grey[200]};
border-radius: 16px;
background: ${colors.black.main};
`;
48 changes: 0 additions & 48 deletions src/components/common/SearchBar.tsx

This file was deleted.

2 changes: 1 addition & 1 deletion src/components/common/TitleHeader.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ const NextButton = styled.div<{ active: boolean }>`
const InnerHeader = styled.div`
position: relative;
width: 100%;
height: 56px;
height: 24px;
display: flex;
flex-direction: row;
justify-content: space-between;
Expand Down
25 changes: 2 additions & 23 deletions src/components/group/CompletedGroupModal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import leftArrow from '../../assets/common/leftArrow.svg';
import type { Group } from './MyGroupBox';
import { GroupCard } from './GroupCard';
import TitleHeader from '../common/TitleHeader';
import { Modal, Overlay } from './Modal.styles';

interface CompletedGroupModalProps {
onClose: () => void;
Expand Down Expand Up @@ -91,7 +92,7 @@ const CompletedGroupModal = ({ onClose }: CompletedGroupModalProps) => {
<Text>{userName}님이 참여했던 모임방들을 확인해보세요.</Text>
<Content>
{dummyCompletedGroups.map(group => (
<GroupCard key={group.id} group={group}></GroupCard>
<GroupCard key={group.id} group={group} type={'modal'}></GroupCard>
))}
</Content>
</Modal>
Expand All @@ -101,28 +102,6 @@ const CompletedGroupModal = ({ onClose }: CompletedGroupModalProps) => {

export default CompletedGroupModal;

const Overlay = styled.div`
display: flex;
justify-content: center;
align-items: flex-start;
position: fixed;
top: 0;
left: 0;
width: 100vw;
height: 100vh;
background: rgba(0, 0, 0, 1);
z-index: 110;
`;

const Modal = styled.div`
display: flex;
flex-direction: column;
background-color: var(--color-main-black);
width: 100%;
max-width: 767px;
height: 100vh;
`;

const Text = styled.p`
font-size: var(--font-size-medium01);
font-weight: var(--font-weight-regular);
Expand Down
75 changes: 41 additions & 34 deletions src/components/group/GroupCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,53 +2,60 @@ import { forwardRef } from 'react';
import styled from '@emotion/styled';
import peopleIcon from '@/assets/common/darkPeople.svg';
import type { Group } from './MyGroupBox';
import { colors, typography } from '@/styles/global/global';

interface Props {
group: Group;
type?: 'recruiting' | 'ongoing';
isOngoing?: boolean;
type?: 'main' | 'search' | 'modal';
}

export const GroupCard = forwardRef<HTMLDivElement, Props>(({ group, type }, ref) => {
return (
<Card ref={ref}>
<Cover src={group.coverUrl} alt="cover" />
<Info>
<Title>{group.title}</Title>
<Bottom>
<Participant>
<img src={peopleIcon} alt="people" />
<p>{group.participants}</p>
<MaximumParticipants>/ {group.maximumParticipants}명</MaximumParticipants>
</Participant>
{type === 'recruiting' ? (
<RecruitingDeadline>{group.deadLine}일 뒤 모집 마감</RecruitingDeadline>
) : (
<OngoingDeadline>{group.deadLine}일 뒤 종료</OngoingDeadline>
)}
</Bottom>
</Info>
</Card>
);
});
export const GroupCard = forwardRef<HTMLDivElement, Props>(
({ group, isOngoing, type = 'main' }, ref) => {
return (
<Card ref={ref} cardType={type}>
<Cover src={group.coverUrl} alt="cover" cardType={type} />
<Info>
<Title>{group.title}</Title>
<Bottom>
<Participant>
<img src={peopleIcon} alt="people" />
<p>{group.participants}</p>
<MaximumParticipants>/ {group.maximumParticipants}명</MaximumParticipants>
</Participant>
{isOngoing === true ? (
<RecruitingDeadline>{group.deadLine}일 뒤 모집 마감</RecruitingDeadline>
) : (
<OngoingDeadline>{group.deadLine}일 뒤 종료</OngoingDeadline>
)}
Comment on lines +26 to +30
Copy link

Choose a reason for hiding this comment

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

💡 Verification agent

🧩 Analysis chain

isOngoing 로직을 검증해주세요.

현재 isOngoing === true일 때 "모집 마감"을 표시하고, false일 때 "종료"를 표시합니다. 이 로직이 의도한 동작인지 확인이 필요합니다.

일반적으로 isOngoing이 true면 진행 중인 상태를 의미하므로 "종료"를 표시하는 것이 맞을 것 같습니다.

다음 스크립트로 이 컴포넌트를 사용하는 다른 파일들을 확인하여 의도된 동작인지 검증해보겠습니다:


🏁 Script executed:

#!/bin/bash
# GroupCard 컴포넌트 사용처에서 isOngoing prop 사용 패턴 확인
rg -A 3 -B 3 "isOngoing.*=" --type tsx

Length of output: 68


🏁 Script executed:

#!/bin/bash
# GroupCard 컴포넌트 파일 경로 찾기
fd -e tsx GroupCard.tsx
# 해당 파일에서 isOngoing 사용 위치 출력
rg -n "isOngoing" $(fd -e tsx GroupCard.tsx)
# 전체 tsx 파일에서 GroupCard 사용 시 isOngoing prop 전달 패턴 확인
rg -g "*.tsx" -n "GroupCard" -A 3 -B 3

Length of output: 8322


🏁 Script executed:

#!/bin/bash
# Group 타입 정의에서 isOnGoing 필드 의미 확인
rg -n "export (interface|type) Group" -A10 src/components/group/MyGroupBox.tsx

Length of output: 345


GroupCard.tsx의 isOngoing 분기 로직 반전 필요

group.isOnGoingtrue일 때는 모임이 진행 중이므로 “종료”를,
false일 때는 모집 중이므로 “모집 마감”을 표시해야 합니다.
현재 코드는 그 반대로 동작하고 있어 수정이 필요합니다.

수정 위치

  • 파일: src/components/group/GroupCard.tsx
  • 라인: 26–30

제안 변경사항

-{isOngoing === true ? (
-  <RecruitingDeadline>{group.deadLine}일 뒤 모집 마감</RecruitingDeadline>
-) : (
-  <OngoingDeadline>{group.deadLine}일 뒤 종료</OngoingDeadline>
-)}
+{isOngoing === true ? (
+  <OngoingDeadline>{group.deadLine}일 뒤 종료</OngoingDeadline>
+) : (
+  <RecruitingDeadline>{group.deadLine}일 뒤 모집 마감</RecruitingDeadline>
+)}
🤖 Prompt for AI Agents
In src/components/group/GroupCard.tsx around lines 26 to 30, the conditional
rendering for isOngoing is reversed; when group.isOnGoing is true, it should
display the "종료" message, and when false, the "모집 마감" message. Swap the JSX
elements inside the ternary operator so that the true condition renders the
OngoingDeadline with "종료" text and the false condition renders
RecruitingDeadline with "모집 마감" text.

</Bottom>
</Info>
</Card>
);
},
);

const Card = styled.div`
const Card = styled.div<{ cardType: 'main' | 'search' | 'modal' }>`
display: flex;
align-items: center;
background: var(--color-darkgrey-main);
border-radius: 12px;
background: ${({ cardType }) =>
cardType === 'search' ? 'var(--color-black-main)' : 'var(--color-darkgrey-main)'};
border-radius: ${({ cardType }) => (cardType === 'search' ? `none` : '12px')};
box-sizing: border-box;
padding: 12px;
padding: ${({ cardType }) => (cardType === 'search' ? '24px 12px 12px 12px' : '12px')};
gap: 12px;
width: 100%;
border: 1px solid var(--color-grey-300);
border: ${({ cardType }) => (cardType === 'main' ? '1px solid var(--color-grey-300)' : 'none')};
border-top: ${({ cardType }) =>
cardType === 'search' ? `1px solid ${colors.darkgrey.dark}` : ''};
`;

const Cover = styled.img`
width: 80px;
height: 107px;
border-radius: 6px;
const Cover = styled.img<{ cardType: 'main' | 'search' | 'modal' }>`
object-fit: cover;
flex-shrink: 0;

width: ${({ cardType }) => (cardType === 'search' ? '60px' : '80px')};
height: ${({ cardType }) => (cardType === 'search' ? '80px' : '107px')};
`;

const Info = styled.div`
Expand All @@ -59,8 +66,8 @@ const Info = styled.div`
`;

const Title = styled.h3`
font-size: var(--font-size-large01);
font-weight: var(--font-weight-bold);
font-size: ${typography.fontSize.lg};
font-weight: ${typography.fontWeight.semibold};
color: #ffffff;
margin-bottom: 10px;
line-height: 1.4;
Expand Down
24 changes: 24 additions & 0 deletions src/components/group/Modal.styles.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import { colors } from '@/styles/global/global';
import styled from '@emotion/styled';

export const Overlay = styled.div`
display: flex;
justify-content: center;
align-items: flex-start;
position: fixed;
top: 0;
left: 0;
width: 100vw;
height: 100vh;
background: ${colors.black.main};
z-index: 110;
`;

export const Modal = styled.div`
display: flex;
flex-direction: column;
background: ${colors.black.main};
width: 100%;
max-width: 767px;
height: 100vh;
`;
26 changes: 3 additions & 23 deletions src/components/group/MyGroupModal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import TitleHeader from '../common/TitleHeader';
import leftArrow from '../../assets/common/leftArrow.svg';
import type { Group } from './MyGroupBox';
import { GroupCard } from './GroupCard';
import { Modal, Overlay } from './Modal.styles';

interface MyGroupModalProps {
onClose: () => void;
Expand Down Expand Up @@ -116,7 +117,8 @@ export const MyGroupModal = ({ onClose }: MyGroupModalProps) => {
<GroupCard
key={group.id}
group={group}
type={group.isOnGoing ? 'ongoing' : 'recruiting'}
isOngoing={group.isOnGoing ? true : false}
type={'modal'}
/>
))}
</Content>
Expand All @@ -125,28 +127,6 @@ export const MyGroupModal = ({ onClose }: MyGroupModalProps) => {
);
};

const Overlay = styled.div`
display: flex;
justify-content: center;
align-items: flex-start;
position: fixed;
top: 0;
left: 0;
width: 100vw;
height: 100vh;
background: rgba(0, 0, 0, 1);
z-index: 110;
`;

const Modal = styled.div`
display: flex;
flex-direction: column;
background-color: var(--color-main-black);
width: 100%;
max-width: 767px;
height: 100vh;
`;

const TabContainer = styled.div`
display: flex;
gap: 8px;
Expand Down
2 changes: 1 addition & 1 deletion src/components/group/RecruitingGroupBox.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ export function RecruitingGroupBox({ groups, title }: Props) {
</TabContainer>
<Grid>
{filtered.map(group => (
<GroupCard key={group.id} group={group} type={'recruiting'} />
<GroupCard key={group.id} group={group} type={'main'} />
))}
</Grid>
</Container>
Expand Down
Loading