mirror of
https://github.com/Myzel394/kleckrelay-website.git
synced 2025-06-19 07:55:25 +02:00
added NoSearchResults.tsx to AliasesRoute.tsx
This commit is contained in:
parent
dcde72e4d2
commit
bd883431ff
@ -298,6 +298,10 @@
|
|||||||
"AliasTypeIndicator": {
|
"AliasTypeIndicator": {
|
||||||
"random": "This is a randomly generated alias",
|
"random": "This is a randomly generated alias",
|
||||||
"custom": "This is a custom-made alias"
|
"custom": "This is a custom-made alias"
|
||||||
|
},
|
||||||
|
"NoSearchResults": {
|
||||||
|
"title": "Nothing found",
|
||||||
|
"description": "We couldn't find anything for your search query. Try again with a different query."
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
25
src/components/NoSearchResults.tsx
Normal file
25
src/components/NoSearchResults.tsx
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
import {ReactElement} from "react"
|
||||||
|
import {useTranslation} from "react-i18next"
|
||||||
|
import {FaQuestion} from "react-icons/fa"
|
||||||
|
|
||||||
|
import {Grid, Typography} from "@mui/material"
|
||||||
|
|
||||||
|
export default function NoSearchResults(): ReactElement {
|
||||||
|
const {t} = useTranslation()
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Grid container spacing={4} direction="column" alignItems="center">
|
||||||
|
<Grid item>
|
||||||
|
<Typography variant="h6">{t("components.NoSearchResults.title")}</Typography>
|
||||||
|
</Grid>
|
||||||
|
<Grid item>
|
||||||
|
<FaQuestion size={40} />
|
||||||
|
</Grid>
|
||||||
|
<Grid item>
|
||||||
|
<Typography variant="body1">
|
||||||
|
{t("components.NoSearchResults.description")}
|
||||||
|
</Typography>
|
||||||
|
</Grid>
|
||||||
|
</Grid>
|
||||||
|
)
|
||||||
|
}
|
@ -34,5 +34,7 @@ export * from "./SimpleOverlayInformation"
|
|||||||
export {default as SimpleOverlayInformation} from "./SimpleOverlayInformation"
|
export {default as SimpleOverlayInformation} from "./SimpleOverlayInformation"
|
||||||
export * from "./SimpleInformationContainer"
|
export * from "./SimpleInformationContainer"
|
||||||
export {default as SimpleInformationContainer} from "./SimpleInformationContainer"
|
export {default as SimpleInformationContainer} from "./SimpleInformationContainer"
|
||||||
|
export * from "./NoSearchResults"
|
||||||
|
export {default as NoSearchResults} from "./NoSearchResults"
|
||||||
|
|
||||||
export * as SimplePageBuilder from "./simple-page-builder"
|
export * as SimplePageBuilder from "./simple-page-builder"
|
||||||
|
@ -12,3 +12,5 @@ export * from "./use-navigate-to-next"
|
|||||||
export {default as useNavigateToNext} from "./use-navigate-to-next"
|
export {default as useNavigateToNext} from "./use-navigate-to-next"
|
||||||
export * from "./use-error-success-snacks"
|
export * from "./use-error-success-snacks"
|
||||||
export {default as useErrorSuccessSnacks} from "./use-error-success-snacks"
|
export {default as useErrorSuccessSnacks} from "./use-error-success-snacks"
|
||||||
|
export * from "./use-is-any-input-focused"
|
||||||
|
export {default as useIsAnyInputFocused} from "./use-is-any-input-focused"
|
||||||
|
21
src/hooks/use-is-any-input-focused.ts
Normal file
21
src/hooks/use-is-any-input-focused.ts
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
import {useCallback, useState} from "react"
|
||||||
|
import {useEvent} from "react-use"
|
||||||
|
|
||||||
|
export default function useIsAnyInputFocused(): boolean {
|
||||||
|
const [isFocused, setIsFocused] = useState<boolean>(false)
|
||||||
|
|
||||||
|
const focusHandler = useCallback<EventListener>(event => {
|
||||||
|
const target = event.target as HTMLElement
|
||||||
|
if (target.tagName === "INPUT" || target.tagName === "TEXTAREA") {
|
||||||
|
setIsFocused(true)
|
||||||
|
}
|
||||||
|
}, [])
|
||||||
|
const blurHandler = useCallback<EventListener>(() => {
|
||||||
|
setIsFocused(false)
|
||||||
|
}, [])
|
||||||
|
|
||||||
|
useEvent("focus", focusHandler, window, {capture: true})
|
||||||
|
useEvent("blur", blurHandler, window, {capture: true})
|
||||||
|
|
||||||
|
return isFocused
|
||||||
|
}
|
@ -17,10 +17,9 @@ import {
|
|||||||
|
|
||||||
import {AliasTypeIndicator} from "~/components"
|
import {AliasTypeIndicator} from "~/components"
|
||||||
import {AliasList} from "~/server-types"
|
import {AliasList} from "~/server-types"
|
||||||
import {useUIState} from "~/hooks"
|
import {useIsAnyInputFocused, useUIState} from "~/hooks"
|
||||||
import {SUCCESS_SNACKBAR_SHOW_DURATION} from "~/constants/values"
|
import {SUCCESS_SNACKBAR_SHOW_DURATION} from "~/constants/values"
|
||||||
import CreateAliasButton from "~/route-widgets/AliasesRoute/CreateAliasButton"
|
import CreateAliasButton from "~/route-widgets/AliasesRoute/CreateAliasButton"
|
||||||
import EmptyStateScreen from "~/route-widgets/AliasesRoute/EmptyStateScreen"
|
|
||||||
|
|
||||||
export interface AliasesDetailsProps {
|
export interface AliasesDetailsProps {
|
||||||
aliases: AliasList[]
|
aliases: AliasList[]
|
||||||
@ -31,14 +30,16 @@ const getAddress = (alias: AliasList): string => `${alias.local}@${alias.domain}
|
|||||||
export default function AliasesDetails({aliases}: AliasesDetailsProps): ReactElement {
|
export default function AliasesDetails({aliases}: AliasesDetailsProps): ReactElement {
|
||||||
const {t} = useTranslation()
|
const {t} = useTranslation()
|
||||||
const {enqueueSnackbar} = useSnackbar()
|
const {enqueueSnackbar} = useSnackbar()
|
||||||
const [isInCopyAddressMode] = useKeyPress("Control")
|
const [isPressingControl] = useKeyPress("Control")
|
||||||
|
const isAnyInputFocused = useIsAnyInputFocused()
|
||||||
|
|
||||||
const [aliasesUIState, setAliasesUIState] = useUIState<AliasList[]>(aliases)
|
const [aliasesUIState, setAliasesUIState] = useUIState<AliasList[]>(aliases)
|
||||||
|
|
||||||
|
const isInCopyAddressMode = !isAnyInputFocused && isPressingControl
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Grid container spacing={4} direction="column">
|
<Grid container spacing={4} direction="column">
|
||||||
<Grid item>
|
<Grid item>
|
||||||
{aliasesUIState.length > 0 ? (
|
|
||||||
<List>
|
<List>
|
||||||
{aliasesUIState.map(alias => (
|
{aliasesUIState.map(alias => (
|
||||||
<ListItemButton
|
<ListItemButton
|
||||||
@ -76,9 +77,6 @@ export default function AliasesDetails({aliases}: AliasesDetailsProps): ReactEle
|
|||||||
</ListItemButton>
|
</ListItemButton>
|
||||||
))}
|
))}
|
||||||
</List>
|
</List>
|
||||||
) : (
|
|
||||||
<EmptyStateScreen />
|
|
||||||
)}
|
|
||||||
</Grid>
|
</Grid>
|
||||||
<Grid item>
|
<Grid item>
|
||||||
<CreateAliasButton
|
<CreateAliasButton
|
||||||
|
@ -7,7 +7,7 @@ import {useQuery} from "@tanstack/react-query"
|
|||||||
import {InputAdornment, TextField} from "@mui/material"
|
import {InputAdornment, TextField} from "@mui/material"
|
||||||
|
|
||||||
import {AliasList, PaginationResult} from "~/server-types"
|
import {AliasList, PaginationResult} from "~/server-types"
|
||||||
import {QueryResult, SimplePage} from "~/components"
|
import {NoSearchResults, QueryResult, SimplePage} from "~/components"
|
||||||
import AliasesDetails from "~/route-widgets/AliasesRoute/AliasesDetails"
|
import AliasesDetails from "~/route-widgets/AliasesRoute/AliasesDetails"
|
||||||
import EmptyStateScreen from "~/route-widgets/AliasesRoute/EmptyStateScreen"
|
import EmptyStateScreen from "~/route-widgets/AliasesRoute/EmptyStateScreen"
|
||||||
import getAliases from "~/apis/get-aliases"
|
import getAliases from "~/apis/get-aliases"
|
||||||
@ -18,6 +18,7 @@ export default function AliasesRoute(): ReactElement {
|
|||||||
const [searchValue, setSearchValue] = useState<string>("")
|
const [searchValue, setSearchValue] = useState<string>("")
|
||||||
const [queryValue, setQueryValue] = useState<string>("")
|
const [queryValue, setQueryValue] = useState<string>("")
|
||||||
const [, startTransition] = useTransition()
|
const [, startTransition] = useTransition()
|
||||||
|
const [showSearch, setShowSearch] = useState<boolean>(false)
|
||||||
|
|
||||||
const query = useQuery<PaginationResult<AliasList>, AxiosError>(
|
const query = useQuery<PaginationResult<AliasList>, AxiosError>(
|
||||||
["get_aliases", queryValue],
|
["get_aliases", queryValue],
|
||||||
@ -25,14 +26,22 @@ export default function AliasesRoute(): ReactElement {
|
|||||||
getAliases({
|
getAliases({
|
||||||
query: queryValue,
|
query: queryValue,
|
||||||
}),
|
}),
|
||||||
|
{
|
||||||
|
onSuccess: ({items}) => {
|
||||||
|
if (items.length) {
|
||||||
|
setShowSearch(true)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<SimplePage
|
<SimplePage
|
||||||
title={t("routes.AliasesRoute.title")}
|
title={t("routes.AliasesRoute.title")}
|
||||||
pageOptionsActions={
|
pageOptionsActions={
|
||||||
(query.data?.items?.length || 0) > 0 && (
|
showSearch && (
|
||||||
<TextField
|
<TextField
|
||||||
|
key="search"
|
||||||
value={searchValue}
|
value={searchValue}
|
||||||
onChange={event => {
|
onChange={event => {
|
||||||
setSearchValue(event.target.value)
|
setSearchValue(event.target.value)
|
||||||
@ -55,7 +64,19 @@ export default function AliasesRoute(): ReactElement {
|
|||||||
}
|
}
|
||||||
>
|
>
|
||||||
<QueryResult<PaginationResult<AliasList>, AxiosError> query={query}>
|
<QueryResult<PaginationResult<AliasList>, AxiosError> query={query}>
|
||||||
{result => <AliasesDetails aliases={result.items} />}
|
{result =>
|
||||||
|
(() => {
|
||||||
|
if (result.items.length === 0) {
|
||||||
|
if (searchValue === "") {
|
||||||
|
return <EmptyStateScreen />
|
||||||
|
} else {
|
||||||
|
return <NoSearchResults />
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return <AliasesDetails aliases={result.items} />
|
||||||
|
})()
|
||||||
|
}
|
||||||
</QueryResult>
|
</QueryResult>
|
||||||
</SimplePage>
|
</SimplePage>
|
||||||
)
|
)
|
||||||
|
Loading…
x
Reference in New Issue
Block a user