diff --git a/CHANGELOG.md b/CHANGELOG.md index e568308ee..40d30c136 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -51,6 +51,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Improvements +- [#917](https://github.com/alleslabs/celatone-frontend/pull/917) Update filter selection input to support multiple use case - [#826](https://github.com/alleslabs/celatone-frontend/pull/826) Apply dynamic component theme generation - [#795](https://github.com/alleslabs/celatone-frontend/pull/795) Rebranding - [#907](https://github.com/alleslabs/celatone-frontend/pull/907) Migrate mahalo and minitias from 2 to 3 diff --git a/src/lib/components/filter/FilterInput.tsx b/src/lib/components/filter/FilterInput.tsx index bf93f751f..30bc44b62 100644 --- a/src/lib/components/filter/FilterInput.tsx +++ b/src/lib/components/filter/FilterInput.tsx @@ -2,14 +2,11 @@ import { Flex, FormLabel, Input } from "@chakra-ui/react"; import type { Dispatch, ForwardedRef, RefObject, SetStateAction } from "react"; import { DropdownChevron } from "../DropdownChevron"; -import type { ProposalStatus, ProposalType } from "lib/types"; import { mergeRefs } from "lib/utils"; -type Result = ProposalType | ProposalStatus; - interface FilterInputProps { keyword: string; - result: Result[]; + result: string[]; isDropdown: boolean; chipContainerComponent: JSX.Element; inputRef: RefObject; @@ -49,7 +46,7 @@ export const FilterInput = ({ autoComplete="off" size="lg" minW="150px" - placeholder={result.length ? "" : placeholder} + placeholder={result.length > 0 ? "" : placeholder} ref={mergeRefs([inputRef, ref])} maxLength={36} style={{ border: 0, maxHeight: "54px" }} diff --git a/src/lib/pages/proposals/components/ProposalStatusFilter.tsx b/src/lib/pages/proposals/components/ProposalStatusFilter.tsx index 81ebde4a7..f1bbe581c 100644 --- a/src/lib/pages/proposals/components/ProposalStatusFilter.tsx +++ b/src/lib/pages/proposals/components/ProposalStatusFilter.tsx @@ -1,10 +1,10 @@ import type { InputProps } from "@chakra-ui/react"; import { Flex, FormControl, useOutsideClick } from "@chakra-ui/react"; import { matchSorter } from "match-sorter"; -import type { Dispatch, SetStateAction } from "react"; import { forwardRef, useMemo, useRef, useState } from "react"; import { AmpEvent, trackUseFilter } from "lib/amplitude"; +import { useTierConfig } from "lib/app-provider"; import { DropdownContainer, FilterChip, @@ -13,17 +13,17 @@ import { } from "lib/components/filter"; import { StatusChip } from "lib/components/table"; import { ProposalStatus } from "lib/types"; +import { toggleItem } from "lib/utils"; export interface ProposalStatusFilterProps extends InputProps { result: ProposalStatus[]; minW?: string; label?: string; placeholder?: string; - setResult: Dispatch>; + setResult: (option: ProposalStatus[]) => void; + isMulti: boolean; } -const OPTIONS = Object.values(ProposalStatus); - export const ProposalStatusFilter = forwardRef< HTMLInputElement, ProposalStatusFilterProps @@ -35,13 +35,24 @@ export const ProposalStatusFilter = forwardRef< setResult, placeholder, label, + isMulti, }: ProposalStatusFilterProps, ref ) => { + const tier = useTierConfig(); const [keyword, setKeyword] = useState(""); const [isDropdown, setIsDropdown] = useState(false); const inputRef = useRef(null); const boxRef = useRef(null); + const isFullTier = tier === "full"; + + const OPTIONS = isFullTier + ? Object.values(ProposalStatus) + : Object.values(ProposalStatus).filter( + (status) => + status !== ProposalStatus.DEPOSIT_FAILED && + status !== ProposalStatus.CANCELLED + ); const dropdownValue = useMemo( () => @@ -50,22 +61,28 @@ export const ProposalStatusFilter = forwardRef< threshold: matchSorter.rankings.CONTAINS, }) : OPTIONS, - [keyword] + [keyword, OPTIONS] ); const isOptionSelected = (option: ProposalStatus) => result.some((selectedOption) => selectedOption === option); const selectOption = (option: ProposalStatus) => { - if (inputRef.current) { - setKeyword(""); - } + setKeyword(""); + if (result.includes(option)) { trackUseFilter(AmpEvent.USE_FILTER_PROPOSALS_STATUS, result, "remove"); - setResult((prevState) => prevState.filter((value) => value !== option)); } else { trackUseFilter(AmpEvent.USE_FILTER_PROPOSALS_STATUS, result, "add"); - setResult((prevState) => [...prevState, option]); + } + + if (!isMulti) { + setIsDropdown(false); + + if (result[0] === option) setResult([]); + else setResult([option]); + } else { + setResult(toggleItem(result, option)); } }; @@ -92,7 +109,7 @@ export const ProposalStatusFilter = forwardRef< } - onSelect={() => selectOption(option)} + onSelect={() => setResult(toggleItem(result, option))} /> ))} diff --git a/src/lib/pages/proposals/components/ProposalsTableFull.tsx b/src/lib/pages/proposals/components/ProposalsTableFull.tsx index 6f593b290..a1ae0dd67 100644 --- a/src/lib/pages/proposals/components/ProposalsTableFull.tsx +++ b/src/lib/pages/proposals/components/ProposalsTableFull.tsx @@ -145,6 +145,7 @@ export const ProposalsTableFull = () => { result={statuses} setResult={setStatuses} placeholder="All Status" + isMulti /> { const [search, setSearch] = useState(""); const debouncedSearch = useDebounce(search); + const [status, setStatus] = useState([]); const { data: proposalsData, @@ -19,7 +23,7 @@ export const ProposalsTableLite = () => { hasNextPage, isLoading: isProposalsLoading, isFetchingNextPage, - } = useProposalsLcd(""); + } = useProposalsLcd(status[0]); const { data: proposalData, isLoading: isProposalDataLoading } = useProposalDataLcd(debouncedSearch); @@ -53,12 +57,13 @@ export const ProposalsTableLite = () => { /> - {/* */} + isMulti={false} + /> (array: T[], item: T): T[] => { + if (array.includes(item)) { + return array.filter((i) => i !== item); + } + + return [...array, item]; +}; diff --git a/src/lib/utils/index.ts b/src/lib/utils/index.ts index 263d966e9..7afd8c2dd 100644 --- a/src/lib/utils/index.ts +++ b/src/lib/utils/index.ts @@ -38,3 +38,4 @@ export * from "./txHash"; export * from "./validate"; export * from "./window"; export * from "./zod"; +export * from "./array";