diff --git a/public/images/player.svg b/public/images/player.svg
new file mode 100644
index 00000000..7b6e6027
--- /dev/null
+++ b/public/images/player.svg
@@ -0,0 +1,7 @@
+
diff --git a/public/images/search.svg b/public/images/search.svg
new file mode 100644
index 00000000..4890f336
--- /dev/null
+++ b/public/images/search.svg
@@ -0,0 +1,4 @@
+
diff --git a/public/images/team.svg b/public/images/team.svg
new file mode 100644
index 00000000..a44b3e10
--- /dev/null
+++ b/public/images/team.svg
@@ -0,0 +1,8 @@
+
diff --git a/public/images/tournament.svg b/public/images/tournament.svg
new file mode 100644
index 00000000..062ffeed
--- /dev/null
+++ b/public/images/tournament.svg
@@ -0,0 +1,3 @@
+
diff --git a/src/config/colors.tsx b/src/config/colors.tsx
new file mode 100644
index 00000000..ef4a7dd8
--- /dev/null
+++ b/src/config/colors.tsx
@@ -0,0 +1,5 @@
+export const SPORT_COLORS = {
+ basketball: '#f1903b',
+ football: '#00a435',
+ hockey: '#5eb1ff',
+}
diff --git a/src/config/index.tsx b/src/config/index.tsx
index e5851bad..371ac95d 100644
--- a/src/config/index.tsx
+++ b/src/config/index.tsx
@@ -2,3 +2,4 @@ export * from './routes'
export * from './pages'
export * from './authKeys'
export * from './procedures'
+export * from './colors'
diff --git a/src/config/lexics/authenticated.tsx b/src/config/lexics/authenticated.tsx
index 67ae1d09..f2515ddd 100644
--- a/src/config/lexics/authenticated.tsx
+++ b/src/config/lexics/authenticated.tsx
@@ -1,4 +1,10 @@
export const authenticatedLexics = {
+ basketball: 6960,
+ football: 6958,
+ hockey: 6959,
logout: 4306,
+ player: 630,
+ team: 658,
+ tournament: 1009,
user_account: 12928,
}
diff --git a/src/config/procedures.tsx b/src/config/procedures.tsx
index cbfb8f86..3a52bd7a 100644
--- a/src/config/procedures.tsx
+++ b/src/config/procedures.tsx
@@ -2,6 +2,7 @@ export const PROCEDURES = {
auth_user: 'auth_user',
create_user: 'create_user',
get_cities: 'get_cities',
+ get_players_teams_tournaments: 'get_players_teams_tournaments',
lst_c_country: 'lst_c_country',
param_lexical: 'param_lexical',
}
diff --git a/src/config/routes.tsx b/src/config/routes.tsx
index 8d513d50..f6ddfa14 100644
--- a/src/config/routes.tsx
+++ b/src/config/routes.tsx
@@ -1,2 +1,22 @@
export const API_ROOT = 'http://api-staging.instat.tv'
export const DATA_URL = `${API_ROOT}/data`
+
+export const LOGOS_FALLBACKS = {
+ basketball: {
+ players: 'https://basketball.instatscout.com/images/player-no-photo.png',
+ teams: 'https://basketball.instatscout.com/images/team-no-photo.png',
+ tournaments: 'https://basketball.instatscout.com/images/tournaments/180/no-photo.png',
+ },
+
+ football: {
+ players: 'https://football.instatscout.com/images/player-no-photo.png',
+ teams: 'https://football.instatscout.com/images/team-no-photo.png',
+ tournaments: 'https://football.instatscout.com/images/tournament-no-photo.png',
+ },
+
+ hockey: {
+ players: 'https://hockey.instatscout.com/images/player-no-photo.png',
+ teams: 'https://hockey.instatscout.com/images/team-no-photo.png',
+ tournaments: 'https://hockey.instatscout.com/images/tournaments/180/no-photo.png',
+ },
+}
diff --git a/src/features/Combobox/styled.tsx b/src/features/Combobox/styled.tsx
index ee108522..068f4ab7 100644
--- a/src/features/Combobox/styled.tsx
+++ b/src/features/Combobox/styled.tsx
@@ -9,6 +9,7 @@ import {
} from '@reach/combobox'
import { wrapperStyles, inputStyles } from 'features/Common/Input/styled'
+import { сustomScrollbar } from 'features/Common'
export const ComboboxStyled = styled(Combobox)`
${wrapperStyles}
@@ -53,18 +54,7 @@ export const ComboboxListStyled = styled(ComboboxList)`
left: -164px;
overflow: auto;
- ::-webkit-scrollbar {
- width: 8px;
- }
-
- ::-webkit-scrollbar-track {
- background: transparent;
- }
-
- ::-webkit-scrollbar-thumb {
- background: #3F3F3F;
- border-radius: 6px;
- }
+ ${сustomScrollbar}
`
export const ComboboxOptionStyled = styled(ComboboxOption)`
diff --git a/src/features/Common/Input/index.tsx b/src/features/Common/Input/index.tsx
index 793d2bed..43671e73 100644
--- a/src/features/Common/Input/index.tsx
+++ b/src/features/Common/Input/index.tsx
@@ -22,6 +22,7 @@ type Props = {
maxLength?: number,
onBlur?: (event: FocusEvent) => void,
onChange?: (event: ChangeEvent) => void,
+ onFocus?: (event: FocusEvent) => void,
pattern?: string,
required?: boolean,
title?: string,
@@ -40,6 +41,7 @@ export const Input = ({
maxLength,
onBlur,
onChange,
+ onFocus,
paddingX,
pattern,
required,
@@ -72,6 +74,7 @@ export const Input = ({
defaultValue={defaultValue}
onChange={onChange}
onBlur={onBlur}
+ onFocus={onFocus}
maxLength={maxLength}
inputWidth={inputWidth}
pattern={pattern}
diff --git a/src/features/Common/index.tsx b/src/features/Common/index.tsx
index 7161a69c..623e4fbf 100644
--- a/src/features/Common/index.tsx
+++ b/src/features/Common/index.tsx
@@ -3,3 +3,4 @@ export * from './Button'
export * from './Radio'
export * from './Checkbox'
export * from './Arrows'
+export * from './сustomScrollbar'
diff --git a/src/features/Common/сustomScrollbar/index.tsx b/src/features/Common/сustomScrollbar/index.tsx
new file mode 100644
index 00000000..2669561c
--- /dev/null
+++ b/src/features/Common/сustomScrollbar/index.tsx
@@ -0,0 +1,22 @@
+import { css } from 'styled-components/macro'
+
+export const сustomScrollbar = css`
+ ::-webkit-scrollbar {
+ width: 8px;
+ height: 8px;
+ }
+
+ ::-webkit-scrollbar-thumb {
+ border-radius: 6px;
+ background: #3F3F3F;
+ }
+
+ ::-webkit-scrollbar-button {
+ display: none;
+ }
+
+ ::-webkit-scrollbar-track,
+ ::-webkit-scrollbar-corner {
+ background: transparent;
+ }
+`
diff --git a/src/features/ItemsList/hooks.tsx b/src/features/ItemsList/hooks.tsx
new file mode 100644
index 00000000..fabf915e
--- /dev/null
+++ b/src/features/ItemsList/hooks.tsx
@@ -0,0 +1,62 @@
+import {
+ SyntheticEvent,
+ useEffect,
+ useRef,
+ useCallback,
+} from 'react'
+
+import forEach from 'lodash/forEach'
+
+export const useItemsList = () => {
+ const ref = useRef(null)
+
+ const onError = useCallback((fallbackImage: string) => (e: SyntheticEvent) => {
+ // eslint-disable-next-line no-param-reassign
+ e.currentTarget.src = fallbackImage
+ }, [])
+
+ useEffect(() => {
+ if (ref.current) {
+ /**
+ * Реализуем ленивую загрузку изображений
+ * для оптимизации запросов к серверу
+ */
+
+ // устанавливаем настройки
+ const options = {
+ // область просмотра
+ root: ref.current.parentElement,
+ // процент пересечения с областью просмотра - половина изображения
+ threshold: 0.5,
+ }
+
+ // создаем наблюдатель
+ const intersectionObserver = new IntersectionObserver((entries, observer) => {
+ // для каждой записи-целевого элемента
+ forEach(entries, (entry) => {
+ // если элемент является наблюдаемым
+ if (entry.isIntersecting) {
+ const imgElem = entry.target as HTMLImageElement
+ // устанавливаем аттрибут src из data-src
+ imgElem.src = imgElem.dataset.src!
+
+ // прекращаем наблюдение
+ observer.unobserve(imgElem)
+ }
+ })
+ }, options)
+
+ const imgElems = ref.current.getElementsByTagName('img')
+
+ // находим все элементы img и делаем наблюдаемыми
+ forEach(imgElems, (imgElem) => {
+ intersectionObserver.observe(imgElem)
+ })
+ }
+ }, [])
+
+ return {
+ onError,
+ ref,
+ }
+}
diff --git a/src/features/ItemsList/index.tsx b/src/features/ItemsList/index.tsx
new file mode 100644
index 00000000..27913aa4
--- /dev/null
+++ b/src/features/ItemsList/index.tsx
@@ -0,0 +1,72 @@
+import React from 'react'
+
+import map from 'lodash/map'
+
+import { SPORT_COLORS } from 'config'
+import type { SportType } from 'features/Search/config'
+
+import { useItemsList } from './hooks'
+import {
+ Logo,
+ LogoWrapper,
+ Item,
+ ItemInfo,
+ Name,
+ SportName,
+ StyledLink,
+ TeamOrCountry,
+ Wrapper,
+} from './styled'
+
+type SearchItemsListProps = {
+ list: Array<{
+ fallbackImage: string,
+ id: number,
+ logo: string,
+ name: string,
+ profileUrl: string,
+ sportType: SportType,
+ teamOrCountry?: string,
+ }>,
+}
+
+export const ItemsList = ({ list }: SearchItemsListProps) => {
+ const {
+ onError,
+ ref,
+ } = useItemsList()
+
+ return (
+
+ {map(list, ({
+ fallbackImage,
+ id,
+ logo,
+ name,
+ profileUrl,
+ sportType,
+ teamOrCountry,
+ }) => (
+ -
+
+
+
+
+
+ {name}
+
+ {teamOrCountry}
+
+
+
+ ))}
+
+ )
+}
diff --git a/src/features/ItemsList/styled.tsx b/src/features/ItemsList/styled.tsx
new file mode 100644
index 00000000..6fded63e
--- /dev/null
+++ b/src/features/ItemsList/styled.tsx
@@ -0,0 +1,67 @@
+import { Link } from 'react-router-dom'
+
+import styled from 'styled-components/macro'
+
+import { T9n } from 'features/T9n'
+
+export const Wrapper = styled.ul`
+ margin: 0;
+ padding: 0;
+ list-style-type: none;
+`
+
+export const Item = styled.li`
+ :focus {
+ background-color: #999;
+ }
+`
+
+export const StyledLink = styled(Link)`
+ display: flex;
+ align-items: center;
+ width: 100%;
+ height: 56px;
+ background-color: #666;
+
+ :focus-within,
+ :hover {
+ background-color: #999;
+ outline: none;
+ }
+`
+
+export const ItemInfo = styled.div`
+ line-height: 17px;
+`
+
+export const Name = styled.div`
+ font-size: 16px;
+ font-weight: bold;
+ color: #ccc;
+`
+
+export const SportName = styled(T9n)<{ color: string }>`
+ margin-right: 10px;
+ font-size: 11px;
+ color: ${({ color }) => color};
+ letter-spacing: 0.02em;
+ text-transform: uppercase;
+`
+
+export const TeamOrCountry = styled.span`
+ font-size: 11px;
+ color: #ccc;
+`
+
+export const LogoWrapper = styled.div`
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ flex-shrink: 0;
+ width: 55px;
+`
+
+export const Logo = styled.img`
+ width: 24px;
+ height: 24px;
+`
diff --git a/src/features/Search/components/Header/index.tsx b/src/features/Search/components/Header/index.tsx
new file mode 100644
index 00000000..d8c37c17
--- /dev/null
+++ b/src/features/Search/components/Header/index.tsx
@@ -0,0 +1,21 @@
+import React from 'react'
+
+import { T9n } from 'features/T9n'
+
+import {
+ Wrapper,
+ Icon,
+ titleStyles,
+} from './styled'
+
+type HeaderProps = {
+ image: string,
+ title: string,
+}
+
+export const Header = ({ image, title }: HeaderProps) => (
+
+
+
+
+)
diff --git a/src/features/Search/components/Header/styled.tsx b/src/features/Search/components/Header/styled.tsx
new file mode 100644
index 00000000..9fc3ecf3
--- /dev/null
+++ b/src/features/Search/components/Header/styled.tsx
@@ -0,0 +1,24 @@
+import styled, { css } from 'styled-components/macro'
+
+export const Wrapper = styled.div`
+ display: flex;
+ align-items: center;
+ height: 30px;
+ padding-left: 20px;
+ background-color: #005EDD;
+`
+
+export const Icon = styled.div<{ image: string }>`
+ width: 20px;
+ height: 20px;
+ margin-right: 10px;
+ background-image: url(/images/${({ image }) => `${image}.svg`});
+ background-size: 100%;
+ background-repeat: no-repeat;
+`
+
+export const titleStyles = css`
+ letter-spacing: 0.02em;
+ text-transform: uppercase;
+ color: #fff;
+`
diff --git a/src/features/Search/config/index.tsx b/src/features/Search/config/index.tsx
new file mode 100644
index 00000000..782336ae
--- /dev/null
+++ b/src/features/Search/config/index.tsx
@@ -0,0 +1,11 @@
+export const SEARCH_DELAY = 1500 // Задержка поиска в мс
+
+export const MIN_CHARACTERS_LENGTH = 3 // Минимальное число символов для поиска
+
+export type SportType = 'football' | 'basketball' | 'hockey'
+
+export const SPORT_TYPES = {
+ 1: 'football',
+ 2: 'hockey',
+ 3: 'basketball',
+} as const
diff --git a/src/features/Search/hooks/index.tsx b/src/features/Search/hooks/index.tsx
new file mode 100644
index 00000000..cda7504c
--- /dev/null
+++ b/src/features/Search/hooks/index.tsx
@@ -0,0 +1,85 @@
+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'
+import { getSearchItems } from 'requests'
+import { useToggle } from 'hooks'
+
+import { SEARCH_DELAY, MIN_CHARACTERS_LENGTH } from '../config'
+import { useNormalizedItems } from './useNormalizedItems'
+
+export const useSearch = () => {
+ const [searchItems, setSearchItems] = useState({})
+ const abortControllerRef = useRef(null)
+ const {
+ close,
+ isOpen,
+ open,
+ } = useToggle()
+
+ const fetchSearchItems = useCallback(debounce((searchString: string) => {
+ const abortController = new window.AbortController()
+ abortControllerRef.current = abortController
+
+ getSearchItems(searchString, abortController.signal).then((data) => {
+ setSearchItems(data)
+ abortControllerRef.current = null
+ })
+ }, SEARCH_DELAY), [])
+
+ const cancelRequest = useCallback(() => {
+ const abortController = abortControllerRef.current
+
+ if (abortController) {
+ abortController.abort()
+ abortControllerRef.current = null
+ }
+ }, [])
+
+ const onChange = useCallback(({ target: { value } }: ChangeEvent) => {
+ const trimmedValue = trim(value)
+
+ if (size(trimmedValue) >= MIN_CHARACTERS_LENGTH) {
+ cancelRequest()
+ setSearchItems({})
+ fetchSearchItems(trimmedValue)
+ open()
+ } else {
+ close()
+ }
+ }, [
+ cancelRequest,
+ close,
+ fetchSearchItems,
+ open,
+ ])
+
+ const onFocus = useCallback(({ target: { value } }: FocusEvent) => {
+ if (size(value) >= MIN_CHARACTERS_LENGTH) {
+ open()
+ }
+ }, [open])
+
+ const onSubmit = useCallback((e: FormEvent) => {
+ e.preventDefault()
+ }, [])
+
+ return {
+ close,
+ normalizedItems: useNormalizedItems(searchItems),
+ onChange,
+ onFocus,
+ onSubmit,
+ showResults: isOpen && !isEmpty(searchItems),
+ }
+}
diff --git a/src/features/Search/hooks/useNormalizedItems.tsx b/src/features/Search/hooks/useNormalizedItems.tsx
new file mode 100644
index 00000000..313db36f
--- /dev/null
+++ b/src/features/Search/hooks/useNormalizedItems.tsx
@@ -0,0 +1,104 @@
+import map from 'lodash/map'
+
+import type { SearchItems } from 'requests'
+import { LOGOS_FALLBACKS } from 'config'
+import { getLogo } from 'helpers'
+import { useLexicsStore } from 'features/LexicsStore'
+
+import { SPORT_TYPES } from '../config'
+
+type Firstname = 'firstname_eng' | 'firstname_rus'
+type Lastname = 'lastname_eng' | 'lastname_rus'
+type Name = 'name_eng' | 'name_rus'
+
+export const useNormalizedItems = (searchItems: SearchItems) => {
+ const {
+ suffix,
+ } = useLexicsStore()
+
+ const players = map(searchItems.players, (player) => {
+ const type = 'players'
+
+ const firstName = player[`firstname_${suffix}` as Firstname]
+ const lastName = player[`lastname_${suffix}` as Lastname]
+ const teamName = player.team?.[`name_${suffix}` as Name]
+
+ const sportType = SPORT_TYPES[player.sport]
+
+ const { id } = player
+
+ const logo = getLogo({
+ id,
+ sportType,
+ type,
+ })
+
+ return {
+ fallbackImage: LOGOS_FALLBACKS[sportType].players,
+ id,
+ logo,
+ name: `${firstName} ${lastName}`,
+ profileUrl: `/players/${id}`,
+ sportType,
+ teamOrCountry: teamName,
+ }
+ })
+
+ const teams = map(searchItems.teams, (team) => {
+ const name = team[`name_${suffix}` as Name]
+
+ const sportType = SPORT_TYPES[team.sport]
+
+ const { id } = team
+
+ const logo = getLogo({
+ id,
+ sportType,
+ type: 'teams',
+ })
+
+ const country = team.country?.[`name_${suffix}` as Name]
+
+ return {
+ fallbackImage: LOGOS_FALLBACKS[sportType].teams,
+ id,
+ logo,
+ name,
+ profileUrl: `/teams/${id}`,
+ sportType,
+ teamOrCountry: country,
+ }
+ })
+
+ const tournaments = map(searchItems.tournaments, (tournament) => {
+ const name = tournament[`name_${suffix}` as Name]
+
+ const sportType = SPORT_TYPES[tournament.sport]
+
+ const { id } = tournament
+
+ const logo = getLogo({
+ id,
+ sportType,
+ type: 'tournaments',
+ })
+
+ const country = tournament.country?.[`name_${suffix}` as Name]
+
+ return {
+ fallbackImage: LOGOS_FALLBACKS[sportType].tournaments,
+ id,
+ logo,
+ name,
+ profileUrl: `/tournaments/${id}`,
+ sportType,
+ teamOrCountry: country,
+ }
+ })
+
+ return {
+ players,
+ teams,
+ tournaments,
+ }
+}
diff --git a/src/features/Search/index.tsx b/src/features/Search/index.tsx
index 692b8bc3..caabb9f1 100644
--- a/src/features/Search/index.tsx
+++ b/src/features/Search/index.tsx
@@ -1,5 +1,69 @@
-import React from 'react'
+import React, { Fragment } from 'react'
-import { Wrapper } from './styled'
+import isEmpty from 'lodash/isEmpty'
-export const Search = () =>
+import { Input } from 'features/Common'
+import { ItemsList } from 'features/ItemsList'
+import { OutsideClick } from 'features/OutsideClick'
+
+import { useSearch } from './hooks'
+import { Header } from './components/Header'
+import {
+ Wrapper,
+ Form,
+ Results,
+} from './styled'
+
+export const Search = () => {
+ const {
+ close,
+ normalizedItems: {
+ players,
+ teams,
+ tournaments,
+ },
+ onChange,
+ onFocus,
+ onSubmit,
+ showResults,
+ } = useSearch()
+
+ return (
+
+
+
+ {showResults && (
+
+ {!isEmpty(tournaments) && (
+
+
+
+
+ )}
+
+ {!isEmpty(teams) && (
+
+
+
+
+ )}
+
+ {!isEmpty(players) && (
+
+
+
+
+ )}
+
+ )}
+
+
+ )
+}
diff --git a/src/features/Search/styled.tsx b/src/features/Search/styled.tsx
index 8646cd0a..90fc2fe3 100644
--- a/src/features/Search/styled.tsx
+++ b/src/features/Search/styled.tsx
@@ -1,3 +1,64 @@
import styled from 'styled-components/macro'
-export const Wrapper = styled.div``
+import { сustomScrollbar } from 'features/Common'
+import {
+ InputWrapper,
+ InputStyled,
+ Label,
+} from 'features/Common/Input/styled'
+
+export const Wrapper = styled.div`
+ position: relative;
+`
+
+export const Form = styled.form`
+ ${InputWrapper} {
+ margin: 0;
+ padding-bottom: 13px;
+ }
+
+ ${InputStyled} {
+ width: 100%;
+
+ ::-webkit-search-decoration,
+ ::-webkit-search-cancel-button,
+ ::-webkit-search-results-button,
+ ::-webkit-search-results-decoration {
+ display: none;
+ }
+ }
+
+ ${Label} {
+ ::before {
+ content: '';
+ display: block;
+ width: 25px;
+ height: 25px;
+ background-image: url(/images/search.svg);
+ background-repeat: no-repeat;
+ }
+ }
+
+ :focus-within {
+ ${InputWrapper} {
+ padding-left: 0;
+ }
+
+ ${Label} {
+ ::before {
+ display: none;
+ }
+ }
+ }
+
+`
+
+export const Results = styled.div`
+ position: absolute;
+ top: 56px;
+ width: 448px;
+ max-height: 431px;
+ overflow-y: auto;
+
+ ${сustomScrollbar}
+`
diff --git a/src/features/T9n/index.tsx b/src/features/T9n/index.tsx
index 8346c52d..dc8f9231 100644
--- a/src/features/T9n/index.tsx
+++ b/src/features/T9n/index.tsx
@@ -9,22 +9,25 @@ const Text = styled.span`
${({ customStyles }) => customStyles}
`
-type TT9n = {
+type T9nProps = {
+ className?: string,
onClick?: () => void,
t: string | number,
} & TCustomStyles
export const T9n = ({
+ className,
customStyles,
onClick,
t,
-}: TT9n) => {
+}: T9nProps) => {
const { translate } = useLexicsStore()
return (
{translate(String(t))}
diff --git a/src/helpers/getLogo/__tests__/index.tsx b/src/helpers/getLogo/__tests__/index.tsx
new file mode 100644
index 00000000..b96aa7f9
--- /dev/null
+++ b/src/helpers/getLogo/__tests__/index.tsx
@@ -0,0 +1,23 @@
+import { getLogo } from '..'
+
+describe('getLogo helper', () => {
+ it('returns logo url', () => {
+ expect(getLogo({
+ id: 1,
+ sportType: 'football',
+ type: 'players',
+ })).toBe('https://instatscout.com/images/players/180/1.png')
+
+ expect(getLogo({
+ id: 1,
+ sportType: 'basketball',
+ type: 'teams',
+ })).toBe('https://basketball.instatscout.com/images/teams/180/1.png')
+
+ expect(getLogo({
+ id: 1,
+ sportType: 'hockey',
+ type: 'tournaments',
+ })).toBe('https://hockey.instatscout.com/images/tournaments/180/1.png')
+ })
+})
diff --git a/src/helpers/getLogo/index.tsx b/src/helpers/getLogo/index.tsx
new file mode 100644
index 00000000..dc46ee32
--- /dev/null
+++ b/src/helpers/getLogo/index.tsx
@@ -0,0 +1,23 @@
+import type { SportType } from 'features/Search/config'
+
+const IMAGES_URLS = {
+ basketball: 'https://basketball.instatscout.com/images',
+ football: 'https://instatscout.com/images',
+ hockey: 'https://hockey.instatscout.com/images',
+}
+
+type GetLogoArgs = {
+ id: number,
+ size?: number,
+ sportType: SportType,
+ type: 'players' | 'teams' | 'tournaments',
+}
+
+export const getLogo = ({
+ id,
+ size = 180,
+ sportType,
+ type,
+}: GetLogoArgs) => (
+ `${IMAGES_URLS[sportType]}/${type}/${size}/${id}.png`
+)
diff --git a/src/helpers/index.tsx b/src/helpers/index.tsx
index 1c31f046..f955ad23 100644
--- a/src/helpers/index.tsx
+++ b/src/helpers/index.tsx
@@ -1,3 +1,4 @@
export * from './callApi'
export * from './callApi/getResponseData'
export * from './token'
+export * from './getLogo'
diff --git a/src/requests/getSearchItems.tsx b/src/requests/getSearchItems.tsx
new file mode 100644
index 00000000..89f19091
--- /dev/null
+++ b/src/requests/getSearchItems.tsx
@@ -0,0 +1,75 @@
+import { DATA_URL, PROCEDURES } from 'config'
+import { callApi, getResponseData } from 'helpers'
+
+const proc = PROCEDURES.get_players_teams_tournaments
+
+type Gender = 1 | 2
+
+type Sport = 1 | 2 | 3
+
+type Player = {
+ firstname_eng: string,
+ firstname_rus: string,
+ gender?: Gender,
+ id: number,
+ lastname_eng: string,
+ lastname_rus: string,
+ sport: Sport,
+ team?: {
+ id: number,
+ name_eng: string,
+ name_rus: string,
+ },
+}
+
+type Team = {
+ country?: {
+ id: number,
+ name_eng: string,
+ name_rus: string,
+ },
+ gender?: Gender,
+ id: number,
+ name_eng: string,
+ name_rus: string,
+ sport: Sport,
+}
+
+type Tournament = {
+ country?: {
+ id: number,
+ name_eng: string,
+ name_rus: string,
+ },
+ gender?: Gender,
+ id: number,
+ name_eng: string,
+ name_rus: string,
+ sport: Sport,
+}
+
+export type SearchItems = {
+ players?: Array,
+ teams?: Array,
+ tournaments?: Array,
+}
+
+export const getSearchItems = (
+ searchString: string,
+ abortSignal: AbortSignal,
+): Promise => {
+ const config = {
+ body: {
+ params: {
+ _p_name: searchString,
+ },
+ proc,
+ },
+ }
+
+ return callApi({
+ abortSignal,
+ config,
+ url: DATA_URL,
+ }).then(getResponseData(proc))
+}
diff --git a/src/requests/index.tsx b/src/requests/index.tsx
index 54087053..a877c19e 100644
--- a/src/requests/index.tsx
+++ b/src/requests/index.tsx
@@ -3,3 +3,4 @@ export * from './login'
export * from './getCountries'
export * from './getCountryCities'
export * from './getLexics'
+export * from './getSearchItems'