From 650adb274082266b29bf7abefd198a64773b3704 Mon Sep 17 00:00:00 2001 From: ssumanlife Date: Thu, 28 Nov 2024 21:44:50 +0900 Subject: [PATCH 1/5] =?UTF-8?q?feat:=20=EC=A0=84=EB=9E=B5=EB=AA=A9?= =?UTF-8?q?=EB=A1=9D=ED=8E=98=EC=9D=B4=EC=A7=80=20=EC=95=84=EC=BD=94?= =?UTF-8?q?=EB=94=94=EC=96=B8=20=ED=98=95=EC=8B=9D=EC=9D=98=20=EA=B2=80?= =?UTF-8?q?=EC=83=89=20=EC=82=AC=EC=9D=B4=EB=93=9C=EB=B0=94=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80=20(#133)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../search-bar/_hooks/use-accordion-button.ts | 18 ++ .../_hooks/use-accordion-context.ts | 11 ++ .../_store/use-searching-item-store.ts | 89 +++++++++ .../strategies/_ui/search-bar/_type/search.ts | 19 ++ .../_ui/search-bar/_utils/type-validate.ts | 12 ++ .../_ui/search-bar/accordion-button.tsx | 48 +++++ .../_ui/search-bar/accordion-container.tsx | 43 ++++ .../_ui/search-bar/accordion-panel.tsx | 75 +++++++ .../_ui/search-bar/algorithm-item.tsx | 27 +++ .../strategies/_ui/search-bar/index.tsx | 94 +++++++++ .../_ui/search-bar/range-container.tsx | 44 +++++ .../_ui/search-bar/search-bar-tap.tsx | 33 ++++ .../_ui/search-bar/styles.module.scss | 185 ++++++++++++++++++ app/(dashboard)/strategies/page.tsx | 3 +- 14 files changed, 700 insertions(+), 1 deletion(-) create mode 100644 app/(dashboard)/strategies/_ui/search-bar/_hooks/use-accordion-button.ts create mode 100644 app/(dashboard)/strategies/_ui/search-bar/_hooks/use-accordion-context.ts create mode 100644 app/(dashboard)/strategies/_ui/search-bar/_store/use-searching-item-store.ts create mode 100644 app/(dashboard)/strategies/_ui/search-bar/_type/search.ts create mode 100644 app/(dashboard)/strategies/_ui/search-bar/_utils/type-validate.ts create mode 100644 app/(dashboard)/strategies/_ui/search-bar/accordion-button.tsx create mode 100644 app/(dashboard)/strategies/_ui/search-bar/accordion-container.tsx create mode 100644 app/(dashboard)/strategies/_ui/search-bar/accordion-panel.tsx create mode 100644 app/(dashboard)/strategies/_ui/search-bar/algorithm-item.tsx create mode 100644 app/(dashboard)/strategies/_ui/search-bar/index.tsx create mode 100644 app/(dashboard)/strategies/_ui/search-bar/range-container.tsx create mode 100644 app/(dashboard)/strategies/_ui/search-bar/search-bar-tap.tsx create mode 100644 app/(dashboard)/strategies/_ui/search-bar/styles.module.scss diff --git a/app/(dashboard)/strategies/_ui/search-bar/_hooks/use-accordion-button.ts b/app/(dashboard)/strategies/_ui/search-bar/_hooks/use-accordion-button.ts new file mode 100644 index 00000000..bf65d5df --- /dev/null +++ b/app/(dashboard)/strategies/_ui/search-bar/_hooks/use-accordion-button.ts @@ -0,0 +1,18 @@ +import { useRef, useState } from 'react' + +export interface ButtonIdStateModel { + [key: string]: boolean +} + +const useAccordionButton = () => { + const [openIds, setOpenIds] = useState(null) + const panelRef = useRef(null) + + const handleButtonIds = (id: string, isOpen: boolean) => { + setOpenIds((prev) => ({ ...prev, [id]: isOpen })) + } + + return { panelRef, openIds, handleButtonIds } +} + +export default useAccordionButton diff --git a/app/(dashboard)/strategies/_ui/search-bar/_hooks/use-accordion-context.ts b/app/(dashboard)/strategies/_ui/search-bar/_hooks/use-accordion-context.ts new file mode 100644 index 00000000..49707f96 --- /dev/null +++ b/app/(dashboard)/strategies/_ui/search-bar/_hooks/use-accordion-context.ts @@ -0,0 +1,11 @@ +import { useContext } from 'react' + +import { AccordionContext } from '../accordion-container' + +export const useAccordionContext = () => { + const context = useContext(AccordionContext) + if (!context) { + throw new Error('검색 메뉴 로드 실패') + } + return context +} diff --git a/app/(dashboard)/strategies/_ui/search-bar/_store/use-searching-item-store.ts b/app/(dashboard)/strategies/_ui/search-bar/_store/use-searching-item-store.ts new file mode 100644 index 00000000..aa2f094a --- /dev/null +++ b/app/(dashboard)/strategies/_ui/search-bar/_store/use-searching-item-store.ts @@ -0,0 +1,89 @@ +import { create } from 'zustand' + +import { AlgorithmItemType, RangeModel, SearchTermsModel } from '../_type/search' +import { isRangeModel } from '../_utils/type-validate' + +interface StateModel { + searchTerms: SearchTermsModel + errOptions: (keyof SearchTermsModel)[] | null +} + +interface ActionModel { + setAlgorithm: (algorithm: AlgorithmItemType) => void + setPanelItem: (key: keyof SearchTermsModel, item: string) => void + setRangeValue: (key: keyof SearchTermsModel, type: keyof RangeModel, value: number) => void + resetState: () => void + validateRangeValues: () => void +} + +interface ActionsModel { + actions: ActionModel +} + +const initialState = { + searchWord: null, + tradeTypeNames: null, + operationCycles: null, + stockTypeNames: null, + durations: null, + profitRanges: null, + principalRange: null, + mddRange: null, + smScoreRange: null, + algorithmType: null, +} + +const useSearchingItemStore = create((set, get) => ({ + searchTerms: { + ...initialState, + }, + errOptions: [], + + actions: { + setAlgorithm: (algorithm) => + set((state) => ({ + searchTerms: { ...state.searchTerms, algorithmType: algorithm }, + })), + + setPanelItem: (key, item) => + set((state) => { + const currentItems = state.searchTerms[key] + if (Array.isArray(currentItems)) { + const updatedItems = currentItems.includes(item) + ? currentItems.filter((i) => i !== item) + : [...currentItems, item] + return { searchTerms: { ...state.searchTerms, [key]: [...updatedItems] } } + } + return { searchTerms: { ...state.searchTerms, [key]: [item] } } + }), + + setRangeValue: (key, type, value) => + set((state) => ({ + searchTerms: { + ...state.searchTerms, + [key]: { ...(state.searchTerms[key] as RangeModel), [type]: value }, + }, + })), + + resetState: () => set(() => ({ searchTerms: { ...initialState } })), + + validateRangeValues: () => { + const { searchTerms } = get() + const rangeOptions: (keyof SearchTermsModel)[] = [ + 'principalRange', + 'mddRange', + 'smScoreRange', + ] + const errOptions = rangeOptions.filter((option) => { + const value = searchTerms[option] + if (value !== null && isRangeModel(value)) { + return value.min > value.max + } + return false + }) + set({ errOptions }) + }, + }, +})) + +export default useSearchingItemStore diff --git a/app/(dashboard)/strategies/_ui/search-bar/_type/search.ts b/app/(dashboard)/strategies/_ui/search-bar/_type/search.ts new file mode 100644 index 00000000..3bacc688 --- /dev/null +++ b/app/(dashboard)/strategies/_ui/search-bar/_type/search.ts @@ -0,0 +1,19 @@ +export type AlgorithmItemType = 'EFFICIENT_STRATEGY' | 'ATTACK_STRATEGY' | 'DEFENSIVE_STRATE' + +export interface SearchTermsModel { + searchWord: string | null + tradeTypeNames: string[] | null + operationCycles: string[] | null + stockTypeNames: string[] | null + durations: string[] | null + profitRanges: string[] | null + principalRange: RangeModel | null + mddRange: RangeModel | null + smScoreRange: RangeModel | null + algorithmType: AlgorithmItemType | null +} + +export interface RangeModel { + min: number + max: number +} diff --git a/app/(dashboard)/strategies/_ui/search-bar/_utils/type-validate.ts b/app/(dashboard)/strategies/_ui/search-bar/_utils/type-validate.ts new file mode 100644 index 00000000..7f71ad9f --- /dev/null +++ b/app/(dashboard)/strategies/_ui/search-bar/_utils/type-validate.ts @@ -0,0 +1,12 @@ +import { RangeModel } from '../_type/search' + +export const isRangeModel = (value: unknown): value is RangeModel => { + return ( + typeof value === 'object' && + value !== null && + 'min' in value && + 'max' in value && + typeof (value as RangeModel).min === 'number' && + typeof (value as RangeModel).max === 'number' + ) +} diff --git a/app/(dashboard)/strategies/_ui/search-bar/accordion-button.tsx b/app/(dashboard)/strategies/_ui/search-bar/accordion-button.tsx new file mode 100644 index 00000000..73a5db4d --- /dev/null +++ b/app/(dashboard)/strategies/_ui/search-bar/accordion-button.tsx @@ -0,0 +1,48 @@ +'use client' + +import { useContext } from 'react' + +import { CloseIcon, OpenIcon } from '@/public/icons' +import classNames from 'classnames/bind' + +import useSearchingItemStore from './_store/use-searching-item-store' +import { SearchTermsModel } from './_type/search' +import { AccordionContext } from './accordion-container' +import styles from './styles.module.scss' + +const cx = classNames.bind(styles) + +interface Props { + optionId: keyof SearchTermsModel + title: string + size?: number +} + +const AccordionButton = ({ optionId, title, size }: Props) => { + const { openIds, handleButtonIds } = useContext(AccordionContext) + const searchTerms = useSearchingItemStore((state) => state.searchTerms) + + const clickedValue = searchTerms[optionId] + + return ( +
+ +
+ ) +} + +export default AccordionButton diff --git a/app/(dashboard)/strategies/_ui/search-bar/accordion-container.tsx b/app/(dashboard)/strategies/_ui/search-bar/accordion-container.tsx new file mode 100644 index 00000000..53b6246c --- /dev/null +++ b/app/(dashboard)/strategies/_ui/search-bar/accordion-container.tsx @@ -0,0 +1,43 @@ +'use client' + +import { createContext } from 'react' + +import useAccordionButton, { ButtonIdStateModel } from './_hooks/use-accordion-button' +import { SearchTermsModel } from './_type/search' +import AccordionButton from './accordion-button' +import AccordionPanel from './accordion-panel' + +interface AccordionContextModel { + panelRef: React.RefObject + openIds: ButtonIdStateModel | null + handleButtonIds: (id: string, open: boolean) => void +} + +const initialState: AccordionContextModel = { + panelRef: { current: null }, + openIds: null, + handleButtonIds: () => {}, +} + +export const AccordionContext = createContext(initialState) + +interface Props { + optionId: keyof SearchTermsModel + title: string + panels?: string[] +} + +const AccordionContainer = ({ optionId, title, panels }: Props) => { + const { openIds, panelRef, handleButtonIds } = useAccordionButton() + + return ( + +
+ + +
+
+ ) +} + +export default AccordionContainer diff --git a/app/(dashboard)/strategies/_ui/search-bar/accordion-panel.tsx b/app/(dashboard)/strategies/_ui/search-bar/accordion-panel.tsx new file mode 100644 index 00000000..9d8fbb29 --- /dev/null +++ b/app/(dashboard)/strategies/_ui/search-bar/accordion-panel.tsx @@ -0,0 +1,75 @@ +import { useContext, useEffect, useState } from 'react' + +import { CheckedCircleIcon } from '@/public/icons' +import classNames from 'classnames/bind' + +import useSearchingItemStore from './_store/use-searching-item-store' +import { SearchTermsModel } from './_type/search' +import { AccordionContext } from './accordion-container' +import RangeContainer from './range-container' +import styles from './styles.module.scss' + +const cx = classNames.bind(styles) + +interface Props { + optionId: keyof SearchTermsModel + panels?: string[] +} + +const AccordionPanel = ({ optionId, panels }: Props) => { + const { openIds, panelRef } = useContext(AccordionContext) + const [panelHeight, setPanelHeight] = useState(null) + const [isClose, setIsClose] = useState(false) + const searchTerms = useSearchingItemStore((state) => state.searchTerms) + const { setPanelItem } = useSearchingItemStore((state) => state.actions) + + useEffect(() => { + if (panelRef.current && hasOpenId) { + const panelHeight = panelRef.current.clientHeight + 32 * (panels?.length || 1) + setPanelHeight(panelHeight) + panelRef.current.style.setProperty('--panel-height', `${panelHeight}px`) + } + + if (!hasOpenId) { + setIsClose(true) + const timeout = setTimeout(() => { + setIsClose(false) + setPanelHeight(null) + }, 300) + return () => clearTimeout(timeout) + } + }, [openIds, panelRef, optionId]) + + const hasOpenId = openIds?.[optionId] + const clickedValue = searchTerms[optionId] + + return ( + <> + {hasOpenId !== undefined && ( +
+ {panels + ? hasOpenId && + panels?.map((panel, idx) => ( + + )) + : hasOpenId && } +
+ )} + + ) +} + +export default AccordionPanel diff --git a/app/(dashboard)/strategies/_ui/search-bar/algorithm-item.tsx b/app/(dashboard)/strategies/_ui/search-bar/algorithm-item.tsx new file mode 100644 index 00000000..10606e92 --- /dev/null +++ b/app/(dashboard)/strategies/_ui/search-bar/algorithm-item.tsx @@ -0,0 +1,27 @@ +'use client' + +import classNames from 'classnames/bind' + +import { AlgorithmItemType } from './_type/search' +import styles from './styles.module.scss' + +const cx = classNames.bind(styles) + +interface Props { + name: AlgorithmItemType + clickedAlgorithm: AlgorithmItemType | null + onChange: (algorithm: AlgorithmItemType) => void +} + +const AlgorithmItem = ({ name, clickedAlgorithm, onChange }: Props) => { + return ( + + ) +} + +export default AlgorithmItem diff --git a/app/(dashboard)/strategies/_ui/search-bar/index.tsx b/app/(dashboard)/strategies/_ui/search-bar/index.tsx new file mode 100644 index 00000000..b3305a74 --- /dev/null +++ b/app/(dashboard)/strategies/_ui/search-bar/index.tsx @@ -0,0 +1,94 @@ +'use client' + +import { useState } from 'react' + +import classNames from 'classnames/bind' + +import { Button } from '@/shared/ui/button' +import { SearchInput } from '@/shared/ui/search-input' + +import useSearchingItemStore from './_store/use-searching-item-store' +import { SearchTermsModel } from './_type/search' +import AccordionContainer from './accordion-container' +import AlgorithmItem from './algorithm-item' +import SearchBarTap from './search-bar-tap' +import styles from './styles.module.scss' + +const cx = classNames.bind(styles) + +const ALGORITHM_MENU = [ + { EFFICIENT_STRATEGY: '효율형 전략' }, + { ATTACK_STRATEGY: '공격형 전략' }, + { DEFENSIVE_STRATEGY: '방어형 전략' }, +] +interface AccordionMenuDataModel { + id: keyof SearchTermsModel + title: string + panels?: string[] +} +const SearchBarContainer = () => { + const [isMainTab, setIsMainTab] = useState(true) + const searchTerms = useSearchingItemStore((state) => state.searchTerms) + const { setAlgorithm, resetState, validateRangeValues } = useSearchingItemStore( + (state) => state.actions + ) + + const ACCORDION_MENU: AccordionMenuDataModel[] = [ + { id: 'tradeTypeNames', title: '매매 유형', panels: ['수동', '자동', '반자동'] }, + { id: 'operationCycles', title: '운용 주기', panels: ['데이', '포지션'] }, + { id: 'stockTypeNames', title: '운영 종목', panels: ['선물', '해외', '국내'] }, + { id: 'durations', title: '기간', panels: ['1년 이하', '1년 ~ 2년', '2년 ~ 3년', '3년 이상'] }, + { + id: 'profitRanges', + title: '수익률', + panels: ['10% 이하', '10% ~ 20%', '20% ~ 30%', '30% 이상'], + }, + { id: 'principalRange', title: '원금' }, + { id: 'mddRange', title: 'MDD' }, + { id: 'smScoreRange', title: 'SM SCORE' }, + ] + + return ( + <> +
+ +
+
+ + {isMainTab + ? ACCORDION_MENU.map((menu) => ( + + )) + : ALGORITHM_MENU.map((menu) => { + return Object.entries(menu).map(([key, value]) => ( + + )) + })} +
+ + +
+
+ + ) +} + +export default SearchBarContainer diff --git a/app/(dashboard)/strategies/_ui/search-bar/range-container.tsx b/app/(dashboard)/strategies/_ui/search-bar/range-container.tsx new file mode 100644 index 00000000..69f97719 --- /dev/null +++ b/app/(dashboard)/strategies/_ui/search-bar/range-container.tsx @@ -0,0 +1,44 @@ +import classNames from 'classnames/bind' + +import useSearchingItemStore from './_store/use-searching-item-store' +import { SearchTermsModel } from './_type/search' +import styles from './styles.module.scss' + +const cx = classNames.bind(styles) + +interface Props { + optionId: keyof SearchTermsModel +} + +const RangeContainer = ({ optionId }: Props) => { + const errOptions = useSearchingItemStore((state) => state.errOptions) + const { setRangeValue } = useSearchingItemStore((state) => state.actions) + + const handleRangeValue = (e: React.ChangeEvent, type: 'min' | 'max') => { + const value = Number(e.target.value) + setRangeValue(optionId, type, value) + } + + return ( +
+
+ handleRangeValue(e, 'min')} + /> + ~ + handleRangeValue(e, 'max')} + /> +
+ {errOptions?.includes(optionId) &&

최소 값은 최대 값보다 작아야합니다.

} +
+ ) +} + +export default RangeContainer diff --git a/app/(dashboard)/strategies/_ui/search-bar/search-bar-tap.tsx b/app/(dashboard)/strategies/_ui/search-bar/search-bar-tap.tsx new file mode 100644 index 00000000..47f06070 --- /dev/null +++ b/app/(dashboard)/strategies/_ui/search-bar/search-bar-tap.tsx @@ -0,0 +1,33 @@ +import classNames from 'classnames/bind' + +import { Button } from '@/shared/ui/button' + +import styles from './styles.module.scss' + +const cx = classNames.bind(styles) + +interface Props { + isMainTab: boolean + onChangeTab: (isMainTab: boolean) => void +} + +const SearchBarTap = ({ isMainTab, onChangeTab }: Props) => { + return ( +
+ + +
+ ) +} + +export default SearchBarTap diff --git a/app/(dashboard)/strategies/_ui/search-bar/styles.module.scss b/app/(dashboard)/strategies/_ui/search-bar/styles.module.scss new file mode 100644 index 00000000..fe548f66 --- /dev/null +++ b/app/(dashboard)/strategies/_ui/search-bar/styles.module.scss @@ -0,0 +1,185 @@ +@mixin item-align { + display: flex; + justify-content: space-between; +} + +.searchInput-wrapper { + background-color: $color-white; + padding: 20px; + border: 5px; + margin-bottom: 10px; +} + +.search-button-wrapper { + @include item-align; + margin-top: 20px; + .button { + height: 40px; + &.initialize { + width: 90px; + padding: 0; + } + &.searching { + width: 140px; + } + } +} + +.tab-container { + @include item-align; + margin-bottom: 20px; + .button { + border: 0; + width: 118px; + height: 48px; + &.main-on { + background-color: $color-orange-500; + color: $color-white; + } + &.main-off { + background-color: transparent; + color: $color-gray-700; + } + } +} + +.algorithm-button { + width: 100%; + padding: 10.8px 20px; + margin-bottom: 5px; + border-radius: 5px; + background-color: transparent; + box-shadow: 0px 1px 3px rgba(0, 0, 0, 0.25); + @include typo-b3; + color: $color-gray-600; + &:hover { + background-color: $color-orange-100; + } + &.active { + background-color: $color-orange-600; + color: $color-white; + } +} + +.accordion-button, +.panel-wrapper { + padding: 2px 0; + margin-bottom: 5px; + border-radius: 5px; + overflow: hidden; + button { + @include item-align; + width: 100%; + padding: 4px 20px; + align-items: center; + background-color: transparent; + } +} + +.accordion-button { + border: 1px solid $color-gray-200; + background-color: $color-gray-100; + button { + p { + @include typo-c1; + color: $color-gray-800; + span { + color: $color-orange-500; + margin-left: 4px; + } + } + svg { + width: 26px; + path { + fill: #171717; + } + } + } + &:hover { + border: 1px solid $color-orange-300; + } + &.active { + border: 1px solid $color-orange-500; + box-shadow: 0px 0px 2px rgba(255, 119, 82, 1); + } +} + +.panel-wrapper { + display: none; + box-shadow: 0px 1px 3px rgba(0, 0, 0, 0.25); + button { + p { + @include typo-c1; + color: $color-gray-600; + } + svg, + svg circle { + width: 24px; + .checked { + fill: $color-orange-600; + } + } + &.active { + svg, + svg circle { + fill: $color-orange-600; + } + } + &:hover { + background-color: $color-orange-100; + } + } + &.open { + display: block; + animation: accordionDown 0.3s cubic-bezier(0.2, 0.2, 0.2, 0.6); + } + &.close { + display: block; + animation: accordionUp 0.3s cubic-bezier(0.2, 0.2, 0.2, 0.6); + } + .range-container { + padding: 4px 20px; + p { + @include typo-c1; + color: $color-orange-800; + margin-top: 2px; + } + .range-wrapper { + @include item-align; + @include typo-c1; + align-items: center; + .range { + width: 80px; + height: 24px; + border-radius: 2px; + border: 1px solid $color-gray-300; + padding: 0 4px; + &::-webkit-outer-spin-button, + &::-webkit-inner-spin-button { + display: none; + } + } + span { + color: $color-gray-600; + } + } + } +} + +@keyframes accordionDown { + from { + height: 0; + } + to { + height: var(--panel-height); + } +} + +@keyframes accordionUp { + from { + height: var(--panel-height); + } + to { + height: 0; + } +} diff --git a/app/(dashboard)/strategies/page.tsx b/app/(dashboard)/strategies/page.tsx index d8d768c7..eebaff44 100644 --- a/app/(dashboard)/strategies/page.tsx +++ b/app/(dashboard)/strategies/page.tsx @@ -4,6 +4,7 @@ import classNames from 'classnames/bind' import Title from '@/shared/ui/title' +import SearchBarContainer from './_ui/search-bar' import SideContainer from './_ui/side-container' import StrategyList from './_ui/strategy-list' import styles from './page.module.scss' @@ -18,7 +19,7 @@ const StrategiesPage = () => { -

Search-Bar

+
) From ea785016a955decfbbb61f73ec047fad5bdc26f2 Mon Sep 17 00:00:00 2001 From: ssumanlife Date: Thu, 28 Nov 2024 22:36:50 +0900 Subject: [PATCH 2/5] =?UTF-8?q?feat:=20store=20reset=EC=95=A1=EC=85=98?= =?UTF-8?q?=EC=97=90=20errOptions=EC=B4=88=EA=B8=B0=ED=99=94=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80=20(#133)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../_ui/search-bar/_store/use-searching-item-store.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/(dashboard)/strategies/_ui/search-bar/_store/use-searching-item-store.ts b/app/(dashboard)/strategies/_ui/search-bar/_store/use-searching-item-store.ts index aa2f094a..a9353af0 100644 --- a/app/(dashboard)/strategies/_ui/search-bar/_store/use-searching-item-store.ts +++ b/app/(dashboard)/strategies/_ui/search-bar/_store/use-searching-item-store.ts @@ -65,7 +65,7 @@ const useSearchingItemStore = create((set, get) => ({ }, })), - resetState: () => set(() => ({ searchTerms: { ...initialState } })), + resetState: () => set(() => ({ searchTerms: { ...initialState }, errOptions: [] })), validateRangeValues: () => { const { searchTerms } = get() From c4ab54f947dfb8691c817115d3e902cfd29307a8 Mon Sep 17 00:00:00 2001 From: ssumanlife Date: Fri, 29 Nov 2024 10:03:55 +0900 Subject: [PATCH 3/5] =?UTF-8?q?fix:=20=EC=98=A4=ED=83=80=20=EC=88=98?= =?UTF-8?q?=EC=A0=95=20=EB=B0=8F=20client=EC=84=A0=EC=96=B8=EC=B6=94?= =?UTF-8?q?=EA=B0=80=20(#133)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../strategies/_ui/search-bar/accordion-button.tsx | 7 ++++--- .../strategies/_ui/search-bar/accordion-panel.tsx | 4 ++++ app/(dashboard)/strategies/_ui/search-bar/index.tsx | 4 ++-- .../strategies/_ui/search-bar/range-container.tsx | 2 ++ .../search-bar/{search-bar-tap.tsx => search-bar-tab.tsx} | 4 ++-- 5 files changed, 14 insertions(+), 7 deletions(-) rename app/(dashboard)/strategies/_ui/search-bar/{search-bar-tap.tsx => search-bar-tab.tsx} (88%) diff --git a/app/(dashboard)/strategies/_ui/search-bar/accordion-button.tsx b/app/(dashboard)/strategies/_ui/search-bar/accordion-button.tsx index 73a5db4d..78752772 100644 --- a/app/(dashboard)/strategies/_ui/search-bar/accordion-button.tsx +++ b/app/(dashboard)/strategies/_ui/search-bar/accordion-button.tsx @@ -22,11 +22,12 @@ const AccordionButton = ({ optionId, title, size }: Props) => { const { openIds, handleButtonIds } = useContext(AccordionContext) const searchTerms = useSearchingItemStore((state) => state.searchTerms) + const hasOpenId = openIds?.[optionId] const clickedValue = searchTerms[optionId] return ( -
-
) diff --git a/app/(dashboard)/strategies/_ui/search-bar/accordion-panel.tsx b/app/(dashboard)/strategies/_ui/search-bar/accordion-panel.tsx index 9d8fbb29..249f9bad 100644 --- a/app/(dashboard)/strategies/_ui/search-bar/accordion-panel.tsx +++ b/app/(dashboard)/strategies/_ui/search-bar/accordion-panel.tsx @@ -1,3 +1,5 @@ +'use client' + import { useContext, useEffect, useState } from 'react' import { CheckedCircleIcon } from '@/public/icons' @@ -9,6 +11,8 @@ import { AccordionContext } from './accordion-container' import RangeContainer from './range-container' import styles from './styles.module.scss' +/* eslint-disable react-hooks/exhaustive-deps */ + const cx = classNames.bind(styles) interface Props { diff --git a/app/(dashboard)/strategies/_ui/search-bar/index.tsx b/app/(dashboard)/strategies/_ui/search-bar/index.tsx index b3305a74..6633f981 100644 --- a/app/(dashboard)/strategies/_ui/search-bar/index.tsx +++ b/app/(dashboard)/strategies/_ui/search-bar/index.tsx @@ -11,7 +11,7 @@ import useSearchingItemStore from './_store/use-searching-item-store' import { SearchTermsModel } from './_type/search' import AccordionContainer from './accordion-container' import AlgorithmItem from './algorithm-item' -import SearchBarTap from './search-bar-tap' +import SearchBarTab from './search-bar-tab' import styles from './styles.module.scss' const cx = classNames.bind(styles) @@ -54,7 +54,7 @@ const SearchBarContainer = () => {
- + {isMainTab ? ACCORDION_MENU.map((menu) => ( void } -const SearchBarTap = ({ isMainTab, onChangeTab }: Props) => { +const SearchBarTab = ({ isMainTab, onChangeTab }: Props) => { return (