diff --git a/public/images/player-settings.svg b/public/images/settings.svg similarity index 100% rename from public/images/player-settings.svg rename to public/images/settings.svg diff --git a/src/config/env.tsx b/src/config/env.tsx index e97a18a1..284b1643 100644 --- a/src/config/env.tsx +++ b/src/config/env.tsx @@ -1,3 +1,13 @@ +import includes from 'lodash/includes' + +export type ENVType = NodeJS.ProcessEnv['REACT_APP_ENV'] + +const apis: Array = ['staging', 'preproduction', 'production'] + +export const isValidEnv = (value: string): value is ENVType => ( + Boolean(value) && includes(apis, value) +) + export const ENV = process.env.REACT_APP_ENV || 'staging' export const isProduction = ENV === 'production' || ENV === 'preproduction' diff --git a/src/config/routes.tsx b/src/config/routes.tsx index 58a19ef6..06912cb2 100644 --- a/src/config/routes.tsx +++ b/src/config/routes.tsx @@ -1,6 +1,8 @@ -import { ENV } from './env' +import { readSelectedApi } from 'helpers/selectedApi' -const APIS = { +import { ENV, isProduction } from './env' + +export const APIS = { preproduction: { api: 'https://api-test.instat.tv', auth: 'https://auth.instat.tv', @@ -15,6 +17,8 @@ const APIS = { }, } -export const AUTH_SERVICE = APIS[ENV].auth -export const API_ROOT = APIS[ENV].api +const env = isProduction ? ENV : readSelectedApi() ?? ENV + +export const AUTH_SERVICE = APIS[env].auth +export const API_ROOT = APIS[env].api export const DATA_URL = `${API_ROOT}/data` diff --git a/src/features/Animation/index.tsx b/src/features/Animation/index.tsx new file mode 100644 index 00000000..a6e63f95 --- /dev/null +++ b/src/features/Animation/index.tsx @@ -0,0 +1,28 @@ +import type { ReactNode } from 'react' +import styled, { keyframes } from 'styled-components/macro' + +const fadeIn = keyframes` + 0% { + opacity: 0; + } + 100% { + opacity: 1; + } +` + +const Wrapper = styled.div` + opacity: 1; + animation-name: ${fadeIn}; + animation-iteration-count: 1; + animation-timing-function: ease-in-out; + animation-duration: 0.3s; +` + +type Props = { + children: ReactNode, + className?: string, +} + +export const FadeIn = ({ children, className }: Props) => ( + {children} +) diff --git a/src/features/App/AuthenticatedApp.tsx b/src/features/App/AuthenticatedApp.tsx index a25d174b..4edb35ef 100644 --- a/src/features/App/AuthenticatedApp.tsx +++ b/src/features/App/AuthenticatedApp.tsx @@ -7,7 +7,8 @@ import { } from 'react-router-dom' import { indexLexics } from 'config/lexics/indexLexics' -import { PAGES } from 'config' +import { isProduction } from 'config/env' +import { PAGES } from 'config/pages' import { StripeElements } from 'features/StripeElements' @@ -28,6 +29,7 @@ const TeamPage = lazy(() => import('features/TeamPage')) const MatchPage = lazy(() => import('features/MatchPage')) const PlayerPage = lazy(() => import('features/PlayerPage')) const TournamentPage = lazy(() => import('features/TournamentPage')) +const SystemSettings = lazy(() => import('features/SystemSettings')) export const AuthenticatedApp = () => { useLexicsConfig(indexLexics) @@ -69,6 +71,7 @@ export const AuthenticatedApp = () => { + {!isProduction && } diff --git a/src/features/Combobox/hooks/index.tsx b/src/features/Combobox/hooks/index.tsx index f48e67e7..be5ce81a 100644 --- a/src/features/Combobox/hooks/index.tsx +++ b/src/features/Combobox/hooks/index.tsx @@ -88,8 +88,8 @@ export const useCombobox = ({ onSelect, ]) - const onOutsideClick = (event: MouseEvent) => { - if (event.target !== inputFieldRef.current) { + const onOutsideClick = (event?: MouseEvent) => { + if (event?.target !== inputFieldRef.current) { close() } } diff --git a/src/features/HeaderFilters/components/DateFilter/index.tsx b/src/features/HeaderFilters/components/DateFilter/index.tsx index bc7233e7..8fca5e27 100644 --- a/src/features/HeaderFilters/components/DateFilter/index.tsx +++ b/src/features/HeaderFilters/components/DateFilter/index.tsx @@ -1,7 +1,10 @@ +import { Fragment } from 'react' + import map from 'lodash/map' import { OutsideClick } from 'features/OutsideClick' import { Date as DateIcon } from 'features/Icons/Date' +import { BodyBackdrop } from 'features/PageLayout' import { useDateFilter } from './hooks' import { DatePicker } from '../DatePicker' @@ -73,13 +76,16 @@ export const DateFilter = () => { { isOpen && ( - - - + + + + + + ) } diff --git a/src/features/HeaderFilters/components/DatePicker/index.tsx b/src/features/HeaderFilters/components/DatePicker/index.tsx index eafcb1b6..5be2baea 100644 --- a/src/features/HeaderFilters/components/DatePicker/index.tsx +++ b/src/features/HeaderFilters/components/DatePicker/index.tsx @@ -3,7 +3,6 @@ import DatePickerComponent from 'react-datepicker' import 'react-datepicker/dist/react-datepicker.css' import { useLexicsStore } from 'features/LexicsStore' -import { BodyBackdrop } from 'features/PageLayout' import { getDisplayDate } from '../DateFilter/helpers' import { useDatepickerLocales } from './hooks' @@ -58,7 +57,6 @@ export const DatePicker = ({ useDatepickerLocales() return ( - void, + onClick: (event?: MouseEvent) => void, } export const useOutsideClickEffect = ({ @@ -21,7 +21,14 @@ export const useOutsideClickEffect = ({ } } + const onKeyPress = (e: KeyboardEvent) => { + if (e.key === 'Escape') { + onClick() + } + } + useEventListener({ callback: handleOutsideClick, event: 'click' }) + useEventListener({ callback: onKeyPress, event: 'keydown' }) return wrapperRef } diff --git a/src/features/OutsideClick/index.tsx b/src/features/OutsideClick/index.tsx index 0376992d..ddfa62aa 100644 --- a/src/features/OutsideClick/index.tsx +++ b/src/features/OutsideClick/index.tsx @@ -8,7 +8,7 @@ type Props = { /** элемент, которому необходим функционал `OutsideClick` */ children: ReactNode, /** функция-коллбек, отрабатывающая по клику вне области элемента */ - onClick: (event: MouseEvent) => void, + onClick: (event?: MouseEvent) => void, } const OutsideClickWrapper = styled.div`` diff --git a/src/features/SystemSettings/components/APISettings/index.tsx b/src/features/SystemSettings/components/APISettings/index.tsx new file mode 100644 index 00000000..e4036946 --- /dev/null +++ b/src/features/SystemSettings/components/APISettings/index.tsx @@ -0,0 +1,70 @@ +import { Fragment } from 'react' +import styled from 'styled-components/macro' + +import map from 'lodash/map' + +import type { ENVType } from 'config/env' +import { APIS } from 'config/routes' + +import { RadioGroup, RadioButton } from '../RadioButtons' + +import { SettingsDescription } from '../../styled' + +const Details = styled.span` + margin-top: 4px; + font-size: 10px; + text-transform: initial; +` + +const getHost = (url: string) => new URL(url).host + +type Option = { + details: string, + key: ENVType, + label: string, +} + +const options: Array