Skip to content

Commit

Permalink
Fix frontend price filter (#87)
Browse files Browse the repository at this point in the history
QA surfaced several problems with the price filter components:

- [x] Slider input is not usable with ranges > ~1000, since the steps
are too big to find specific numbers
> Fix: removed slider input component, added todo with potential
solutions, and adjusted wrapper ui
- [x] Min/max input components missing `value` property
> Fix: added value property
- [x] Price metrics affected by price filters
> Fix: calculate price metrics using full market instead of filtered
- [x] Min/max input used uIST, while all prices show IST
> Fix: refactor filters to use IST
- [x] Clear all does not clear price filter
> Fix: refactor reset mechanism to work with clear all feature

---------

Co-authored-by: Pandelis Symeonidis <[email protected]>
  • Loading branch information
carlos-kryha and Pandelis Symeonidis authored Nov 10, 2023
1 parent 1cb23f8 commit 977d822
Show file tree
Hide file tree
Showing 9 changed files with 56 additions and 40 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,14 @@ export const AssetCharacterFilters: FC<Props> = ({ section }) => {
const { title, origin, sort, reset, setOrigin, setTitle, setCharacterPrice, setSort, setColors, onReset } = useFilters();
const [filterId, setFilterId] = useState("");
const [prices, fetched] = useGetCharacterMarketPrices();

const openFilter = (id: string) => {
setFilterId(id !== filterId ? id : "");
};

const handlePriceFilter = (range: {min: number, max: number}) => {
setCharacterPrice(range);
}
return (
<>
<AssetFilterWrapper>
Expand All @@ -45,7 +49,7 @@ export const AssetCharacterFilters: FC<Props> = ({ section }) => {
</Filters>
{section === SECTION.SHOP && (
<Filters label={text.filters.price} openFilter={openFilter} id={filterId}>
{fetched && <PriceRangeSlider prices={prices} setPrice={setCharacterPrice} />}
{fetched && <PriceRangeSlider prices={prices} setPrice={handlePriceFilter} reset={reset} />}
</Filters>
)}
<Filters label={text.filters.color} openFilter={openFilter} id={filterId}>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ export const AssetItemFilters: FC<Props> = ({ section }) => {
</Filters>
{section === SECTION.SHOP && (
<Filters label={text.filters.price} openFilter={openFilter} id={filterId}>
{fetched && <PriceRangeSlider prices={prices} setPrice={setItemPrice} />}
{fetched && <PriceRangeSlider prices={prices} setPrice={setItemPrice} reset={reset}/>}
</Filters>
)}
<Filters label={text.filters.color} openFilter={openFilter} id={filterId}>
Expand Down
2 changes: 1 addition & 1 deletion frontend/src/components/input-fields/styles.ts
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,7 @@ export const ButtonContainer = styled.div`
export const RangeContainer = styled.div`
display: flex;
flex-direction: column;
margin-bottom: 40px;
margin-bottom: 0px;
width: 100%;
${SecondaryButton} {
width: 40px;
Expand Down
45 changes: 22 additions & 23 deletions frontend/src/components/price-range-slider/price-range-slider.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import React, { ChangeEvent, FC, useState } from "react";
import { Handles, Slider, Tracks } from "react-compound-slider";
import React, { ChangeEvent, FC, useEffect, useState } from "react";
import { useViewport } from "../../hooks";
import {
ButtonContainer,
Expand All @@ -9,8 +8,6 @@ import {
MaxInput,
MinInput,
RangeContainer,
SliderContainer,
SliderTrack,
TextLabel,
} from "../input-fields/styles";
import { BoldLabel, ButtonText, SecondaryButton } from "../atoms";
Expand All @@ -21,12 +18,12 @@ import { color } from "../../design";
interface Props {
prices: number[];
setPrice: (value: { min: number; max: number }) => void;
reset: boolean;
}
export const PriceRangeSlider: FC<Props> = ({ prices, setPrice }) => {
const range = [Math.min(...prices), Math.max(...prices)];
export const PriceRangeSlider: FC<Props> = ({ prices, setPrice, reset }) => {
const range = [uISTToIST(Math.min(...prices)), uISTToIST(Math.max(...prices))];
const { height } = useViewport();
const [domain] = useState(range);
const [values, setValues] = useState<readonly number[]>(range);
const [, setUpdate] = useState<readonly number[]>(range);
const [inputValues, setInputValues] = useState<readonly number[]>(domain);

Expand All @@ -36,55 +33,57 @@ export const PriceRangeSlider: FC<Props> = ({ prices, setPrice }) => {
};

const onValuesChange = (newValues: readonly number[]) => {
const min = newValues[0];
const max = newValues[1];
setValues(newValues);
const min = (newValues[0]);
const max = (newValues[1]);
setPrice({ min, max });
};

const onStartPriceChange = (event: ChangeEvent<HTMLInputElement>) => {
const value = Number(event.target.value);
const newState = [value, inputValues[1]];
setInputValues(newState);
if (value && value >= domain[0]) {
setValues(newState);
}
onUpdate(newState);
onValuesChange(newState);
};

const onMaxPriceChange = (event: ChangeEvent<HTMLInputElement>) => {
const value = Number(event.target.value);
const newState = [inputValues[0], value];
setInputValues(newState);
if (value && value <= domain[1] && value >= values[0]) {
setValues(newState);
}
onUpdate(newState);
onValuesChange(newState);
};

const onReset = () => {
setValues(domain);
setUpdate(domain);
setInputValues(domain);
setPrice({ min: domain[0], max: domain[1] });
};

useEffect(()=> onReset(), []);
useEffect(()=>{
if(reset) onReset();
}, [reset]);

return (
<ColorBox height={height}>
<RangeContainer>
<InputWrapper>
<InputContainer>
<BoldLabel>{text.store.min}</BoldLabel>
<TextLabel>
<MinInput type="number" placeholder={`${uISTToIST(inputValues[0])}`} onChange={onStartPriceChange} />
<MinInput type="number" placeholder={`${uISTToIST(inputValues[0])}`} value={inputValues[0]} onChange={onStartPriceChange} />
</TextLabel>
</InputContainer>
<InputContainer>
<BoldLabel>{text.store.max}</BoldLabel>
<TextLabel>
<MaxInput type="number" placeholder={`${uISTToIST(inputValues[1])}`} onChange={onMaxPriceChange} />
<MaxInput type="number" placeholder={`${uISTToIST(inputValues[1])}`} value={inputValues[1]} onChange={onMaxPriceChange} />
</TextLabel>
</InputContainer>
</InputWrapper>
<SliderContainer>
{/* Disaled until we decide how to handle large ranges, consider:
1. only show slider when the range is lower than 200 IST, or whatever max range we deem functional
2. increase the steps exponentially to fit larger ranges
<SliderContainer>
<Slider
mode={2}
step={10000}
Expand Down Expand Up @@ -119,7 +118,7 @@ export const PriceRangeSlider: FC<Props> = ({ prices, setPrice }) => {
)}
</Tracks>
</Slider>
</SliderContainer>
</SliderContainer> */}
</RangeContainer>
<ButtonContainer>
<SecondaryButton onClick={onReset}>
Expand Down
2 changes: 1 addition & 1 deletion frontend/src/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { Category, Rarity } from "./interfaces";
export const GO_BACK = -1 as const;
export const BASE_URL = import.meta.env.VITE_BASE_URL || "http://localhost:5001";
export const MIN_PRICE = 0 as const;
export const MAX_PRICE = 1000000000000 as const;
export const MAX_PRICE = 100000 as const;
export const INFORMATION_STEP = 0 as const;
export const WALLET_INTERACTION_STEP = 1 as const;
export const CONFIRMATION_STEP = 2 as const;
Expand Down
5 changes: 3 additions & 2 deletions frontend/src/context/filter-context.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import React, { createContext, FC, useMemo, useState } from "react";
import { useAndRequireContext } from "../hooks";
import { Category, Origin, Rarity, Title } from "../interfaces";
import { useGetCharacterMarketPrices, useGetItemMarketPrices } from "../service";
import { uISTToIST } from "../util";

interface Context {
title: Title[];
Expand Down Expand Up @@ -46,11 +47,11 @@ export const FiltersContextProvider: FC<Props> = ({ children }) => {
const [itemPrice, setItemPrice] = useState<{
min: number;
max: number;
}>({ min: Math.min(...pricesOfItems), max: Math.max(...pricesOfItems) });
}>({ min: uISTToIST(Math.min(...pricesOfItems)), max: uISTToIST(Math.max(...pricesOfItems)) });
const [characterPrice, setCharacterPrice] = useState<{
min: number;
max: number;
}>({ min: Math.min(...pricesOfCharacters), max: Math.max(...pricesOfCharacters) });
}>({ min: uISTToIST(Math.min(...pricesOfCharacters)), max: uISTToIST(Math.max(...pricesOfCharacters)) });

const [rarity, setRarity] = useState<Rarity[]>([]);
const [equippedTo, setEquippedTo] = useState<string>("");
Expand Down
8 changes: 5 additions & 3 deletions frontend/src/pages/shop/characters-shop.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,13 @@ import { AssetFilterCount, AssetHeader, AssetHeaderContainer } from "../../compo
import { color } from "../../design";
import { findAverageValue, findMinimumValue, toTwoDecimals, uISTToIST } from "../../util";
import { MarketplaceMetrics } from "../../components/marketplace-metrics/marketplace-metrics";
import { useCharacterMarketState } from "../../context/character-shop-context";

export const CharactersShop: FC = () => {
const [selectedId, setSelectedId] = useState<string>("");

const [characters, isLoading] = useGetCharactersInShop();
const {characters: allMarketCharacters} = useCharacterMarketState();
const metrics = useGetCharacterMarketMetrics();
const [character] = useGetCharacterInShopById(selectedId);
const assetsCount = characters.length;
Expand All @@ -27,9 +29,9 @@ export const CharactersShop: FC = () => {
let charAverage = 0;
let charMinimum = 0;

if (characters.length != 0) {
charMinimum = findMinimumValue(characters.map((x) => uISTToIST(Number(x.sell.price))));
charAverage = findAverageValue(characters.map((x) => uISTToIST(Number(x.sell.price))));
if (allMarketCharacters.length != 0) {
charMinimum = findMinimumValue(allMarketCharacters.map((x) => uISTToIST(Number(x.sell.price))));
charAverage = findAverageValue(allMarketCharacters.map((x) => uISTToIST(Number(x.sell.price))));
}

metricsData = [
Expand Down
6 changes: 4 additions & 2 deletions frontend/src/pages/shop/items-shop.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,12 @@ import { color } from "../../design";
import { MarketplaceMetrics } from "../../components/marketplace-metrics/marketplace-metrics";
import { findAverageValue, findMinimumValue, toTwoDecimals, uISTToIST } from "../../util";
import { ItemInMarket } from "../../interfaces";
import { useItemMarketState } from "../../context/item-shop-context";

export const ItemsShop: FC = () => {
const [selectedId, setSelectedId] = useState<string>("");
const [items, fetched] = useGetItemsInShop();
const { items: allMarketItems } = useItemMarketState();
const metrics = useGetItemMarketMetrics();
const [item] = useGetItemInShopById(selectedId);

Expand All @@ -41,8 +43,8 @@ export const ItemsShop: FC = () => {
let itemMinimum = 0;

if (filteredItems.length != 0) {
itemMinimum = findMinimumValue(filteredItems.map((x) => uISTToIST(Number(x.sell.price))));
itemAverage = findAverageValue(filteredItems.map((x) => uISTToIST(Number(x.sell.price))));
itemMinimum = findMinimumValue(allMarketItems.map((x) => uISTToIST(Number(x.sell.price))));
itemAverage = findAverageValue(allMarketItems.map((x) => uISTToIST(Number(x.sell.price))));
}

return [
Expand Down
20 changes: 14 additions & 6 deletions frontend/src/util/filter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { CharacterInMarket, ExtendedCharacter, Item, ItemInMarket, Origin, Title
import { sortCharacters, sortCharactersMarket, sortItems, sortItemsMarket } from "./sort";
import { getRarityString } from "../service";
import { useFilters } from "../context/filter-context";
import { ISTTouIST } from "./math";

export interface OfferFilters {
description?: string;
Expand Down Expand Up @@ -36,15 +37,18 @@ export const useFilterItems = (items: Item[]): Item[] => {
export const useFilterItemsInShop = (items: ItemInMarket[]): ItemInMarket[] => {
const { origin, categories, rarity, itemPrice, colors, sort } = useFilters();
if (items.length === 0) return [];

const priceFilterRange = {
max: ISTTouIST(itemPrice.max),
min: ISTTouIST(itemPrice.min)
}
const filteredOrigins = origin.length > 0 ? items.filter((item) => origin.includes(<Origin>item.item.origin.toLowerCase())) : items;
const filteredCategories = categories.length > 0 ? items.filter((item) => categories.includes(item.item.category)) : items;
const filteredRarity = rarity.length > 0 ? items.filter((item) => rarity.includes(getRarityString(item.item.rarity))) : items;
const filteredColors = colors ? items.filter((item) => item.item.colors.includes(colors)) : items;
const filteredPrice = itemPrice
? items.filter(({ sell }) => {
const priceValue = Number(sell.price + sell.royalty + sell.platformFee);
return priceValue >= itemPrice.min && priceValue <= itemPrice.max;
return priceValue >= priceFilterRange.min && priceValue <= priceFilterRange.max;
})
: items;

Expand Down Expand Up @@ -77,13 +81,17 @@ export const useFilterCharactersMarket = (characters: CharacterInMarket[]): Char
const { origin, title, sort, characterPrice } = useFilters();
if (characters.length === 0) return [];

const priceFilterRange = {
max: ISTTouIST(characterPrice.max),
min: ISTTouIST(characterPrice.min)
}
const filteredOrigins =
origin.length > 0 ? characters.filter((character) => origin.includes(<Origin>character.character.origin.toLowerCase())) : characters;
origin.length > 0 ? characters.filter((character) => origin.includes(<Origin>character.character.origin.toLowerCase())) : characters;
const filteredTitles = title.length > 0 ? characters.filter((character) => title.includes(character.character.title)) : characters;
const filteredPrice = characterPrice
? characters.filter(({ sell }) => {
const priceValue = Number(sell.price + sell.royalty + sell.platformFee);
return priceValue >= characterPrice.min && priceValue <= characterPrice.max;
? characters.filter(({ sell }) => {
const priceValue = Number(sell.price + sell.royalty + sell.platformFee);
return priceValue >= priceFilterRange.min && priceValue <= priceFilterRange.max;
})
: characters;

Expand Down

0 comments on commit 977d822

Please sign in to comment.