-
Notifications
You must be signed in to change notification settings - Fork 0
검색 필터 구현 구현 ! #136
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
검색 필터 구현 구현 ! #136
Changes from all commits
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,85 @@ | ||||||||||||||||||||||||||||||||||||||||||||
| import { useEffect, useState } from 'react'; | ||||||||||||||||||||||||||||||||||||||||||||
| import * as s from './style.css'; | ||||||||||||||||||||||||||||||||||||||||||||
| import { MAX_PRICE } from '@/libs/constants'; | ||||||||||||||||||||||||||||||||||||||||||||
| import { useSearchParams } from 'react-router'; | ||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||
| interface Props { | ||||||||||||||||||||||||||||||||||||||||||||
| active: boolean; | ||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||
| const CustomPriceInput = ({ active }: Props) => { | ||||||||||||||||||||||||||||||||||||||||||||
| const [searchParams, setSearchParams] = useSearchParams(); | ||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||
| const defaultStartPrice = searchParams.get('start-price') ? Number(searchParams.get('start-price')) : NaN; | ||||||||||||||||||||||||||||||||||||||||||||
| const defaultEndPrice = searchParams.get('end-price') ? Number(searchParams.get('end-price')) : NaN; | ||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||
| const [startPrice, setStartPrice] = useState<number>(defaultStartPrice); | ||||||||||||||||||||||||||||||||||||||||||||
| const [endPrice, setEndPrice] = useState<number>(defaultEndPrice); | ||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||
| useEffect(() => { | ||||||||||||||||||||||||||||||||||||||||||||
| if (!active) { | ||||||||||||||||||||||||||||||||||||||||||||
| setStartPrice(NaN); | ||||||||||||||||||||||||||||||||||||||||||||
| setEndPrice(NaN); | ||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||
| }, [active]); | ||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||
| const handlePriceChange = (e: React.ChangeEvent<HTMLInputElement>, type: 'start' | 'end') => { | ||||||||||||||||||||||||||||||||||||||||||||
| const handlePrice = type === 'start' ? setStartPrice : setEndPrice; | ||||||||||||||||||||||||||||||||||||||||||||
| const value = e.target.value; | ||||||||||||||||||||||||||||||||||||||||||||
| // 빈 문자열이면 NaN으로 설정 | ||||||||||||||||||||||||||||||||||||||||||||
| if (value === '') { | ||||||||||||||||||||||||||||||||||||||||||||
| handlePrice(NaN); | ||||||||||||||||||||||||||||||||||||||||||||
| return; | ||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||
| // 숫자가 아니거나 0으로 시작하는 경우 (단, '0' 자체는 허용) 무시 | ||||||||||||||||||||||||||||||||||||||||||||
| if (!/^\d+$/.test(value) || (value.length > 1 && value.startsWith('0'))) { | ||||||||||||||||||||||||||||||||||||||||||||
| return; | ||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||
| const numValue = Number(value); | ||||||||||||||||||||||||||||||||||||||||||||
| if (numValue > MAX_PRICE) return; | ||||||||||||||||||||||||||||||||||||||||||||
| handlePrice(numValue); | ||||||||||||||||||||||||||||||||||||||||||||
| }; | ||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||
| const submitPrice = () => { | ||||||||||||||||||||||||||||||||||||||||||||
| if (isNaN(startPrice) || isNaN(endPrice)) return; | ||||||||||||||||||||||||||||||||||||||||||||
| if (startPrice > endPrice) { | ||||||||||||||||||||||||||||||||||||||||||||
| alert('시작 가격이 끝 가격보다 클 수 없습니다.'); | ||||||||||||||||||||||||||||||||||||||||||||
| return; | ||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+40
to
+47
|
||||||||||||||||||||||||||||||||||||||||||||
| }; | |
| const submitPrice = () => { | |
| if (isNaN(startPrice) || isNaN(endPrice)) return; | |
| if (startPrice > endPrice) { | |
| alert('시작 가격이 끝 가격보다 클 수 없습니다.'); | |
| return; | |
| } | |
| setErrorMessage(''); | |
| }; | |
| const submitPrice = () => { | |
| if (isNaN(startPrice) || isNaN(endPrice)) { | |
| setErrorMessage(''); | |
| return; | |
| } | |
| if (startPrice > endPrice) { | |
| setErrorMessage('시작 가격이 끝 가격보다 클 수 없습니다.'); | |
| return; | |
| } | |
| setErrorMessage(''); |
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.
토스트 알림 같은것도 하나 있으면 좋긴 하겠다
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.
토스트 알림 인정 정보 수정했을 때 띄워주면 좋을 듯
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.
블루베리쨈 토스트 먹고 싶다
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.
전남친 토스트?
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,19 @@ | ||
| import * as s from './style.css'; | ||
|
|
||
| interface Props { | ||
| isSelected: boolean; | ||
| onClick: () => void; | ||
| label: string; | ||
| } | ||
| const PriceButton = ({ isSelected, onClick, label }: Props) => { | ||
| return ( | ||
| <button className={s.PriceTagButton({ isSelected })} onClick={onClick}> | ||
| {label} | ||
| <div className={s.RightIcon({ isSelected })}> | ||
| <div /> | ||
| </div> | ||
| </button> | ||
| ); | ||
| }; | ||
|
|
||
| export default PriceButton; |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,103 @@ | ||
| import { MAX_PRICE } from '@/libs/constants'; | ||
| import { useSearchParams } from 'react-router'; | ||
|
|
||
| import * as s from './style.css'; | ||
| import { useMemo } from 'react'; | ||
| import PriceButton from '@/features/home/components/Filter/PriceFilter/PriceButton'; | ||
| import CustomPriceInput from '@/features/home/components/Filter/PriceFilter/CustomPriceInput'; | ||
|
|
||
| const PriceOptions = [ | ||
| { | ||
| label: '1만원 미만', | ||
| start: 0, | ||
| end: 9999, | ||
| }, | ||
| { | ||
| label: '1만원 - 2만원', | ||
| start: 10000, | ||
| end: 19999, | ||
| }, | ||
| { | ||
| label: '2만원 - 3만원', | ||
| start: 20000, | ||
| end: 29999, | ||
| }, | ||
| { | ||
| label: '3만원 - 4만원', | ||
| start: 30000, | ||
| end: 39999, | ||
| }, | ||
| { | ||
| label: '4만원 - 5만원', | ||
| start: 40000, | ||
| end: 49999, | ||
| }, | ||
| { label: '5만원 이상', start: 50000, end: MAX_PRICE }, | ||
|
halionaz marked this conversation as resolved.
|
||
| ]; | ||
|
|
||
| const PriceFilter = () => { | ||
| const [searchParams, setSearchParams] = useSearchParams(); | ||
| const startPrice = Number(searchParams.get('start-price') || 0); | ||
| const endPrice = Number(searchParams.get('end-price') || MAX_PRICE); | ||
|
|
||
| const resetPrice = () => { | ||
| setSearchParams(prev => { | ||
| prev.delete('start-price'); | ||
| prev.delete('end-price'); | ||
| return prev; | ||
| }); | ||
| }; | ||
|
|
||
| const handlePriceChange = (start: number, end: number) => { | ||
| setSearchParams(prev => { | ||
| prev.set('start-price', start.toString()); | ||
| prev.set('end-price', end.toString()); | ||
| return prev; | ||
| }); | ||
| }; | ||
|
|
||
| const isCustomPrice = useMemo(() => { | ||
| return ( | ||
| searchParams.get('start-price') !== null && | ||
| searchParams.get('end-price') !== null && | ||
| !PriceOptions.some(option => option.start === startPrice && option.end === endPrice) | ||
| ); | ||
| }, [searchParams, startPrice, endPrice]); | ||
|
|
||
| return ( | ||
| <div className={s.Container}> | ||
| <div className={s.PriceWrapper}> | ||
| {PriceOptions.map(option => { | ||
| const isSelected = option.start === startPrice && option.end === endPrice; | ||
| return ( | ||
| <PriceButton | ||
| key={option.start} | ||
| isSelected={isSelected} | ||
| onClick={() => { | ||
| if (isSelected) { | ||
| resetPrice(); | ||
| return; | ||
| } | ||
| handlePriceChange(option.start, option.end); | ||
| }} | ||
| label={option.label} | ||
| /> | ||
| ); | ||
| })} | ||
| <PriceButton | ||
| isSelected={isCustomPrice} | ||
| onClick={() => { | ||
| if (isCustomPrice) { | ||
| resetPrice(); | ||
| return; | ||
| } | ||
| handlePriceChange(0, MAX_PRICE); | ||
| }} | ||
| label="직접 입력" | ||
| /> | ||
| </div> | ||
| <CustomPriceInput active={isCustomPrice} /> | ||
| </div> | ||
| ); | ||
| }; | ||
| export default PriceFilter; | ||
Uh oh!
There was an error while loading. Please reload this page.