diff --git a/public/locales/en-US/translation.json b/public/locales/en-US/translation.json index 871fcf5..c4f3fbb 100644 --- a/public/locales/en-US/translation.json +++ b/public/locales/en-US/translation.json @@ -130,6 +130,14 @@ "search": { "label": "Search", "placeholder": "Search for names" + }, + "searchFilter": { + "active": "Active", + "inactive": "Inactive" + }, + "typeFilter": { + "custom": "Custom made", + "random": "Randomly generated" } }, "actions": { diff --git a/src/apis/get-aliases.ts b/src/apis/get-aliases.ts index 64b907a..c6dd6b8 100644 --- a/src/apis/get-aliases.ts +++ b/src/apis/get-aliases.ts @@ -1,14 +1,18 @@ -import {AliasList, GetPageData, PaginationResult} from "~/server-types" +import {AliasList, AliasType, GetPageData, PaginationResult} from "~/server-types" import {client} from "~/constants/axios-client" export interface GetAliasesData extends GetPageData { query?: string + active?: boolean + type?: AliasType } export default async function getAliases({ query, size, page, + active, + type, }: GetAliasesData): Promise> { const {data} = await client.get(`${import.meta.env.VITE_SERVER_BASE_URL}/v1/alias`, { withCredentials: true, @@ -16,6 +20,8 @@ export default async function getAliases({ query, size, page, + active, + aliasType: type, }, }) diff --git a/src/components/AliasTypeIndicator.tsx b/src/components/AliasTypeIndicator.tsx index 3b7505e..ebcd0a6 100644 --- a/src/components/AliasTypeIndicator.tsx +++ b/src/components/AliasTypeIndicator.tsx @@ -11,7 +11,7 @@ export interface AliasTypeIndicatorProps { type: AliasType } -const ALIAS_TYPE_ICON_MAP: Record = { +export const ALIAS_TYPE_ICON_MAP: Record = { [AliasType.RANDOM]: , [AliasType.CUSTOM]: , } diff --git a/src/routes/AliasesRoute.tsx b/src/routes/AliasesRoute.tsx index 985f9ee..d1faa4d 100644 --- a/src/routes/AliasesRoute.tsx +++ b/src/routes/AliasesRoute.tsx @@ -5,16 +5,45 @@ import {useTranslation} from "react-i18next" import {useCopyToClipboard, useKeyPress, useUpdateEffect} from "react-use" import {useQuery} from "@tanstack/react-query" -import {Alert, Grid, InputAdornment, List, Snackbar, TextField} from "@mui/material" +import { + Alert, + Chip, + Divider, + Grid, + InputAdornment, + List, + Snackbar, + TextField, + ToggleButton, +} from "@mui/material" -import {AliasList, PaginationResult} from "~/server-types" -import {ErrorSnack, NoSearchResults, QueryResult, SimplePage, SuccessSnack} from "~/components" +import {AliasList, AliasType, PaginationResult} from "~/server-types" +import { + ALIAS_TYPE_ICON_MAP, + ErrorSnack, + NoSearchResults, + QueryResult, + SimplePage, + SuccessSnack, +} from "~/components" import {useIsAnyInputFocused} from "~/hooks" import {CreateAliasButton} from "~/route-widgets/AliasesRoute/CreateAliasButton" import AliasesListItem from "~/route-widgets/AliasesRoute/AliasesListItem" import EmptyStateScreen from "~/route-widgets/AliasesRoute/EmptyStateScreen" import getAliases from "~/apis/get-aliases" +enum SearchFilter { + All = "all", + Active = "active", + Inactive = "inactive", +} + +enum TypeFilter { + All = "all", + Custom = "custom", + Random = "random", +} + export default function AliasesRoute(): ReactElement { const {t} = useTranslation() @@ -22,6 +51,8 @@ export default function AliasesRoute(): ReactElement { const [queryValue, setQueryValue] = useState("") const [, startTransition] = useTransition() const [showSearch, setShowSearch] = useState(false) + const [searchFilter, setSearchFilter] = useState(SearchFilter.All) + const [typeFilter, setTypeFilter] = useState(TypeFilter.All) const [{value, error}, copyToClipboard] = useCopyToClipboard() const [isPressingControl] = useKeyPress("Shift") @@ -30,15 +61,37 @@ export default function AliasesRoute(): ReactElement { const isInCopyAddressMode = !isAnyInputFocused && !lockDisabledCopyMode && isPressingControl const query = useQuery, AxiosError>( - ["get_aliases", queryValue], + ["get_aliases", queryValue, searchFilter, typeFilter], () => getAliases({ query: queryValue, + active: (() => { + switch (searchFilter) { + case SearchFilter.All: + return undefined + case SearchFilter.Active: + return true + case SearchFilter.Inactive: + return false + } + })(), + type: (() => { + switch (typeFilter) { + case TypeFilter.All: + return undefined + case TypeFilter.Custom: + return AliasType.CUSTOM + case TypeFilter.Random: + return AliasType.RANDOM + } + })(), }), { onSuccess: ({items}) => { if (items.length) { setShowSearch(true) + setSearchFilter(SearchFilter.All) + setTypeFilter(TypeFilter.All) } }, }, @@ -55,26 +108,131 @@ export default function AliasesRoute(): ReactElement { title={t("routes.AliasesRoute.title")} pageOptionsActions={ showSearch && ( - { - setSearchValue(event.target.value) - startTransition(() => { - setQueryValue(event.target.value) - }) - }} - label={t("routes.AliasesRoute.pageActions.search.label")} - placeholder={t("routes.AliasesRoute.pageActions.search.placeholder")} - id="search" - InputProps={{ - startAdornment: ( - - - - ), - }} - /> + + + { + setSearchValue(event.target.value) + startTransition(() => { + setQueryValue(event.target.value) + }) + }} + label={t("routes.AliasesRoute.pageActions.search.label")} + placeholder={t( + "routes.AliasesRoute.pageActions.search.placeholder", + )} + id="search" + InputProps={{ + startAdornment: ( + + + + ), + }} + /> + + + + + + + + setSearchFilter(value => { + if (value === SearchFilter.Active) { + return SearchFilter.All + } + + return SearchFilter.Active + }) + } + /> + + + + setSearchFilter(value => { + if (value === SearchFilter.Inactive) { + return SearchFilter.All + } + + return SearchFilter.Inactive + }) + } + /> + + + + + + + + setTypeFilter(value => { + if (value === TypeFilter.Custom) { + return TypeFilter.All + } + + return TypeFilter.Custom + }) + } + /> + + + + setTypeFilter(value => { + if (value === TypeFilter.Random) { + return TypeFilter.All + } + + return TypeFilter.Random + }) + } + /> + + + + + + ) } > @@ -85,7 +243,11 @@ export default function AliasesRoute(): ReactElement { {(() => { if (aliases.length === 0) { - if (searchValue === "") { + if ( + searchValue === "" && + searchFilter === SearchFilter.All && + typeFilter === TypeFilter.All + ) { return } else { return