-
Notifications
You must be signed in to change notification settings - Fork 0
feat: 모임방 검색 페이지 & Filter 컴포넌트 #36
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
84efbc4
4f3948a
85ae348
ed7bfa5
4e9326c
9104ddd
508fc42
b2b837f
c6229f6
c3469bf
a23d8cf
2463357
7c1b803
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| 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); | ||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||
| }; | ||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||
| 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
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 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
Suggested change
🧰 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. (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. (lint/correctness/useJsxKeyInIterable) 🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||||||||
| </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}; | ||||||||||||||||||||||||||||||||||||||
| `; | ||||||||||||||||||||||||||||||||||||||
This file was deleted.
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -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
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 💡 Verification agent 🧩 Analysis chainisOngoing 로직을 검증해주세요. 현재 일반적으로 다음 스크립트로 이 컴포넌트를 사용하는 다른 파일들을 확인하여 의도된 동작인지 검증해보겠습니다: 🏁 Script executed: #!/bin/bash
# GroupCard 컴포넌트 사용처에서 isOngoing prop 사용 패턴 확인
rg -A 3 -B 3 "isOngoing.*=" --type tsxLength 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 3Length of output: 8322 🏁 Script executed: #!/bin/bash
# Group 타입 정의에서 isOnGoing 필드 의미 확인
rg -n "export (interface|type) Group" -A10 src/components/group/MyGroupBox.tsxLength of output: 345 GroupCard.tsx의 isOngoing 분기 로직 반전 필요
수정 위치
제안 변경사항 -{isOngoing === true ? (
- <RecruitingDeadline>{group.deadLine}일 뒤 모집 마감</RecruitingDeadline>
-) : (
- <OngoingDeadline>{group.deadLine}일 뒤 종료</OngoingDeadline>
-)}
+{isOngoing === true ? (
+ <OngoingDeadline>{group.deadLine}일 뒤 종료</OngoingDeadline>
+) : (
+ <RecruitingDeadline>{group.deadLine}일 뒤 모집 마감</RecruitingDeadline>
+)}🤖 Prompt for AI Agents |
||
| </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` | ||
|
|
@@ -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; | ||
|
|
||
| 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; | ||
| `; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
모달 닫기 기능이 누락되었습니다.
현재
handleModalClick은 모달을 열기만 하고 닫는 기능이 없습니다. 사용자가 컨테이너를 다시 클릭했을 때 모달이 닫혀야 합니다.다음과 같이 수정하세요:
📝 Committable suggestion
🤖 Prompt for AI Agents