From 0d965a4fd2bddeb42724e8a98ecca518717f494f Mon Sep 17 00:00:00 2001 From: Carson Date: Tue, 26 Nov 2024 12:13:34 -0800 Subject: [PATCH] Add filters to transactions page (#13534) GitOrigin-RevId: b38c948a5b1d252ca03943a2067caf9626e96cc3 --- .../DataManagerTable/CurrencyFilter.tsx | 162 ++++++++++++++++++ .../DataManagerTable/DataManagerTable.tsx | 40 ++++- .../components/DataManagerTable/filters.ts | 9 +- 3 files changed, 202 insertions(+), 9 deletions(-) create mode 100644 packages/ui/src/components/DataManagerTable/CurrencyFilter.tsx diff --git a/packages/ui/src/components/DataManagerTable/CurrencyFilter.tsx b/packages/ui/src/components/DataManagerTable/CurrencyFilter.tsx new file mode 100644 index 00000000..9ed765b8 --- /dev/null +++ b/packages/ui/src/components/DataManagerTable/CurrencyFilter.tsx @@ -0,0 +1,162 @@ +import styled from "@emotion/styled"; +import { type CurrencyAmountInputObj, CurrencyUnit } from "@lightsparkdev/core"; +import { useState } from "react"; +import { ButtonRow } from "../ButtonRow.js"; +import NumberInput from "../NumberInput.js"; +import { Filter, type FilterState } from "./Filter.js"; +import { FilterType } from "./filters.js"; + +enum FilterRangeType { + LessThan = "Less than", + Between = "Between", + MoreThan = "More than", +} + +export interface CurrencyFilterState extends FilterState { + type: FilterType.CURRENCY; + min_amount: CurrencyAmountInputObj | null; + max_amount: CurrencyAmountInputObj | null; +} + +export const isCurrencyFilterState = ( + state: FilterState, +): state is CurrencyFilterState => state.type === FilterType.CURRENCY; + +export const getDefaultCurrencyFilterState = () => ({ + type: FilterType.CURRENCY, + min_amount: null, + max_amount: null, + isApplied: false, +}); + +export const CurrencyFilter = ({ + updateFilterState, + state, + label, +}: { + updateFilterState: (state: CurrencyFilterState) => void; + state: CurrencyFilterState; + label: string; +}) => { + const [filterRangeType, setFilterRangeType] = useState( + FilterRangeType.LessThan, + ); + + const isMinDisplayed = filterRangeType !== FilterRangeType.LessThan; + const isMaxDisplayed = filterRangeType !== FilterRangeType.MoreThan; + + const handleMinChange = (value: string) => { + updateFilterState({ + ...state, + min_amount: value + ? { value: parseInt(value), unit: CurrencyUnit.SATOSHI } + : null, + isApplied: state.max_amount !== null || !!value, + }); + }; + + const handleMaxChange = (value: string) => { + updateFilterState({ + ...state, + max_amount: value + ? { value: parseInt(value), unit: CurrencyUnit.SATOSHI } + : null, + isApplied: state.min_amount !== null || !!value, + }); + }; + + const handleClick = (type: FilterRangeType) => { + setFilterRangeType(type); + + updateFilterState({ + ...state, + min_amount: null, + max_amount: null, + isApplied: false, + }); + }; + + return ( +
+ + handleClick(FilterRangeType.LessThan), + size: "ExtraSmall", + }, + { + text: "Between", + kind: + filterRangeType === FilterRangeType.Between + ? "primary" + : undefined, + onClick: () => handleClick(FilterRangeType.Between), + size: "ExtraSmall", + }, + { + text: "More than", + kind: + filterRangeType === FilterRangeType.MoreThan + ? "primary" + : undefined, + onClick: () => handleClick(FilterRangeType.MoreThan), + size: "ExtraSmall", + }, + ]} + /> + + {isMinDisplayed && ( + + )} + + {isMinDisplayed && isMaxDisplayed && } + {isMaxDisplayed && ( + + )} + + +
+ ); +}; + +const NumberButtonRowContainer = styled(ButtonRow)` + padding: 0px !important; +`; + +const InputContainer = styled.div` + display: flex; + flex-direction: row; + margin-top: 24px; + gap: 10px; + align-items: center; +`; + +const Divider = styled.div` + height: 4px; + width: 12px; + flex-shrink: 0; + border-radius: 999px; + background-color: ${({ theme }) => theme.c1Neutral}; +`; diff --git a/packages/ui/src/components/DataManagerTable/DataManagerTable.tsx b/packages/ui/src/components/DataManagerTable/DataManagerTable.tsx index 06ac7530..b9bc07ba 100644 --- a/packages/ui/src/components/DataManagerTable/DataManagerTable.tsx +++ b/packages/ui/src/components/DataManagerTable/DataManagerTable.tsx @@ -2,7 +2,7 @@ import styled from "@emotion/styled"; import { useEffect, useState } from "react"; import { type useClipboard } from "../../hooks/useClipboard.js"; -import { Breakpoints, bp, useBreakpoints } from "../../styles/breakpoints.js"; +import { bp, useBreakpoints } from "../../styles/breakpoints.js"; import { standardContentInset } from "../../styles/common.js"; import { Spacing } from "../../styles/tokens/spacing.js"; import { Button, StyledButton } from "../Button.js"; @@ -17,6 +17,11 @@ import { getDefaultBooleanFilterState, type BooleanFilterState, } from "./BooleanFilter.js"; +import { + CurrencyFilter, + getDefaultCurrencyFilterState, + type CurrencyFilterState, +} from "./CurrencyFilter.js"; import { DateFilter, getDefaultDateFilterState, @@ -29,6 +34,11 @@ import { type EnumFilterState, } from "./EnumFilter.js"; import { type FilterState } from "./Filter.js"; +import { + FilterType, + type Filter, + type StringFilter as StringFilterType, +} from "./filters.js"; import { IdFilter, getDefaultIdFilterState, @@ -42,11 +52,6 @@ import { isStringFilterState, type StringFilterState, } from "./StringFilter.js"; -import { - FilterType, - type Filter, - type StringFilter as StringFilterType, -} from "./filters.js"; interface FilterOptions< T extends Record, @@ -112,6 +117,8 @@ function getDefaultFilterState>( return getDefaultIdFilterState(filter.allowedEntities); case FilterType.BOOLEAN: return getDefaultBooleanFilterState(); + case FilterType.CURRENCY: + return getDefaultCurrencyFilterState(); default: throw new Error("Invalid filter type"); } @@ -165,7 +172,7 @@ export function DataManagerTable< props.filterOptions?.initialQueryVariables || ({} as QueryVariablesType), ); - const isSm = breakPoint.current(Breakpoints.sm); + const isSm = breakPoint.isSm(); useEffect(() => { setIsLoading(Boolean(props.loading)); @@ -333,7 +340,7 @@ export function DataManagerTable< updateFilterState(filter)(newFilterState); } else if (filterState.appliedValues?.length === 0) { // If there are no more applied values, remove the filter - updateFilterState(filter)(getDefaultStringFilterState()); + updateFilterState(filter)(getDefaultEnumFilterState()); filterState.isApplied = false; } } @@ -554,6 +561,23 @@ export function DataManagerTable< /> ); + case FilterType.CURRENCY: + return ( +
+ { + setFilterStates((prevState) => ({ + ...prevState, + [filter.accessorKey]: state, + })); + }} + label={filter.label} + state={ + filterStates[filter.accessorKey] as CurrencyFilterState + } + /> +
+ ); default: return null; } diff --git a/packages/ui/src/components/DataManagerTable/filters.ts b/packages/ui/src/components/DataManagerTable/filters.ts index 0f408199..a4f6c438 100644 --- a/packages/ui/src/components/DataManagerTable/filters.ts +++ b/packages/ui/src/components/DataManagerTable/filters.ts @@ -58,12 +58,18 @@ export interface BooleanFilter> queryVariable: string; } +export interface CurrencyFilter> + extends FilterBase { + type: FilterType.CURRENCY; +} + export type Filter> = | DateFilter | EnumFilter | StringFilter | IdFilter - | BooleanFilter; + | BooleanFilter + | CurrencyFilter; export enum FilterType { DATE = "date", @@ -71,5 +77,6 @@ export enum FilterType { STRING = "string", ID = "id", NUMBER = "number", + CURRENCY = "currency", BOOLEAN = "boolean", }