diff --git a/src/config/lexics/indexLexics.tsx b/src/config/lexics/indexLexics.tsx
index 9990cb70..a4f5ba24 100644
--- a/src/config/lexics/indexLexics.tsx
+++ b/src/config/lexics/indexLexics.tsx
@@ -13,7 +13,10 @@ export const indexLexics = {
game_finished: 13026,
game_time: 13029,
gender_female: 9648,
+ gender_female_long: 13374,
gender_male: 9647,
+ gender_male_long: 13373,
+ go_to_extended_search_page: 13375,
goals: 13030,
hide_score: 12982,
highlights: 13033,
@@ -30,6 +33,7 @@ export const indexLexics = {
player: 630,
players_video: 13032,
round_highilights: 13050,
+ search_results: 9014,
select_language: 1005,
sport: 12993,
team: 658,
diff --git a/src/config/pages.tsx b/src/config/pages.tsx
index affdcc95..aaa94cd9 100644
--- a/src/config/pages.tsx
+++ b/src/config/pages.tsx
@@ -1,4 +1,5 @@
export const PAGES = {
+ extendedSearch: '/search',
home: '/',
login: '/login',
match: '/matches',
diff --git a/src/features/App/AuthenticatedApp.tsx b/src/features/App/AuthenticatedApp.tsx
index 0328370f..8ee87ba8 100644
--- a/src/features/App/AuthenticatedApp.tsx
+++ b/src/features/App/AuthenticatedApp.tsx
@@ -1,4 +1,4 @@
-import React, { Fragment } from 'react'
+import React from 'react'
import {
Route,
Redirect,
@@ -15,88 +15,60 @@ import { TeamPage } from 'features/TeamPage'
import { MatchPage } from 'features/MatchPage'
import { PlayerPage } from 'features/PlayerPage'
import { TournamentPage } from 'features/TournamentPage'
+import { ExtendedSearchPage } from 'features/ExtendedSearchPage'
import { LanguageSelect } from 'features/LanguageSelect'
import { UserAccount } from 'features/UserAccount'
import { ScoreStore, ToggleScore } from 'features/ToggleScore'
-import { Header } from 'features/Header'
-import { SportFilterWrapper } from 'features/Header/styled'
import { MainWrapper } from 'features/MainWrapper'
-import {
- HeaderFiltersStore,
- TournamentFilter,
- SportTypeFilter,
-} from 'features/HeaderFilters'
import { UserFavorites } from 'features/UserFavorites'
import { UserFavoritesStore } from 'features/UserFavorites/store'
import { useMediaQuery } from 'features/MediaQuery'
-import { HeaderMobile } from 'features/HeaderMobile'
import { FormStore } from 'features/FormStore'
export const AuthenticatedApp = () => {
useLexicsConfig(indexLexics)
const isMobile = useMediaQuery({ query: devices.tablet })
const isUserAccountPage = useRouteMatch(PAGES.useraccount)?.isExact || false
+ const isExtendedSearchPage = useRouteMatch(PAGES.extendedSearch)?.isExact || false
return (
{
isMobile || isUserAccountPage
? null
- : (
-
-
-
-
- )
+ :
}
+ {isExtendedSearchPage ? null : }
-
-
-
- {
- isMobile
- ?
- : (
-
-
-
-
- )
- }
-
-
-
+
+
+ {!isMobile && }
+
+
+
-
-
-
+
+
+
-
-
-
-
-
-
-
-
-
- {
- isMobile
- ? (
-
-
-
-
- )
- : null
- }
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/features/HeaderFilters/components/MatchStatusFilter/styled.tsx b/src/features/Common/RadioButtons/index.tsx
similarity index 87%
rename from src/features/HeaderFilters/components/MatchStatusFilter/styled.tsx
rename to src/features/Common/RadioButtons/index.tsx
index e90421cc..628fa33a 100644
--- a/src/features/HeaderFilters/components/MatchStatusFilter/styled.tsx
+++ b/src/features/Common/RadioButtons/index.tsx
@@ -1,4 +1,4 @@
-import styled from 'styled-components/macro'
+import styled, { css } from 'styled-components/macro'
import { devices } from 'config/devices'
@@ -14,6 +14,7 @@ export const RadioButtonGroup = styled.div.attrs({
type RadioButtonProps = {
buttonWidth?: number,
selected?: boolean,
+ upperCase?: boolean,
}
export const RadioButton = styled.button.attrs(({ selected }: RadioButtonProps) => ({
@@ -29,7 +30,7 @@ export const RadioButton = styled.button.attrs(({ selected }: RadioButtonProps)
${({ selected }) => (
selected
- ? `
+ ? css`
background-color: #666666;
color: #ffffff;
@@ -43,12 +44,13 @@ export const RadioButton = styled.button.attrs(({ selected }: RadioButtonProps)
}
:last-child {
background: transparent;
+ color: #ffffff;
border: 1px solid #fff;
}
-
+
}
`
- : `
+ : css`
background-color: #3F3F3F;
color: #999999;
@@ -58,9 +60,9 @@ export const RadioButton = styled.button.attrs(({ selected }: RadioButtonProps)
`
)}
- :first-child {
- text-transform: uppercase;
- }
+ ${({ upperCase }) => (
+ upperCase ? 'text-transform: uppercase;' : ''
+ )}
:not(:last-child) {
border-right: 1px solid #222222;
diff --git a/src/features/Common/index.tsx b/src/features/Common/index.tsx
index 6e0bea4f..8e64ad19 100644
--- a/src/features/Common/index.tsx
+++ b/src/features/Common/index.tsx
@@ -9,3 +9,4 @@ export * from './StarIcon'
export * from './customScrollbar'
export * from './customStyles'
export * from './Image'
+export * from './RadioButtons'
diff --git a/src/features/ExtendedSearchPage/components/DesktopHeader/index.tsx b/src/features/ExtendedSearchPage/components/DesktopHeader/index.tsx
new file mode 100644
index 00000000..49ac8fbb
--- /dev/null
+++ b/src/features/ExtendedSearchPage/components/DesktopHeader/index.tsx
@@ -0,0 +1,23 @@
+import React from 'react'
+
+import { PAGES } from 'config'
+
+import { Menu } from 'features/Menu'
+import {
+ Wrapper,
+ MenuWrapper,
+ HomeButtonLink,
+} from 'features/ProfileHeader/styled'
+
+import { Filters } from '../Filters'
+
+export const DesktopHeader = () => (
+
+
+
+
+
+
+
+
+)
diff --git a/src/features/ExtendedSearchPage/components/Filters/index.tsx b/src/features/ExtendedSearchPage/components/Filters/index.tsx
new file mode 100644
index 00000000..747093d7
--- /dev/null
+++ b/src/features/ExtendedSearchPage/components/Filters/index.tsx
@@ -0,0 +1,29 @@
+import React, { Fragment } from 'react'
+
+import { SportTypeFilter } from '../SportTypeFilter'
+import { SearchInput } from '../SearchInput'
+import { GenderFilter } from '../GenderFilter'
+import { ProfileFilter } from '../ProfileFilter'
+
+import {
+ SportFilterWrapper,
+ FilterWrapper,
+} from './styled'
+
+export const Filters = () => (
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+)
diff --git a/src/features/ExtendedSearchPage/components/Filters/styled.tsx b/src/features/ExtendedSearchPage/components/Filters/styled.tsx
new file mode 100644
index 00000000..932d0b8d
--- /dev/null
+++ b/src/features/ExtendedSearchPage/components/Filters/styled.tsx
@@ -0,0 +1,43 @@
+import styled from 'styled-components/macro'
+
+import { devices } from 'config'
+
+export const BaseWrapper = styled.div`
+ position: relative;
+ height: 48px;
+ margin-right: 16px;
+ display: flex;
+`
+
+export const SportFilterWrapper = styled(BaseWrapper)`
+ width: 173px;
+
+ @media ${devices.tablet} {
+ width: auto;
+ margin: 20px 14px 0 14px;
+ justify-content: center;
+ }
+`
+
+export const FilterWrapper = styled(BaseWrapper)`
+ width: auto;
+
+ @media ${devices.tablet} {
+ height: 30px;
+ margin: 20px 20vw 0 20vw;
+ justify-content: center;
+ }
+`
+
+export const SearchInput = styled.input`
+ width: 100%;
+ padding-left: 20px;
+ padding-right: 20px;
+ font-weight: bold;
+ font-size: 18px;
+ background-color: #3F3F3F;
+ color: #fff;
+ border: transparent;
+ border-color: transparent;
+ outline: none;
+`
diff --git a/src/features/ExtendedSearchPage/components/GenderFilter/index.tsx b/src/features/ExtendedSearchPage/components/GenderFilter/index.tsx
new file mode 100644
index 00000000..deadc86e
--- /dev/null
+++ b/src/features/ExtendedSearchPage/components/GenderFilter/index.tsx
@@ -0,0 +1,36 @@
+import React from 'react'
+
+import { Gender } from 'requests'
+
+import { T9n } from 'features/T9n'
+import {
+ RadioButtonGroup,
+ RadioButton,
+} from 'features/Common'
+import { useExtendedSearchStore } from 'features/ExtendedSearchPage/store'
+
+export const GenderFilter = () => {
+ const {
+ onGenderChange,
+ selectedGender,
+ } = useExtendedSearchStore()
+
+ return (
+
+ onGenderChange(Gender.MALE)}
+ buttonWidth={122}
+ >
+
+
+ onGenderChange(Gender.FEMALE)}
+ buttonWidth={122}
+ >
+
+
+
+ )
+}
diff --git a/src/features/ExtendedSearchPage/components/MobileHeader/index.tsx b/src/features/ExtendedSearchPage/components/MobileHeader/index.tsx
new file mode 100644
index 00000000..35c723b7
--- /dev/null
+++ b/src/features/ExtendedSearchPage/components/MobileHeader/index.tsx
@@ -0,0 +1,31 @@
+import React, { Fragment } from 'react'
+import { Link } from 'react-router-dom'
+
+import { PAGES } from 'config'
+
+import { Logo } from 'features/Logo'
+import { Menu } from 'features/Menu'
+
+import {
+ HeaderMobileWrapper,
+ HeaderIconsWrapper,
+ IconFavWrapper,
+} from 'features/HeaderMobile/styled'
+
+import { Filters } from '../Filters'
+
+export const MobileHeader = () => (
+
+
+
+
+
+
+
+
+
+
+
+
+
+)
diff --git a/src/features/ExtendedSearchPage/components/ProfileFilter/index.tsx b/src/features/ExtendedSearchPage/components/ProfileFilter/index.tsx
new file mode 100644
index 00000000..806202d1
--- /dev/null
+++ b/src/features/ExtendedSearchPage/components/ProfileFilter/index.tsx
@@ -0,0 +1,44 @@
+import React from 'react'
+
+import { ProfileTypes } from 'config'
+
+import { T9n } from 'features/T9n'
+import {
+ RadioButtonGroup,
+ RadioButton,
+} from 'features/Common'
+
+import { useExtendedSearchStore } from '../../store'
+
+export const ProfileFilter = () => {
+ const {
+ onProfileChange,
+ selectedProfile,
+ } = useExtendedSearchStore()
+
+ return (
+
+ onProfileChange(ProfileTypes.TEAMS)}
+ buttonWidth={122}
+ >
+
+
+ onProfileChange(ProfileTypes.PLAYERS)}
+ buttonWidth={122}
+ >
+
+
+ onProfileChange(ProfileTypes.TOURNAMENTS)}
+ buttonWidth={122}
+ >
+
+
+
+ )
+}
diff --git a/src/features/ExtendedSearchPage/components/Results/index.tsx b/src/features/ExtendedSearchPage/components/Results/index.tsx
new file mode 100644
index 00000000..8fe2169b
--- /dev/null
+++ b/src/features/ExtendedSearchPage/components/Results/index.tsx
@@ -0,0 +1,47 @@
+import React, {
+ Fragment,
+ memo,
+} from 'react'
+
+import { isEmpty } from 'lodash'
+
+import { ProfileTypes } from 'config'
+
+import type { NormalizedSearchResults } from 'features/Search/helpers'
+
+import {
+ Title,
+ ResultsList,
+} from '../../styled'
+
+type Props = {
+ results: NormalizedSearchResults,
+}
+
+export const Results = memo(({ results }: Props) => {
+ const hasResults = (
+ !isEmpty(results.players)
+ || !isEmpty(results.teams)
+ || !isEmpty(results.tournaments)
+ )
+ if (hasResults) {
+ return (
+
+
+
+
+
+
+ )
+ }
+ return null
+})
diff --git a/src/features/ExtendedSearchPage/components/SearchInput/index.tsx b/src/features/ExtendedSearchPage/components/SearchInput/index.tsx
new file mode 100644
index 00000000..75e7de0e
--- /dev/null
+++ b/src/features/ExtendedSearchPage/components/SearchInput/index.tsx
@@ -0,0 +1,54 @@
+import React, { Fragment } from 'react'
+
+import styled from 'styled-components/macro'
+
+import { Loader } from 'features/Loader'
+import { useExtendedSearchStore } from 'features/ExtendedSearchPage/store'
+
+export const LoaderWrapper = styled.div`
+ position: absolute;
+ top: 0;
+ background-color: rgba(129, 129, 129, 0.5);
+ width: 100%;
+ height: 100%;
+ box-shadow: 0px 1px 1px rgba(0, 0, 0, 0.3);
+ border-radius: 2px;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+`
+
+const Input = styled.input`
+ width: 100%;
+ padding-left: 20px;
+ padding-right: 20px;
+ font-weight: bold;
+ font-size: 18px;
+ background-color: #3F3F3F;
+ color: #fff;
+ border: transparent;
+ border-color: transparent;
+ outline: none;
+`
+
+export const SearchInput = () => {
+ const {
+ isFetching,
+ onQueryChange,
+ query,
+ } = useExtendedSearchStore()
+ return (
+
+
+ {isFetching && (
+
+
+
+ )}
+
+ )
+}
diff --git a/src/features/ExtendedSearchPage/components/SportTypeFilter/index.tsx b/src/features/ExtendedSearchPage/components/SportTypeFilter/index.tsx
new file mode 100644
index 00000000..967488a0
--- /dev/null
+++ b/src/features/ExtendedSearchPage/components/SportTypeFilter/index.tsx
@@ -0,0 +1,19 @@
+import React from 'react'
+
+import { SportTypeFilter as Filter } from 'features/SportTypeFilter'
+
+import { useExtendedSearchStore } from '../../store'
+
+export const SportTypeFilter = () => {
+ const {
+ onSportChange,
+ selectedSport,
+ } = useExtendedSearchStore()
+ return (
+ onSportChange(null)}
+ />
+ )
+}
diff --git a/src/features/ExtendedSearchPage/index.tsx b/src/features/ExtendedSearchPage/index.tsx
new file mode 100644
index 00000000..2a45ffed
--- /dev/null
+++ b/src/features/ExtendedSearchPage/index.tsx
@@ -0,0 +1,35 @@
+import React, { Fragment } from 'react'
+
+import { devices } from 'config'
+
+import { useMediaQuery } from 'features/MediaQuery'
+
+import { MobileHeader } from './components/MobileHeader'
+import { DesktopHeader } from './components/DesktopHeader'
+import { Results } from './components/Results'
+import { ExtendedSearchStore, useExtendedSearchStore } from './store'
+
+import { Main } from './styled'
+
+const ExtendedSearch = () => {
+ const { searchItems } = useExtendedSearchStore()
+ const isMobile = useMediaQuery({ query: devices.tablet })
+ return (
+
+ {
+ isMobile
+ ?
+ :
+ }
+
+
+
+
+ )
+}
+
+export const ExtendedSearchPage = () => (
+
+
+
+)
diff --git a/src/features/ExtendedSearchPage/store/hooks/index.tsx b/src/features/ExtendedSearchPage/store/hooks/index.tsx
new file mode 100644
index 00000000..bb7fc004
--- /dev/null
+++ b/src/features/ExtendedSearchPage/store/hooks/index.tsx
@@ -0,0 +1,87 @@
+import type { ChangeEvent } from 'react'
+import {
+ useCallback,
+ useEffect,
+ useState,
+} from 'react'
+
+import trim from 'lodash/trim'
+import size from 'lodash/size'
+
+import { ProfileTypes, SportTypes } from 'config'
+
+import { Gender } from 'requests'
+
+import { MIN_CHARACTERS_LENGTH } from 'features/Search/config'
+import type { NormalizedSearchResults } from 'features/Search/helpers'
+
+import { useSearchRequest } from './useSearchRequest'
+
+const initialState = {
+ players: [],
+ teams: [],
+ tournaments: [],
+}
+
+export const useExtendedSearch = () => {
+ const [searchItems, setSearchItems] = useState(initialState)
+
+ const [query, setQuery] = useState('')
+ const [selectedSport, setSelectedSport] = useState(null)
+ const [selectedGender, setSelectedGender] = useState(null)
+ const [selectedProfile, setSelectedProfile] = useState(null)
+
+ const {
+ cancelSearch,
+ isFetching,
+ search,
+ } = useSearchRequest(setSearchItems)
+
+ const onQueryChange = useCallback(
+ ({ target: { value } }: ChangeEvent) => {
+ setQuery(value)
+ },
+ [],
+ )
+
+ const onGenderChange = useCallback((gender: Gender | null) => {
+ setSelectedGender((oldGender) => (gender === oldGender ? null : gender))
+ }, [])
+
+ const onProfileChange = useCallback((profile: ProfileTypes | null) => {
+ setSelectedProfile((oldProfile) => (profile === oldProfile ? null : profile))
+ }, [])
+
+ const trimmedQuery = trim(query)
+ useEffect(() => {
+ cancelSearch()
+ if (size(trimmedQuery) >= MIN_CHARACTERS_LENGTH) {
+ search({
+ gender: selectedGender,
+ profileType: selectedProfile,
+ query: trimmedQuery,
+ sportType: selectedSport,
+ })
+ }
+ }, [
+ trimmedQuery,
+ selectedSport,
+ selectedGender,
+ selectedProfile,
+ search,
+ cancelSearch,
+ ])
+
+ return {
+ isFetching,
+ onGenderChange,
+ onProfileChange,
+ onQueryChange,
+ onSportChange: setSelectedSport,
+ query,
+ searchItems,
+ selectedGender,
+ selectedProfile,
+ selectedSport,
+ }
+}
diff --git a/src/features/ExtendedSearchPage/store/hooks/useSearchRequest.tsx b/src/features/ExtendedSearchPage/store/hooks/useSearchRequest.tsx
new file mode 100644
index 00000000..64c8ba59
--- /dev/null
+++ b/src/features/ExtendedSearchPage/store/hooks/useSearchRequest.tsx
@@ -0,0 +1,53 @@
+import {
+ useCallback,
+ useMemo,
+ useRef,
+} from 'react'
+
+import debounce from 'lodash/debounce'
+
+import { extendedSearch } from 'requests'
+
+import { useRequest } from 'hooks'
+
+import { SEARCH_DELAY } from 'features/Search/config'
+import type { NormalizedSearchResults } from 'features/Search/helpers'
+import { normalizeItems } from 'features/Search/helpers'
+
+type Updater = (results: NormalizedSearchResults) => void
+type SearchArgs = Omit[0], 'abortSignal'>
+
+export const useSearchRequest = (updater: Updater) => {
+ const abortRef = useRef(null)
+
+ const {
+ isFetching,
+ request: searchItemsRequest,
+ } = useRequest(extendedSearch)
+
+ const search = useMemo(
+ () => debounce((args: SearchArgs) => {
+ const abortController = new AbortController()
+ searchItemsRequest({
+ abortSignal: abortController.signal,
+ ...args,
+ }).then((searchResult) => {
+ abortRef.current = null
+ updater(normalizeItems(searchResult))
+ })
+ abortRef.current = abortController
+ }, SEARCH_DELAY),
+ [searchItemsRequest, updater],
+ )
+
+ const cancelSearch = useCallback(() => {
+ abortRef.current?.abort()
+ abortRef.current = null
+ }, [])
+
+ return {
+ cancelSearch,
+ isFetching,
+ search,
+ }
+}
diff --git a/src/features/ExtendedSearchPage/store/index.tsx b/src/features/ExtendedSearchPage/store/index.tsx
new file mode 100644
index 00000000..c30e4c5f
--- /dev/null
+++ b/src/features/ExtendedSearchPage/store/index.tsx
@@ -0,0 +1,19 @@
+import type { ReactNode } from 'react'
+import React, {
+ createContext,
+ useContext,
+} from 'react'
+
+import { useExtendedSearch } from './hooks'
+
+type Context = ReturnType
+type Props = { children: ReactNode }
+
+const SearchContext = createContext({} as Context)
+
+export const ExtendedSearchStore = ({ children }: Props) => {
+ const value = useExtendedSearch()
+ return {children}
+}
+
+export const useExtendedSearchStore = () => useContext(SearchContext)
diff --git a/src/features/ExtendedSearchPage/styled.tsx b/src/features/ExtendedSearchPage/styled.tsx
new file mode 100644
index 00000000..18ac8e46
--- /dev/null
+++ b/src/features/ExtendedSearchPage/styled.tsx
@@ -0,0 +1,41 @@
+import styled from 'styled-components/macro'
+
+import { devices } from 'config'
+
+import { T9n } from 'features/T9n'
+import { ItemsList } from 'features/ItemsList'
+import { GenderComponent, StyledLink } from 'features/ItemsList/styled'
+
+export const Main = styled.main`
+ margin-top: 75px;
+ margin-bottom: 30px;
+
+ @media ${devices.tablet} {
+ margin-top: 30px;
+ }
+`
+
+export const Title = styled(T9n)`
+ display: block;
+ font-weight: bold;
+ font-size: 36px;
+ line-height: 24px;
+ color: #fff;
+ margin-bottom: 30px;
+ padding: 0 14px;
+`
+
+export const ResultsList = styled(ItemsList)`
+ ${StyledLink} {
+ background-color: transparent;
+
+ :focus-within,
+ :hover {
+ background-color: rgba(153, 153, 153, 0.4);
+ }
+ }
+
+ ${GenderComponent} {
+ display: none;
+ }
+`
diff --git a/src/features/Header/index.tsx b/src/features/Header/index.tsx
deleted file mode 100644
index 27bc4943..00000000
--- a/src/features/Header/index.tsx
+++ /dev/null
@@ -1,62 +0,0 @@
-import React, { Fragment } from 'react'
-import { Route, Switch } from 'react-router-dom'
-
-import { Menu } from 'features/Menu'
-import { Search } from 'features/Search'
-
-import {
- DateFilter,
- MatchStatusFilter,
- SportTypeFilter,
- TournamentFilter,
-} from 'features/HeaderFilters'
-
-import { PAGES } from 'config'
-
-import {
- Wrapper,
- FilterWrapper,
- HomeButtonLink,
- SearchWrapper,
- SportFilterWrapper,
- MenuWrapper,
-} from './styled'
-
-export const Header = () => (
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-)
diff --git a/src/features/HeaderFilters/components/MatchStatusFilter/index.tsx b/src/features/HeaderFilters/components/MatchStatusFilter/index.tsx
index 963c9b5a..5ea763a1 100644
--- a/src/features/HeaderFilters/components/MatchStatusFilter/index.tsx
+++ b/src/features/HeaderFilters/components/MatchStatusFilter/index.tsx
@@ -1,18 +1,19 @@
import React from 'react'
import { T9n } from 'features/T9n'
-
-import { MatchStatuses, useHeaderFiltersStore } from '../../store'
import {
RadioButtonGroup,
RadioButton,
-} from './styled'
+} from 'features/Common'
+
+import { MatchStatuses, useHeaderFiltersStore } from '../../store'
export const MatchStatusFilter = () => {
const { selectedMatchStatus, setSelectedMatchStatus } = useHeaderFiltersStore()
return (
setSelectedMatchStatus(MatchStatuses.Live)}
buttonWidth={80}
diff --git a/src/features/HeaderFilters/components/SportTypeFilter/index.tsx b/src/features/HeaderFilters/components/SportTypeFilter/index.tsx
index 506f5864..55498e5d 100644
--- a/src/features/HeaderFilters/components/SportTypeFilter/index.tsx
+++ b/src/features/HeaderFilters/components/SportTypeFilter/index.tsx
@@ -1,72 +1,65 @@
import React from 'react'
-import map from 'lodash/map'
+import { css } from 'styled-components/macro'
-import { T9n } from 'features/T9n'
-import { OutsideClick } from 'features/OutsideClick'
+import { devices } from 'config'
-import { useSportTypeFilter } from './hooks'
-import {
- Wrapper,
- SportList,
- CustomOption,
-} from './styled'
-import {
- DropdownButton,
- ButtonTitle,
- ClearButton,
- Arrows,
-} from '../TournamentFilter/styled'
+import { useHeaderFiltersStore } from 'features/HeaderFilters/store'
+import { SportTypeFilter as Filter } from 'features/SportTypeFilter'
+
+import { DropdownButton } from '../TournamentFilter/styled'
+
+const wrapperStyles = css`
+ width: 50%;
+ border-right: 1px solid #222222;
+
+ @media ${devices.tablet} {
+ border-right: 0;
+ }
+
+ ${DropdownButton} {
+ border-radius: 0;
+ border-top-left-radius: 2px;
+ border-bottom-left-radius: 2px;
+ }
+`
+
+const useSportTypeFilter = () => {
+ const {
+ selectedSportTypeId,
+ setSelectedSportTypeId,
+ setSelectedTournamentId,
+ } = useHeaderFiltersStore()
+
+ const onSelect = (id: number) => {
+ setSelectedSportTypeId(id)
+ setSelectedTournamentId(null)
+ }
+
+ const onReset = () => {
+ setSelectedSportTypeId(null)
+ setSelectedTournamentId(null)
+ }
+
+ return {
+ onReset,
+ onSelect,
+ selectedSportTypeId,
+ }
+}
export const SportTypeFilter = () => {
const {
- close,
- isOpen,
- onResetSelectedSport,
+ onReset,
onSelect,
- open,
- selectedSportType,
- sportList,
+ selectedSportTypeId,
} = useSportTypeFilter()
-
return (
-
-
-
-
-
- {selectedSportType && }
-
-
-
- {
- isOpen && (
-
-
- {
- map(sportList, ({
- id,
- lexic,
- }) => (
- onSelect(id)}
- role='option'
- >
-
-
- ))
- }
-
-
- )
- }
-
+
)
}
diff --git a/src/features/HeaderFilters/components/TournamentFilter/styled.tsx b/src/features/HeaderFilters/components/TournamentFilter/styled.tsx
index ab6bd535..025ec45a 100644
--- a/src/features/HeaderFilters/components/TournamentFilter/styled.tsx
+++ b/src/features/HeaderFilters/components/TournamentFilter/styled.tsx
@@ -45,6 +45,7 @@ export const DropdownButton = styled.button`
padding-right: 45px;
outline: none;
border: none;
+ border-radius: 2px;
display: flex;
align-items: center;
background-color: #3F3F3F;
@@ -114,6 +115,7 @@ export const Wrapper = styled.div`
position: relative;
${DropdownButton} {
+ border-radius: 0;
border-top-right-radius: 2px;
border-bottom-right-radius: 2px;
}
diff --git a/src/features/HomePage/components/Header/index.tsx b/src/features/HomePage/components/Header/index.tsx
new file mode 100644
index 00000000..4ae4c626
--- /dev/null
+++ b/src/features/HomePage/components/Header/index.tsx
@@ -0,0 +1,44 @@
+import React from 'react'
+
+import { Menu } from 'features/Menu'
+import { Search } from 'features/Search'
+
+import {
+ DateFilter,
+ MatchStatusFilter,
+ SportTypeFilter,
+ TournamentFilter,
+} from 'features/HeaderFilters'
+
+import {
+ Wrapper,
+ FilterWrapper,
+ SearchWrapper,
+ SportFilterWrapper,
+ MenuWrapper,
+} from 'features/ProfileHeader/styled'
+
+export const Header = () => (
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+)
diff --git a/src/features/HomePage/index.tsx b/src/features/HomePage/index.tsx
index 8b969735..cb227fe6 100644
--- a/src/features/HomePage/index.tsx
+++ b/src/features/HomePage/index.tsx
@@ -3,41 +3,64 @@ import { useMediaQuery } from 'react-responsive'
import { devices } from 'config'
+import { HeaderMobile } from 'features/HeaderMobile'
import { Matches } from 'features/Matches'
import {
+ HeaderFiltersStore,
DateFilter,
MatchStatusFilter,
+ TournamentFilter,
+ SportTypeFilter,
} from 'features/HeaderFilters'
import {
HeaderMobileMidle,
HeaderMobileBottom,
} from 'features/HeaderMobile/styled'
+import { SportFilterWrapper } from 'features/ProfileHeader/styled'
import { useHomePage } from './hooks'
+import { Header } from './components/Header'
import { Content } from './styled'
-export const HomePage = () => {
+const Home = () => {
const isMobile = useMediaQuery({ query: devices.tablet })
const { fetchMatches } = useHomePage()
return (
{
isMobile
- ? (
-
-
-
-
-
-
-
-
- )
- : null
+ ?
+ :
+ }
+ {
+ isMobile && (
+
+
+
+
+
+
+
+
+ )
}
+ {
+ isMobile && (
+
+
+
+
+ )
+ }
)
}
+
+export const HomePage = () => (
+
+
+
+)
diff --git a/src/features/ItemsList/index.tsx b/src/features/ItemsList/index.tsx
index b8f25460..812ca9a1 100644
--- a/src/features/ItemsList/index.tsx
+++ b/src/features/ItemsList/index.tsx
@@ -27,7 +27,8 @@ type Name = {
}
type SearchItemsListProps = {
- close: () => void,
+ className?: string,
+ close?: () => void,
list: Array<{
gender?: Gender,
id: number,
@@ -40,6 +41,7 @@ type SearchItemsListProps = {
}
export const ItemsList = ({
+ className,
close,
list,
profileType,
@@ -47,7 +49,7 @@ export const ItemsList = ({
const { ref } = useItemsList()
return (
-
+
{map(list, (item) => (
-
{
const isFinishedMatch = !isEmpty(videos) && !isLastPlayPositionFetching
return (
-
-
-
- {
- isLiveMatch && (
-
- )
- }
- {
- isFinishedMatch && (
-
- )
- }
-
-
+
+
+
+
+
+ {
+ isLiveMatch && (
+
+ )
+ }
+ {
+ isFinishedMatch && (
+
+ )
+ }
+
+
+
)
}
diff --git a/src/features/PlayerPage/index.tsx b/src/features/PlayerPage/index.tsx
index b4ed576a..b9c7ff57 100644
--- a/src/features/PlayerPage/index.tsx
+++ b/src/features/PlayerPage/index.tsx
@@ -1,11 +1,13 @@
-import React from 'react'
+import React, { Fragment } from 'react'
import { ProfileTypes } from 'config'
+
+import { ProfileHeader } from 'features/ProfileHeader'
import { ProfileCard } from 'features/ProfileCard'
import { Matches } from 'features/Matches'
-import { Content } from './styled'
import { usePlayerPage } from './hooks'
+import { Content } from './styled'
export const PlayerPage = () => {
const {
@@ -15,13 +17,16 @@ export const PlayerPage = () => {
} = usePlayerPage()
return (
-
-
-
-
+
+
+
+
+
+
+
)
}
diff --git a/src/features/ProfileHeader/index.tsx b/src/features/ProfileHeader/index.tsx
new file mode 100644
index 00000000..b22bb0f7
--- /dev/null
+++ b/src/features/ProfileHeader/index.tsx
@@ -0,0 +1,35 @@
+import React from 'react'
+
+import { devices, PAGES } from 'config'
+
+import { Menu } from 'features/Menu'
+import { Search } from 'features/Search'
+import { HeaderMobile } from 'features/HeaderMobile'
+import { useMediaQuery } from 'features/MediaQuery'
+
+import {
+ Wrapper,
+ HomeButtonLink,
+ SearchWrapper,
+ MenuWrapper,
+} from './styled'
+
+export const ProfileHeader = () => {
+ const isMobile = useMediaQuery({ query: devices.tablet })
+
+ return (
+ isMobile
+ ?
+ : (
+
+
+
+
+
+
+
+
+
+ )
+ )
+}
diff --git a/src/features/Header/styled.tsx b/src/features/ProfileHeader/styled.tsx
similarity index 95%
rename from src/features/Header/styled.tsx
rename to src/features/ProfileHeader/styled.tsx
index e9ee6b6e..003de4fa 100644
--- a/src/features/Header/styled.tsx
+++ b/src/features/ProfileHeader/styled.tsx
@@ -9,7 +9,7 @@ export const HomeButtonLink = styled(Link)`
background-image: url('/images/home-btn.svg');
background-repeat: no-repeat;
background-position: center;
-
+
&:hover {
background-image: url('/images/home-btn-hover.svg');
cursor:pointer;
@@ -35,11 +35,6 @@ export const FilterWrapper = styled.div`
`
export const SearchWrapper = styled(FilterWrapper)`
- width: 288px;
- height: 48px;
- margin-right: 16px;
- display: flex;
-
@media ${devices.desktop} {
width: 51px;
margin-right: 9px;
diff --git a/src/features/ProfileLogo/index.tsx b/src/features/ProfileLogo/index.tsx
index 170d3b89..0d4b8f35 100644
--- a/src/features/ProfileLogo/index.tsx
+++ b/src/features/ProfileLogo/index.tsx
@@ -51,7 +51,7 @@ export const ProfileLogo = ({
export const normalizeItems = (searchItems: SearchItems) => {
const players = map(searchItems.players, (player) => ({
diff --git a/src/features/Search/hooks/index.tsx b/src/features/Search/hooks/index.tsx
index b981e8cd..bc99052b 100644
--- a/src/features/Search/hooks/index.tsx
+++ b/src/features/Search/hooks/index.tsx
@@ -1,14 +1,12 @@
import type { FormEvent, ChangeEvent } from 'react'
import {
useState,
- FocusEvent,
useRef,
useCallback,
} from 'react'
import trim from 'lodash/trim'
import debounce from 'lodash/debounce'
-import isEmpty from 'lodash/isEmpty'
import size from 'lodash/size'
import type { SearchItems } from 'requests'
@@ -19,7 +17,7 @@ import {
} from 'hooks'
import { SEARCH_DELAY, MIN_CHARACTERS_LENGTH } from '../config'
-import { normalizeItems } from './useNormalizedItems'
+import { normalizeItems } from '../helpers'
export const useSearch = () => {
const [searchItems, setSearchItems] = useState({})
@@ -72,12 +70,6 @@ export const useSearch = () => {
open,
])
- const onFocus = useCallback(({ target: { value } }: FocusEvent) => {
- if (size(value) >= MIN_CHARACTERS_LENGTH) {
- open()
- }
- }, [open])
-
const onSubmit = useCallback((e: FormEvent) => {
e.preventDefault()
}, [])
@@ -87,8 +79,8 @@ export const useSearch = () => {
isFetching,
normalizedItems: normalizeItems(searchItems),
onChange,
- onFocus,
+ onFocus: open,
onSubmit,
- showResults: isOpen && !isEmpty(searchItems),
+ showResults: isOpen,
}
}
diff --git a/src/features/Search/index.tsx b/src/features/Search/index.tsx
index 195dc10e..3fca7528 100644
--- a/src/features/Search/index.tsx
+++ b/src/features/Search/index.tsx
@@ -4,6 +4,7 @@ import isEmpty from 'lodash/isEmpty'
import { PAGES, ProfileTypes } from 'config'
+import { T9n } from 'features/T9n'
import { Input } from 'features/Common'
import { ItemsList } from 'features/ItemsList'
import { OutsideClick } from 'features/OutsideClick'
@@ -16,6 +17,9 @@ import {
Form,
Results,
LoaderWrapper,
+ ResultsWrapper,
+ LinkWrapper,
+ StyledLink,
} from './styled'
export const Search = () => {
@@ -51,40 +55,47 @@ export const Search = () => {
)}
{showResults && (
-
- {!isEmpty(tournaments) && (
-
-
-
-
- )}
+
+
+ {!isEmpty(tournaments) && (
+
+
+
+
+ )}
- {!isEmpty(teams) && (
-
-
-
-
- )}
+ {!isEmpty(teams) && (
+
+
+
+
+ )}
- {!isEmpty(players) && (
-
-
-
-
- )}
-
+ {!isEmpty(players) && (
+
+
+
+
+ )}
+
+
+
+
+
+
+
)}
diff --git a/src/features/Search/styled.tsx b/src/features/Search/styled.tsx
index 44ac376d..7e8d02c9 100644
--- a/src/features/Search/styled.tsx
+++ b/src/features/Search/styled.tsx
@@ -1,7 +1,9 @@
+import { Link } from 'react-router-dom'
+
import styled from 'styled-components/macro'
import { devices } from 'config/devices'
-import { customScrollbar } from 'features/Common'
+import { customScrollbar, solidButtonStyles } from 'features/Common'
import {
InputWrapper,
InputStyled,
@@ -33,7 +35,6 @@ export const LoaderWrapper = styled.div`
top: 26px;
height: 28px;
}
-
`
export const Form = styled.form<{isMatch: boolean}>`
@@ -53,12 +54,12 @@ export const Form = styled.form<{isMatch: boolean}>`
margin-top: 25px;
background-color: transparent;
}
-
+
@media ${devices.mobile} {
margin-top: 20px;
}
}
-
+
@media ${devices.desktop} {
width: ${({ isMatch }) => (isMatch ? '244px' : '51px')};
@@ -83,7 +84,7 @@ export const Form = styled.form<{isMatch: boolean}>`
top: -33px;
}
}
-
+
${InputStyled} {
width: 100%;
@@ -91,7 +92,7 @@ export const Form = styled.form<{isMatch: boolean}>`
::-webkit-search-cancel-button,
::-webkit-search-results-button,
::-webkit-search-results-decoration {
- display: none;
+ display: none;
}
@media ${devices.tablet} {
@@ -122,7 +123,7 @@ export const Form = styled.form<{isMatch: boolean}>`
}
}
}
-
+
:focus-within {
${InputWrapper} {
padding-left: 0;
@@ -131,7 +132,7 @@ export const Form = styled.form<{isMatch: boolean}>`
background-color: #3F3F3F;
}
}
-
+
${Label} {
::before {
display: none;
@@ -145,15 +146,19 @@ export const Form = styled.form<{isMatch: boolean}>`
max-width: 335px;
}
}
-
`
export const Results = styled.div`
+ overflow-y: auto;
+ max-height: 431px;
+
+ ${customScrollbar}
+`
+
+export const ResultsWrapper = styled.div`
position: absolute;
top: 56px;
width: 448px;
- max-height: 431px;
- overflow-y: auto;
z-index: 1;
@media ${devices.tablet} {
@@ -166,6 +171,24 @@ export const Results = styled.div`
left: 0;
width: 100vw;
}
+`
- ${customScrollbar}
+export const LinkWrapper = styled.div`
+ background-color: #666;
+ width: 440px;
+ padding: 10px 0;
+`
+
+export const StyledLink = styled(Link)`
+ ${solidButtonStyles}
+
+ width: 217px;
+ height: 30px;
+ font-weight: 600;
+ font-size: 11px;
+ margin: 0 auto;
+ padding: 0;
+ display: flex;
+ justify-content: center;
+ align-items: center;
`
diff --git a/src/features/HeaderFilters/components/SportTypeFilter/hooks.tsx b/src/features/SportTypeFilter/hooks.tsx
similarity index 61%
rename from src/features/HeaderFilters/components/SportTypeFilter/hooks.tsx
rename to src/features/SportTypeFilter/hooks.tsx
index cc0be53d..38d5dfb5 100644
--- a/src/features/HeaderFilters/components/SportTypeFilter/hooks.tsx
+++ b/src/features/SportTypeFilter/hooks.tsx
@@ -3,18 +3,28 @@ import { useState, useEffect } from 'react'
import find from 'lodash/find'
+import { SportTypes } from 'config'
+
import type { SportList } from 'requests/getSportList'
import { getSportList } from 'requests/getSportList'
-import { useHeaderFiltersStore } from 'features/HeaderFilters/store'
+
import { useToggle } from 'hooks'
-export const useSportTypeFilter = () => {
+import type { CustomStyles } from 'features/Common'
+
+export type Props = {
+ onReset: () => void,
+ onSelect: (sport: SportTypes) => void,
+ selectedSportId: SportTypes | null,
+ wrapperStyles?: CustomStyles,
+}
+
+export const useSportTypeFilter = ({
+ onReset,
+ onSelect,
+ selectedSportId,
+}: Props) => {
const [sportList, setSportList] = useState([])
- const {
- selectedSportTypeId,
- setSelectedSportTypeId,
- setSelectedTournamentId,
- } = useHeaderFiltersStore()
const {
close,
@@ -26,24 +36,22 @@ export const useSportTypeFilter = () => {
getSportList().then(setSportList)
}, [])
- const onSelect = (id: number) => {
- setSelectedSportTypeId(id)
- setSelectedTournamentId(null)
+ const handleSelect = (id: SportTypes) => {
+ onSelect(id)
close()
}
const onResetSelectedSport = (e: MouseEvent) => {
e.stopPropagation()
- setSelectedSportTypeId(null)
- setSelectedTournamentId(null)
+ onReset()
}
- const selectedSportType = find(sportList, (sport) => sport.id === selectedSportTypeId)
+ const selectedSportType = find(sportList, (sport) => sport.id === selectedSportId)
return {
close,
isOpen,
onResetSelectedSport,
- onSelect,
+ onSelect: handleSelect,
open,
selectedSportType,
sportList,
diff --git a/src/features/SportTypeFilter/index.tsx b/src/features/SportTypeFilter/index.tsx
new file mode 100644
index 00000000..09b70b76
--- /dev/null
+++ b/src/features/SportTypeFilter/index.tsx
@@ -0,0 +1,74 @@
+import React from 'react'
+
+import map from 'lodash/map'
+
+import { T9n } from 'features/T9n'
+import { OutsideClick } from 'features/OutsideClick'
+
+import type { Props } from './hooks'
+import { useSportTypeFilter } from './hooks'
+import {
+ Wrapper,
+ SportList,
+ SportItem,
+} from './styled'
+import {
+ DropdownButton,
+ ButtonTitle,
+ ClearButton,
+ Arrows,
+} from '../HeaderFilters/components/TournamentFilter/styled'
+
+export const SportTypeFilter = (props: Props) => {
+ const { wrapperStyles } = props
+ const {
+ close,
+ isOpen,
+ onResetSelectedSport,
+ onSelect,
+ open,
+ selectedSportType,
+ sportList,
+ } = useSportTypeFilter(props)
+
+ return (
+
+
+
+
+
+ {selectedSportType && }
+
+
+
+ {
+ isOpen && (
+
+
+ {
+ map(sportList, ({
+ id,
+ lexic,
+ }) => (
+ onSelect(id)}
+ role='option'
+ >
+
+
+ ))
+ }
+
+
+ )
+ }
+
+ )
+}
diff --git a/src/features/HeaderFilters/components/SportTypeFilter/styled.tsx b/src/features/SportTypeFilter/styled.tsx
similarity index 73%
rename from src/features/HeaderFilters/components/SportTypeFilter/styled.tsx
rename to src/features/SportTypeFilter/styled.tsx
index eb398abb..e0610746 100644
--- a/src/features/HeaderFilters/components/SportTypeFilter/styled.tsx
+++ b/src/features/SportTypeFilter/styled.tsx
@@ -4,21 +4,13 @@ import toLower from 'lodash/toLower'
import { devices, SportTypes } from 'config'
-import { DropdownButton } from '../TournamentFilter/styled'
+import { customStylesMixin } from 'features/Common'
export const Wrapper = styled.div`
- width: 50%;
position: relative;
- border-right: 1px solid #222222;
-
- @media ${devices.tablet} {
- border-right: 0;
- }
+ width: 100%;
- ${DropdownButton} {
- border-top-left-radius: 2px;
- border-bottom-left-radius: 2px;
- }
+ ${customStylesMixin}
`
export const SportList = styled.ul`
@@ -39,11 +31,11 @@ export const SportList = styled.ul`
}
`
-type CustomOptionProps = {
+type SportItemProps = {
sport: SportTypes,
}
-export const CustomOption = styled.li`
+export const SportItem = styled.li`
width: 100%;
height: 48px;
display: flex;
diff --git a/src/features/TeamPage/index.tsx b/src/features/TeamPage/index.tsx
index 5951c300..3d54ff51 100644
--- a/src/features/TeamPage/index.tsx
+++ b/src/features/TeamPage/index.tsx
@@ -2,6 +2,7 @@ import React, { Fragment } from 'react'
import { ProfileTypes } from 'config'
+import { ProfileHeader } from 'features/ProfileHeader'
import { ProfileCard } from 'features/ProfileCard'
import { Matches } from 'features/Matches'
@@ -17,6 +18,7 @@ export const TeamPage = () => {
return (
+
{
return (
+
{
+ const filterFunc = (item: Item) => {
+ let result = true
+ if (!isNull(gender)) {
+ result = item.gender === gender
+ }
+ if (!isNull(sportType)) {
+ result = item.sport === sportType
+ }
+ return result
+ }
+
+ const filteredResults = {
+ players: filter(results.players, filterFunc),
+ teams: filter(results.teams, filterFunc),
+ tournaments: filter(results.tournaments, filterFunc),
+ }
+
+ if (isNull(profileType)) return filteredResults
+
+ const key = PROFILE_NAMES[profileType]
+ return { [key]: filteredResults[key] }
+}
+
+type Args = {
+ abortSignal: AbortSignal,
+ gender: Gender | null,
+ profileType: ProfileTypes | null,
+ query: string,
+ sportType: SportTypes | null,
+}
+
+export const extendedSearch = async ({
+ abortSignal,
+ gender,
+ profileType,
+ query,
+ sportType,
+}: Args) => {
+ const results = await getSearchItems(query, abortSignal)
+ return filterResults({
+ gender,
+ profileType,
+ results,
+ sportType,
+ })
+}