From ada3c6ecfea2a6f941ad35f070c0c709bb953ca6 Mon Sep 17 00:00:00 2001 From: Mirlan Date: Tue, 24 Aug 2021 16:35:56 +0600 Subject: [PATCH] Ott 1111 redesign (#435) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * refactor(1115): added some design tokens (#362) * style(1115): login page redesign (#363) * style(1115): password reset popup (#364) * Ott 1117 part 1 (#365) * style(1115): redesign * refactor(1115): auth refactor * refactor(1115): removed old registration step components (#366) * style(1114): user favorites (#367) * style(1114): user favorites * Update src/features/UserFavorites/TooltipBlock/styled.tsx Co-authored-by: Andrey Razdorskiy * Update src/features/UserFavorites/TooltipBlock/styled.tsx Co-authored-by: Andrey Razdorskiy Co-authored-by: Andrey Razdorskiy * Ott 1112 header (#372) * style(1112): removed old components * style(1112): header redesign (#369) * style(1112): wip (#370) * style(1112): date filter (#371) Co-authored-by: Ruslan Khayrullin * Ott 1133 profile header color (#374) * refactor(1133): removed unused AvailableMatches * feat(1133): dynamic header gradient * Ott 1113 home page (#375) * fix(1113): date filter day click fix * style(1113): match list * fix(1113): comment fix * fix(1113): date filter bug fix * style(#ott1167): responsive styles added (#377) Co-authored-by: Farber Denis * Ott 1186 match page (#381) * refactor: removed extended search page (#379) * Ott 1186 part 2 (#380) * style: match page redesign * style: restyled right playlists block * style: responsive date filter * fix(1197): display country flag in match card (#383) * style(1188): reduced match preview brightness (#384) * fix(1191): hover on team logos (#386) * Ott 1190 remove match popup (#385) * refactor(1190): redirecting to match profile * refactor(1190): removed finished match popup * fix(1190): player, two player stay active on last episode * style(1121): profile cards (#387) * style(1212): changed match preview opacity (#389) * Ott 1119 search (#388) * refactor(1119): removed SportType filter * refactor(1119): search redesign * feat(ott-273): scaling every element on the site (#391) * feat(ott-273): scaling every element on the site * refactor(ott-273): short fix for PR * fix(1111): fixed styled theme color reference (#392) * Ott 1211 finished match side block (#396) * refactor(1211): finished match right block redesign * feat(ott-1211): added lexics to match side playlists (#394) * refactor(ott-1211): added lexics to side playlists * refactor(ott-1211): refactor Co-authored-by: Mirlan * refactor(ott-1124): live match popup new design (#398) * Ott 1127 user account redesign (#402) * refactor(1127): removed subs select modal in user account (#397) * style(1127): user account redesign (#399) * Ott 1127 part 3 (#400) * refactor(1127): updated form fields * refactor(1127): added password inputs * fix(1127): fixed types * Ott 1127 part 4 (#401) * fix(1127): design fixes * fix(1127): language change * fix(1127): combobox fix * fix(1276): video endpoint change (#403) * Ott 1126 buy match popup (#406) * fix(ott-1280): return match overview (#407) * Ott 1249 auth mobile (#408) * feat(ott-1295): added some changes for preprod build (#409) * fix(ott-1295): small fix (#410) * Ott 1250 registration mobile (#411) * fix(ott-1341): fixed live match label position (#412) * fix(1345): close popup on live match click (#413) * fix(ott-1290): fix switch color (#415) Co-authored-by: Alex * fix(ott-1291): rm target blank on user favourites (#416) * fix(#1289): added adaptive(1370 px) for pay form (#417) * fix(#1289): added adaptive(1370 px) for pay form * refactor(#1289): added space * Ott 1282 buy match responsive (#414) * style(#1282): buy match page responcive styles added * style(#1282): buy match responsive styles added * style(#1282): cards styles fixes Co-authored-by: Farber Denis * feat(ott-1388): added env to configure api url (#418) * Ott 1387 main page mobile (#419) * style(#1387): main page mobile styles added part 1 * style(#1387): datepicker styles added part 2 * style(#1387): search bar styles fix * style(#1387): fixed match time icon * style(#1387): comments fix Co-authored-by: Farber Denis * fix(ott-1348): show team name abbrs (#420) * fix(ott-1409): add tournament link (#421) * feat(ott-1414): added make prod script (#422) * fix(ott-1413): hide fb google login (#424) * fix(1407): fixed switch colors (#423) * Ott 1371 hide pay tab (#425) * feat(ott-1371): hide unused pay tabs * feat(ott-1371): code review fix * feat(ott-1371): code review fix again * feat(ott-1371): add use memo and correct types * Ott 1285 video player mobile (#426) * style(#1285): video player mobile styles added * style(#1285): min styles fixes Co-authored-by: Farber Denis * Ott 1394 logging (#427) * feat(ott-1394): page change logging * feat(1394): player playlist change logging * style(#1422): min style fixes (#428) Co-authored-by: Farber Denis * style(#1423): video player play button fixed (#429) Co-authored-by: Farber Denis * fix(1396): player fix on ios (#430) * style(#1431): fixed live popup (#431) Co-authored-by: Farber Denis * style(fix): payment block style fix (#432) Co-authored-by: Farber Denis * fix(1434): play hls until has video true (#433) * fix(1373): show/hide card form labels (#434) Co-authored-by: Andrey Razdorskiy Co-authored-by: Ruslan Khayrullin Co-authored-by: Farber Denis <42491613+Bombamuerta@users.noreply.github.com> Co-authored-by: Farber Denis Co-authored-by: Sergiu <46888793+Serj10GR@users.noreply.github.com> Co-authored-by: Иван Пиминов <61900450+ivan-piminov@users.noreply.github.com> Co-authored-by: Alex Co-authored-by: Serg <936x936@gmail.com> Co-authored-by: KarelinDm <58763564+KarelinDm@users.noreply.github.com> --- Makefile | 22 +- public/images/arrowUp.svg | 2 +- public/images/basketball-icon.svg | 11 + public/images/clear.svg | 4 +- public/images/date.svg | 3 + public/images/facebook.png | Bin 0 -> 604 bytes public/images/football-icon.svg | 3 + public/images/google.png | Bin 0 -> 873 bytes public/images/header-settings.svg | 16 + public/images/hockey-icon.svg | 4 + public/images/logo.svg | 17 +- public/images/logout.svg | 7 +- public/images/score-switch-mobile-off.svg | 17 + public/images/score-switch-mobile-on.svg | 17 + public/images/score-switch-off.svg | 45 ++- public/images/score-switch-on.svg | 45 ++- public/images/search.svg | 2 +- public/images/settings.svg | 11 - public/images/visibility.svg | 3 + public/images/worldIcon.svg | 2 +- public/index.html | 7 +- src/config/env.tsx | 4 +- src/config/form.tsx | 3 + src/config/lexics/indexLexics.tsx | 26 +- src/config/lexics/public.tsx | 4 +- src/config/lexics/userAccount.tsx | 12 + src/config/pages.tsx | 1 - src/config/procedures.tsx | 1 + src/config/routes.tsx | 10 +- src/config/userAgent.tsx | 5 + .../components/ElementContainer/index.tsx | 33 +- .../components/Form/hooks/index.tsx | 91 +++++- .../AddCardForm/components/Form/index.tsx | 95 ++++-- src/features/AddCardForm/index.tsx | 15 +- src/features/AddCardForm/styled.tsx | 42 ++- src/features/App/AuthenticatedApp.tsx | 23 +- src/features/App/UnauthenticatedApp.tsx | 25 +- src/features/Background/styled.tsx | 1 + .../components/CardStep/index.tsx | 34 +- .../components/CardsList/index.tsx | 46 ++- .../components/ErrorStep/index.tsx | 30 +- .../components/SelectedCard/index.tsx | 51 ++- .../SubscriptionSelectionStep/index.tsx | 16 +- .../components/Subscriptions/index.tsx | 17 +- .../components/SubscriptionsList/index.tsx | 32 +- .../components/SubscriptionsList/styled.tsx | 160 ++++++++- .../components/SuccessStep/index.tsx | 32 +- src/features/BuyMatchPopup/index.tsx | 9 +- src/features/BuyMatchPopup/store/config.tsx | 23 ++ src/features/BuyMatchPopup/store/helpers.tsx | 114 +++++-- .../BuyMatchPopup/store/hooks/index.tsx | 39 ++- .../store/hooks/useLexicsFetcher.tsx | 25 -- .../store/hooks/useSubscriptions.tsx | 39 ++- src/features/BuyMatchPopup/styled.tsx | 137 ++++++-- src/features/BuyMatchPopup/types.tsx | 16 +- src/features/CardsStore/hooks/index.tsx | 14 +- src/features/Combobox/hooks/index.tsx | 21 +- src/features/Combobox/index.tsx | 2 + src/features/Combobox/types.tsx | 3 +- src/features/Common/Arrows/index.tsx | 37 --- src/features/Common/Arrows/stories.tsx | 21 -- src/features/Common/Button/styled.tsx | 41 +-- src/features/Common/Checkbox/styled.tsx | 2 +- src/features/Common/Input/index.tsx | 3 +- src/features/Common/Input/styled.tsx | 127 +++----- src/features/Common/InputGroup/index.tsx | 14 + src/features/Common/NewInput/index.tsx | 55 ++++ src/features/Common/NewInput/styled.tsx | 84 +++++ src/features/Common/PasswordInput/index.tsx | 58 ++++ src/features/Common/Radio/styled.tsx | 2 +- src/features/Common/RadioButtons/index.tsx | 93 ------ src/features/Common/StarIcon/index.tsx | 22 -- src/features/Common/Tabs/index.tsx | 86 +++++ src/features/Common/customScrollbar/index.tsx | 5 - src/features/Common/index.tsx | 7 +- .../components/DesktopHeader/index.tsx | 24 -- .../components/Filters/index.tsx | 29 -- .../components/Filters/styled.tsx | 43 --- .../components/GenderFilter/index.tsx | 34 -- .../components/MobileHeader/index.tsx | 31 -- .../components/ProfileFilter/index.tsx | 42 --- .../components/Results/index.tsx | 44 --- .../components/SearchInput/index.tsx | 54 ---- .../components/SportTypeFilter/index.tsx | 17 - src/features/ExtendedSearchPage/index.tsx | 30 -- .../ExtendedSearchPage/store/hooks/index.tsx | 41 +-- .../store/hooks/useSearchRequest.tsx | 12 +- src/features/ExtendedSearchPage/styled.tsx | 41 --- src/features/GlobalStyles/index.tsx | 9 +- .../components/DateFilter/helpers.tsx | 37 ++- .../components/DateFilter/hooks/index.tsx | 19 +- .../components/DateFilter/index.tsx | 98 +++--- .../components/DateFilter/styled.tsx | 305 +++++++++--------- .../components/DatePicker/index.tsx | 27 +- .../components/DatePicker/styled.tsx | 136 ++++++-- .../components/MatchStatusFilter/index.tsx | 45 --- .../components/SportTypeFilter/index.tsx | 63 ---- .../components/TournamentFilter/hooks.tsx | 81 ----- .../components/TournamentFilter/index.tsx | 63 ---- .../components/TournamentFilter/styled.tsx | 134 -------- .../components/TournamentList/index.tsx | 47 --- .../components/TournamentList/styled.tsx | 27 -- src/features/HeaderFilters/index.tsx | 3 - .../store/helpers/dateSerializers/index.tsx | 8 + .../HeaderFilters/store/hooks/index.tsx | 2 + src/features/HeaderMobile/index.tsx | 34 +- src/features/HeaderMobile/styled.tsx | 24 +- .../HomePage/components/Header/index.tsx | 58 ++-- src/features/HomePage/index.tsx | 56 ++-- src/features/HomePage/styled.tsx | 23 -- src/features/Icons/Close/index.tsx | 10 +- src/features/Icons/Date/index.tsx | 5 + src/features/ItemsList/index.tsx | 74 ++--- src/features/ItemsList/styled.tsx | 87 ++--- src/features/LanguageSelect/styled.tsx | 73 +++-- src/features/LexicsStore/hooks/useLang.tsx | 2 +- .../LexicsStore/hooks/useTranslations.tsx | 12 +- src/features/LexicsStore/types.tsx | 2 + src/features/Loader/styled.tsx | 6 +- .../components/AuthProviderButton/index.tsx | 74 +++++ .../components/PasswordResetPopup/index.tsx | 53 +++ .../components/PasswordResetPopup/styled.tsx | 57 ++++ src/features/Login/hooks.tsx | 3 - src/features/Login/index.tsx | 36 ++- src/features/Login/styled.tsx | 170 ++++++---- src/features/Logo/index.tsx | 27 +- src/features/MainWrapper/index.tsx | 12 - .../MatchCard/CardFrontside/index.tsx | 118 ++++--- .../MatchCard/NoAccessMessage/index.tsx | 12 +- src/features/MatchCard/config.tsx | 3 + src/features/MatchCard/config/index.tsx | 3 - src/features/MatchCard/styled.tsx | 291 +++++++++++------ .../components/FinishedMatch/hooks/index.tsx | 15 +- .../FinishedMatch/hooks/usePlayerLogger.tsx | 72 +++++ .../components/FinishedMatch/index.tsx | 6 - .../MatchPage/components/LiveMatch/index.tsx | 10 +- .../LiveMatchSidePlaylists/index.tsx | 49 ++- .../components/MatchProfileCard/index.tsx | 48 ++- .../components/MatchProfileCard/styled.tsx | 126 ++++---- .../MatchPage/helpers/buildPlaylists.tsx | 13 +- src/features/MatchPage/index.tsx | 29 +- src/features/MatchPage/styled.tsx | 49 ++- src/features/MatchPage/types.tsx | 14 +- .../components/ApplyButton/index.tsx | 28 -- .../components/BackButton/index.tsx | 15 - .../EpisodeDurationInputs/styled.tsx | 20 +- .../FinishedMatchPlaylist/index.tsx | 74 ----- .../components/FinishedMatchPopup/index.tsx | 56 ---- .../components/FinishedMatchPopup/styled.tsx | 27 -- .../components/FinishedPlaylistPage/index.tsx | 81 ----- .../components/GroupedPlayersList/index.tsx | 79 ----- .../components/GroupedPlayersList/styled.tsx | 29 -- .../components/InterviewCard/index.tsx | 73 ----- .../components/Interviews/index.tsx | 84 ----- .../components/LiveMatchPlaylist/index.tsx | 28 +- .../components/LiveMatchPlaylist/styled.tsx | 28 ++ .../components/LiveMatchPopup/styled.tsx | 22 +- .../components/LivePlaylistPage/index.tsx | 10 +- .../components/LivePlaylistPage/styled.tsx | 54 +++- .../components/MatchSettingsPage/index.tsx | 38 --- .../components/PlayerSettingsPage/index.tsx | 66 ---- .../components/PlayerSettingsPage/styled.tsx | 13 - .../components/PlayersList/index.tsx | 71 ---- .../components/PlayersList/styled.tsx | 130 -------- .../components/PlayersListDesktop/index.tsx | 57 ---- .../components/PlayersListMobile/index.tsx | 55 ---- .../components/PlayersListMobile/styled.tsx | 47 --- .../components/PlaylistButton/index.tsx | 118 ------- .../components/SettingsButton/index.tsx | 15 - .../components/SettingsDesktop/index.tsx | 75 ----- .../components/SettingsMobile/hooks.tsx | 38 --- .../components/SettingsMobile/index.tsx | 41 --- .../components/SimplePlaylistButton/index.tsx | 99 ++---- .../SimplePlaylistButton/styled.tsx | 87 +++++ src/features/MatchPopup/index.tsx | 3 +- src/features/MatchPopup/store/hooks/index.tsx | 29 +- .../store/hooks/usePlayerClickHandler.tsx | 16 +- .../store/hooks/usePopupNavigation.tsx | 54 ---- src/features/MatchPopup/styled.tsx | 23 -- src/features/MatchPopup/types.tsx | 5 - .../components/DropdownSection/index.tsx | 10 +- .../components/DropdownSection/styled.tsx | 29 +- .../components/MatchPlaylists/index.tsx | 15 +- .../components/PlayButton/index.tsx | 10 +- .../components/PlayersPlaylists/index.tsx | 111 ++++--- .../components/PlayersPlaylists/styled.tsx | 72 +++++ .../components/SideInterviews/index.tsx | 2 +- .../components/TabWatch/index.tsx | 49 +++ src/features/MatchSidePlaylists/config.tsx | 5 + src/features/MatchSidePlaylists/helpers.tsx | 5 + src/features/MatchSidePlaylists/hooks.tsx | 12 + src/features/MatchSidePlaylists/index.tsx | 106 +++--- src/features/MatchSidePlaylists/styled.tsx | 115 +++++-- .../AvailableMatchesSwitch/index.tsx | 47 --- .../components/ScoreSwitch/index.tsx | 16 +- src/features/MatchSwitches/index.tsx | 1 - src/features/MatchSwitches/styled.tsx | 72 +++-- .../Matches/components/MatchesList/index.tsx | 4 +- .../helpers/getMatchClickAction/index.tsx | 3 +- .../Matches/helpers/prepareMatches.tsx | 2 + src/features/Matches/index.tsx | 3 +- src/features/Matches/styled.tsx | 14 +- src/features/MatchesGrid/styled.tsx | 24 +- src/features/MatchesSlider/styled.tsx | 2 +- src/features/Menu/index.tsx | 45 +-- src/features/Menu/styled.tsx | 66 ++-- src/features/Modal/styled.tsx | 24 +- .../components/Settings/styled.tsx | 17 +- .../MultiSourcePlayer/hooks/index.tsx | 14 +- src/features/MultiSourcePlayer/index.tsx | 2 +- src/features/MultiSourcePlayer/styled.tsx | 18 +- src/features/Name/index.tsx | 20 +- src/features/OutsideClick/index.tsx | 2 +- src/features/PageLayout/index.tsx | 1 + src/features/PageLayout/styled.tsx | 38 +++ src/features/PaymentPeriodTabs/helpers.tsx | 31 ++ src/features/PaymentPeriodTabs/index.tsx | 97 ++++-- src/features/PlayerPage/hooks.tsx | 23 +- src/features/PlayerPage/index.tsx | 37 +-- src/features/PlayerPage/styled.tsx | 11 - .../PopupComponents/BaseButton/index.tsx | 45 ++- .../PopupComponents/CloseButton/index.tsx | 15 +- src/features/PopupComponents/Header/index.tsx | 57 ++-- src/features/Price/index.tsx | 10 +- src/features/Price/styled.tsx | 21 +- .../ProfileCard/components/StarIcon/index.tsx | 46 +++ src/features/ProfileCard/hooks.tsx | 14 +- src/features/ProfileCard/index.tsx | 75 +++-- src/features/ProfileCard/styled.tsx | 150 ++++----- src/features/ProfileCard/types.tsx | 9 - src/features/ProfileHeader/hooks.tsx | 31 ++ src/features/ProfileHeader/index.tsx | 61 ++-- src/features/ProfileHeader/styled.tsx | 124 +++---- src/features/ProfileRoutes/index.tsx | 33 ++ .../AdditionalSubscription/index.tsx | 28 -- .../AdditionalSubscription/styled.tsx | 44 --- .../Register/components/CardStep/index.tsx | 44 --- .../components/MainSubscription/index.tsx | 28 -- .../RegistrationStep/hooks/index.tsx | 36 +-- .../hooks/useSubmitHandler.tsx | 36 --- .../hooks/useValidateForm.tsx | 40 --- .../components/RegistrationStep/index.tsx | 93 ++++-- .../RegistrationSuccessful/styled.tsx | 34 +- .../components/SubscriptionsStep/index.tsx | 69 ---- .../components/SubscriptionsStep/styled.tsx | 181 ----------- src/features/Register/index.tsx | 37 +-- src/features/Register/styled.tsx | 201 ------------ src/features/Search/helpers.tsx | 19 +- src/features/Search/hooks/index.tsx | 59 +++- src/features/Search/index.tsx | 112 ++++--- src/features/Search/styled.tsx | 270 +++++++++------- src/features/SportIcon/index.tsx | 27 ++ src/features/SportTypeFilter/hooks.tsx | 59 ---- src/features/SportTypeFilter/index.tsx | 72 ----- src/features/SportTypeFilter/styled.tsx | 61 ---- .../components/ProgressBar/styled.tsx | 7 + .../components/VolumeBar/styled.tsx | 31 +- src/features/StreamPlayer/index.tsx | 2 +- src/features/StreamPlayer/styled.tsx | 78 +++++ src/features/T9n/index.tsx | 7 +- src/features/TeamPage/hooks.tsx | 17 +- src/features/TeamPage/index.tsx | 37 +-- src/features/TeamPage/styled.tsx | 37 --- src/features/Theme/config.tsx | 24 +- src/features/Tooltip/index.tsx | 14 +- src/features/TournamentPage/hooks.tsx | 9 +- src/features/TournamentPage/index.tsx | 37 +-- src/features/TournamentPage/styled.tsx | 11 - .../UserAccount/components/BankCard/index.tsx | 4 +- .../components/BankCard/styled.tsx | 3 +- .../UserAccount/components/Header/index.tsx | 22 +- .../components/LogoutButton/index.tsx | 50 +++ .../components/PageBankCards/index.tsx | 36 ++- .../components/PageBankCards/styled.tsx | 44 ++- .../components/PagePaymentsHistory/index.tsx | 97 +++--- .../components/PagePaymentsHistory/styled.tsx | 4 + .../PagePersonalInfo/hooks/index.tsx | 30 +- .../components/PageSubscriptions/index.tsx | 77 +++-- .../components/PageSubscriptions/styled.tsx | 41 ++- .../components/PersonalInfoForm/config.tsx | 14 + .../PersonalInfoForm/hooks/useCities.tsx | 73 ----- .../PersonalInfoForm/hooks/useUserInfo.tsx | 35 +- .../hooks/useUserInfoForm.tsx | 44 +-- .../components/PersonalInfoForm/index.tsx | 105 +++--- .../components/PersonalInfoForm/styled.tsx | 48 ++- .../components/ScoreSwitch/index.tsx | 52 +++ .../components/SubscriptionsBySport/index.tsx | 105 ------ .../components/SubscriptionsList/index.tsx | 48 --- .../components/SubscriptionsList/styled.tsx | 83 ----- .../components/SubscriptionsModal/index.tsx | 39 --- .../components/SubscriptionsModal/styled.tsx | 76 ----- .../UserSubscriptionsList/index.tsx | 13 +- .../UserSubscriptionsList/styled.tsx | 30 +- src/features/UserAccount/index.tsx | 16 +- src/features/UserAccount/styled.tsx | 82 +++-- .../UserFavorites/TooltipBlock/index.tsx | 10 +- .../UserFavorites/TooltipBlock/styled.tsx | 66 ++-- src/features/UserFavorites/hooks/index.tsx | 8 + src/features/UserFavorites/index.tsx | 24 +- src/features/UserFavorites/styled.tsx | 96 +++--- src/features/VideoPlayer/hooks/index.tsx | 4 +- src/features/VideoPlayer/index.tsx | 7 +- src/features/VideoPlayer/styled.tsx | 10 +- src/hooks/useAuthForm.tsx | 62 ++++ src/hooks/useEventListener.tsx | 2 +- src/hooks/usePageLogger.tsx | 46 +++ src/hooks/usePageParams.tsx | 27 ++ src/hooks/useStorage/helpers.tsx | 12 - src/hooks/useStorage/index.tsx | 16 +- src/react-app-env.d.ts | 6 + src/requests/getMatchInfo.tsx | 3 + src/requests/getMatches/getPreviews.tsx | 50 +-- src/requests/getMatches/types.tsx | 3 +- src/requests/getPlayerInfo.tsx | 5 + src/requests/getProfileColor.tsx | 47 +++ src/requests/getSearchItems.tsx | 25 +- src/requests/getSubscriptions.tsx | 44 ++- src/requests/getTeamInfo.tsx | 13 +- src/requests/getUserSportFavs.tsx | 1 + src/requests/getVideos.tsx | 1 + src/requests/logUserAction.tsx | 56 ++++ 321 files changed, 6005 insertions(+), 6873 deletions(-) create mode 100644 public/images/basketball-icon.svg create mode 100644 public/images/date.svg create mode 100644 public/images/facebook.png create mode 100644 public/images/football-icon.svg create mode 100644 public/images/google.png create mode 100644 public/images/header-settings.svg create mode 100644 public/images/hockey-icon.svg create mode 100644 public/images/score-switch-mobile-off.svg create mode 100644 public/images/score-switch-mobile-on.svg delete mode 100644 public/images/settings.svg create mode 100644 public/images/visibility.svg create mode 100644 src/config/userAgent.tsx create mode 100644 src/features/BuyMatchPopup/store/config.tsx delete mode 100644 src/features/BuyMatchPopup/store/hooks/useLexicsFetcher.tsx delete mode 100644 src/features/Common/Arrows/index.tsx delete mode 100644 src/features/Common/Arrows/stories.tsx create mode 100644 src/features/Common/InputGroup/index.tsx create mode 100644 src/features/Common/NewInput/index.tsx create mode 100644 src/features/Common/NewInput/styled.tsx create mode 100644 src/features/Common/PasswordInput/index.tsx delete mode 100644 src/features/Common/RadioButtons/index.tsx delete mode 100644 src/features/Common/StarIcon/index.tsx create mode 100644 src/features/Common/Tabs/index.tsx delete mode 100644 src/features/ExtendedSearchPage/components/DesktopHeader/index.tsx delete mode 100644 src/features/ExtendedSearchPage/components/Filters/index.tsx delete mode 100644 src/features/ExtendedSearchPage/components/Filters/styled.tsx delete mode 100644 src/features/ExtendedSearchPage/components/GenderFilter/index.tsx delete mode 100644 src/features/ExtendedSearchPage/components/MobileHeader/index.tsx delete mode 100644 src/features/ExtendedSearchPage/components/ProfileFilter/index.tsx delete mode 100644 src/features/ExtendedSearchPage/components/Results/index.tsx delete mode 100644 src/features/ExtendedSearchPage/components/SearchInput/index.tsx delete mode 100644 src/features/ExtendedSearchPage/components/SportTypeFilter/index.tsx delete mode 100644 src/features/ExtendedSearchPage/styled.tsx delete mode 100644 src/features/HeaderFilters/components/MatchStatusFilter/index.tsx delete mode 100644 src/features/HeaderFilters/components/SportTypeFilter/index.tsx delete mode 100644 src/features/HeaderFilters/components/TournamentFilter/hooks.tsx delete mode 100644 src/features/HeaderFilters/components/TournamentFilter/index.tsx delete mode 100644 src/features/HeaderFilters/components/TournamentFilter/styled.tsx delete mode 100644 src/features/HeaderFilters/components/TournamentList/index.tsx delete mode 100644 src/features/HeaderFilters/components/TournamentList/styled.tsx create mode 100644 src/features/HeaderFilters/store/helpers/dateSerializers/index.tsx delete mode 100644 src/features/HomePage/styled.tsx create mode 100644 src/features/Icons/Date/index.tsx create mode 100644 src/features/Login/components/AuthProviderButton/index.tsx create mode 100644 src/features/Login/components/PasswordResetPopup/index.tsx create mode 100644 src/features/Login/components/PasswordResetPopup/styled.tsx delete mode 100644 src/features/MainWrapper/index.tsx create mode 100644 src/features/MatchCard/config.tsx delete mode 100644 src/features/MatchCard/config/index.tsx create mode 100644 src/features/MatchPage/components/FinishedMatch/hooks/usePlayerLogger.tsx delete mode 100644 src/features/MatchPopup/components/ApplyButton/index.tsx delete mode 100644 src/features/MatchPopup/components/BackButton/index.tsx delete mode 100644 src/features/MatchPopup/components/FinishedMatchPlaylist/index.tsx delete mode 100644 src/features/MatchPopup/components/FinishedMatchPopup/index.tsx delete mode 100644 src/features/MatchPopup/components/FinishedMatchPopup/styled.tsx delete mode 100644 src/features/MatchPopup/components/FinishedPlaylistPage/index.tsx delete mode 100644 src/features/MatchPopup/components/GroupedPlayersList/index.tsx delete mode 100644 src/features/MatchPopup/components/GroupedPlayersList/styled.tsx delete mode 100644 src/features/MatchPopup/components/InterviewCard/index.tsx delete mode 100644 src/features/MatchPopup/components/Interviews/index.tsx create mode 100644 src/features/MatchPopup/components/LiveMatchPlaylist/styled.tsx delete mode 100644 src/features/MatchPopup/components/MatchSettingsPage/index.tsx delete mode 100644 src/features/MatchPopup/components/PlayerSettingsPage/index.tsx delete mode 100644 src/features/MatchPopup/components/PlayerSettingsPage/styled.tsx delete mode 100644 src/features/MatchPopup/components/PlayersList/index.tsx delete mode 100644 src/features/MatchPopup/components/PlayersList/styled.tsx delete mode 100644 src/features/MatchPopup/components/PlayersListDesktop/index.tsx delete mode 100644 src/features/MatchPopup/components/PlayersListMobile/index.tsx delete mode 100644 src/features/MatchPopup/components/PlayersListMobile/styled.tsx delete mode 100644 src/features/MatchPopup/components/PlaylistButton/index.tsx delete mode 100644 src/features/MatchPopup/components/SettingsButton/index.tsx delete mode 100644 src/features/MatchPopup/components/SettingsDesktop/index.tsx delete mode 100644 src/features/MatchPopup/components/SettingsMobile/hooks.tsx delete mode 100644 src/features/MatchPopup/components/SettingsMobile/index.tsx create mode 100644 src/features/MatchPopup/components/SimplePlaylistButton/styled.tsx delete mode 100644 src/features/MatchPopup/store/hooks/usePopupNavigation.tsx create mode 100644 src/features/MatchSidePlaylists/components/PlayersPlaylists/styled.tsx create mode 100644 src/features/MatchSidePlaylists/components/TabWatch/index.tsx create mode 100644 src/features/MatchSidePlaylists/config.tsx create mode 100644 src/features/MatchSidePlaylists/helpers.tsx create mode 100644 src/features/MatchSidePlaylists/hooks.tsx delete mode 100644 src/features/MatchSwitches/components/AvailableMatchesSwitch/index.tsx create mode 100644 src/features/PageLayout/index.tsx create mode 100644 src/features/PageLayout/styled.tsx create mode 100644 src/features/PaymentPeriodTabs/helpers.tsx delete mode 100644 src/features/PlayerPage/styled.tsx create mode 100644 src/features/ProfileCard/components/StarIcon/index.tsx delete mode 100644 src/features/ProfileCard/types.tsx create mode 100644 src/features/ProfileHeader/hooks.tsx create mode 100644 src/features/ProfileRoutes/index.tsx delete mode 100644 src/features/Register/components/AdditionalSubscription/index.tsx delete mode 100644 src/features/Register/components/AdditionalSubscription/styled.tsx delete mode 100644 src/features/Register/components/CardStep/index.tsx delete mode 100644 src/features/Register/components/MainSubscription/index.tsx delete mode 100644 src/features/Register/components/RegistrationStep/hooks/useSubmitHandler.tsx delete mode 100644 src/features/Register/components/RegistrationStep/hooks/useValidateForm.tsx delete mode 100644 src/features/Register/components/SubscriptionsStep/index.tsx delete mode 100644 src/features/Register/components/SubscriptionsStep/styled.tsx delete mode 100644 src/features/Register/styled.tsx create mode 100644 src/features/SportIcon/index.tsx delete mode 100644 src/features/SportTypeFilter/hooks.tsx delete mode 100644 src/features/SportTypeFilter/index.tsx delete mode 100644 src/features/SportTypeFilter/styled.tsx delete mode 100644 src/features/TeamPage/styled.tsx delete mode 100644 src/features/TournamentPage/styled.tsx create mode 100644 src/features/UserAccount/components/LogoutButton/index.tsx create mode 100644 src/features/UserAccount/components/PersonalInfoForm/config.tsx delete mode 100644 src/features/UserAccount/components/PersonalInfoForm/hooks/useCities.tsx create mode 100644 src/features/UserAccount/components/ScoreSwitch/index.tsx delete mode 100644 src/features/UserAccount/components/SubscriptionsBySport/index.tsx delete mode 100644 src/features/UserAccount/components/SubscriptionsList/index.tsx delete mode 100644 src/features/UserAccount/components/SubscriptionsList/styled.tsx delete mode 100644 src/features/UserAccount/components/SubscriptionsModal/index.tsx delete mode 100644 src/features/UserAccount/components/SubscriptionsModal/styled.tsx create mode 100644 src/hooks/useAuthForm.tsx create mode 100644 src/hooks/usePageLogger.tsx create mode 100644 src/hooks/usePageParams.tsx create mode 100644 src/requests/getProfileColor.tsx create mode 100644 src/requests/logUserAction.tsx diff --git a/Makefile b/Makefile index 277e001a..ad33d2d0 100644 --- a/Makefile +++ b/Makefile @@ -4,17 +4,27 @@ install: develop: npm start -build: +clean: rm -rf build - REACT_APP_PRODUCTION=false npm run build -production-build: - rm -rf build - REACT_APP_PRODUCTION=true REACT_APP_STRIPE_PK=pk_live_ANI76cBhSo69DZUxPmyRVIZW npm run build +build: clean + REACT_APP_ENV=staging npm run build + +preproduction-build: clean + REACT_APP_ENV=preproduction REACT_APP_STRIPE_PK=pk_live_ANI76cBhSo69DZUxPmyRVIZW npm run build + +production-build: clean + REACT_APP_ENV=production REACT_APP_STRIPE_PK=pk_live_ANI76cBhSo69DZUxPmyRVIZW npm run build .PHONY: build -stage: production-build +prod: production-build + rsync -zavP build/ -e 'ssh -p 666' ott@instat.tv:/usr/local/www/ott/wwwroot/ + +preprod: preproduction-build + rsync -zavP build/ -e 'ssh -p 666' ott-test@test.instat.tv:/usr/local/www/ott-test/wwwroot/ + +stage: build rsync -zavP build/ -e 'ssh -p 666' ott-staging@staging.instat.tv:/usr/local/www/ott-staging/wwwroot/ a-stage: build diff --git a/public/images/arrowUp.svg b/public/images/arrowUp.svg index 8cdd1d73..4df36f09 100644 --- a/public/images/arrowUp.svg +++ b/public/images/arrowUp.svg @@ -1,3 +1,3 @@ - + diff --git a/public/images/basketball-icon.svg b/public/images/basketball-icon.svg new file mode 100644 index 00000000..2fd26980 --- /dev/null +++ b/public/images/basketball-icon.svg @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/public/images/clear.svg b/public/images/clear.svg index b86003e5..a90cbda0 100644 --- a/public/images/clear.svg +++ b/public/images/clear.svg @@ -1,3 +1,3 @@ - - + + diff --git a/public/images/date.svg b/public/images/date.svg new file mode 100644 index 00000000..74da27f2 --- /dev/null +++ b/public/images/date.svg @@ -0,0 +1,3 @@ + + + diff --git a/public/images/facebook.png b/public/images/facebook.png new file mode 100644 index 0000000000000000000000000000000000000000..c3282eb716d8549c52a17b0eedb320db2eea7ef5 GIT binary patch literal 604 zcmV-i0;BzjP)VXU`&e z1N%D3WHKH%j6mWZub{k2E|;5=F~X+dzzpI-z-9-BRGoxDZpu2H4kzl8l25+XYDKZo z3U)CigMcB->-AE_qy+VB+Il{pUqFkU2_5w%BaDgWl5xjYLZOg83CPggj)~C$ci3_b z>Vsq!aVg6`4gad`h{a-*l}aVkDRHq_+zyAs@3IYbe8FpTuma~BcqL`O=n6;1KA&$Q z2IZWW|7WvV%7}tso6BHj)M~YIFc_>@EGf#kB*^Q!z9!>ESCDu-t~?+}bZG};beG2A zlF1~~hzkL8rPfTR(?ly23U1m>Nd{9bYQ+@=*W#5OJgF3LQw@a{`mG8R^s8g(FrANr z^Z?jsG){ + + diff --git a/public/images/google.png b/public/images/google.png new file mode 100644 index 0000000000000000000000000000000000000000..29a9ca13b12ec59ea85580f3a34fda0a04f855db GIT binary patch literal 873 zcmV-v1D5=WP)% z6j2!d&djc}uI{du*Nl`*Bf`o83(|V%Dp^4fMiO>0BI!lRr-U+msE3Rox?+h&S%N0m zLl06}p-@C#LeYl`FRLk`OkmO{|_jo6?72@jydWX zHB>gJ2m--6i@6ui;rjd$o}MnzLgyI*?S_rucISfIu?vjB7;t?+VS94} z>^1j6MR9*nxVt;0W`Fz;;m1!ypLfJ>7UOHM+^&M{*7YEwKBt-(hrRl)bh?lp+iD-c z+4KzF~+;DQ4$jieZfiS_8b7C z*UMfy9dNyE#av4xj2A1xu2_jcP=xvJN)WvV)GnLY_(Yg)+yk4Cs9L~qI_UQ0Ni7tl z7*oeLXcJz-aB2WdluIJKNAwl_7f9|2lWp=YCKn7v{opNUz$Pa7P44{R1|=k`iAYxx)=o8OH-AO0Pl&w%D9qL<^ZwvzhX)>>D6?YcpR_C-dk7dP!!RF- zl9_EVPmW+s`XJIwt5hO|Ih<-dGR33r1Fwuz;|MgYyouyDqf5cNhenBRtOV;gvVFN#yHilTZI=U0!5GmgO& zW + + + + + + + + + + + + + + + diff --git a/public/images/hockey-icon.svg b/public/images/hockey-icon.svg new file mode 100644 index 00000000..479b0e84 --- /dev/null +++ b/public/images/hockey-icon.svg @@ -0,0 +1,4 @@ + + + + diff --git a/public/images/logo.svg b/public/images/logo.svg index b36a5c9c..93271fc3 100644 --- a/public/images/logo.svg +++ b/public/images/logo.svg @@ -1,10 +1,9 @@ - - - - - - - - - + + + + + + + + diff --git a/public/images/logout.svg b/public/images/logout.svg index d2219dcc..b0bf83a3 100644 --- a/public/images/logout.svg +++ b/public/images/logout.svg @@ -1,5 +1,4 @@ - - - - + + + diff --git a/public/images/score-switch-mobile-off.svg b/public/images/score-switch-mobile-off.svg new file mode 100644 index 00000000..ddd2f89a --- /dev/null +++ b/public/images/score-switch-mobile-off.svg @@ -0,0 +1,17 @@ + + + + + + + + + + + + + + + + + diff --git a/public/images/score-switch-mobile-on.svg b/public/images/score-switch-mobile-on.svg new file mode 100644 index 00000000..86b0dfe9 --- /dev/null +++ b/public/images/score-switch-mobile-on.svg @@ -0,0 +1,17 @@ + + + + + + + + + + + + + + + + + diff --git a/public/images/score-switch-off.svg b/public/images/score-switch-off.svg index 102d88b5..fc638032 100644 --- a/public/images/score-switch-off.svg +++ b/public/images/score-switch-off.svg @@ -1,25 +1,22 @@ - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + diff --git a/public/images/score-switch-on.svg b/public/images/score-switch-on.svg index 9ad123da..87de3109 100644 --- a/public/images/score-switch-on.svg +++ b/public/images/score-switch-on.svg @@ -1,25 +1,22 @@ - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + diff --git a/public/images/search.svg b/public/images/search.svg index 4890f336..abbdfcf0 100644 --- a/public/images/search.svg +++ b/public/images/search.svg @@ -1,4 +1,4 @@ - + diff --git a/public/images/settings.svg b/public/images/settings.svg deleted file mode 100644 index e04a2b84..00000000 --- a/public/images/settings.svg +++ /dev/null @@ -1,11 +0,0 @@ - - - - - - - - - - - diff --git a/public/images/visibility.svg b/public/images/visibility.svg new file mode 100644 index 00000000..3b0a0943 --- /dev/null +++ b/public/images/visibility.svg @@ -0,0 +1,3 @@ + + + diff --git a/public/images/worldIcon.svg b/public/images/worldIcon.svg index f6fa9772..054b4cb9 100644 --- a/public/images/worldIcon.svg +++ b/public/images/worldIcon.svg @@ -1,4 +1,4 @@ - diff --git a/public/index.html b/public/index.html index 77ff5080..b29a6f78 100644 --- a/public/index.html +++ b/public/index.html @@ -3,10 +3,11 @@ - + - + + Instat TV @@ -15,4 +16,4 @@
- + \ No newline at end of file diff --git a/src/config/env.tsx b/src/config/env.tsx index de85b24d..e97a18a1 100644 --- a/src/config/env.tsx +++ b/src/config/env.tsx @@ -1,3 +1,5 @@ -export const isProduction = process.env.REACT_APP_PRODUCTION === 'true' +export const ENV = process.env.REACT_APP_ENV || 'staging' + +export const isProduction = ENV === 'production' || ENV === 'preproduction' export const STRIPE_PUBLIC_KEY = process.env.REACT_APP_STRIPE_PK || 'pk_test_fkEjSoWfJXuCwMgwHRpbOGPt' diff --git a/src/config/form.tsx b/src/config/form.tsx index 6ef1b45f..b4e6ffb5 100644 --- a/src/config/form.tsx +++ b/src/config/form.tsx @@ -9,7 +9,10 @@ export const formIds = { firstname: 'firstname', formError: 'formError', initialCountryId: 'initialCountryId', + language: 'language', lastname: 'lastname', + newPassword1: 'newPassword1', + newPassword2: 'newPassword2', password: 'password', phone: 'phone', postalCode: 'postal_code', diff --git a/src/config/lexics/indexLexics.tsx b/src/config/lexics/indexLexics.tsx index 32f9cbef..3f53847a 100644 --- a/src/config/lexics/indexLexics.tsx +++ b/src/config/lexics/indexLexics.tsx @@ -5,10 +5,13 @@ import { publicLexics } from './public' const matchPopupLexics = { apply: 13491, episode_duration: 13410, + events: 1020, gk: 3515, go_back_to_match: 13405, + languages: 15030, match_interviews: 13031, match_settings: 13490, + players_episodes: 13398, playlist_format: 13406, playlist_format_all_actions: 13408, playlist_format_all_match_time: 13407, @@ -16,20 +19,32 @@ const matchPopupLexics = { sec_after: 13412, sec_before: 13411, selected_player_actions: 13413, - team_players: 13398, + watch: 818, watch_from: 13022, watch_live_stream: 13020, watch_players_episodes: 14052, } const buyMatchPopupLexics = { + add: 15075, + adding_card: 15074, buy_for: 14095, buy_subscription: 13565, change_card: 13564, choose_subscription: 13563, + description_all_season_matches: 15069, + description_all_team_matches: 15070, + description_away_team_matches: 15072, + description_home_team_matches: 15071, + description_match_live_and_on_demand: 15068, error_not_enough_balance: 14098, for_month: 13561, + for_view: 15064, for_year: 13562, + pass_league: 15065, + pass_match_access: 15067, + pass_team: 15066, + pay: 15073, payment: 14096, payment_confirmation: 14094, per_month: 13573, @@ -39,9 +54,8 @@ const buyMatchPopupLexics = { } export const indexLexics = { - add_to_favorites: 1701, + add_to_favorites: 14967, add_to_favorites_error: 12943, - added_to_favorites: 13048, all_matches_shown: 13386, available_matches_shown: 13385, basketball: 6960, @@ -72,15 +86,15 @@ export const indexLexics = { match_video: 13025, no_match_access_body: 13419, no_match_access_title: 13418, - player: 630, + player: 14975, players_video: 13032, round_highilights: 13050, save: 828, search_results: 9014, sport: 12993, - team: 658, + team: 14973, to_home: 13376, - tournament: 1009, + tournament: 14974, user_account: 12928, watch_from_beginning: 13021, watch_from_last_pause: 13022, diff --git a/src/config/lexics/public.tsx b/src/config/lexics/public.tsx index fc34e997..071e23bd 100644 --- a/src/config/lexics/public.tsx +++ b/src/config/lexics/public.tsx @@ -24,14 +24,16 @@ export const publicLexics = { form_phone: 1656, form_postal_code: 12913, form_region: 12932, + go_back: 1907, login: 13404, next: 10875, register: 13328, registration_successful: 12945, select_language: 1005, + sign_up: 1305, step_title_card: 12917, step_title_login: 13404, - step_title_registration: 13328, + step_title_registration: 1306, step_title_subscription: 12919, subscription_done: 2668, subscription_extra: 6036, diff --git a/src/config/lexics/userAccount.tsx b/src/config/lexics/userAccount.tsx index cf6e031b..b093526e 100644 --- a/src/config/lexics/userAccount.tsx +++ b/src/config/lexics/userAccount.tsx @@ -3,6 +3,8 @@ import { paymentLexics } from './payment' const navigations = { bank_card: 14205, + logout: 15058, + my_devices: 15052, my_subscriptions: 8316, payment_history: 14206, personal_info: 14204, @@ -10,20 +12,30 @@ const navigations = { export const userAccountLexics = { change: 12614, + change_password: 15054, country: 835, delete: 848, delete_card: 8692, + language: 15053, lastname: 858, mail: 12912, main: 13014, month: 13019, name: 645, next_debit: 13018, + password_current: 15055, + password_new: 15056, + password_repeat: 15057, payment: 13015, + payment_info: 15059, phone: 1656, + remove: 15063, save_changes: 13017, select_subscription: 12583, + subs_will_not_be_renewed: 15060, subscriptions: 13016, + subscriptions_active: 15061, + subscriptions_removed: 15062, user_account: 12928, ...navigations, ...publicLexics, diff --git a/src/config/pages.tsx b/src/config/pages.tsx index aaa94cd9..affdcc95 100644 --- a/src/config/pages.tsx +++ b/src/config/pages.tsx @@ -1,5 +1,4 @@ export const PAGES = { - extendedSearch: '/search', home: '/', login: '/login', match: '/matches', diff --git a/src/config/procedures.tsx b/src/config/procedures.tsx index 8fe91f0b..3ea1a294 100644 --- a/src/config/procedures.tsx +++ b/src/config/procedures.tsx @@ -29,5 +29,6 @@ export const PROCEDURES = { save_user_favorite: 'save_user_favorite', save_user_info: 'save_user_info', save_user_match_second: 'save_user_match_second', + save_user_page: 'save_user_page', save_user_subscription: 'save_user_subscription', } diff --git a/src/config/routes.tsx b/src/config/routes.tsx index 897c6015..5afec547 100644 --- a/src/config/routes.tsx +++ b/src/config/routes.tsx @@ -1,2 +1,10 @@ -export const API_ROOT = 'https://api.instat.tv' +import { ENV } from './env' + +const APIS = { + preproduction: 'https://api-test.instat.tv', + production: 'https://api.instat.tv', + staging: 'https://api-staging.instat.tv', +} + +export const API_ROOT = APIS[ENV] export const DATA_URL = `${API_ROOT}/data` diff --git a/src/config/userAgent.tsx b/src/config/userAgent.tsx new file mode 100644 index 00000000..2e588933 --- /dev/null +++ b/src/config/userAgent.tsx @@ -0,0 +1,5 @@ +import { includes } from 'lodash' + +export const isMobileDevice = includes(window.navigator.userAgent, 'Android') || includes(window.navigator.userAgent, 'iPhone') +// удалю когда закончу с адаптивом. +// || includes(window.navigator.userAgent, 'Linux') diff --git a/src/features/AddCardForm/components/ElementContainer/index.tsx b/src/features/AddCardForm/components/ElementContainer/index.tsx index 45be3d32..49bd4cd7 100644 --- a/src/features/AddCardForm/components/ElementContainer/index.tsx +++ b/src/features/AddCardForm/components/ElementContainer/index.tsx @@ -1,6 +1,8 @@ import { ReactNode } from 'react' -import styled from 'styled-components/macro' +import styled, { css } from 'styled-components/macro' + +import { isMobileDevice } from 'config/userAgent' import { T9n } from 'features/T9n' @@ -30,15 +32,42 @@ const Label = styled.label` :not(:first-child) { margin-top: 10px; } + + ${isMobileDevice + ? css` + height: 36px; + font-size: 12px; + padding: 0 13px; + align-items: center; + :nth-child(3){ + width: 48%; + } + :nth-child(4){ + width: 48%; + } + ` + : ''}; ` const Text = styled(T9n)` - margin-right: 20px; + margin-right: 5px; + white-space: nowrap; + ${isMobileDevice + ? css` + line-height: 12px; + ` + : ''}; ` const ElementWrapper = styled.div` height: 100%; flex-grow: 1; + ${isMobileDevice + ? css` + height: auto; + line-height: 12px; + ` + : ''}; ` type Props = { diff --git a/src/features/AddCardForm/components/Form/hooks/index.tsx b/src/features/AddCardForm/components/Form/hooks/index.tsx index 23051384..370ff9eb 100644 --- a/src/features/AddCardForm/components/Form/hooks/index.tsx +++ b/src/features/AddCardForm/components/Form/hooks/index.tsx @@ -1,26 +1,94 @@ -import { FormEvent, useState } from 'react' +import type { + FormEvent, + ReactNode, + ChangeEvent, +} from 'react' +import { + useState, + useEffect, +} from 'react' +import type { StripeElementChangeEvent } from '@stripe/stripe-js' import { CardNumberElement, useStripe, useElements, } from '@stripe/react-stripe-js' +import toUpper from 'lodash/toUpper' + +import { useObjectState } from 'hooks' + import { useCardsStore } from 'features/CardsStore' +export enum ElementTypes { + CardCvc = 'cardCvc', + CardExpiry = 'cardExpiry', + CardHolder = 'cardHolder', + CardNumber = 'cardNumber', +} + export type Props = { + children?: ReactNode, initialformOpen?: boolean, inputsBackground?: string, onAddSuccess?: () => void, - submitButton?: 'outline' |'solid', +} + +const inputState = { + empty: true, + focused: false, +} + +const initialState = { + cardCvc: inputState, + cardExpiry: inputState, + cardHolder: inputState, + cardNumber: inputState, } export const useFormSubmit = ({ onAddSuccess }: Props) => { const stripe = useStripe() const elements = useElements() - const { onAddCard } = useCardsStore() + const { onAddCard, setError: setCardError } = useCardsStore() + const [name, setName] = useState('') + const [inputStates, setInputStates] = useObjectState(initialState) const [error, setError] = useState('') + const onNameChange = (e: ChangeEvent) => { + const { value } = e.target + if (/^[A-Za-z]{0,500}$/.test(value)) { + setName(toUpper(value)) + + const cardHolderState = inputStates[ElementTypes.CardHolder] + setInputStates({ + [ElementTypes.CardHolder]: { + ...cardHolderState, + empty: !value, + }, + }) + } + } + + const onInputsChange = (e: StripeElementChangeEvent) => { + const value = inputStates[e.elementType as ElementTypes] + setInputStates({ [e.elementType]: { ...value, empty: e.empty } }) + } + + const onInputsFocus = (elementType: ElementTypes) => () => { + const value = inputStates[elementType as ElementTypes] + setInputStates({ [elementType]: { ...value, focused: true } }) + } + + const onInputsBlur = (elementType: ElementTypes) => () => { + const value = inputStates[elementType as ElementTypes] + setInputStates({ [elementType]: { ...value, focused: false } }) + } + + const isLabelVisible = (elementType: ElementTypes) => ( + inputStates[elementType].empty && !inputStates[elementType].focused + ) + const handleSubmit = async (e: FormEvent) => { e.preventDefault() @@ -29,8 +97,6 @@ export const useFormSubmit = ({ onAddSuccess }: Props) => { return } - const name: string = e.currentTarget.cardHolderName.value - if (!name) { setError('Name can not be empty') return @@ -51,5 +117,18 @@ export const useFormSubmit = ({ onAddSuccess }: Props) => { } } - return { error, handleSubmit } + useEffect(() => { + setCardError('') + }, [setCardError]) + + return { + error, + handleSubmit, + isLabelVisible, + name, + onInputsBlur, + onInputsChange, + onInputsFocus, + onNameChange, + } } diff --git a/src/features/AddCardForm/components/Form/index.tsx b/src/features/AddCardForm/components/Form/index.tsx index 243cb349..424b24f7 100644 --- a/src/features/AddCardForm/components/Form/index.tsx +++ b/src/features/AddCardForm/components/Form/index.tsx @@ -4,14 +4,16 @@ import { CardCvcElement, } from '@stripe/react-stripe-js' +import { isMobileDevice } from 'config/userAgent' + import { T9n } from 'features/T9n' import { useCardsStore } from 'features/CardsStore' -import { OutlineButton, SolidButton } from 'features/UserAccount/styled' +import { SolidButton } from 'features/UserAccount/styled' import { ElementContainer } from '../ElementContainer' import type { Props } from './hooks' -import { useFormSubmit } from './hooks' +import { useFormSubmit, ElementTypes } from './hooks' import { Form, @@ -19,64 +21,97 @@ import { ButtonsBlock, Input, Errors, + SectionTitle, } from '../../styled' -const baseStyles = { - color: '#fff', - fontFamily: 'Montserrat, Tahoma, sans-serif', - fontSize: '20px', - fontWeight: 'bold', - lineHeight: '50px', -} +const baseStyles = isMobileDevice + ? { + color: '#fff', + fontSize: '12px', + fontWeight: 'bold', + lineHeight: '12px', + } + : { + color: '#fff', + fontFamily: 'Montserrat, Tahoma, sans-serif', + fontSize: '20px', + fontWeight: 'bold', + lineHeight: '50px', + } const options = { placeholder: '', style: { base: baseStyles } } -const buttons = { - outline: OutlineButton, - solid: SolidButton, -} - export const AddCardFormInner = (props: Props) => { const { + children, inputsBackground, - submitButton = 'solid', } = props const { error: cardError } = useCardsStore() - const { error: formError, handleSubmit } = useFormSubmit(props) - - const SubmitButton = buttons[submitButton] + const { + error: formError, + handleSubmit, + isLabelVisible, + name, + onInputsBlur, + onInputsChange, + onInputsFocus, + onNameChange, + } = useFormSubmit(props) return (
+ + + - + - + - + - + @@ -87,9 +122,11 @@ export const AddCardFormInner = (props: Props) => { ) } - - Сохранить - + {children || ( + + Сохранить + + )}
) diff --git a/src/features/AddCardForm/index.tsx b/src/features/AddCardForm/index.tsx index adef78fe..68e68d07 100644 --- a/src/features/AddCardForm/index.tsx +++ b/src/features/AddCardForm/index.tsx @@ -9,9 +9,10 @@ import type { Props } from './components/Form/hooks' import { AddCardFormInner } from './components/Form' export const AddCardForm = ({ + children, initialformOpen, inputsBackground, - submitButton, + onAddSuccess, }: Props) => { const { isOpen, toggle } = useToggle(initialformOpen) @@ -20,14 +21,20 @@ export const AddCardForm = ({ toggle() } + const onSuccess = () => { + onAddSuccess?.() + toggle() + } + return ( isOpen ? ( + onAddSuccess={onSuccess} + > + {children} + ) : ( colors.text}; - -webkit-text-fill-color: ${({ theme: { colors } }) => colors.text}; + caret-color: ${({ theme: { colors } }) => colors.text100}; + -webkit-text-fill-color: ${({ theme: { colors } }) => colors.text100}; } + ${isMobileDevice + ? css` + line-height: 16px; + font-size: 12px; + max-height: 12px; + height: auto; + ` + : ''}; ` export const Errors = styled.span` @@ -44,4 +53,27 @@ export const Errors = styled.span` font-size: 16px; line-height: 16px; color: red; + ${isMobileDevice + ? css` + margin-bottom: 10px; + font-size: 12px; + ` + : ''}; +` + +export const SectionTitle = styled.span` + display: inline-block; + font-weight: 600; + font-size: 12px; + line-height: 18px; + text-transform: uppercase; + color: ${({ theme }) => theme.colors.text50}; + margin-bottom: 8px; + ${isMobileDevice + ? css` + margin-bottom: 25px; + font-size: 10px; + ` + : ''}; + ` diff --git a/src/features/App/AuthenticatedApp.tsx b/src/features/App/AuthenticatedApp.tsx index 962cf6ec..a4eb41ae 100644 --- a/src/features/App/AuthenticatedApp.tsx +++ b/src/features/App/AuthenticatedApp.tsx @@ -13,18 +13,15 @@ import { StripeElements } from 'features/StripeElements' import { useLexicsConfig } from 'features/LexicsStore' -import { ExtendedSearchStore, ExtendedSearchPage } from 'features/ExtendedSearchPage' +import { ExtendedSearchStore } from 'features/ExtendedSearchPage' import { MatchSwitchesStore } from 'features/MatchSwitches' import { UserFavoritesStore } from 'features/UserFavorites/store' import { MatchPopup, MatchPopupStore } from 'features/MatchPopup' import { BuyMatchPopup, BuyMatchPopupStore } from 'features/BuyMatchPopup' import { CardsStore } from 'features/CardsStore' +import { ProfileRoutes } from 'features/ProfileRoutes' const HomePage = lazy(() => import('features/HomePage')) -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 UserAccount = lazy(() => import('features/UserAccount')) export const AuthenticatedApp = () => { @@ -49,21 +46,7 @@ export const AuthenticatedApp = () => { - - - - - - - - - - - - - - - + diff --git a/src/features/App/UnauthenticatedApp.tsx b/src/features/App/UnauthenticatedApp.tsx index 7fd733cc..70936ceb 100644 --- a/src/features/App/UnauthenticatedApp.tsx +++ b/src/features/App/UnauthenticatedApp.tsx @@ -6,14 +6,15 @@ import { Switch, } from 'react-router-dom' -import styled from 'styled-components/macro' +import styled, { css } from 'styled-components/macro' import { PAGES } from 'config' import { publicLexics } from 'config/lexics/public' import { useLexicsConfig } from 'features/LexicsStore' import { LanguageSelect } from 'features/LanguageSelect' -import { HeaderStyled, HeaderGroup } from 'features/ProfileHeader/styled' +import { HeaderGroup } from 'features/ProfileHeader/styled' +import { isMobileDevice } from 'config/userAgent' import { RedirectCallback } from './RedirectCallback' @@ -22,6 +23,26 @@ const Register = lazy(() => import('features/Register')) const Main = styled.main` width: 100%; + ${isMobileDevice + ? css` + @media screen and (orientation: landscape){ + min-height: 100vh; + } + ` + : ''}; +` + +const HeaderStyled = styled.header` + display: flex; + justify-content: space-between; + height: 3.02rem; + padding: 0.755rem 1.369rem 0 1.04rem; + margin-bottom: 1.416rem; + ${isMobileDevice + ? css` + padding-top: 15px; + ` + : ''}; ` export const UnauthenticatedApp = () => { diff --git a/src/features/Background/styled.tsx b/src/features/Background/styled.tsx index 3bc49b1b..7bc52cef 100644 --- a/src/features/Background/styled.tsx +++ b/src/features/Background/styled.tsx @@ -3,6 +3,7 @@ import styled from 'styled-components/macro' const Container = styled.div` width: 100%; min-height: 100vh; + overflow-x: hidden; ` export const ImageBackground = styled(Container)` diff --git a/src/features/BuyMatchPopup/components/CardStep/index.tsx b/src/features/BuyMatchPopup/components/CardStep/index.tsx index f30f6eff..82849b07 100644 --- a/src/features/BuyMatchPopup/components/CardStep/index.tsx +++ b/src/features/BuyMatchPopup/components/CardStep/index.tsx @@ -1,13 +1,11 @@ import isEmpty from 'lodash/isEmpty' -import { AddCardForm } from 'features/AddCardForm' +import { AddCardFormInner } from 'features/AddCardForm/components/Form' import { useCardsStore } from 'features/CardsStore' import { useBuyMatchPopupStore } from 'features/BuyMatchPopup/store' import { CloseButton, - Header, HeaderActions, - HeaderTitle, } from 'features/PopupComponents' import { T9n } from 'features/T9n' @@ -16,41 +14,39 @@ import { CardsList } from '../CardsList' import { Wrapper, Body, - Footer, + Header, + HeaderTitle, Button, } from '../../styled' export const CardStep = () => { const { cards } = useCardsStore() - const { close, subscribeToMatch } = useBuyMatchPopupStore() + const { close, goBack } = useBuyMatchPopupStore() const emptyCards = isEmpty(cards) return ( - -
+ +
- {emptyCards ? 'Добавление карты' : 'Выберите карту для оплаты'} +
- + - - -
- {!emptyCards && ( - - )} -
+ +
) } diff --git a/src/features/BuyMatchPopup/components/CardsList/index.tsx b/src/features/BuyMatchPopup/components/CardsList/index.tsx index 79e698af..e2f4e252 100644 --- a/src/features/BuyMatchPopup/components/CardsList/index.tsx +++ b/src/features/BuyMatchPopup/components/CardsList/index.tsx @@ -1,15 +1,21 @@ import map from 'lodash/map' -import styled from 'styled-components/macro' +import styled, { css } from 'styled-components/macro' -import { Radio } from 'features/Common' +import { isMobileDevice } from 'config/userAgent' + +import { Radio as RadioBase } from 'features/Common' +import { Label } from 'features/Common/Radio/styled' +import { RadioSvg } from 'features/Common/Radio/Icon' import { useCardsStore } from 'features/CardsStore' const List = styled.ul` width: 100%; display: flex; flex-wrap: wrap; - margin-top: 20px; - margin-bottom: 25px; + + :not(:empty) { + margin-bottom: 10px; + } ` const Item = styled.li` @@ -17,6 +23,38 @@ const Item = styled.li` height: 50px; display: flex; align-items: center; + ${isMobileDevice + ? css` + width: 100%; + height: 21px; + margin-bottom: 14px; + ` + : ''} +` + +const Radio = styled(RadioBase)` + ${Label} { + font-weight: normal; + font-size: 14px; + line-height: 21px; + ${isMobileDevice + ? css` + font-size: 12px; + line-height: 100%; + ` + : ''} + } + + ${RadioSvg} { + margin-right: 12px; + ${isMobileDevice + ? css` + margin-right: 18px; + width: 16px; + height: 16px; + ` + : ''} + } ` export const CardsList = () => { diff --git a/src/features/BuyMatchPopup/components/ErrorStep/index.tsx b/src/features/BuyMatchPopup/components/ErrorStep/index.tsx index f8ff71e9..c9b662a6 100644 --- a/src/features/BuyMatchPopup/components/ErrorStep/index.tsx +++ b/src/features/BuyMatchPopup/components/ErrorStep/index.tsx @@ -1,36 +1,38 @@ import { T9n } from 'features/T9n' -import { - Header, - HeaderTitle, - HeaderActions, - CloseButton, -} from 'features/PopupComponents' +import { Header, HeaderTitle } from 'features/PopupComponents' -import { useBuyMatchPopupStore } from '../../store' import { Wrapper, Body, + Footer, ResultText, + SmallButton, } from '../../styled' +import { useBuyMatchPopupStore } from '../../store' export const ErrorStep = () => { - const { close, error: paymentError } = useBuyMatchPopupStore() + const { + close, + error: paymentError, + } = useBuyMatchPopupStore() return ( - -
+ +
- - -
- + {paymentError || } +
+ + Ок + +
) } diff --git a/src/features/BuyMatchPopup/components/SelectedCard/index.tsx b/src/features/BuyMatchPopup/components/SelectedCard/index.tsx index fb6ec811..9148bb77 100644 --- a/src/features/BuyMatchPopup/components/SelectedCard/index.tsx +++ b/src/features/BuyMatchPopup/components/SelectedCard/index.tsx @@ -1,14 +1,25 @@ -import styled from 'styled-components/macro' +import styled, { css } from 'styled-components/macro' +import { isMobileDevice } from 'config/userAgent' import capitalize from 'lodash/capitalize' import { useCardsStore } from 'features/CardsStore' +import { ButtonOutline } from 'features/Common' +import { T9n } from 'features/T9n' +import { useBuyMatchPopupStore } from 'features/BuyMatchPopup/store' +import { Steps } from 'features/BuyMatchPopup/types' const Wrapper = styled.div` display: flex; - margin-top: 40px; - margin-bottom: 30px; - padding: 0 35px; + margin-top: 25px; + padding: 0 40px; + ${isMobileDevice + ? css` + padding: 0; + justify-content: center; + align-items: center; + ` + : ''}; ` const CardInfo = styled.span` @@ -16,9 +27,38 @@ const CardInfo = styled.span` font-size: 18px; line-height: 20px; color: rgba(255, 255, 255, 0.7); + ${isMobileDevice + ? css` + font-size: 14px; + ` + : ''}; +` + +const ChangeCardButton = styled(ButtonOutline)` + border: none; + padding: 0; + width: auto; + height: auto; + + padding: 0 10px; + margin-left: 10px; + line-height: 20px; + font-size: 14px; + color: rgba(255, 255, 255, 0.5); + cursor: pointer; + + :hover { + color: rgba(255, 255, 255); + } + ${isMobileDevice + ? css` + font-size: 12px; + ` + : ''}; ` export const SelectedCard = () => { + const { goTo } = useBuyMatchPopupStore() const { defaultCard } = useCardsStore() if (!defaultCard) return null @@ -26,6 +66,9 @@ export const SelectedCard = () => { return ( {capitalize(defaultCard?.brand)} •••• {defaultCard?.last4} + goTo(Steps.CardSelection, e)}> + + ) } diff --git a/src/features/BuyMatchPopup/components/SubscriptionSelectionStep/index.tsx b/src/features/BuyMatchPopup/components/SubscriptionSelectionStep/index.tsx index af5a4850..48496fbf 100644 --- a/src/features/BuyMatchPopup/components/SubscriptionSelectionStep/index.tsx +++ b/src/features/BuyMatchPopup/components/SubscriptionSelectionStep/index.tsx @@ -6,22 +6,22 @@ import { MDASH } from 'config' import { CloseButton, - Header, HeaderActions, - HeaderTitle, } from 'features/PopupComponents' +import { T9n } from 'features/T9n' import { Name } from 'features/Name' import { useCardsStore } from 'features/CardsStore' import { useBuyMatchPopupStore } from '../../store' import { SelectedCard } from '../SelectedCard' import { Subscriptions } from '../Subscriptions' -import { Steps } from '../../types' import { Wrapper, Body, Footer, Button, + Header, + HeaderTitle, } from '../../styled' export const SubscriptionSelectionStep = () => { @@ -32,8 +32,8 @@ export const SubscriptionSelectionStep = () => { const { close, - goTo, match, + onBuyClick, selectedSubscription, } = useBuyMatchPopupStore() @@ -47,7 +47,7 @@ export const SubscriptionSelectionStep = () => { return ( -
+
{` ${MDASH} `} @@ -57,16 +57,16 @@ export const SubscriptionSelectionStep = () => {
- +
diff --git a/src/features/BuyMatchPopup/components/Subscriptions/index.tsx b/src/features/BuyMatchPopup/components/Subscriptions/index.tsx index d864606e..dbadc76c 100644 --- a/src/features/BuyMatchPopup/components/Subscriptions/index.tsx +++ b/src/features/BuyMatchPopup/components/Subscriptions/index.tsx @@ -1,6 +1,5 @@ import styled from 'styled-components/macro' -import { T9n } from 'features/T9n' import { PaymentPeriodTabs } from 'features/PaymentPeriodTabs' import { useBuyMatchPopupStore } from '../../store' @@ -10,34 +9,26 @@ const Wrapper = styled.div` width: 100%; display: flex; flex-direction: column; -` - -const Title = styled.span` - font-weight: normal; - font-size: 20px; - line-height: 21px; - text-transform: uppercase; - margin-top: 30px; - padding: 0 35px; + align-items: center; ` export const Subscriptions = () => { const { + matchSubscriptions, onPeriodSelect, onSubscriptionSelect, selectedPeriod, selectedSubscription, subscriptions, } = useBuyMatchPopupStore() + return ( - - <T9n t='choose_subscription' /> - { - const { - currency, - id, - lexic, - price, - type, - } = subscription + const { description, type } = subscription return ( onSelect?.(subscription)} active={subscription === selectedSubscription} > -
- -
+ + + + + {subscription.name} + + + +
) diff --git a/src/features/BuyMatchPopup/components/SubscriptionsList/styled.tsx b/src/features/BuyMatchPopup/components/SubscriptionsList/styled.tsx index 8293a459..beb007d6 100644 --- a/src/features/BuyMatchPopup/components/SubscriptionsList/styled.tsx +++ b/src/features/BuyMatchPopup/components/SubscriptionsList/styled.tsx @@ -1,18 +1,39 @@ -import styled from 'styled-components/macro' +import styled, { css } from 'styled-components/macro' +import { isMobileDevice } from 'config/userAgent' import { popupScrollbarStyles } from 'features/PopupComponents' import { Price as BasePrice } from 'features/Price' -import { PriceAmount, PriceDetails } from 'features/Price/styled' +import { + PriceAmount, + PriceDetails, + Period, +} from 'features/Price/styled' export const List = styled.ul` - height: 364px; + width: 100%; + height: 460px; display: flex; flex-direction: column; overflow-y: auto; - margin-top: 20px; - padding: 0 35px; + margin-top: 25px; + padding: 0 40px; ${popupScrollbarStyles} + + @media (max-width: 1370px) { + max-height: 415px; + } + ${isMobileDevice + ? css` + padding: 0; + height: 176px; + margin-top: 19px; + @media screen and (orientation: landscape){ + margin-top: 10px; + height: 166px; + } + ` + : ''}; ` type ItemProps = { @@ -23,9 +44,9 @@ export const Item = styled.li.attrs(() => ({ tabIndex: 0, }))` width: 100%; - min-height: 108px; - padding: 20px; - background: linear-gradient(180deg, rgba(255, 255, 255, 0.1) 0%, rgba(255, 255, 255, 0) 100%), #5C5C5C; + min-height: 140px; + padding: 20px 30px 20px 20px; + background: linear-gradient(180deg, rgba(255, 255, 255, 0.1) 0%, rgba(255, 255, 255, 0) 100%), #3F3F3F; box-shadow: 0px 1px 1px rgba(0, 0, 0, 0.3); border-radius: 2px; @@ -34,10 +55,6 @@ export const Item = styled.li.attrs(() => ({ align-items: center; cursor: pointer; - :first-child { - margin-top: 2px; - } - :not(:last-child) { margin-bottom: 20px; } @@ -45,17 +62,116 @@ export const Item = styled.li.attrs(() => ({ ${({ active }) => ( active ? 'background-color: #294FC3' : '' )}; + + @media (max-width: 1370px) { + min-height: 125px; + } + + @media (max-width: 750px) { + min-height: 52.29px; + height: 52.29px; + } + ${isMobileDevice + ? css` + padding: 5px 10px; + min-height: 52.29px; + height: 52.29px; + + :not(:last-child) { + margin-bottom: 10px; + @media screen and (orientation: landscape){ + margin-bottom: 5.24px; + } + @media (max-width: 850px) and (orientation: landscape){ + min-height: 52.29px; + height: 52.29px; + } + } + ` + : ''}; + + @media (max-width: 850px) and (orientation: landscape){ + min-height: 52.29px; + height: 52.29px; + } ` export const InfoWrapper = styled.div` - width: 80%; + width: 75%; + display: flex; + flex-direction: column; + align-self: flex-start; + ${isMobileDevice + ? css` + align-self: center; + @media (max-width: 850px) and (orientation: landscape){ + height: 100%; + } + ` + : ''}; ` -export const Header = styled.span` - font-weight: 600; +export const Name = styled.span` + font-weight: 500; font-size: 20px; line-height: 23px; letter-spacing: 0.03em; + + @media (max-width: 1370px) { + line-height: 20px; + } + ${isMobileDevice + ? css` + @media (max-width: 750px){ + font-size: 12px; + line-height: 10.04px; + } + @media screen and (orientation: landscape){ + font-size: 14px; + } + ` + : ''}; +` + +export const Pass = styled(Name)` + font-weight: 600; + text-transform: uppercase; + ${isMobileDevice + ? css` + line-height: 12px; + @media screen and (orientation: landscape){ + line-height: 14px; + } + ` + : ''}; +` + +export const Description = styled.span` + width: 68%; + margin-top: 13px; + font-weight: 500; + font-size: 15px; + line-height: 20px; + letter-spacing: 0.03em; + + @media (max-width: 1370px) { + line-height: 18px; + } + ${isMobileDevice + ? css` + @media (max-width: 750px){ + font-size: 8px; + line-height: 8px; + margin-top: 5px; + width: 100%; + } + @media (max-width: 850px) and (orientation: landscape){ + margin-top: 0; + line-height: 8px; + font-size: 10px; + } + ` + : ''}; ` export const Price = styled(BasePrice)` @@ -63,11 +179,25 @@ export const Price = styled(BasePrice)` font-size: 24px; line-height: 24px; font-weight: normal; + ${isMobileDevice + ? css` + font-size: 14px; + ` + : ''}; } ${PriceDetails} { font-weight: 500; font-size: 12px; line-height: 18px; + ${isMobileDevice + ? css` + font-size: 8px; + ` + : ''}; + } + + ${Period} { + text-transform: capitalize; } ` diff --git a/src/features/BuyMatchPopup/components/SuccessStep/index.tsx b/src/features/BuyMatchPopup/components/SuccessStep/index.tsx index 3f9947a8..e00bbdab 100644 --- a/src/features/BuyMatchPopup/components/SuccessStep/index.tsx +++ b/src/features/BuyMatchPopup/components/SuccessStep/index.tsx @@ -1,24 +1,32 @@ import { T9n } from 'features/T9n' -import { - Header, - HeaderTitle, -} from 'features/PopupComponents' +import { Header, HeaderTitle } from 'features/PopupComponents' -import { useBuyMatchPopupStore } from '../../store' import { Wrapper, Body, Footer, - Button, ResultText, + SmallButton, } from '../../styled' +import { useBuyMatchPopupStore } from '../../store' + +const useSuccessStep = () => { + const { close, postPaymentHandler } = useBuyMatchPopupStore() + + const closeAfterPayment = () => { + postPaymentHandler() + close() + } + + return { closeAfterPayment } +} export const SuccessStep = () => { - const { close } = useBuyMatchPopupStore() + const { closeAfterPayment } = useSuccessStep() return ( - -
+ +
@@ -29,9 +37,9 @@ export const SuccessStep = () => {
- + + Ок +
) diff --git a/src/features/BuyMatchPopup/index.tsx b/src/features/BuyMatchPopup/index.tsx index 6f2c0779..6b88dd38 100644 --- a/src/features/BuyMatchPopup/index.tsx +++ b/src/features/BuyMatchPopup/index.tsx @@ -1,10 +1,11 @@ -import { useBuyMatchPopupStore } from './store' -import { SubscriptionSelectionStep } from './components/SubscriptionSelectionStep' import { CardStep } from './components/CardStep' -import { SuccessStep } from './components/SuccessStep' import { ErrorStep } from './components/ErrorStep' -import { Modal } from './styled' +import { SuccessStep } from './components/SuccessStep' +import { SubscriptionSelectionStep } from './components/SubscriptionSelectionStep' + import { Steps } from './types' +import { Modal } from './styled' +import { useBuyMatchPopupStore } from './store' export * from './store' export * from './store/helpers' diff --git a/src/features/BuyMatchPopup/store/config.tsx b/src/features/BuyMatchPopup/store/config.tsx new file mode 100644 index 00000000..5c206c63 --- /dev/null +++ b/src/features/BuyMatchPopup/store/config.tsx @@ -0,0 +1,23 @@ +export const passLexics = { + all: 'pass_league', + team_away: 'pass_team', + team_home: 'pass_team', + team1: 'pass_team', + team2: 'pass_team', +} + +export const descriptionLexics = { + all: 'description_all_season_matches', + team_away: 'description_away_team_matches', + team_home: 'description_home_team_matches', + team1: 'description_all_team_matches', + team2: 'description_all_team_matches', +} + +export const passNameKeys = { + all: 'tournament', + team_away: 'team2', + team_home: 'team1', + team1: 'team1', + team2: 'team2', +} as const diff --git a/src/features/BuyMatchPopup/store/helpers.tsx b/src/features/BuyMatchPopup/store/helpers.tsx index 806f2504..2ef42b62 100644 --- a/src/features/BuyMatchPopup/store/helpers.tsx +++ b/src/features/BuyMatchPopup/store/helpers.tsx @@ -1,30 +1,98 @@ import map from 'lodash/map' -import reduce from 'lodash/reduce' import { currencySymbols } from 'config' -import type { MatchSubscriptionsResponse } from 'requests' +import type { + SubscriptionsByPeriods, + Subscriptions, +} from 'requests/getSubscriptions' -import { SubscriptionType, MatchSubscriptions } from '../types' +import type { Match } from 'features/Matches' +import { getName } from 'features/Name' -export const transformSubsciptions = ( - subscriptions: MatchSubscriptionsResponse, -) => ( - reduce( - subscriptions, - ( - acc, - periodSubscriptions, +import type { MatchSubscriptions, MatchSubscription } from '../types' +import { SubscriptionType } from '../types' +import { + passLexics, + passNameKeys, + descriptionLexics, +} from './config' + +type SubscriptionArgs = { + match: Match, + subscriptions: SubscriptionsByPeriods, + suffix: string, +} + +const transformSubscription = ({ + match, + subscriptions, + suffix, +}: SubscriptionArgs) => ( + type: SubscriptionType.Month | SubscriptionType.Year, +): Array => { + const { season } = subscriptions + return map(subscriptions[type], (subscription, rawKey) => { + const key = rawKey as keyof Subscriptions + const teamName = getName({ + nameObj: match[passNameKeys[key]], + suffix, + }) + return { + currency: currencySymbols[subscription.currency_iso], + description: { + lexic: descriptionLexics[key], + values: { + season: season.name, + team: teamName, + }, + }, + id: `${key} ${subscription.id}`, + name: teamName, + originalObject: subscription, + pass: passLexics[key], + price: subscription.price, + seasonName: season.name, type, - ) => { - const period = type as SubscriptionType - acc[period] = map(periodSubscriptions, (subscription) => ({ - currency: currencySymbols[subscription.currency_iso || 'RUB'], - type: period, - ...subscription, - })) - return acc - }, - {} as MatchSubscriptions, - ) -) + } + }) +} + +type SubsciptionsArgs = { + match: Match, + subscriptions: SubscriptionsByPeriods, + suffix: string, +} + +export const transformSubsciptions = ({ + match, + subscriptions, + suffix, +}: SubsciptionsArgs): MatchSubscriptions => { + const { pay_per_view: payPerView } = subscriptions + const team1Name = getName({ nameObj: match.team1, suffix }) + const team2Name = getName({ nameObj: match.team2, suffix }) + const transformByType = transformSubscription({ + match, + subscriptions, + suffix, + }) + + return { + [SubscriptionType.Month]: transformByType(SubscriptionType.Month), + [SubscriptionType.Year]: transformByType(SubscriptionType.Year), + [SubscriptionType.PayPerView]: [{ + currency: currencySymbols[payPerView.currency_iso], + description: { + lexic: 'description_match_live_and_on_demand', + values: {}, + }, + id: '0', + name: `${team1Name} - ${team2Name}`, + originalObject: payPerView, + pass: 'pass_match_access', + price: payPerView.price, + type: SubscriptionType.PayPerView, + }], + } +} diff --git a/src/features/BuyMatchPopup/store/hooks/index.tsx b/src/features/BuyMatchPopup/store/hooks/index.tsx index b56e2b38..c2515097 100644 --- a/src/features/BuyMatchPopup/store/hooks/index.tsx +++ b/src/features/BuyMatchPopup/store/hooks/index.tsx @@ -21,9 +21,7 @@ import { import type { OnFailedPaymentActionData } from 'requests/buySubscription' import type { Match } from 'features/Matches/hooks' -import type { MatchSubscription } from 'features/BuyMatchPopup/types' import { useCardsStore } from 'features/CardsStore' -import { useMatchPopupStore } from 'features/MatchPopup' import { Steps, SubscriptionType } from 'features/BuyMatchPopup/types' import { getProfileUrl } from 'features/ProfileLink/helpers' @@ -33,21 +31,12 @@ import { useStripe3DSecure } from './useStripe3DSecure' const requests = { [SubscriptionType.Month]: buyMatchSubscription, [SubscriptionType.Year]: buyMatchPayOnceSubscription, + [SubscriptionType.PayPerView]: buyMatchPayOnceSubscription, } -const getSubscriptionItem = (subscription: MatchSubscription) => ({ - currency_id: subscription.currency_id, - currency_iso: subscription.currency_iso, - id: subscription.id, - lexic: subscription.lexic, - price: subscription.price, - sub: subscription.sub, -}) - export const useBuyMatchPopup = () => { const history = useHistory() const { defaultCard } = useCardsStore() - const { openMatchPopup } = useMatchPopupStore() const { handle3DSecure } = useStripe3DSecure() const [steps, setSteps] = useState>([]) @@ -76,6 +65,7 @@ export const useBuyMatchPopup = () => { const { fetchSubscriptions, + matchSubscriptions, onPeriodSelect, onSubscriptionSelect, resetSubscriptions, @@ -100,12 +90,11 @@ export const useBuyMatchPopup = () => { history.push(matchLink) } - const onSuccessfulSubscription = () => { + const onSuccessfulSubscription = () => goTo(Steps.Success) + const postPaymentHandler = () => { closePopup() if (!match) return - if (match.calc) { - openMatchPopup(match) - } else if (match.hasVideo) { + if (match.hasVideo || match.calc) { redirectToMatchProfile(match) } else { window.location.reload() @@ -121,7 +110,7 @@ export const useBuyMatchPopup = () => { const onConfirmationSuccess = ({ id }: PaymentIntent) => { if (!selectedSubscription) return - const item = getSubscriptionItem(selectedSubscription) + const item = selectedSubscription.originalObject notifySuccessfulSubscription({ item, paymentIntentId: id }) .then(onSuccessfulSubscription, goToError) } @@ -143,7 +132,7 @@ export const useBuyMatchPopup = () => { const subscribeToMatch = () => { if (!selectedSubscription || !defaultCard) return - const item = getSubscriptionItem(selectedSubscription) + const item = selectedSubscription.originalObject const buy = requests[selectedSubscription.type] buy({ cardId: defaultCard.id, item }).then( @@ -152,9 +141,17 @@ export const useBuyMatchPopup = () => { ) } + const onBuyClick = (e?: MouseEvent) => { + if (defaultCard) { + subscribeToMatch() + } else { + goTo(Steps.CardSelection, e) + } + } + useEffect(() => { if (match) { - fetchSubscriptions(match.sportType, match.id) + fetchSubscriptions(match) } }, [match, fetchSubscriptions]) @@ -165,12 +162,14 @@ export const useBuyMatchPopup = () => { goBack, goTo, match, + matchSubscriptions, + onBuyClick, onPeriodSelect, onSubscriptionSelect, open: openPopup, + postPaymentHandler, selectedPeriod, selectedSubscription, - subscribeToMatch, subscriptions, } } diff --git a/src/features/BuyMatchPopup/store/hooks/useLexicsFetcher.tsx b/src/features/BuyMatchPopup/store/hooks/useLexicsFetcher.tsx deleted file mode 100644 index dfde3b6b..00000000 --- a/src/features/BuyMatchPopup/store/hooks/useLexicsFetcher.tsx +++ /dev/null @@ -1,25 +0,0 @@ -import { useCallback } from 'react' - -import isEmpty from 'lodash/isEmpty' -import flatMap from 'lodash/flatMap' -import map from 'lodash/map' - -import { useLexicsStore } from 'features/LexicsStore' - -import type { MatchSubscriptions } from '../../types' - -export const useLexicsFetcher = () => { - const { addLexicsConfig } = useLexicsStore() - const fetchLexics = useCallback((subscriptions: MatchSubscriptions) => { - const lexics = flatMap( - subscriptions, - (periodSubscriptions) => map(periodSubscriptions, ({ lexic }) => lexic), - ) - if (!isEmpty(lexics)) { - addLexicsConfig(lexics) - } - return subscriptions - }, [addLexicsConfig]) - - return { fetchLexics } -} diff --git a/src/features/BuyMatchPopup/store/hooks/useSubscriptions.tsx b/src/features/BuyMatchPopup/store/hooks/useSubscriptions.tsx index e2ab2ce9..438de230 100644 --- a/src/features/BuyMatchPopup/store/hooks/useSubscriptions.tsx +++ b/src/features/BuyMatchPopup/store/hooks/useSubscriptions.tsx @@ -3,43 +3,60 @@ import { useCallback, } from 'react' +import find from 'lodash/find' +import isEmpty from 'lodash/isEmpty' +import first from 'lodash/first' + import { getSubscriptions } from 'requests' -import { SportTypes } from 'config' +import type { Match } from 'features/Matches' +import { useLexicsStore } from 'features/LexicsStore' import type { MatchSubscriptions, MatchSubscription } from '../../types' import { SubscriptionType } from '../../types' import { transformSubsciptions } from '../helpers' -import { useLexicsFetcher } from './useLexicsFetcher' const defaultSubscriptions: MatchSubscriptions = { [SubscriptionType.Month]: [], [SubscriptionType.Year]: [], + [SubscriptionType.PayPerView]: [], } export const useSubscriptions = () => { - const { fetchLexics } = useLexicsFetcher() + const { suffix } = useLexicsStore() + const [selectedPeriod, setSelectedPeriod] = useState(SubscriptionType.Month) const [matchSubscriptions, setMatchSubscriptionsList] = useState(defaultSubscriptions) const [selectedSubscription, setSelectedSubscription] = useState(null) - const fetchSubscriptions = useCallback((sport: SportTypes, id: number) => { - getSubscriptions(sport, id) - .then(transformSubsciptions) - .then(fetchLexics) - .then(setMatchSubscriptionsList) - }, [fetchLexics]) + const fetchSubscriptions = useCallback((match: Match) => { + getSubscriptions(match.sportType, match.id) + .then((subscriptions) => transformSubsciptions({ + match, + subscriptions, + suffix, + })) + .then((subscriptions) => { + setMatchSubscriptionsList(subscriptions) + const firstSubscription = find(subscriptions, (subscription) => !isEmpty(subscription)) + setSelectedPeriod(first(firstSubscription)?.type || SubscriptionType.Month) + }) + }, [suffix]) const resetSubscriptions = useCallback(() => { - setSelectedPeriod(SubscriptionType.Month) setSelectedSubscription(null) setMatchSubscriptionsList(defaultSubscriptions) }, []) + const onSubscriptionSelect = (subscription: MatchSubscription) => { + setSelectedSubscription(subscription === selectedSubscription ? null : subscription) + } + return { fetchSubscriptions, + matchSubscriptions, onPeriodSelect: setSelectedPeriod, - onSubscriptionSelect: setSelectedSubscription, + onSubscriptionSelect, resetSubscriptions, selectedPeriod, selectedSubscription, diff --git a/src/features/BuyMatchPopup/styled.tsx b/src/features/BuyMatchPopup/styled.tsx index 16a96679..d38a6c32 100644 --- a/src/features/BuyMatchPopup/styled.tsx +++ b/src/features/BuyMatchPopup/styled.tsx @@ -1,37 +1,73 @@ -import styled from 'styled-components/macro' +import styled, { css } from 'styled-components/macro' -import { devices } from 'config' +import { isMobileDevice } from 'config/userAgent' -import { Modal as BaseModal } from 'features/Modal' -import { ModalWindow } from 'features/Modal/styled' import { ButtonSolid } from 'features/Common' +import { ModalWindow } from 'features/Modal/styled' +import { Modal as BaseModal } from 'features/Modal' export const Modal = styled(BaseModal)` background-color: rgba(0, 0, 0, 0.7); ${ModalWindow} { - min-width: 517px; - min-height: auto; - padding: 20px 0; - background-color: #3F3F3F; + padding: 0; + background-color: #333333; border-radius: 5px; - @media ${devices.mobile} { - width: 100vw; - height: 100vh; - padding: 0; - } + ${isMobileDevice + ? css` + width: calc(100vw - 60px); + @media screen and (orientation: landscape){ + max-width: calc(100vw - 80px); + height: calc(100vh - 60px); + overflow: auto; + } + ` + : ''}; } ` +export const Header = styled.div` + display: flex; + justify-content: center; + padding: 0 40px; +` + +export const HeaderTitle = styled.h2` + font-weight: 600; + font-size: 24px; + color: #FFFFFF; + text-align: center; + ${isMobileDevice + ? css` + font-size: 14px; + font-weight: 700; + ` + : ''}; + +` + export const Button = styled(ButtonSolid)` - min-width: 142px; + min-width: 270px; width: auto; height: 50px; padding: 0 20px; - background-color: #294FC4; - box-shadow: 0px 1px 1px rgba(0, 0, 0, 0.3); + background-color: #294FC3; + box-shadow: 0 1px 1px rgba(0, 0, 0, 0.3); border-radius: 5px; + font-weight: 600; + font-size: 20px; + ${isMobileDevice + ? css` + height: 32px; + min-width: 117px; + font-size: 12px; + @media screen and (orientation: landscape){ + min-width: 178px; + } + + ` + : ''}; :disabled { opacity: 0.5; @@ -39,11 +75,38 @@ export const Button = styled(ButtonSolid)` ` type WrapperProps = { + height?: number, width?: number, } export const Wrapper = styled.div` - width: ${({ width }) => (width ? `${width}px` : '870px')}; + position: relative; + height: ${({ height }) => (height ? `${height}px` : 'auto')}; + width: ${({ width }) => (width ? `${width}px` : '830px')}; + padding: 40px 0 41px; + + + ${isMobileDevice + ? css` + + @media screen and (orientation: landscape){ + padding: 17px 10px 15px; + width: 100%; + } + + @media (max-width: 1370px) { + max-width: 743px; + max-height: 650px; + } + @media (max-width: 650px){ + width: 100%; + padding: 40px 10px 20px; + display: flex; + flex-direction: column; + justify-content: space-evenly; + } + ` + : ''}; ` type BodyProps = { @@ -54,8 +117,22 @@ type BodyProps = { export const Body = styled.div` margin-top: ${({ marginTop }) => (marginTop ? `${marginTop}px` : '')}; - margin-bottom: ${({ marginBottom }) => (marginBottom ? `${marginBottom}px` : '')}; + + margin-bottom: ${({ marginBottom }) => ( + marginBottom ? `${marginBottom}px` : '' + )}; + padding: ${({ padding }) => (padding || '')}; + ${isMobileDevice + ? css` + margin-top: 0; + padding: 10px 0 0 0; + @media screen and (orientation: landscape){ + margin-top: 0; + padding-top: 0; + } + ` + : ''}; ` type FooterProps = { @@ -66,9 +143,18 @@ export const Footer = styled.div` width: 100%; display: flex; justify-content: center; + margin-top: 40px; - margin-top: ${({ marginTop }) => (marginTop ? `${marginTop}px` : '')}; - margin-bottom: 15px; + @media (max-width: 1370px) { + margin-top: 15px; + } + ${isMobileDevice + ? css` + @media screen and (orientation: landscape){ + margin-top: 10px; + } + ` + : ''}; ` export const ResultText = styled.span` @@ -78,4 +164,15 @@ export const ResultText = styled.span` font-weight: normal; font-size: 20px; line-height: 27px; + ${isMobileDevice + ? css` + font-size: 14px; + line-height: 18px; + /* margin-top: 10px; */ + ` + : ''}; +` + +export const SmallButton = styled(Button)` + min-width: 70px; ` diff --git a/src/features/BuyMatchPopup/types.tsx b/src/features/BuyMatchPopup/types.tsx index 218a245f..6879387b 100644 --- a/src/features/BuyMatchPopup/types.tsx +++ b/src/features/BuyMatchPopup/types.tsx @@ -1,3 +1,4 @@ +import type { LexicsId, Values } from 'features/LexicsStore/types' import type { SubscriptionResponse } from 'requests' export enum Steps { @@ -9,11 +10,24 @@ export enum Steps { export enum SubscriptionType { Month = 'month', + PayPerView = 'pay_per_view', Year = 'year', } -export type MatchSubscription = SubscriptionResponse & { +type Desciption = { + lexic: LexicsId, + values: Values, +} + +export type MatchSubscription = { currency: string, + description: Desciption, + id: string, + name: string, + originalObject: SubscriptionResponse, + pass: string, + price: number, + seasonName?: string, type: SubscriptionType, } diff --git a/src/features/CardsStore/hooks/index.tsx b/src/features/CardsStore/hooks/index.tsx index 65a194e7..71495343 100644 --- a/src/features/CardsStore/hooks/index.tsx +++ b/src/features/CardsStore/hooks/index.tsx @@ -12,7 +12,13 @@ import { addCard } from 'requests/addCard' import { deleteCard } from 'requests/deleteCard' import { setDefaultCard as setDefaultCardRequest } from 'requests/setDefaultCard' +import { useToggle } from 'hooks' + export const useBankCards = () => { + const { + isOpen: infoModalOpen, + toggle: toggleInfoModal, + } = useToggle() const [error, setError] = useState('') const [cards, setCards] = useState(null) const defaultCard = useMemo( @@ -32,7 +38,10 @@ export const useBankCards = () => { ) const onDeleteCard = (cardId: string) => { - deleteCard(cardId).then(fetchCards) + deleteCard(cardId).then(() => { + fetchCards() + toggleInfoModal() + }) } const onSetDefaultCard = (cardId: string) => { @@ -44,8 +53,11 @@ export const useBankCards = () => { defaultCard, error, fetchCards, + infoModalOpen, onAddCard, onDeleteCard, onSetDefaultCard, + setError, + toggleInfoModal, } } diff --git a/src/features/Combobox/hooks/index.tsx b/src/features/Combobox/hooks/index.tsx index 34ed64f4..f48e67e7 100644 --- a/src/features/Combobox/hooks/index.tsx +++ b/src/features/Combobox/hooks/index.tsx @@ -16,6 +16,7 @@ import toLower from 'lodash/toLower' import find from 'lodash/find' import trim from 'lodash/trim' import size from 'lodash/size' +import findIndex from 'lodash/findIndex' import { useToggle } from 'hooks' @@ -28,6 +29,7 @@ const isOptionClicked = (target: HTMLElement | null) => ( ) export const useCombobox = ({ + noSearch, onBlur, onChange, onSelect, @@ -50,7 +52,7 @@ export const useCombobox = ({ toggle, } = useToggle() - const filteredOptions = useMemo(() => (selected ? options : matchSort( + const filteredOptions = useMemo(() => (selected && noSearch ? options : matchSort( options, 'name', query, @@ -58,6 +60,7 @@ export const useCombobox = ({ options, query, selected, + noSearch, ]) const findOptionByName = useCallback((optionName: string) => ( @@ -101,14 +104,9 @@ export const useCombobox = ({ } useEffect(() => { - const lastElementIndex = size(filteredOptions) - 1 - if (index < 0) setIndex(0) - if (index >= lastElementIndex) setIndex(lastElementIndex) - }, [ - index, - options, - filteredOptions, - ]) + const selectedIndex = findIndex(filteredOptions, ({ name }) => name === query) + setIndex(selectedIndex) + }, [query, filteredOptions]) const onKeyDown = useCallback((event: KeyboardEvent) => { onKeyDownScroll(event) @@ -116,9 +114,10 @@ export const useCombobox = ({ close() inputFieldRef.current?.blur() } else if (event.key === 'ArrowUp') { - setIndex(index - 1) + setIndex(index < 0 ? 0 : index - 1) } else if (event.key === 'ArrowDown') { - setIndex(index + 1) + const lastElementIndex = size(filteredOptions) - 1 + setIndex(index >= lastElementIndex ? lastElementIndex : index + 1) } else if (event.key === 'Enter') { onSelect?.(filteredOptions[index]) close() diff --git a/src/features/Combobox/index.tsx b/src/features/Combobox/index.tsx index 4912dbf4..8534854c 100644 --- a/src/features/Combobox/index.tsx +++ b/src/features/Combobox/index.tsx @@ -34,6 +34,7 @@ export const Combobox = (props: Props) => { labelLexic, labelWidth, maxLength, + noSearch, title, withError, } = props @@ -74,6 +75,7 @@ export const Combobox = (props: Props) => { title={title} value={query} disabled={disabled} + readOnly={noSearch} onChange={onQueryChange} onKeyDown={onKeyDown} placeholder={translate(labelLexic || '')} diff --git a/src/features/Combobox/types.tsx b/src/features/Combobox/types.tsx index a074252c..064de442 100644 --- a/src/features/Combobox/types.tsx +++ b/src/features/Combobox/types.tsx @@ -6,7 +6,7 @@ import type { import type { CustomStyles } from 'features/Common' export type Option = { - id: number, + id: number | string, name: string, } @@ -22,6 +22,7 @@ export type Props = Pick, ( labelLexic?: string, labelWidth?: number, maxLength?: number, + noSearch?: boolean, onChange?: (event: ChangeEvent) => void, onSelect?: (option: T | null) => void, options: Array, diff --git a/src/features/Common/Arrows/index.tsx b/src/features/Common/Arrows/index.tsx deleted file mode 100644 index c8f1575e..00000000 --- a/src/features/Common/Arrows/index.tsx +++ /dev/null @@ -1,37 +0,0 @@ -import styled from 'styled-components/macro' - -const ArrowStyled = styled.button` - width: 40px; - height: 40px; - padding: 0; - border: 1px solid transparent; - border-radius: 50%; - background-color: transparent; - background-position: center; - outline: none; - z-index: 1; - position: absolute; - cursor: pointer; - - :active { - background-color: rgba(117, 117, 117, 1); - } - - :hover, :focus { - background-color: rgba(117, 117, 117, 0.5); - } -` - -export const ArrowLeft = styled(ArrowStyled)` - background-image: url(/images/arrowLeft.svg); - top: 50%; - left: 0px; - transform: translate(-10px, -50%); -` - -export const ArrowRight = styled(ArrowStyled)` - background-image: url(/images/arrowRight.svg); - top: 50%; - right: 0px; - transform: translate(10px, -50%); -` diff --git a/src/features/Common/Arrows/stories.tsx b/src/features/Common/Arrows/stories.tsx deleted file mode 100644 index f99d3766..00000000 --- a/src/features/Common/Arrows/stories.tsx +++ /dev/null @@ -1,21 +0,0 @@ -import { ArrowLeft, ArrowRight } from 'features/Common' - -const Story = { - component: ArrowLeft, - title: 'Arrows', -} - -export default Story - -const backgroundStyles = { - backgroundColor: '#333', - height: '200px', - padding: '20px', -} - -export const Group = () => ( -
- - -
-) diff --git a/src/features/Common/Button/styled.tsx b/src/features/Common/Button/styled.tsx index a676317f..b7e6e039 100644 --- a/src/features/Common/Button/styled.tsx +++ b/src/features/Common/Button/styled.tsx @@ -1,19 +1,25 @@ import styled, { css } from 'styled-components/macro' -import { devices } from 'config/devices' +import { isMobileDevice } from 'config/userAgent' const baseButtonStyles = css` width: 272px; - height: 50px; + height: 2.36rem; border-width: 0.7px; border-style: solid; border-radius: 2px; - padding: 0 12px; - + padding: 0 0.567rem; font-style: normal; - font-size: 20px; + font-size: 0.95rem; outline-color: white; cursor: pointer; + ${isMobileDevice + ? css` + height: 27px; + min-height: 27px; + font-size: 16px; + ` + : ''}; :disabled { opacity: 0.5; @@ -23,11 +29,11 @@ const baseButtonStyles = css` export const outlineButtonStyles = css` ${baseButtonStyles} - padding-top: 8.6px; - padding-bottom: 10.6px; - color: ${({ theme: { colors } }) => colors.secondary}; + padding-top: 0.42rem; + padding-bottom: 0.475rem; + color: ${({ theme: { colors } }) => colors.text100}; font-weight: normal; - border-color: ${({ theme: { colors } }) => colors.secondary}; + border-color: ${({ theme: { colors } }) => colors.text100}; background: transparent; ` @@ -37,17 +43,14 @@ export const solidButtonStyles = css` color: #fff; font-weight: bold; border-color: transparent; - background-color: #294FC4; + background-color: #294FC3; box-shadow: 0px 1px 1px rgba(0, 0, 0, 0.3); - - /* TODO: удалить медиа запросы из стайледа, - добавить специфичные юз кейсу правила - через враппер компоненты или др способом - */ - @media ${devices.mobile} { - width: 335px; - height: 40px; - } + ${isMobileDevice + ? css` + height: 30px; + min-height: 30px; + ` + : ''}; ` export const ButtonSolid = styled.button` diff --git a/src/features/Common/Checkbox/styled.tsx b/src/features/Common/Checkbox/styled.tsx index ae0f8266..9024b9a6 100644 --- a/src/features/Common/Checkbox/styled.tsx +++ b/src/features/Common/Checkbox/styled.tsx @@ -8,7 +8,7 @@ export const Wrapper = styled.span.attrs(() => ({ export const Label = styled.label` display: flex; align-items: center; - color: ${({ theme: { colors } }) => colors.text}; + color: ${({ theme: { colors } }) => colors.text100}; font-style: normal; font-weight: bold; font-size: 18px; diff --git a/src/features/Common/Input/index.tsx b/src/features/Common/Input/index.tsx index 47c1cf1d..fcb4488a 100644 --- a/src/features/Common/Input/index.tsx +++ b/src/features/Common/Input/index.tsx @@ -51,7 +51,6 @@ export const Input = ({ onBlur, onChange, onFocus, - paddingX, pattern, required, title, @@ -67,7 +66,7 @@ export const Input = ({
+ + + + + + Отправить + + + + ) +} diff --git a/src/features/Login/components/PasswordResetPopup/styled.tsx b/src/features/Login/components/PasswordResetPopup/styled.tsx new file mode 100644 index 00000000..30024c8d --- /dev/null +++ b/src/features/Login/components/PasswordResetPopup/styled.tsx @@ -0,0 +1,57 @@ +import styled, { css } from 'styled-components/macro' + +import { isMobileDevice } from 'config/userAgent' + +import { Modal as BaseModal } from 'features/Modal' +import { ModalWindow } from 'features/Modal/styled' +import { HeaderTitle } from 'features/PopupComponents' + +export const Modal = styled(BaseModal)` + background-color: rgba(0, 0, 0, 0.7); + + ${ModalWindow} { + width: 577px; + padding: 15px 0; + background-color: #333333; + border-radius: 5px; + ${isMobileDevice + ? css` + width: 100%; + padding: 15px 30px 0; + ` + : ''} + } +` + +export const Body = styled.div` + display: flex; + flex-direction: column; + align-items: center; + + margin-top: 24px; + padding: 0 40px; + ${isMobileDevice + ? css` + width: 100%; + padding: 0; + @media screen and (orientation: landscape){ + margin-top: 10px; + } + ` + : ''} +` + +export const Title = styled(HeaderTitle)`` + +export const ButtonWrapper = styled.div` + padding: 25px 0; + ${isMobileDevice + ? css` + width: 100%; + padding: 12px 0 35px; + @media screen and (orientation: landscape){ + padding-top: 20px; + } + ` + : ''} +` diff --git a/src/features/Login/hooks.tsx b/src/features/Login/hooks.tsx index 35ac2541..841c6ce8 100644 --- a/src/features/Login/hooks.tsx +++ b/src/features/Login/hooks.tsx @@ -1,10 +1,8 @@ import type { FormEvent } from 'react' import { useAuthStore } from 'features/AuthStore' -import { useForm } from 'features/FormStore' export const useLoginForm = () => { - const { readFormError } = useForm() const { login } = useAuthStore() const handleSubmit = async (event: FormEvent) => { @@ -14,6 +12,5 @@ export const useLoginForm = () => { return { handleSubmit, - readFormError, } } diff --git a/src/features/Login/index.tsx b/src/features/Login/index.tsx index 9f85e47b..f1f5c92a 100644 --- a/src/features/Login/index.tsx +++ b/src/features/Login/index.tsx @@ -1,11 +1,7 @@ import { PAGES } from 'config' -import { formIds } from 'config/form' import { T9n } from 'features/T9n' import { Logo } from 'features/Logo' -import { ButtonSolid } from 'features/Common' -import { Error } from 'features/Common/Input/styled' -import { FormStore } from 'features/FormStore' import { useLoginForm } from './hooks' import { @@ -14,16 +10,18 @@ import { ButtonsBlock, Form, RegisterButton, + ButtonSolid, + // Or, + // BorderlessButton, } from './styled' +// import { AuthProviderButton } from './components/AuthProviderButton' +// import { PasswordResetPopup } from './components/PasswordResetPopup' -const LoginForm = () => { +const Login = () => { const { handleSubmit, - readFormError, } = useLoginForm() - const requestError = readFormError(formIds.formError) - return ( @@ -31,7 +29,6 @@ const LoginForm = () => { - @@ -39,15 +36,24 @@ const LoginForm = () => { + {/* TODO: раскомментить когда будет готово */} + {/* или + + + + Забыли пароль? + + { + isResetPopupOpen && ( + + ) + } */} ) } -const Login = () => ( - - - -) - export default Login diff --git a/src/features/Login/styled.tsx b/src/features/Login/styled.tsx index 9cab5845..f1f107d8 100644 --- a/src/features/Login/styled.tsx +++ b/src/features/Login/styled.tsx @@ -1,9 +1,14 @@ import { Link } from 'react-router-dom' -import styled from 'styled-components/macro' -import { devices } from 'config/devices' +import styled, { css } from 'styled-components/macro' -import { outlineButtonStyles } from 'features/Common' +import { isMobileDevice } from 'config/userAgent' + +import { + outlineButtonStyles, + ButtonSolid as BaseButtonSolid, + ButtonOutline, +} from 'features/Common' import { T9n } from 'features/T9n' export const CenterBlock = styled.div` @@ -11,53 +16,55 @@ export const CenterBlock = styled.div` flex-direction: column; align-items: center; justify-content: flex-start; - margin-top: 76px; - - @media ${devices.laptop} { - margin-top: 177px; - } - - @media ${devices.mobile} { - margin-top: 150px; - } + margin-top: 6.133rem; + ${isMobileDevice + ? css` + margin-top: 110px; + @media screen and (orientation: landscape) { + width: 290px; + margin: auto; + } + ` + : ''}; ` -export const Form = styled.form<{forRegPage?: boolean}>` - width: 544px; - margin: 80px 0 140px 0; +export const Form = styled.form<{ forRegPage?: boolean }>` + width: 23.255rem; + margin-top: 1.982rem; + margin-bottom: 6.604rem; display: flex; flex-direction: column; align-items: center; - - @media ${devices.laptop} { - margin: ${({ forRegPage }) => (forRegPage ? '75px 0 140px 0' : '80px 0 140px 0')}; - } - - @media ${devices.mobile} { - width: auto; - margin: 50px 0 140px 0; - } + ${isMobileDevice + ? css` + width: 100%; + padding: 0 28px; + margin-top: 22px; + @media screen and (orientation: landscape){ + margin-bottom: 20px; + margin-top: 10px; + padding: 0; + } + ` + : ''}; ` export const BlockTitle = styled(T9n)` display: block; font-style: normal; font-weight: bold; - font-size: 36px; - line-height: 24px; - color: ${({ theme: { colors } }) => colors.text}; - margin-bottom: 20px; + font-size: 1.14rem; + line-height: 1.14rem; + color: ${({ theme: { colors } }) => colors.text100}; + margin-bottom: 1.3rem; transition: color 0.3s ease-in-out; - - @media ${devices.laptop} { - max-height: 24px; - margin-bottom: 23px; - } - - @media ${devices.mobile} { - font-size: 24px; - margin-bottom: 20px; - } + ${isMobileDevice + ? css` + font-size: 14.77px; + line-height: 14.77px; + margin-bottom: 18.46px; + ` + : ''}; ` type ButtonsBlockProps = { @@ -65,33 +72,86 @@ type ButtonsBlockProps = { } export const ButtonsBlock = styled.div` + width: 100%; display: flex; flex-direction: column; - align-items: center; - position: relative; - margin-top: ${({ marginTop = 2 }) => marginTop}px; + align-items: flex-start; + margin-top: ${({ marginTop = 0 }) => marginTop}px; +` - @media ${devices.laptop} { - margin-top: 32px; - } +type ButtonSolidProps = { + width?: string, +} - @media ${devices.mobile} { - bottom: 10px; - margin-bottom: 0; - position: relative; - } +export const ButtonSolid = styled(BaseButtonSolid)` + width: ${({ width = '100%' }) => width}; + border-radius: 5px; + font-weight: normal; + ${isMobileDevice + ? css` + display: block; + width: 100%; + font-size: 12.31px; + @media screen and (orientation: landscape) { + max-width: 290px; + margin: auto; + } + ` + : ''}; ` export const RegisterButton = styled(Link)` ${outlineButtonStyles} - margin-top: 15px; + width: 100%; + border-radius: 5px; + margin-top: 0.71rem; display: flex; align-items: center; justify-content: center; + ${isMobileDevice + ? css` + min-height: 30px; + margin-top: 9.23px; + font-size: 12.31px; + @media screen and (orientation: landscape) { + max-width: 290px; + margin: 9.23px auto 0; + } + ` + : ''}; +` - @media ${devices.mobile} { - width: 335px; - height: 40px; - margin-top: 10px; - } +export const BorderlessButton = styled(ButtonOutline)` + border: none; + padding: 0; + width: auto; + height: 2.123rem; + font-weight: normal; + font-size: 0.755rem; + align-self: flex-start; + ${isMobileDevice + ? css` + font-size: 9.8px; + ` + : ''}; +` + +export const Or = styled.span` + margin-top: 0.85rem; + margin-bottom: 0.65rem; + color: ${({ theme: { colors } }) => colors.text100}; + font-style: normal; + font-weight: normal; + font-size: 0.755rem; + line-height: 2.123rem; + ${isMobileDevice + ? css` + font-size: 14.77px; + padding: 20px 0; + margin: 0; + @media screen and (orientation: landscape){ + padding: 10px 0; + } + ` + : ''}; ` diff --git a/src/features/Logo/index.tsx b/src/features/Logo/index.tsx index ac2801ad..72ff50c2 100644 --- a/src/features/Logo/index.tsx +++ b/src/features/Logo/index.tsx @@ -1,6 +1,5 @@ -import styled from 'styled-components/macro' - -import { devices } from 'config/devices' +import styled, { css } from 'styled-components/macro' +import { isMobileDevice } from '../../config/userAgent' type Props = { height?: number, @@ -9,17 +8,19 @@ type Props = { export const Logo = styled.div` display: block; - width: ${({ width = 174 }) => width}px; - height: ${({ height = 40 }) => height}px; + width: ${({ width = 11.04 }) => width}rem; + height: ${({ height = 2.55 }) => height}rem; background-size: contain; background-repeat: no-repeat; background-image: url(/images/logo.svg); - - @media ${devices.tablet} { - width: ${({ width }) => width}px; - height: ${({ height }) => height}px; - display: flex; - justify-content: center; - align-items: center; - } + ${isMobileDevice + ? css` + width: 144px; + height: 33px; + @media screen and (orientation: landscape){ + width: 92px; + height: 22px; + } + ` + : ''} ` diff --git a/src/features/MainWrapper/index.tsx b/src/features/MainWrapper/index.tsx deleted file mode 100644 index d012b6fd..00000000 --- a/src/features/MainWrapper/index.tsx +++ /dev/null @@ -1,12 +0,0 @@ -import styled from 'styled-components/macro' - -import { devices } from 'config/devices' - -export const MainWrapper = styled.div` - width: 100%; - padding-left: 80px; - - @media ${devices.tablet} { - padding: 0 - } -` diff --git a/src/features/MatchCard/CardFrontside/index.tsx b/src/features/MatchCard/CardFrontside/index.tsx index 0cf6e01f..f1ad5cbc 100644 --- a/src/features/MatchCard/CardFrontside/index.tsx +++ b/src/features/MatchCard/CardFrontside/index.tsx @@ -6,14 +6,16 @@ import getUnixTime from 'date-fns/getUnixTime' import { ProfileTypes, PAGES } from 'config' import type { Match } from 'features/Matches' -import { SportName } from 'features/Common' +import { SportIcon } from 'features/SportIcon' import { useMatchSwitchesStore } from 'features/MatchSwitches' import { useName } from 'features/Name' import { T9n } from 'features/T9n' import { MatchAccess } from 'features/Matches/helpers/getMatchClickAction' +import { useUserFavoritesStore } from 'features/UserFavorites/store' import { NoAccessMessage } from '../NoAccessMessage' import { + CardWrapperOuter, CardWrapper, Info, LiveSign, @@ -30,6 +32,11 @@ import { TeamLogos, TeamLogo, BuyMatchBadge, + CountryFlag, + SecondaryInfo, + FavoriteSign, + NameSignWrapper, + HoverFrame, } from '../styled' import { MatchStatuses, useHeaderFiltersStore } from '../../HeaderFilters' @@ -61,6 +68,7 @@ export const CardFrontside = ({ } = match const isHomePage = useRouteMatch(PAGES.home)?.isExact const tournamentName = useName(tournament) + const { isInFavorites } = useUserFavoritesStore() const { isScoreHidden } = useMatchSwitchesStore() const { selectedMatchStatus } = useHeaderFiltersStore() const isInFuture = getUnixTime(date) > getUnixTime(new Date()) @@ -70,22 +78,29 @@ export const CardFrontside = ({ || selectedMatchStatus === MatchStatuses.Soon || isScoreHidden ) || live + const tournamentInFavorites = isInFavorites(ProfileTypes.TOURNAMENTS, tournament.id) + const team1InFavorites = isInFavorites(ProfileTypes.TEAMS, team1.id) + const team2InFavorites = isInFavorites(ProfileTypes.TEAMS, team2.id) return ( - - - { - preview - ? ( + + + + + { + preview && ( ) + } + {access === MatchAccess.NoCountryAccess + ? : ( - ) - } - {access === MatchAccess.NoCountryAccess && } - {access === MatchAccess.CanBuyMatch && } - - - {!isHomePage ? formattedDate : ''} + )} + {access === MatchAccess.CanBuyMatch && } + + + {isHomePage ? null : formattedDate} - {(isInFuture && (!hasVideo || !storage)) - ? - : null} - - {live && ( - - - - )} - - - - {isHomePage && } - {tournament && ( - - {tournamentName} - - )} - - - - {showScore && {team1.score}} - - - - {showScore && {team2.score}} - - - - + {(isInFuture && (!hasVideo || !storage)) + ? + : null} + + {live && ( + + + + )} + + + + + + + + {team1InFavorites && } + + {showScore && {team1.score}} + + + + + {team2InFavorites && } + + {showScore && {team2.score}} + + + + + + {tournament && ( + + + {tournamentName} + + {tournamentInFavorites && } + + )} + + + + ) } diff --git a/src/features/MatchCard/NoAccessMessage/index.tsx b/src/features/MatchCard/NoAccessMessage/index.tsx index 164193f7..2b239c28 100644 --- a/src/features/MatchCard/NoAccessMessage/index.tsx +++ b/src/features/MatchCard/NoAccessMessage/index.tsx @@ -13,8 +13,8 @@ const Wrapper = styled.div` ` const Message = styled.div` - width: 240px; - padding: 12px; + width: 12rem; + padding: 0.567; color: #fff; background-color: #EB5757; box-shadow: 0px 1px 5px rgba(0, 0, 0, 0.25); @@ -23,13 +23,13 @@ const Message = styled.div` const Title = styled.h3` font-weight: bold; - font-size: 16px; - line-height: 20px; + font-size: 0.75rem; + line-height: 0.95rem; ` const Text = styled.p` - font-size: 13px; - line-height: 16px; + font-size: 0.614; + line-height: 0.75rem; ` export const NoAccessMessage = () => ( diff --git a/src/features/MatchCard/config.tsx b/src/features/MatchCard/config.tsx new file mode 100644 index 00000000..9930e6b5 --- /dev/null +++ b/src/features/MatchCard/config.tsx @@ -0,0 +1,3 @@ +export const MATCH_CARD_WIDTH = 12 + +export const MATCH_CARD_GAP = 20 diff --git a/src/features/MatchCard/config/index.tsx b/src/features/MatchCard/config/index.tsx deleted file mode 100644 index a42755d3..00000000 --- a/src/features/MatchCard/config/index.tsx +++ /dev/null @@ -1,3 +0,0 @@ -export const MATCH_CARD_WIDTH = 288 - -export const MATCH_CARD_GAP = 16 diff --git a/src/features/MatchCard/styled.tsx b/src/features/MatchCard/styled.tsx index 8e89d67f..b85622bd 100644 --- a/src/features/MatchCard/styled.tsx +++ b/src/features/MatchCard/styled.tsx @@ -1,85 +1,111 @@ -import styled from 'styled-components/macro' +import styled, { css } from 'styled-components/macro' import { devices } from 'config/devices' +import { isMobileDevice } from 'config/userAgent' import { Name } from 'features/Name' import { ProfileLogo } from 'features/ProfileLogo' -import { MATCH_CARD_WIDTH } from './config' +export const CardWrapperOuter = styled.li.attrs({ + tabIndex: 0, +})` + padding-top: 100%; + position: relative; + ${isMobileDevice + ? css` + width: 100%; + padding-top: 0; + height: 90px; + margin-bottom: 10px; + + + @media screen and (orientation: landscape){ + width: 49%; + :nth-child(odd){ + margin-right: 10px; + } + } + ` + : ''}; +` export const CardWrapper = styled.li.attrs({ tabIndex: 0, })` - position: relative; - flex: 0 0 auto; - min-width: ${MATCH_CARD_WIDTH}px; - height: 100%; - padding-bottom: 18px; + position: absolute; + position: absolute; + top: 0; + left: 0; + bottom: 0; + right: 0; + padding-bottom: 0.75rem; border-radius: 2px; - border: 2px solid transparent; background-color: #3F3F3F; box-shadow: 0px 1px 2px rgba(0, 0, 0, 0.4); cursor: pointer; - transition: border 0.5s ease-out; - margin-right: 16px; - - @media ${devices.laptop} { - height: 279px; - min-width: 279px; - } + ${isMobileDevice + ? css` + width: 100%; + height: 90px; + padding-bottom: 0; + ` + : ''}; +` - @media ${devices.tablet} { - height: 299px; - } - @media ${devices.mobile} { - width: 100%; - margin-bottom: 10px; - } +export const HoverFrame = styled.div` + position: absolute; + min-width: 100%; + min-height: 100%; + border-radius: 2px; + border: 2px solid transparent; + transition: border 0.5s ease-out; + z-index: 2; &:hover { - border: 2px solid #A4A4A4 + border: 2px solid #fff } ` export const PreviewWrapper = styled.div` position: relative; display: flex; - flex-direction: column-reverse; - height: 147px; - - @media ${devices.tablet} { - height: 208px; - } + width: 100%; + height: 60%; + ${isMobileDevice + ? css` + width: 50%; + height: 100%; + ` + : ''}; ` export const Preview = styled.img` + position: absolute; width: 100%; height: 100%; + object-fit: cover; + opacity: 0.4; ` + export const MatchTimeInfo = styled.div` position: absolute; - top: 25px; - left: 24px; - width: auto; - display: inline-flex; - - @media ${devices.tablet} { - top: 10px; - left: 20px; - } + top: 0.519rem; + left: 0.519rem; ` -type TMatchDate = { +type MatchDateProps = { isHomePage?: boolean, } -export const MatchDate = styled.div` - height: 24px; + +export const MatchDate = styled.div` + height: 0.9rem; border-radius: 2px; - padding: ${({ isHomePage }) => (!isHomePage ? '8px' : '')}; - font-size: 11px; + padding: ${({ isHomePage }) => (!isHomePage ? '0 0.27rem' : '')}; font-weight: bold; + font-size: 0.472rem; + line-height: 0.567rem; + letter-spacing: 0.05em; text-transform: uppercase; - letter-spacing: 0.1em; display: flex; align-items: center; justify-content: center; @@ -88,105 +114,190 @@ export const MatchDate = styled.div` background-color: #6D6D6D; @media ${devices.tablet} { - padding: ${({ isHomePage }) => (!isHomePage ? '6px 8px' : '')}; + padding: ${({ isHomePage }) => (!isHomePage ? '0.27rem 0.36rem' : '')}; } + ${isMobileDevice + ? css` + height: 15px; + font-size: 8px; + ` + : ''}; ` + export const LiveSign = styled(MatchDate)` background-color: #CC0000; + position: absolute; + top: 0; ` export const Time = styled.span` - margin: 0 8px; + margin: 0 0.2rem; ` export const Info = styled.div` - padding: 33px 24px 0; + display: flex; + flex-direction: column; + padding: 0.85rem 0.472rem 0 0.519rem; color: #fff; text-overflow: ellipsis; overflow: hidden; white-space: nowrap; + ${isMobileDevice + ? css` + position: absolute; + top: 0; + left: 50%; + width: 50%; + height: 100%; + padding: 13px 12px 13px 10px; + ` + : ''}; + +` - @media ${devices.tablet} { - padding: 13px 18px 17px 18px; - } +export const SecondaryInfo = styled.div` + display: flex; + align-items: center; +` + +export const CountryFlag = styled.img` + width: 0.71rem; + height: 0.75rem; + margin-left: 0.567rem; + object-fit: contain; + object-position: bottom; + ${isMobileDevice + ? css` + width: 12px; + height: 8px; + margin-left: 3.5px; + ` + : ''}; +` + +type FavoriteSignProps = { + marginLeft?: number, +} + +export const FavoriteSign = styled.span` + display: inline-block; + width: 0.472rem; + height: 0.472rem; + margin-top: 0.08rem; + margin-left: ${({ marginLeft = 9 }) => marginLeft}px; + background: url('/images/sportFavStar.png') no-repeat center / 100% 100%; + ${isMobileDevice + ? css` + width: 10px; + height: 10px; + ` + : ''}; +` + +const nameStyles = css` + text-overflow: ellipsis; + white-space: nowrap; + overflow: hidden; ` export const TournamentName = styled.span` - color: rgba(255, 255, 255, 0.5); - font-size: 10px; + max-width: 95%; + color: rgba(255, 255, 255, 0.7); + font-size: 0.567rem; + line-height: 0.95rem; + margin-left: 0.567rem; + ${isMobileDevice + ? css` + font-size: 10px; + line-height: 100%; + max-width: 100%; + ` + : ''}; - @media ${devices.tablet} { - margin-top: 10px; - } + ${nameStyles} ` export const Teams = styled.div` - margin-top: 21px; - - @media ${devices.tablet} { - margin-top: 0; - } + margin-bottom: 0.567rem; + ${isMobileDevice + ? css` + margin-bottom: 15px; + ` + : ''}; ` -export const Team = styled.div` +export const Team = styled.span` display: flex; justify-content: space-between; - margin-bottom: 5px; - font-size: 17px; - font-weight: bold; - line-height: 21px; + align-items: center; + font-weight: 600; + font-size: 0.85rem; + line-height: 1.14rem; color: #fff; + ${isMobileDevice + ? css` + font-size: 14px; + line-height: 20px; + ` + : ''}; ` -export const TeamName = styled(Name)` +export const NameSignWrapper = styled.div` + display: flex; max-width: 90%; - text-overflow: ellipsis; - white-space: nowrap; - overflow: hidden; + align-items: center; ` -export const Score = styled.div` - margin-left: auto; - text-align: center; - width: 10%; +export const TeamName = styled(Name)` + ${nameStyles} ` +export const Score = styled.div`` + export const TeamLogos = styled.div` display: flex; - padding-left: 24px; - - @media ${devices.mobile} { - justify-content: space-between; - padding-right: 20px; - } + align-items: center; + justify-content: center; + margin: 0.71rem auto 0 auto; + z-index: 1; ` export const TeamLogo = styled(ProfileLogo)` - width: 60px; + width: 33%; :last-child { - margin-left: 8px; - } + margin-left: 0.8rem; - @media ${devices.mobile} { - width: 134px; } + ${isMobileDevice + ? css` + width: 30%; + ` + : ''}; ` export const BuyMatchBadge = styled.span` position: absolute; - right: 12px; - bottom: 12px; - width: 40px; - height: 40px; + left: 0.472rem; + bottom: 0.519rem; + width: 1.18rem; + height: 1.18rem; cursor: pointer; - z-index: 1; background-image: url(/images/dollar-sign.svg); background-position: center; background-repeat: no-repeat; + background-size: 0.472rem 0.71rem; background-color: #fff; border-radius: 50%; border: 0.5px solid rgba(0, 0, 0, 0.2); - box-shadow: 0px 2px 6px rgba(0, 0, 0, 0.5); + box-shadow: 0px 0.08rem 0.283rem rgba(0, 0, 0, 0.5); + + ${isMobileDevice + ? css` + width: 19px; + height: 17px; + background-size: 40%; + ` + : ''}; ` diff --git a/src/features/MatchPage/components/FinishedMatch/hooks/index.tsx b/src/features/MatchPage/components/FinishedMatch/hooks/index.tsx index fabfc537..5ce9849f 100644 --- a/src/features/MatchPage/components/FinishedMatch/hooks/index.tsx +++ b/src/features/MatchPage/components/FinishedMatch/hooks/index.tsx @@ -10,8 +10,8 @@ import { import type { Settings } from 'features/MatchPopup' import { useMatchPopupStore } from 'features/MatchPopup' -import { usePlayerProgressReporter } from 'features/MatchPage/hooks/usePlayerProgressReporter' +import { usePlayerLogger } from './usePlayerLogger' import { useEpisodes } from './useEpisodes' import { useChapters } from './useChapters' @@ -38,6 +38,8 @@ export const useFinishedMatch = ({ profile }: Props) => { const { episodes } = useEpisodes() + const { logPlaylistChange, onPlayingChange } = usePlayerLogger() + useEffect(() => { if (profile) { const match = { @@ -64,10 +66,18 @@ export const useFinishedMatch = ({ profile }: Props) => { closeSettingsPopup() } + const onPlaylistSelect: typeof handlePlaylistClick = (playlist, e) => { + if (selectedPlaylist) { + logPlaylistChange(selectedPlaylist) + } + handlePlaylistClick(playlist, e) + } + return { closeSettingsPopup, isSettingsPopupOpen, - onPlaylistSelect: handlePlaylistClick, + onPlayingChange, + onPlaylistSelect, openSettingsPopup, playlists: matchPlaylists, profile, @@ -77,6 +87,5 @@ export const useFinishedMatch = ({ profile }: Props) => { episodes, selectedPlaylist, }), - ...usePlayerProgressReporter(), } } diff --git a/src/features/MatchPage/components/FinishedMatch/hooks/usePlayerLogger.tsx b/src/features/MatchPage/components/FinishedMatch/hooks/usePlayerLogger.tsx new file mode 100644 index 00000000..52c4c978 --- /dev/null +++ b/src/features/MatchPage/components/FinishedMatch/hooks/usePlayerLogger.tsx @@ -0,0 +1,72 @@ +import { + useCallback, + useRef, +} from 'react' +import { useLocation } from 'react-router' + +import { LogActions, logUserAction } from 'requests/logUserAction' + +import { useInterval } from 'hooks/useInterval' +import { usePageParams } from 'hooks/usePageParams' + +import { PlaylistOption, PlaylistTypes } from 'features/MatchPage/types' + +const playlistTypeConfig = { + ball_in_play: 2, + full_game: 1, + goals: 4, + highlights: 3, + players: 5, +} + +const getInitialData = () => ({ dateVisit: new Date().toISOString(), seconds: 0 }) + +export const usePlayerLogger = () => { + const location = useLocation() + const { profileId, sportType } = usePageParams() + const data = useRef(getInitialData()) + + const incrementSeconds = () => data.current.seconds++ + + const resetData = () => { + data.current = getInitialData() + } + + const { start, stop } = useInterval({ + callback: incrementSeconds, + intervalDuration: 1000, + startImmediate: false, + }) + + const onPlayingChange = useCallback((playing: boolean) => { + if (playing) { + start() + } else { + stop() + } + }, [start, stop]) + + const logPlaylistChange = (prevPlaylist: PlaylistOption) => { + const args = prevPlaylist.type === PlaylistTypes.MATCH + ? { + playlistType: playlistTypeConfig[prevPlaylist.id], + } + : { + playerId: prevPlaylist.id, + playlistType: playlistTypeConfig.players, + } + + logUserAction({ + actionType: LogActions.VideoChange, + dateVisit: data.current.dateVisit, + duration: data.current.seconds, + matchId: profileId, + sportType, + url: location.pathname, + ...args, + }) + resetData() + } + + return { logPlaylistChange, onPlayingChange } +} diff --git a/src/features/MatchPage/components/FinishedMatch/index.tsx b/src/features/MatchPage/components/FinishedMatch/index.tsx index e6be9cc2..ab5baf12 100644 --- a/src/features/MatchPage/components/FinishedMatch/index.tsx +++ b/src/features/MatchPage/components/FinishedMatch/index.tsx @@ -5,7 +5,6 @@ import isEmpty from 'lodash/isEmpty' import { MatchSidePlaylists } from 'features/MatchSidePlaylists' import { MultiSourcePlayer } from 'features/MultiSourcePlayer' -import { MatchProfileCard } from '../MatchProfileCard' import { SettingsPopup } from '../SettingsPopup' import type { Props } from './hooks' @@ -19,10 +18,8 @@ export const FinishedMatch = (props: Props) => { chapters, closeSettingsPopup, isSettingsPopupOpen, - onPlayerProgressChange, onPlayingChange, onPlaylistSelect, - openSettingsPopup, playlists, selectedPlaylist, setEpisodesSettings, @@ -44,12 +41,10 @@ export const FinishedMatch = (props: Props) => { - {!isEmpty(chapters) && ( )} @@ -60,7 +55,6 @@ export const FinishedMatch = (props: Props) => { selectedPlaylist={selectedPlaylist} onSelect={onPlaylistSelect} profile={profile} - openPopup={openSettingsPopup} /> )} diff --git a/src/features/MatchPage/components/LiveMatch/index.tsx b/src/features/MatchPage/components/LiveMatch/index.tsx index bc7f2f36..129acbb8 100644 --- a/src/features/MatchPage/components/LiveMatch/index.tsx +++ b/src/features/MatchPage/components/LiveMatch/index.tsx @@ -1,19 +1,12 @@ import { Fragment } from 'react' -import type { MatchInfo } from 'requests' - import { StreamPlayer } from 'features/StreamPlayer' import { useLiveMatch } from './hooks' -import { MatchProfileCard } from '../MatchProfileCard' import { LiveMatchSidePlaylists } from '../LiveMatchSidePlaylists' import { Container } from '../../styled' -type Props = { - profile: MatchInfo, -} - -export const LiveMatch = ({ profile }: Props) => { +export const LiveMatch = () => { const { onPlayerProgressChange, onPlayingChange, @@ -24,7 +17,6 @@ export const LiveMatch = ({ profile }: Props) => { return ( - ( - + + + + + + + + + + + ) diff --git a/src/features/MatchPage/components/MatchProfileCard/index.tsx b/src/features/MatchPage/components/MatchProfileCard/index.tsx index 206c1be2..c114c6bb 100644 --- a/src/features/MatchPage/components/MatchProfileCard/index.tsx +++ b/src/features/MatchPage/components/MatchProfileCard/index.tsx @@ -1,10 +1,7 @@ -import format from 'date-fns/format' - import type { MatchInfo } from 'requests' import { ProfileTypes } from 'config' -import { SportName } from 'features/Common/SportName' import { useMatchSwitchesStore } from 'features/MatchSwitches' import { Name } from 'features/Name' @@ -12,16 +9,14 @@ import { useSportNameParam } from 'hooks/useSportNameParam' import { Wrapper, - Teams, + Team, Score, - Tournament, Dash, StyledLink, - DateStyled, + ScoreWrapper, + Logo, } from './styled' -const dateFormat = 'dd.MM.yyyy' - type Props = { profile: MatchInfo, } @@ -35,48 +30,49 @@ export const MatchProfileCard = ({ profile }: Props) => { const { team1, team2, - tournament, } = profile return ( - + - + + + + { isScoreHidden ? : ( - {team1?.score} : {team2?.score} + {team1.score} - {team2.score} ) } + + - - - - - - - - + + - - {format(new Date(profile.date), dateFormat)} + ) } diff --git a/src/features/MatchPage/components/MatchProfileCard/styled.tsx b/src/features/MatchPage/components/MatchProfileCard/styled.tsx index 887c21f1..a4b3275d 100644 --- a/src/features/MatchPage/components/MatchProfileCard/styled.tsx +++ b/src/features/MatchPage/components/MatchProfileCard/styled.tsx @@ -1,94 +1,84 @@ -import styled from 'styled-components/macro' +import styled, { css } from 'styled-components/macro' + +import { isMobileDevice } from 'config/userAgent' -import { devices } from 'config/devices' import { ProfileLink } from 'features/ProfileLink' +import { ProfileLogo } from 'features/ProfileLogo' export const Wrapper = styled.div` + width: 70%; + margin: 0 auto; display: flex; - font-weight: bold; - font-size: 36px; - line-height: 24px; + align-items: center; + justify-content: center; + font-weight: 600; color: white; - min-height: 28px; - width: 100%; - margin-bottom: 14px; - - @media ${devices.desktop} { - font-size: 22px; - } + ${isMobileDevice + ? css` + height: 78px; + margin-top: 30px; + ` + : ''}; +` - @media ${devices.laptop} { - font-size: 18px; - } +type TeamProps = { + position?: 'left' | 'right', +} - @media ${devices.tablet} { - order: 2; - font-size: 16px; - padding: 15px 20px 0 20px; - flex-wrap: wrap; - margin-bottom: 0; - } -` +export const Team = styled.div` + font-size: 21px; + position: absolute; -export const Teams = styled.div` - display: flex; + ${({ position }) => (position === 'left' ? 'right: 54%' : 'left: 54%')}; + ${isMobileDevice + ? css` + font-size: 16px; + ` + : ''}; ` export const StyledLink = styled(ProfileLink)` - font-weight: bold; + display: flex; + align-items: center; color: white; + &:hover { text-decoration: underline; } ` export const Dash = styled.span` - position: relative; - width: 40px; - border-bottom: 3px solid white; - margin: 0 15px; - height: fit-content; - align-self: center; - - @media ${devices.tablet} { - width: 16px; - border-bottom: 1px solid white; - margin: 0 4px; - } + display: inline-block; + margin-top: 5px; + width: 30px; + height: 3px; + background-color: white; ` -export const Score = styled.div` - display: flex; - margin: 0 20px; - - @media ${devices.tablet} { - margin: 0 10px; - color: #EACB6F; - } +export const ScoreWrapper = styled.div` + margin: 0 10px; ` -export const Tournament = styled.div` - display: flex; - font-weight: 500; - font-size: 12px; - line-height: 16px; - align-self: flex-end; - margin-left: 48px; - color: #666666; - - @media ${devices.tablet} { - width: 100%; - margin-left: 0; - margin-top: 4px; - font-size: 11px; - } +export const Score = styled.span` + width: 80px; + font-size: 23px; + line-height: 18px; + text-align: center; + ${isMobileDevice + ? css` + font-size: 16px; + ` + : ''}; ` -export const DateStyled = styled.span` - margin-left: auto; - - @media ${devices.tablet} { - margin-top: 4px; - margin-left: 0; - } +export const Logo = styled(ProfileLogo)` + width: 41px; + height: 41px; + margin: 0 9px; + ${isMobileDevice + ? css` + width: 30px; + height: 30px; + ` + : ''}; ` diff --git a/src/features/MatchPage/helpers/buildPlaylists.tsx b/src/features/MatchPage/helpers/buildPlaylists.tsx index fa84c9b4..5a6749e2 100644 --- a/src/features/MatchPage/helpers/buildPlaylists.tsx +++ b/src/features/MatchPage/helpers/buildPlaylists.tsx @@ -12,12 +12,13 @@ import { PlaylistTypes } from 'features/MatchPage/types' const MATCH_KEYS = [ 'full_game', - 'highlights', 'ball_in_play', + 'highlights', 'goals', ] as const export const FULL_GAME_KEY = MATCH_KEYS[0] +export type MatchPlaylistIds = typeof MATCH_KEYS[number] const getMatchPlaylists = (matchPlaylists: MatchPlaylists | null): MatchPlaylistOptions => { if (!matchPlaylists) return [] @@ -48,14 +49,8 @@ export const buildPlaylists = (matchPlaylists: MatchPlaylists | null) => { lexics: matchPlaylists?.lexics, match: getMatchPlaylists(matchPlaylists), players: { - team1: { - color: matchPlaylists?.color1 || '222222', - list: getPlayerPlaylists(matchPlaylists?.players1), - }, - team2: { - color: matchPlaylists?.color2 || 'FFFFFF', - list: getPlayerPlaylists(matchPlaylists?.players2), - }, + team1: getPlayerPlaylists(matchPlaylists?.players1), + team2: getPlayerPlaylists(matchPlaylists?.players2), }, } return playlists diff --git a/src/features/MatchPage/index.tsx b/src/features/MatchPage/index.tsx index 5d376495..db95b31b 100644 --- a/src/features/MatchPage/index.tsx +++ b/src/features/MatchPage/index.tsx @@ -1,8 +1,11 @@ import { ProfileHeader } from 'features/ProfileHeader' -import { MainWrapper } from 'features/MainWrapper' import { UserFavorites } from 'features/UserFavorites' -import { MediaQuery } from 'features/MediaQuery' +import { + PageWrapper, + Main, +} from 'features/PageLayout' +import { MatchProfileCard } from './components/MatchProfileCard' import { FinishedMatch } from './components/FinishedMatch' import { LiveMatch } from './components/LiveMatch' import { useMatchProfile } from './hooks/useMatchProfile' @@ -10,19 +13,21 @@ import { Wrapper } from './styled' const MatchPage = () => { const profile = useMatchProfile() - const isLiveMatch = profile?.live + const hasVideo = profile?.has_video return ( - - + + + + +
- - - - {(isLiveMatch && profile) && } - {(!isLiveMatch && profile) && } - - + + {(!hasVideo && profile) && } + {(hasVideo && profile) && } + +
+
) } diff --git a/src/features/MatchPage/styled.tsx b/src/features/MatchPage/styled.tsx index f404c1a3..a4467182 100644 --- a/src/features/MatchPage/styled.tsx +++ b/src/features/MatchPage/styled.tsx @@ -1,19 +1,33 @@ -import styled from 'styled-components/macro' +import styled, { css } from 'styled-components/macro' import { devices } from 'config/devices' +import { isMobileDevice } from 'config/userAgent' export const Wrapper = styled.div` - margin: 63px 0px 0 22px; + width: 100%; + height: calc(100vh - 115px); + margin: 20px 0px 0 10px; display: flex; - @media ${devices.laptop} { - margin: 0px 16px; - } - @media ${devices.tablet} { + width: 100%; flex-direction: column; - margin: 0 0 16px 0; + margin: 0; } + + ${isMobileDevice + ? css` + @media (max-width: 750px){ + margin-top: 50px; + } + @media screen and (orientation: landscape){ + flex-direction: row; + justify-content: space-between; + margin-left: 10px; + margin-top: 55px; + } + ` + : ''}; ` export const Container = styled.div` @@ -23,16 +37,19 @@ export const Container = styled.div` flex-direction: column; flex-grow: 1; - @media ${devices.laptop} { - max-width: 80%; - } - @media ${devices.tablet} { - order: 1; - } - - @media ${devices.mobile} { + flex-grow: 0; max-width: 100%; - margin-right: 0; } + + ${isMobileDevice + ? css` + max-width: 100%; + margin-right: 0; + padding: 0; + @media screen and (orientation: landscape){ + flex: 2; + } + ` + : ''}; ` diff --git a/src/features/MatchPage/types.tsx b/src/features/MatchPage/types.tsx index 70af1fda..0c10a36a 100644 --- a/src/features/MatchPage/types.tsx +++ b/src/features/MatchPage/types.tsx @@ -1,5 +1,7 @@ import type { Lexics, Episodes } from 'requests' +import type { MatchPlaylistIds } from './helpers/buildPlaylists' + export enum PlaylistTypes { MATCH, PLAYER, @@ -9,7 +11,7 @@ export enum PlaylistTypes { export type MatchPlaylistOption = { data: Episodes, duration?: number, - id: string, + id: MatchPlaylistIds, lexic: number | string, type: PlaylistTypes.MATCH, } @@ -46,13 +48,7 @@ export type Playlists = { lexics?: Lexics, match: MatchPlaylistOptions, players: { - team1: { - color: string, - list: PlayerPlaylistOptions, - }, - team2: { - color: string, - list: PlayerPlaylistOptions, - }, + team1: PlayerPlaylistOptions, + team2: PlayerPlaylistOptions, }, } diff --git a/src/features/MatchPopup/components/ApplyButton/index.tsx b/src/features/MatchPopup/components/ApplyButton/index.tsx deleted file mode 100644 index 42af40d9..00000000 --- a/src/features/MatchPopup/components/ApplyButton/index.tsx +++ /dev/null @@ -1,28 +0,0 @@ -import styled from 'styled-components/macro' - -import { T9n } from 'features/T9n' -import { ButtonSolid } from 'features/Common' - -const Button = styled(ButtonSolid)` - position: absolute; - bottom: 46px; - left: 12px; - width: calc(100% - 24px); - height: 44px; - background-color: #294FC4; - border-radius: 5px; - font-weight: 600; - font-size: 17px; - line-height: 22px; - letter-spacing: -0.408px; -` - -type Props = { - onClick: () => void, -} - -export const ApplyButton = ({ onClick }: Props) => ( - -) diff --git a/src/features/MatchPopup/components/BackButton/index.tsx b/src/features/MatchPopup/components/BackButton/index.tsx deleted file mode 100644 index 7c40dea6..00000000 --- a/src/features/MatchPopup/components/BackButton/index.tsx +++ /dev/null @@ -1,15 +0,0 @@ -import styled from 'styled-components/macro' - -import { useMatchPopupStore } from 'features/MatchPopup' -import { BaseButton } from 'features/PopupComponents' - -const Button = styled(BaseButton)` - background-image: url(/images/back-icon.svg); -` - -export const BackButton = () => { - const { goBack } = useMatchPopupStore() - return ( -
- { - matchPlaylists && - } + ) } diff --git a/src/features/MatchPopup/components/LivePlaylistPage/styled.tsx b/src/features/MatchPopup/components/LivePlaylistPage/styled.tsx index 99d32fa9..a3120271 100644 --- a/src/features/MatchPopup/components/LivePlaylistPage/styled.tsx +++ b/src/features/MatchPopup/components/LivePlaylistPage/styled.tsx @@ -1,39 +1,77 @@ -import styled from 'styled-components/macro' +import styled, { css } from 'styled-components/macro' +import { isMobileDevice } from 'config/userAgent' import { customScrollbar } from 'features/Common' export const Content = styled.div` - width: 495px; + width: 23.35rem; + margin: 0 auto; height: 100%; overflow-y: auto; ${customScrollbar} + ${isMobileDevice + ? css` + width: 90%; + margin-top: 30%; + background: #333; + border-radius: 4px; + padding: 30px 0; + + @media (orientation: landscape){ + margin: 0 auto; + padding: 10px 0; + } + @media (max-width: 600px) and (orientation: landscape){ + margin-top: 50px; + } + ` + : ''}; ` export const Header = styled.div` display: flex; align-items: center; + ${isMobileDevice + ? css` + position: relative; + ` + : ''}; ` export const HeaderActions = styled.div` position: absolute; display: flex; - top: 20px; - right: 20px; + top: 0.71rem; + right: 0.71rem; + ${isMobileDevice + ? css` + top: 20%; + right: 40px; + z-index: 10; + ` + : ''}; ` export const HeaderTitle = styled.h2` margin: 0 auto; width: 70%; font-weight: 600; - font-size: 24px; - line-height: 42px; + font-size: 1.14rem; + line-height: 1.982rem; color: #FFFFFF; text-align: center; + ${isMobileDevice + ? css` + font-size: 14px; + line-height: 18px; + margin-bottom: 20px; + ` + : ''}; ` export const BlockTitle = styled.h3` font-weight: normal; - font-size: 24px; - line-height: 50px; + font-size: 1.14rem; + line-height: 2.36rem; text-transform: capitalize; ` diff --git a/src/features/MatchPopup/components/MatchSettingsPage/index.tsx b/src/features/MatchPopup/components/MatchSettingsPage/index.tsx deleted file mode 100644 index 9963c7e6..00000000 --- a/src/features/MatchPopup/components/MatchSettingsPage/index.tsx +++ /dev/null @@ -1,38 +0,0 @@ -import { MediaQuery } from 'features/MediaQuery' -import { T9n } from 'features/T9n' -import { - CloseButton, - Header, - HeaderActions, - HeaderTitle, -} from 'features/PopupComponents' - -import { useMatchPopupStore } from '../../store' -import { BackButton } from '../BackButton' -import { Content } from '../../styled' - -export const MatchSettingsPage = () => { - const { closePopup } = useMatchPopupStore() - - return ( - -
- - - - - - - - - - - - - - - -
-
- ) -} diff --git a/src/features/MatchPopup/components/PlayerSettingsPage/index.tsx b/src/features/MatchPopup/components/PlayerSettingsPage/index.tsx deleted file mode 100644 index 3a4df718..00000000 --- a/src/features/MatchPopup/components/PlayerSettingsPage/index.tsx +++ /dev/null @@ -1,66 +0,0 @@ -import { MDASH } from 'config' - -import { MediaQuery } from 'features/MediaQuery' -import { Name } from 'features/Name' -import { T9n } from 'features/T9n' -import { - CloseButton, - Header, - HeaderActions, - HeaderTitle, -} from 'features/PopupComponents' - -import { useMatchPopupStore } from '../../store' -import { BackButton } from '../BackButton' -import { SettingsDesktop } from '../SettingsDesktop' -import { SettingsMobile } from '../SettingsMobile' -import { Content } from '../../styled' -import { TitleWrapper, TeamNames } from './styled' - -export const PlayerSettingsPage = () => { - const { - closePopup, - match, - selectedPlaylist, - } = useMatchPopupStore() - - if (!match) return null - - return ( - -
- - - - - - - - - - - - - - - - {selectedPlaylist && } - - - {` ${MDASH} `} - - - - -
- - - - - - - - -
- ) -} diff --git a/src/features/MatchPopup/components/PlayerSettingsPage/styled.tsx b/src/features/MatchPopup/components/PlayerSettingsPage/styled.tsx deleted file mode 100644 index c9698041..00000000 --- a/src/features/MatchPopup/components/PlayerSettingsPage/styled.tsx +++ /dev/null @@ -1,13 +0,0 @@ -import styled from 'styled-components/macro' - -import { HeaderTitle } from 'features/PopupComponents' - -export const TitleWrapper = styled(HeaderTitle)` - display: flex; - flex-direction: column; - line-height: 32px; -` - -export const TeamNames = styled.span` - font-weight: normal; -` diff --git a/src/features/MatchPopup/components/PlayersList/index.tsx b/src/features/MatchPopup/components/PlayersList/index.tsx deleted file mode 100644 index 348b2b85..00000000 --- a/src/features/MatchPopup/components/PlayersList/index.tsx +++ /dev/null @@ -1,71 +0,0 @@ -import map from 'lodash/map' - -import { ProfileTypes } from 'config' - -import { T9n } from 'features/T9n' -import { Name } from 'features/Name' -import { PlayerPlaylistOptions } from 'features/MatchPage/types' -import { useMatchPopupStore } from 'features/MatchPopup/store' - -import { Teams } from '../../types' -import { - List, - Item, - Logo, - NameWrapper, - ShirtNumber, - Button, - PlayerPhoto, - PlayerGk, -} from './styled' - -type Props = { - allLoaded: boolean, - onLoad: (id: number) => void, - players: PlayerPlaylistOptions, - team: Teams, - teamColor: string, -} - -export const PlayersList = ({ - allLoaded, - onLoad, - players, - team, - teamColor, -}: Props) => { - const { handlePlayerClick, match } = useMatchPopupStore() - - if (!match) return null - - return ( - - { - map(players, (player) => ( - - - - )) - } - - ) -} diff --git a/src/features/MatchPopup/components/PlayersList/styled.tsx b/src/features/MatchPopup/components/PlayersList/styled.tsx deleted file mode 100644 index f23338b3..00000000 --- a/src/features/MatchPopup/components/PlayersList/styled.tsx +++ /dev/null @@ -1,130 +0,0 @@ -import styled from 'styled-components/macro' - -import { devices } from 'config' - -import { ProfileLogo } from 'features/ProfileLogo' - -import { Teams } from '../../types' - -type ListProps = { - team: Teams, -} - -type ItemProps = { - isReady?: boolean, -} - -export const List = styled.ul` - display: flex; - flex-wrap: wrap; - align-content: flex-start; - justify-content: ${({ team }) => ( - team === Teams.TEAM1 - ? 'flex-end' - : '' - )}; - - @media ${devices.mobile} { - width: 100%; - height: auto; - flex-direction: column; - } -` - -export const Item = styled.li` - width: 76px; - min-height: 95px; - max-height: 110px; - margin: 10px; - transition: .3s; - opacity: ${({ isReady }) => ( - isReady - ? '1' - : '0' - )}; - - @media ${devices.mobile} { - width: 100%; - height: 60px; - flex-direction: row; - justify-content: flex-start; - margin: 0px; - padding: 10px; - } -` - -export const Button = styled.button` - border: none; - background: none; - cursor: pointer; - padding: 0; - - width: 100%; - height: 100%; - display: flex; - flex-direction: column; - align-items: center; - - @media ${devices.mobile} { - flex-direction: row; - justify-content: flex-start; - } -` - -type LogoProps = { - teamColor: string, -} - -export const Logo = styled(ProfileLogo)` - width: 100%; - height: 100%; - object-fit: contain; - border-radius: 50%; - background-color: ${({ teamColor }) => `#${teamColor}`}; -` - -export const NameWrapper = styled.span` - color: #fff; - line-height: 10px; - font-size: 10px; - text-align: center; - letter-spacing: 0.02em; - - @media ${devices.mobile} { - text-align: start; - font-size: 16px; - line-height: 20px; - letter-spacing: 0.1px; - } -` - -export const ShirtNumber = styled.span` - font-weight: bold; - margin-right: 6px; -` - -export const PlayerPhoto = styled.div` - position: relative; - width: 65px; - height: 65px; - margin-bottom: 10px; - - @media ${devices.mobile} { - width: 49px; - height: 49px; - margin-right: 11px; - } -` - -export const PlayerGk = styled.span` - position: absolute; - bottom: 0; - left: 50%; - transform: translate(-50%, 50%); - width: 24px; - font-weight: bold; - font-size: 9px; - line-height: 12px; - background-color: #fff; - border-radius: 12px; -` diff --git a/src/features/MatchPopup/components/PlayersListDesktop/index.tsx b/src/features/MatchPopup/components/PlayersListDesktop/index.tsx deleted file mode 100644 index 0bff89cc..00000000 --- a/src/features/MatchPopup/components/PlayersListDesktop/index.tsx +++ /dev/null @@ -1,57 +0,0 @@ -import styled from 'styled-components/macro' - -import { T9n } from 'features/T9n' -import { popupScrollbarStyles } from 'features/PopupComponents' -import { useMatchPopupStore } from 'features/MatchPopup' - -import { Teams } from '../../types' -import { BlockTitle } from '../../styled' -import { GroupedPlayersList } from '../GroupedPlayersList' - -const Wrapper = styled.div` - display: flex; - flex-direction: column; - align-items: center; - margin-top: 7px; -` - -const ListsWrapper = styled.div` - width: 100%; - max-height: 515px; - overflow-y: auto; - margin-top: 10px; - display: flex; - flex-direction: row; - justify-content: space-around; - - ${popupScrollbarStyles}; -` - -export const PlayersListDesktop = () => { - const { - match, - matchPlaylists, - } = useMatchPopupStore() - - if (!match || !matchPlaylists) return null - - return ( - - - - - - - - - - ) -} diff --git a/src/features/MatchPopup/components/PlayersListMobile/index.tsx b/src/features/MatchPopup/components/PlayersListMobile/index.tsx deleted file mode 100644 index c9b92404..00000000 --- a/src/features/MatchPopup/components/PlayersListMobile/index.tsx +++ /dev/null @@ -1,55 +0,0 @@ -import { useState } from 'react' - -import { T9n } from 'features/T9n' -import { Name } from 'features/Name' -import { useMatchPopupStore } from 'features/MatchPopup' - -import { Teams } from '../../types' -import { BlockTitle } from '../../styled' -import { GroupedPlayersList } from '../GroupedPlayersList' -import { - Wrapper, - Tabs, - Tab, -} from './styled' - -export const PlayersListMobile = () => { - const { - match, - matchPlaylists, - } = useMatchPopupStore() - const [selectedTeam, setSelectedTeam] = useState(Teams.TEAM1) - - if (!match || !matchPlaylists) return null - - const players = selectedTeam === Teams.TEAM1 - ? matchPlaylists.players.team1 - : matchPlaylists.players.team2 - - return ( - - - - - - setSelectedTeam(Teams.TEAM1)} - > - - - setSelectedTeam(Teams.TEAM2)} - > - - - - - - ) -} diff --git a/src/features/MatchPopup/components/PlayersListMobile/styled.tsx b/src/features/MatchPopup/components/PlayersListMobile/styled.tsx deleted file mode 100644 index 2e008a60..00000000 --- a/src/features/MatchPopup/components/PlayersListMobile/styled.tsx +++ /dev/null @@ -1,47 +0,0 @@ -import styled, { css } from 'styled-components/macro' - -export const Wrapper = styled.div` - display: flex; - flex-direction: column; - align-items: center; - margin-top: 8px; -` - -export const Tabs = styled.ul` - width: calc(100% - 24px); - margin: 20px 0 12px 0; - height: 32px; - display: flex; - border: 1px solid rgba(255, 255, 255, 0.5); - border-radius: 10px; - overflow: hidden; -` - -type TabProps = { - selected?: boolean, -} - -export const Tab = styled.li.attrs(() => ({ - tabIndex: 0, -}))` - width: 50%; - padding: 0 12px; - display: flex; - justify-content: center; - align-items: center; - font-weight: 500; - font-size: 14px; - line-height: 18px; - cursor: pointer; - - ${({ selected }) => ( - selected - ? css` - color: #000000; - background-color: #ffffff; - ` - : css` - color: #ffffff; - ` - )} -` diff --git a/src/features/MatchPopup/components/PlaylistButton/index.tsx b/src/features/MatchPopup/components/PlaylistButton/index.tsx deleted file mode 100644 index 3dde38a4..00000000 --- a/src/features/MatchPopup/components/PlaylistButton/index.tsx +++ /dev/null @@ -1,118 +0,0 @@ -import { Link } from 'react-router-dom' - -import styled, { css } from 'styled-components/macro' - -import { devices } from 'config' -import { secondsToHms } from 'helpers' - -import { T9n } from 'features/T9n' -import { MatchPlaylistOption } from 'features/MatchPage/types' - -import { useMatchPopupStore } from '../../store' - -type ButtonsStypesProps = { - disabled?: boolean, -} - -export const buttonStyles = css` - border: none; - - display: flex; - justify-content: space-between; - align-items: center; - width: 100%; - height: 100%; - padding: 0 25px; - background: linear-gradient( - 180deg, - rgba(255, 255, 255, 0.1) 0%, - rgba(255, 255, 255, 0) 100% - ), - #5c5c5c; - box-shadow: 0px 1px 1px rgba(0, 0, 0, 0.3); - border-radius: 2px; - - ${({ disabled }) => ( - disabled - ? css` - pointer-events: none; - opacity: 0.5; - ` - : css` - cursor: pointer; - ` - )} - - :hover { - background-color: #555555; - } - - @media ${devices.mobile} { - justify-content: center; - border-radius: 5px; - } -` - -const StyledLink = styled(Link)` - ${buttonStyles} -` - -type TitleProps = { - textTransform?: 'uppercase' | 'capitalize', -} - -export const Title = styled.span` - font-weight: 500; - font-size: 20px; - line-height: 50px; - letter-spacing: 0.03em; - text-transform: ${({ textTransform = 'uppercase' }) => textTransform}; - color: #ffffff; - - @media ${devices.tablet} { - font-size: 17px; - } - - @media ${devices.mobile} { - line-height: 16px; - margin-right: 16px; - text-transform: none; - } -` - -export const Duration = styled(Title)` - font-weight: 300; - font-size: 24px; - letter-spacing: 0.05em; - - @media ${devices.mobile} { - font-size: 17px; - line-height: 16px; - text-transform: none; - } -` - -type Props = { - playlist: MatchPlaylistOption, - to: string, -} - -export const PlaylistButton = ({ - playlist, - to, -}: Props) => { - const { handlePlaylistClick } = useMatchPopupStore() - return ( - handlePlaylistClick(playlist, e)} - disabled={!playlist.duration} - tabIndex={!playlist.duration ? -1 : 0} - > - - <T9n t={playlist.lexic} /> - - {playlist.duration && {secondsToHms(playlist.duration)}} - - ) -} diff --git a/src/features/MatchPopup/components/SettingsButton/index.tsx b/src/features/MatchPopup/components/SettingsButton/index.tsx deleted file mode 100644 index de7676d9..00000000 --- a/src/features/MatchPopup/components/SettingsButton/index.tsx +++ /dev/null @@ -1,15 +0,0 @@ -import styled from 'styled-components/macro' - -import { useMatchPopupStore, PopupPages } from 'features/MatchPopup' -import { BaseButton } from 'features/PopupComponents' - -const Button = styled(BaseButton)` - background-image: url(/images/settings.svg); -` - -export const SettingsButton = () => { - const { goTo } = useMatchPopupStore() - return ( - + ) +} diff --git a/src/features/UserAccount/components/PageBankCards/index.tsx b/src/features/UserAccount/components/PageBankCards/index.tsx index 991858f7..69d19362 100644 --- a/src/features/UserAccount/components/PageBankCards/index.tsx +++ b/src/features/UserAccount/components/PageBankCards/index.tsx @@ -5,17 +5,33 @@ import isNull from 'lodash/isNull' import { AddCardForm } from 'features/AddCardForm' import { useCardsStore } from 'features/CardsStore' +import { + CloseButton, + HeaderActions, +} from 'features/PopupComponents' +import { SolidButton } from 'features/UserAccount/styled' +import { T9n } from 'features/T9n' import { BankCard } from '../BankCard' -import { FormWrapper, Wrapper } from './styled' +import { + FormWrapper, + Wrapper, + Modal, + Header, + ModalBody, + PaymentInfoText, + InfoText, +} from './styled' export const PageBankCards = () => { const { cards, defaultCard, fetchCards, + infoModalOpen, onDeleteCard, onSetDefaultCard, + toggleInfoModal, } = useCardsStore() useEffect(() => { @@ -35,9 +51,27 @@ export const PageBankCards = () => { onDelete={onDeleteCard} /> ))} + + + + + +
+ + + +
+ + + + + + Ok + +
) } diff --git a/src/features/UserAccount/components/PageBankCards/styled.tsx b/src/features/UserAccount/components/PageBankCards/styled.tsx index b19757ba..4ee9e8dc 100644 --- a/src/features/UserAccount/components/PageBankCards/styled.tsx +++ b/src/features/UserAccount/components/PageBankCards/styled.tsx @@ -2,7 +2,12 @@ import styled from 'styled-components/macro' import { devices } from 'config' -export const Wrapper = styled.div`` +import { Modal as ModalBase } from 'features/Modal' +import { ModalWindow } from 'features/Modal/styled' + +export const Wrapper = styled.div` + margin-top: 28px; +` export const FormWrapper = styled.div` width: 560px; @@ -12,3 +17,40 @@ export const FormWrapper = styled.div` max-width: auto; } ` + +export const PaymentInfoText = styled.span` + display: inline-block; + margin-top: 6px; + font-weight: 500; + font-size: 16px; + line-height: 24px; + color: rgba(255, 255, 255, 0.3); +` + +export const Modal = styled(ModalBase)` + background-color: rgba(0, 0, 0, 0.7); + ${ModalWindow} { + padding: 40px 86px; + background: #333333; + border-radius: 5px; + } +` + +export const Header = styled.div` + position: absolute; + top: 15px; + right: 0; +` + +export const ModalBody = styled.div` + display: flex; + flex-direction: column; + align-items: center; +` + +export const InfoText = styled.div` + font-weight: normal; + font-size: 20px; + line-height: 27px; + margin-bottom: 40px; +` diff --git a/src/features/UserAccount/components/PagePaymentsHistory/index.tsx b/src/features/UserAccount/components/PagePaymentsHistory/index.tsx index 707d2e97..bf21f862 100644 --- a/src/features/UserAccount/components/PagePaymentsHistory/index.tsx +++ b/src/features/UserAccount/components/PagePaymentsHistory/index.tsx @@ -1,4 +1,5 @@ import { + Wrapper, Table, THead, Th, @@ -8,50 +9,54 @@ import { } from './styled' export const PagePaymentsHistory = () => ( - - - - - - - - - - - - - - - - - - - - - -
- Дата платежа - - Подписка - - Тип - - Сумма -
- 12.01.21 - - Матч Спартак-Динамо - - На год - - 1999 руб. - - 22.12.20 - - Все матчи Спартака - - На месяц - - 999 руб. -
+ + + + + + + + + + + + + + + + + + + + + + + + +
+ Дата платежа + + Подписка + + Тип + + Сумма +
+ 12.01.21 + + Матч Спартак-Динамо + + На год + + 1999 руб. + + 22.12.20 + + Все матчи Спартака + + На месяц + + 999 руб. +
+
) diff --git a/src/features/UserAccount/components/PagePaymentsHistory/styled.tsx b/src/features/UserAccount/components/PagePaymentsHistory/styled.tsx index 10d583e0..a2c2dd51 100644 --- a/src/features/UserAccount/components/PagePaymentsHistory/styled.tsx +++ b/src/features/UserAccount/components/PagePaymentsHistory/styled.tsx @@ -2,6 +2,10 @@ import styled from 'styled-components/macro' import { devices } from 'config' +export const Wrapper = styled.div` + margin-top: 26px; +` + export const Table = styled.table`` export const THead = styled.thead`` diff --git a/src/features/UserAccount/components/PagePersonalInfo/hooks/index.tsx b/src/features/UserAccount/components/PagePersonalInfo/hooks/index.tsx index 78fb8580..e2354cdd 100644 --- a/src/features/UserAccount/components/PagePersonalInfo/hooks/index.tsx +++ b/src/features/UserAccount/components/PagePersonalInfo/hooks/index.tsx @@ -1,4 +1,5 @@ import { + useMemo, useState, useEffect, useCallback, @@ -21,6 +22,12 @@ import { } from 'requests' import type { FormState } from 'features/FormStore/hooks/useFormState' +import type { Languages } from 'features/LanguageSelect/config' +import { useLexicsStore } from 'features/LexicsStore' + +export type SaveWithLang = SaveUserInfo & { + language?: string | null, +} const transformToFormState = async (userInfo: UserInfo) => { const cities = userInfo.country?.id @@ -54,21 +61,34 @@ const transformToFormState = async (userInfo: UserInfo) => { } export const useUserInfo = () => { + const { changeLang, lang } = useLexicsStore() const [userInfo, setUserInfo] = useState() const fetchUserInfo = useCallback(() => { getUserInfo() - .then((data) => transformToFormState(data)) + .then(transformToFormState) .then(setUserInfo) }, []) - const onSubmit = useCallback((data: SaveUserInfo) => { - saveUserInfo(data).then(fetchUserInfo) - }, [fetchUserInfo]) + const onSubmit = useCallback((data: SaveWithLang) => { + saveUserInfo(data).then(() => { + fetchUserInfo() + if (data.language) { + changeLang(data.language as Languages) + } + }) + }, [fetchUserInfo, changeLang]) useEffect(() => { fetchUserInfo() }, [fetchUserInfo]) - return { onSubmit, userInfo } + const user = useMemo(() => (userInfo + ? { + ...userInfo, + [formIds.language]: { value: lang }, + } + : null), [userInfo, lang]) + + return { onSubmit, userInfo: user } } diff --git a/src/features/UserAccount/components/PageSubscriptions/index.tsx b/src/features/UserAccount/components/PageSubscriptions/index.tsx index f4e6f931..689940c3 100644 --- a/src/features/UserAccount/components/PageSubscriptions/index.tsx +++ b/src/features/UserAccount/components/PageSubscriptions/index.tsx @@ -3,17 +3,20 @@ import map from 'lodash/map' import { SportTypes } from 'config' import { SubscriptionType } from 'features/BuyMatchPopup/types' +import { T9n } from 'features/T9n' -import { useToggle } from 'hooks' - -import { SubscriptionModal } from '../SubscriptionsModal' import { UserSubscriptionsList } from '../UserSubscriptionsList' -import { Wrapper, SubscriptionsWrapper } from './styled' -import { SolidButton, Icon } from '../../styled' +import { + Wrapper, + SubscriptionsWrapper, + Tabs, + Tab, +} from './styled' -export type MatchSubscription = { +type MatchSubscription = { description: string, header: string, + isActive?: boolean, price: number, subscription_id: number, type: SubscriptionType, @@ -26,6 +29,7 @@ const data: Record = { { description: 'Доступ к прямой трансляции, записи и хайлайты матча', header: 'подписка на матч спартак-динамо', + isActive: true, price: 999, subscription_id: 1, type: SubscriptionType.Month, @@ -33,6 +37,7 @@ const data: Record = { { description: 'все матчи спартака в сезоне 2020-2021', header: 'подписка на матч спартак-динамо', + isActive: true, price: 999, subscription_id: 2, type: SubscriptionType.Month, @@ -40,6 +45,7 @@ const data: Record = { { description: 'Доступ к прямой трансляции, записи и хайлайты матча', header: 'подписка на матч спартак-динамо', + isActive: true, price: 999, subscription_id: 3, type: SubscriptionType.Month, @@ -50,6 +56,7 @@ const data: Record = { { description: 'все матчи спартака в сезоне 2020-2021', header: 'подписка на матч спартак-динамо', + isActive: true, price: 999, subscription_id: 4, type: SubscriptionType.Month, @@ -57,6 +64,7 @@ const data: Record = { { description: 'Доступ к прямой трансляции, записи и хайлайты матча', header: 'подписка на матч спартак-динамо', + isActive: true, price: 999, subscription_id: 5, type: SubscriptionType.Month, @@ -64,6 +72,7 @@ const data: Record = { { description: 'Доступ к прямой трансляции, записи и хайлайты матча', header: 'подписка на матч спартак-динамо', + isActive: true, price: 999, subscription_id: 6, type: SubscriptionType.Month, @@ -71,36 +80,26 @@ const data: Record = { ], } -export const PageSubscriptions = () => { - const { - close, - isOpen, - open, - } = useToggle() - - return ( - - - { - map(data, (subscriptions, sport: SportTypes) => ( - - )) - } - - - - - Выбрать подписку - - - - - ) -} +export const PageSubscriptions = () => ( + + + + + + + + + + + { + map(data, (subscriptions, sport: SportTypes) => ( + + )) + } + + +) diff --git a/src/features/UserAccount/components/PageSubscriptions/styled.tsx b/src/features/UserAccount/components/PageSubscriptions/styled.tsx index fac54bff..686953b7 100644 --- a/src/features/UserAccount/components/PageSubscriptions/styled.tsx +++ b/src/features/UserAccount/components/PageSubscriptions/styled.tsx @@ -1,4 +1,4 @@ -import styled from 'styled-components/macro' +import styled, { css } from 'styled-components/macro' import { popupScrollbarStyles } from 'features/PopupComponents' @@ -14,3 +14,42 @@ export const SubscriptionsWrapper = styled.div` ${popupScrollbarStyles} ` + +export const Tabs = styled.div` + display: flex; + margin-bottom: 40px; +` + +type TabProps = { + active?: boolean, +} + +export const Tab = styled.button` + position: relative; + border: none; + padding: 0; + background-color: transparent; + width: 288px; + font-weight: 600; + font-size: 16px; + line-height: 50px; + color: rgba(255, 255, 255, 0.5); + cursor: pointer; + + ${({ active, theme }) => ( + active + ? css` + color: ${theme.colors.text100}; + + ::after { + position: absolute; + bottom: 0; + left: 0; + content: ''; + width: 100%; + height: 4px; + background-color: #fff; + } + ` + : '')} +` diff --git a/src/features/UserAccount/components/PersonalInfoForm/config.tsx b/src/features/UserAccount/components/PersonalInfoForm/config.tsx new file mode 100644 index 00000000..441a87ad --- /dev/null +++ b/src/features/UserAccount/components/PersonalInfoForm/config.tsx @@ -0,0 +1,14 @@ +import map from 'lodash/map' + +import type { Languages } from 'features/LanguageSelect/config' +import { langsList } from 'features/LanguageSelect/config' + +export type Lang = { + id: Languages, + name: string, +} + +export const langOptions = map(langsList, (lang) => ({ + id: lang.locale, + name: lang.title, +})) diff --git a/src/features/UserAccount/components/PersonalInfoForm/hooks/useCities.tsx b/src/features/UserAccount/components/PersonalInfoForm/hooks/useCities.tsx deleted file mode 100644 index 89d97d85..00000000 --- a/src/features/UserAccount/components/PersonalInfoForm/hooks/useCities.tsx +++ /dev/null @@ -1,73 +0,0 @@ -import type { FocusEvent } from 'react' -import { - useMemo, - useState, - useCallback, -} from 'react' - -import debounce from 'lodash/debounce' - -import type { Cities, City } from 'requests' -import { getCountryCities } from 'requests' - -import { formIds } from 'config/form' - -import { useForm } from 'features/FormStore' - -const useCitiesList = () => { - const [cities, setCities] = useState([]) - - const getCities = useCallback((selectedCountryId: number) => { - getCountryCities('', selectedCountryId).then(setCities) - }, []) - - const getCitiesDebounced = useMemo( - () => debounce(getCities, 500), - [getCities], - ) - - const resetCities = useCallback(() => setCities([]), []) - - return { - cities, - getCities: getCitiesDebounced, - resetCities, - } -} - -export const useCities = () => { - const { updateFormValue } = useForm() - - const { - cities, - getCities, - resetCities, - } = useCitiesList() - - const onCitySelect = useCallback((newCity: City | null) => { - if (newCity) { - updateFormValue(formIds.city)(newCity.name) - updateFormValue(formIds.cityId)(String(newCity.id)) - } else { - updateFormValue(formIds.cityId)('') - } - }, [updateFormValue]) - - const resetSelectedCity = useCallback(() => { - updateFormValue(formIds.city)('') - updateFormValue(formIds.cityId)('') - }, [updateFormValue]) - - const onCityBlur = (e: FocusEvent) => { - updateFormValue(formIds.city)(e.target.value ?? '') - } - - return { - cities, - getCities, - onCityBlur, - onCitySelect, - resetCities, - resetSelectedCity, - } -} diff --git a/src/features/UserAccount/components/PersonalInfoForm/hooks/useUserInfo.tsx b/src/features/UserAccount/components/PersonalInfoForm/hooks/useUserInfo.tsx index 3ccf61b8..deb4c9ef 100644 --- a/src/features/UserAccount/components/PersonalInfoForm/hooks/useUserInfo.tsx +++ b/src/features/UserAccount/components/PersonalInfoForm/hooks/useUserInfo.tsx @@ -1,18 +1,20 @@ -import { useCallback } from 'react' +import { useCallback, useMemo } from 'react' import trim from 'lodash/trim' +import find from 'lodash/find' import { formIds } from 'config/form' -import type { SaveUserInfo } from 'requests/saveUserInfo' - +import type { Languages } from 'features/LanguageSelect/config' import { useForm } from 'features/FormStore' import { useUserInfoForm } from './useUserInfoForm' import { useValidateForm } from './useValidateForm' +import { Lang, langOptions } from '../config' +import type { SaveWithLang } from '../../PagePersonalInfo/hooks' export type Props = { - onSubmit: (data: SaveUserInfo) => void, + onSubmit: (data: SaveWithLang) => void, } export const useUserInfo = ({ onSubmit }: Props) => { @@ -24,14 +26,10 @@ export const useUserInfo = ({ onSubmit }: Props) => { } = useForm() const validateForm = useValidateForm() const { - cities, countries, - onCityBlur, - onCitySelect, onCountryBlur, onCountrySelect, onPhoneBlur, - onRegionOrCityChange, selectedCountry, } = useUserInfoForm() const readTrimmedValue = useCallback( @@ -49,6 +47,7 @@ export const useUserInfo = ({ onSubmit }: Props) => { if (validateForm()) { const firstname = readTrimmedValue(formIds.firstname) const lastname = readTrimmedValue(formIds.lastname) + const language = readTrimmedValue(formIds.language) const phone = readTrimmedValue(formIds.phone) const password = readTrimmedValue(formIds.password) const postalCode = readNumberValue(formIds.postalCode) @@ -65,6 +64,7 @@ export const useUserInfo = ({ onSubmit }: Props) => { cityId, countryId, firstname, + language, lastname, password, phone, @@ -79,17 +79,28 @@ export const useUserInfo = ({ onSubmit }: Props) => { validateForm, ]) + const lang = readFormValue(formIds.language) as Languages + + const selectedlangOption = useMemo( + () => find(langOptions, { id: lang })?.name || '', + [lang], + ) + + const onLangSelect = (selectedLang: Lang | null) => { + if (selectedLang) { + updateFormValue(formIds.language)(selectedLang.id) + } + } + return { - cities, countries, handleSubmit, hasChanges, - onCityBlur, - onCitySelect, + lang: selectedlangOption, onCountryBlur, onCountrySelect, + onLangSelect, onPhoneBlur, - onRegionOrCityChange, readFormError, readFormValue, selectedCountry, diff --git a/src/features/UserAccount/components/PersonalInfoForm/hooks/useUserInfoForm.tsx b/src/features/UserAccount/components/PersonalInfoForm/hooks/useUserInfoForm.tsx index d99bd9dd..bb164040 100644 --- a/src/features/UserAccount/components/PersonalInfoForm/hooks/useUserInfoForm.tsx +++ b/src/features/UserAccount/components/PersonalInfoForm/hooks/useUserInfoForm.tsx @@ -1,8 +1,5 @@ import type { ChangeEvent } from 'react' -import { - useEffect, - useCallback, -} from 'react' +import { useCallback } from 'react' import trim from 'lodash/trim' @@ -15,7 +12,6 @@ import { useForm } from 'features/FormStore' import type { SelectedCountry } from './useCountries' import { useCountries } from './useCountries' -import { useCities } from './useCities' export const useUserInfoForm = () => { const { @@ -31,15 +27,6 @@ export const useUserInfoForm = () => { setSelectedCountry, } = useCountries() - const { - cities, - getCities, - onCityBlur, - onCitySelect, - resetCities, - resetSelectedCity, - } = useCities() - const onPhoneBlur = useCallback(({ target }: ChangeEvent) => { const phone = target.value if (phone && !isValidPhone(phone)) { @@ -55,9 +42,6 @@ export const useUserInfoForm = () => { updateFormValue(formIds.countryId)(country?.id ? String(country?.id) : '') updateFormValue(formIds.region)('') - resetCities() - resetSelectedCity() - const selectedCountryCode = formatPhoneCode(selectedCountry?.phone_code || '') const hasPhoneNumber = ( phone @@ -71,41 +55,15 @@ export const useUserInfoForm = () => { }, [ phone, selectedCountry, - resetCities, - resetSelectedCity, setSelectedCountry, updateFormValue, ]) - const onRegionOrCityChange = useCallback((fieldName: string) => ( - ({ target }: ChangeEvent) => { - if (selectedCountry) { - updateFormValue(fieldName)(target.value) - } else { - updateFormError(formIds.country, 'error_select_country_first') - } - } - ), [ - selectedCountry, - updateFormError, - updateFormValue, - ]) - - useEffect(() => { - if (selectedCountry?.id) { - getCities(selectedCountry.id) - } - }, [selectedCountry, getCities]) - return { - cities, countries, - onCityBlur, - onCitySelect, onCountryBlur, onCountrySelect, onPhoneBlur, - onRegionOrCityChange, readFormValue, selectedCountry, setSelectedCountry, diff --git a/src/features/UserAccount/components/PersonalInfoForm/index.tsx b/src/features/UserAccount/components/PersonalInfoForm/index.tsx index 9e7b3a3d..ed7b2d1f 100644 --- a/src/features/UserAccount/components/PersonalInfoForm/index.tsx +++ b/src/features/UserAccount/components/PersonalInfoForm/index.tsx @@ -8,37 +8,39 @@ import { Error } from 'features/Common/Input/styled' import type { Props } from './hooks/useUserInfo' import { useUserInfo } from './hooks/useUserInfo' +import { langOptions } from './config' import { SolidButton } from '../../styled' -import { Form, ButtonWrapper } from './styled' +import { + Form, + ButtonWrapper, + PasswordInput, + SectionTitle, +} from './styled' -const labelWidth = 104 +const labelWidth = 76 const { - address1, - address2, - city, - cityId, country, + email, firstname, formError, lastname, + newPassword1, + newPassword2, password, phone, - region, } = formIds export const PersonalInfoForm = (props: Props) => { const { - cities, countries, handleSubmit, hasChanges, - onCityBlur, - onCitySelect, + lang, onCountryBlur, onCountrySelect, + onLangSelect, onPhoneBlur, - onRegionOrCityChange, readFormError, readFormValue, selectedCountry, @@ -67,18 +69,6 @@ export const PersonalInfoForm = (props: Props) => { maxLength={500} withError={false} /> - { maxLength={100} withError={false} /> + { withError={false} selected={Boolean(selectedCountry)} /> - - + } + value={readFormValue(password)} + onChange={updateFormValue(password)} /> - } + value={readFormValue(newPassword1)} + onChange={updateFormValue(newPassword1)} + /> + } + value={readFormValue(newPassword2)} + onChange={updateFormValue(newPassword2)} /> theme.colors.text50}; +` + +export const PasswordInput = styled(PasswordInputBase)` + height: 50px; + margin-bottom: 10px; + padding-left: 24px; - @media ${devices.mobile} { - width: 335px; + ${InputStyled} { + padding: 0; + padding-left: 24px; } + ${isMobileDevice + ? css` + height: 40px; + padding-left: 10px; + ` + : ''}; ` diff --git a/src/features/UserAccount/components/ScoreSwitch/index.tsx b/src/features/UserAccount/components/ScoreSwitch/index.tsx new file mode 100644 index 00000000..9cad67c8 --- /dev/null +++ b/src/features/UserAccount/components/ScoreSwitch/index.tsx @@ -0,0 +1,52 @@ +import styled, { css } from 'styled-components/macro' + +import { isMobileDevice } from 'config/userAgent' +import { useMatchSwitchesStore } from 'features/MatchSwitches' +import { + Switch, + Icon, + Title as TitleBase, +} from 'features/MatchSwitches/styled' + +const Wrapper = styled.div` + margin-top: 90px; + ${isMobileDevice + ? css` + margin-top: 0; + ` + : ''}; +` + +const Title = styled(TitleBase)` + margin-right: 28px; + font-weight: normal; + font-size: 25px; + text-transform: none; + align-self: flex-start; +` + +type Props = { + className?: string, +} + +export const ScoreSwitch = ({ className }: Props) => { + const { isScoreHidden, toggleScore } = useMatchSwitchesStore() + + return ( + + + + <Icon + width={1.8} + height={1.4} + iconName={isMobileDevice ? 'score-switch-mobile' : 'score-switch'} + isOn={isScoreHidden} + /> + </Switch> + </Wrapper> + ) +} diff --git a/src/features/UserAccount/components/SubscriptionsBySport/index.tsx b/src/features/UserAccount/components/SubscriptionsBySport/index.tsx deleted file mode 100644 index 79caa160..00000000 --- a/src/features/UserAccount/components/SubscriptionsBySport/index.tsx +++ /dev/null @@ -1,105 +0,0 @@ -import styled from 'styled-components/macro' - -import map from 'lodash/map' - -import { SportTypes } from 'config' - -import { SubscriptionType } from 'features/BuyMatchPopup/types' - -import { popupScrollbarStyles } from 'features/PopupComponents' - -import { SubscriptionsList } from '../SubscriptionsList' - -type Subscription = { - header: string, - id: number, - price: number, - type: SubscriptionType, -} - -export type Subscriptions = Array<Subscription> - -const Wrapper = styled.div` - width: 100%; - height: 474px; - overflow-y: auto; - margin-top: 36px; - - ${popupScrollbarStyles} -` - -const data: Record<SportTypes, Subscriptions> = { - [SportTypes.FOOTBALL]: [ - { - header: 'Российская премьер-лига', - id: 1, - price: 199, - type: SubscriptionType.Month, - }, - { - header: 'Primera División', - id: 2, - price: 999, - type: SubscriptionType.Month, - }, - { - header: 'Manchester United', - id: 3, - price: 999, - type: SubscriptionType.Month, - }, - { - header: 'Российская премьер-лига', - id: 4, - price: 199, - type: SubscriptionType.Month, - }, - ], - [SportTypes.HOCKEY]: [ - { - header: 'Российская премьер-лига', - id: 1, - price: 199, - type: SubscriptionType.Month, - }, - { - header: 'Primera División', - id: 2, - price: 999, - type: SubscriptionType.Month, - }, - { - header: 'Manchester United', - id: 3, - price: 999, - type: SubscriptionType.Month, - }, - { - header: 'Российская премьер-лига', - id: 4, - price: 199, - type: SubscriptionType.Month, - }, - { - header: 'Российская премьер-лига', - id: 5, - price: 199, - type: SubscriptionType.Month, - }, - ], - [SportTypes.BASKETBALL]: [], -} - -export const SubscriptionsBySport = () => ( - <Wrapper> - { - map(data, (subscriptions, sport: SportTypes) => ( - <SubscriptionsList - key={sport} - list={subscriptions} - sport={sport} - /> - )) - } - </Wrapper> -) diff --git a/src/features/UserAccount/components/SubscriptionsList/index.tsx b/src/features/UserAccount/components/SubscriptionsList/index.tsx deleted file mode 100644 index 59aeb164..00000000 --- a/src/features/UserAccount/components/SubscriptionsList/index.tsx +++ /dev/null @@ -1,48 +0,0 @@ -import isEmpty from 'lodash/isEmpty' -import map from 'lodash/map' - -import { SportTypes } from 'config' - -import type { Subscriptions } from '../SubscriptionsBySport' -import { - Wrapper, - SportName, - Header, - Count, - List, - Item, - Checkbox, - Price, -} from './styled' - -type Props = { - list: Subscriptions, - sport: SportTypes, -} - -export const SubscriptionsList = ({ list, sport }: Props) => { - if (isEmpty(list)) return null - return ( - <Wrapper> - <Header> - <SportName sport={sport} /> - <Count>2</Count> - </Header> - <List> - { - map(list, ({ - header, - id, - price, - type, - }) => ( - <Item key={id}> - <Checkbox label={header} checked /> - <Price amount={price} perPeriod={`per_${type}`} /> - </Item> - )) - } - </List> - </Wrapper> - ) -} diff --git a/src/features/UserAccount/components/SubscriptionsList/styled.tsx b/src/features/UserAccount/components/SubscriptionsList/styled.tsx deleted file mode 100644 index 53575c0e..00000000 --- a/src/features/UserAccount/components/SubscriptionsList/styled.tsx +++ /dev/null @@ -1,83 +0,0 @@ -import styled, { css } from 'styled-components/macro' - -import { Checkbox as CheckboxBase, SportName as SportNameBase } from 'features/Common' -import { CheckboxSvg } from 'features/Common/Checkbox/Icon' -import { Label } from 'features/Common/Checkbox/styled' - -import { Price as BasePrice } from 'features/Price' -import { PriceAmount, PriceDetails } from 'features/Price/styled' - -export const Wrapper = styled.div` - :not(:first-child) { - margin-top: 21px; - } -` - -export const Header = styled.div` - display: flex; - padding: 0 90px; -` - -const textStyles = css` - font-style: normal; - font-weight: normal; - font-size: 20px; - line-height: 21px; -` - -export const SportName = styled(SportNameBase)` - color: #fff; - ${textStyles} -` - -export const Count = styled.span` - color: rgba(255, 255, 255, 0.5); - ${textStyles} -` - -export const List = styled.ul` - margin-top: 4px; - padding-left: 40px; - padding-right: 32px; -` - -export const Item = styled.li` - height: 50px; - display: flex; - justify-content: space-between; - align-items: center; - font-weight: 600; - font-size: 20px; - line-height: 50px; - color: #fff; -` - -export const Checkbox = styled(CheckboxBase)` - align-items: center; - - ${CheckboxSvg} { - margin-right: 25px; - align-self: center; - } - - ${Label} { - font-style: normal; - font-weight: 600; - font-size: 20px; - line-height: 50px; - } -` - -export const Price = styled(BasePrice)` - ${PriceAmount} { - font-size: 24px; - line-height: 24px; - font-weight: normal; - } - - ${PriceDetails} { - font-weight: 500; - font-size: 12px; - line-height: 18px; - } -` diff --git a/src/features/UserAccount/components/SubscriptionsModal/index.tsx b/src/features/UserAccount/components/SubscriptionsModal/index.tsx deleted file mode 100644 index a5732600..00000000 --- a/src/features/UserAccount/components/SubscriptionsModal/index.tsx +++ /dev/null @@ -1,39 +0,0 @@ -import { SubscriptionType } from 'features/BuyMatchPopup/types' - -import { SubscriptionsBySport } from '../SubscriptionsBySport' -import { - Modal, - PaymentPeriodTabs, - Header, - ButtonsWrapper, - SaveButton, - CancelButton, -} from './styled' - -type Props = { - close: () => void, - isOpen: boolean, -} - -export const SubscriptionModal = ({ - close, - isOpen, -}: Props) => ( - <Modal - isOpen={isOpen} - close={close} - withCloseButton={false} - > - <Header t='select_subscription' /> - <PaymentPeriodTabs - onPeriodSelect={() => {}} - selectedPeriod={SubscriptionType.Month} - /> - <SubscriptionsBySport /> - - <ButtonsWrapper> - <CancelButton onClick={close}>Отменить</CancelButton> - <SaveButton onClick={close}>Сохранить</SaveButton> - </ButtonsWrapper> - </Modal> -) diff --git a/src/features/UserAccount/components/SubscriptionsModal/styled.tsx b/src/features/UserAccount/components/SubscriptionsModal/styled.tsx deleted file mode 100644 index 42e5a81b..00000000 --- a/src/features/UserAccount/components/SubscriptionsModal/styled.tsx +++ /dev/null @@ -1,76 +0,0 @@ -import styled, { css } from 'styled-components/macro' - -import { devices } from 'config/devices' - -import { PaymentPeriodTabs as PaymentPeriodTabsBase, Item } from 'features/PaymentPeriodTabs' -import { popupScrollbarStyles } from 'features/PopupComponents' -import { Modal as ModalBase } from 'features/Modal' -import { ModalWindow } from 'features/Modal/styled' -import { T9n } from 'features/T9n' - -import { SolidButton, OutlineButton } from '../../styled' - -export const Modal = styled(ModalBase)` - background-color: rgba(0, 0, 0, 0.7); - - ${ModalWindow} { - height: 728px; - width: 615px; - padding: 0; - border-radius: 5px; - } -` - -export const Header = styled(T9n)` - display: block; - width: 100%; - padding-top: 18px; - font-weight: bold; - font-size: 24px; - line-height: 50px; - text-align: center; -` - -export const PaymentPeriodTabs = styled(PaymentPeriodTabsBase)` - padding: 0 40px; - margin-top: 9px; - - ${Item} { - line-height: 36px; - } -` - -export const ButtonsWrapper = styled.div` - display: flex; - padding: 0 40px; - margin-top: 16px; -` - -const buttonStyles = css` - width: 50%; - font-weight: normal; - font-size: 20px; - line-height: 50px; - justify-content: center; -` - -export const SaveButton = styled(SolidButton)` - ${buttonStyles} -` - -export const CancelButton = styled(OutlineButton)` - ${buttonStyles} - margin-right: 10px; -` - -export const SubscriptionsWrapper = styled.div` - margin-top: 45px; - width: 100%; - height: 100%; - overflow-y: auto; - ${popupScrollbarStyles}; - - @media ${devices.tablet} { - background-color: transparent; - } -` diff --git a/src/features/UserAccount/components/UserSubscriptionsList/index.tsx b/src/features/UserAccount/components/UserSubscriptionsList/index.tsx index b86c6dd3..59160cb8 100644 --- a/src/features/UserAccount/components/UserSubscriptionsList/index.tsx +++ b/src/features/UserAccount/components/UserSubscriptionsList/index.tsx @@ -16,7 +16,6 @@ import { Description, Price, SubscriptionEnd, - ActionsWrapper, } from './styled' type Props = { @@ -34,6 +33,7 @@ export const UserSubscriptionsList = ({ list, sport }: Props) => { map(list, ({ description, header, + isActive, price, subscription_id, type, @@ -49,13 +49,10 @@ export const UserSubscriptionsList = ({ list, sport }: Props) => { </Description> </InfoWrapper> - <ActionsWrapper> - <Price amount={price} perPeriod={`per_${type}`} /> - - <InlineButton> - Удалить - </InlineButton> - </ActionsWrapper> + <Price amount={price} perPeriod={`per_${type}`} /> + <InlineButton color={isActive ? '#eb5757' : '#294FC3'}> + {isActive ? 'Удалить' : 'Восстановить'} + </InlineButton> </Subscription> <SubscriptionEnd>Следующее списание 31.02.2020</SubscriptionEnd> </Item> diff --git a/src/features/UserAccount/components/UserSubscriptionsList/styled.tsx b/src/features/UserAccount/components/UserSubscriptionsList/styled.tsx index 6dc7e351..9cab2518 100644 --- a/src/features/UserAccount/components/UserSubscriptionsList/styled.tsx +++ b/src/features/UserAccount/components/UserSubscriptionsList/styled.tsx @@ -17,7 +17,7 @@ export const SportName = styled(SportNameBase)` font-weight: 500; font-size: 18px; line-height: 22px; - color: #fff; + color: rgba(255, 255, 255, 0.6); ` export const List = styled.ul` @@ -45,22 +45,12 @@ export const Price = styled(BasePrice)` } ` -export const ActionsWrapper = styled.div` - height: 100%; - display: flex; - justify-content: end; - align-items: center; - transform: translateX(83px); - transition: transform 0.3s ease-in-out; - - ${InlineButton} { - transform: translateX(0); - } -` - -export const Subscription = styled.div.attrs(() => ({ - tabIndex: 0, -}))` +export const Subscription = styled.div.attrs( + () => ({ + tabIndex: 0, + }), +)` + position: relative; width: 800px; height: 70px; display: flex; @@ -72,8 +62,12 @@ export const Subscription = styled.div.attrs(() => ({ border-radius: 2px; overflow: hidden; + ${InlineButton} { + width: 133px; + } + :focus-within, :hover { - ${ActionsWrapper} { + ${InlineButton} { transform: translateX(0); } } diff --git a/src/features/UserAccount/index.tsx b/src/features/UserAccount/index.tsx index e9870719..b9613a6b 100644 --- a/src/features/UserAccount/index.tsx +++ b/src/features/UserAccount/index.tsx @@ -1,8 +1,11 @@ import { Route } from 'react-router-dom' import { PAGES } from 'config' +import { isProduction } from 'config/env' import { userAccountLexics } from 'config/lexics/userAccount' +import { usePageLogger } from 'hooks/usePageLogger' + import { useLexicsConfig } from 'features/LexicsStore' import { T9n } from 'features/T9n' @@ -11,6 +14,8 @@ import { PagePersonalInfo } from './components/PagePersonalInfo' import { PageBankCards } from './components/PageBankCards' import { PageSubscriptions } from './components/PageSubscriptions' import { PagePaymentsHistory } from './components/PagePaymentsHistory' +import { ScoreSwitch } from './components/ScoreSwitch' +import { LogoutButton } from './components/LogoutButton' import { UserAccountWrapper, ContentWrapper, @@ -21,6 +26,7 @@ import { } from './styled' const UserAccount = () => { + usePageLogger(PAGES.useraccount) useLexicsConfig(userAccountLexics) return ( @@ -35,13 +41,19 @@ const UserAccount = () => { <StyledLink to={`${PAGES.useraccount}/bank-cards`}> <T9n t='bank_card' /> </StyledLink> - <StyledLink to={`${PAGES.useraccount}/subscriptions`}> + <StyledLink disabled={isProduction} to={`${PAGES.useraccount}/subscriptions`}> <T9n t='my_subscriptions' /> </StyledLink> - <StyledLink to={`${PAGES.useraccount}/payment-history`}> + <StyledLink disabled={isProduction} to={`${PAGES.useraccount}/payment-history`}> <T9n t='payment_history' /> </StyledLink> + <StyledLink disabled={isProduction} to={`${PAGES.useraccount}/devices`}> + <T9n t='my_devices' /> + </StyledLink> + + <ScoreSwitch /> </Navigations> + <LogoutButton /> </Aside> <ContentWrapper> <Route path={`${PAGES.useraccount}/personal-info`}> diff --git a/src/features/UserAccount/styled.tsx b/src/features/UserAccount/styled.tsx index eb034c05..cc5ee9d8 100644 --- a/src/features/UserAccount/styled.tsx +++ b/src/features/UserAccount/styled.tsx @@ -1,8 +1,9 @@ import { NavLink } from 'react-router-dom' -import styled from 'styled-components/macro' +import styled, { css } from 'styled-components/macro' import { devices } from 'config/devices' +import { isMobileDevice } from 'config/userAgent' import { ButtonSolid, ButtonOutline } from 'features/Common/Button' @@ -12,7 +13,7 @@ export const SolidButton = styled(ButtonSolid)` padding: 0 20px; color: white; font-weight: bold; - background-color: #294FC4; + background-color: #294fc4; box-shadow: 0px 1px 1px rgba(0, 0, 0, 0.3); border-color: transparent; border-radius: 5px; @@ -59,7 +60,6 @@ export const Icon = styled.span<IconProps>` export const ContentWrapper = styled.div` width: 100%; display: flex; - padding-top: 26px; padding-left: 70px; @media ${devices.tablet} { @@ -70,34 +70,40 @@ export const ContentWrapper = styled.div` export const UserAccountWrapper = styled.div` width: 100%; - height: 100%; + height: 100vh; display: flex; flex-direction: column; - padding: 30px 30px 70px 70px; - - @media ${devices.laptop} { - justify-self: flex-start; - } - - @media ${devices.tablet} { - justify-self: center; - width: auto; - padding: 0px 20px 70px 20px; - } + padding: 30px 30px 60px 35px; + ${isMobileDevice + ? css` + padding: 15px 15px 0; + align-items: center; + ` + : ''}; ` export const Body = styled.div` display: flex; + height: 100%; @media ${devices.tablet} { flex-direction: column; } + + ${isMobileDevice + ? css` + width: 100%; + ` + : ''}; ` -export const Aside = styled.aside`` +export const Aside = styled.aside` + position: relative; + height: 100%; +` export const Navigations = styled.nav` - width: 388px; + width: 424px; height: 622px; display: flex; flex-direction: column; @@ -109,32 +115,62 @@ export const Navigations = styled.nav` border: none; margin-bottom: 20px; } + + @media ${devices.mobile} { + width: 100%; + } ` -export const StyledLink = styled(NavLink).attrs(() => ({ - activeStyle: { color: 'rgba(255, 255, 255)' }, -}))` +type StyledLinkProps = { + disabled?: boolean, +} + +export const StyledLink = styled(NavLink).attrs( + () => ({ + activeStyle: { color: 'rgba(255, 255, 255)' }, + }), +)<StyledLinkProps>` font-weight: bold; font-size: 25px; line-height: 68px; color: rgba(255, 255, 255, 0.4); + ${({ disabled }) => (disabled + ? css` + color: rgba(255, 255, 255, 0.1); + pointer-events: none; + ` + : css` + color: rgba(255, 255, 255, 0.4); + `)} + @media ${devices.tablet} { line-height: 48px; } + + @media ${devices.mobile} { + font-size: 14px; + line-height: 35px; + } ` -export const InlineButton = styled.button` +type InlineButtonProps = { + color?: string, +} + +export const InlineButton = styled.button<InlineButtonProps>` + position: absolute; + right: 0; border: none; height: 100%; margin: 0; padding: 12px; color: white; - background-color: #EB5757; + background-color: ${({ color = '#eb5757' }) => color}; font-weight: 600; font-size: 13px; line-height: 14px; cursor: pointer; transition: transform 0.3s ease-in-out; - transform: translateX(101%); + transform: translateX(calc(100% + 1px)); ` diff --git a/src/features/UserFavorites/TooltipBlock/index.tsx b/src/features/UserFavorites/TooltipBlock/index.tsx index f8ea89ea..57a6ccdd 100644 --- a/src/features/UserFavorites/TooltipBlock/index.tsx +++ b/src/features/UserFavorites/TooltipBlock/index.tsx @@ -5,19 +5,18 @@ import { Name, useName } from 'features/Name' import { TooltipBlockWrapper, TooltipBlockItem, - TooltipBlockItemThinUpperCase, TooltipBlockItemThin, + Flag, } from './styled' type TooltipBlockProps = { favorite: UserFavorite, - topPosition: number | null, + topPosition: number, } export const TooltipBlock = ({ favorite: { info, - sport, }, topPosition, }: TooltipBlockProps) => { @@ -32,9 +31,8 @@ export const TooltipBlock = ({ <Name nameObj={info} /> </TooltipBlockItem> <TooltipBlockItemThin> - <TooltipBlockItemThinUpperCase sport={sport} />{' '} - {info.team && <Name nameObj={info.team} />} - {info.country && <Name nameObj={info.country} />} + {info.team && <Name nameObj={info.team} />}{' '} + {info.country && <Flag src={`https://instatscout.com/images/flags/48/${info.country.id}.png`} />} </TooltipBlockItemThin> </TooltipBlockWrapper> ) diff --git a/src/features/UserFavorites/TooltipBlock/styled.tsx b/src/features/UserFavorites/TooltipBlock/styled.tsx index b294efd6..92a76968 100644 --- a/src/features/UserFavorites/TooltipBlock/styled.tsx +++ b/src/features/UserFavorites/TooltipBlock/styled.tsx @@ -1,55 +1,61 @@ -import styled, { css } from 'styled-components/macro' +import styled from 'styled-components/macro' -import { SportName } from 'features/Common' +type TooltipBlockWrapperProps = { + top?: number, +} -export const TooltipBlockWrapper = styled.div<{top?: number | null}>` +export const TooltipBlockWrapper = styled.div<TooltipBlockWrapperProps>` background-color: #fff; - border-radius: 10px; - padding: 12px; + border-radius: 4px; + padding: 0.11rem 0.8rem 0.27rem 0.75rem; white-space: nowrap; position: fixed; - top: ${({ top }) => top && `${top + 50}px`}; - left: 40px; + top: ${({ top }) => top && `${top + 2.54}rem`}; + left: 2.36rem; z-index: 5; &::before { position: absolute; - top: -8px; + top: -0.38rem; + left: 0.27rem; content: ''; - border-bottom: 8px solid #fff; - border-left: 10px solid transparent; - border-right: 10px solid transparent; + border-bottom: 0.38rem solid #fff; + border-left: 0.472rem solid transparent; + border-right: 0.472rem solid transparent; } ` -type TooltipProps = { - color?: string, -} - -export const TooltipStyles = css<TooltipProps>` +export const TooltipBlockItem = styled.span` display: block; - font-family: Montserrat, Tahoma, sans-serif; - font-size: 14px; - line-height: 18px; - color: ${({ color }) => (color ? `${color}` : '#2c2d2e')}; font-weight: 600; + font-size: 0.8rem; + line-height: 1rem; + letter-spacing: 0.3px; + color: ${({ theme }) => theme.colors.black}; &:hover { background-color: rgba(255, 255, 255, 0.7); } ` -export const TooltipBlockItem = styled.span` - ${TooltipStyles} -` - export const TooltipBlockItemThin = styled(TooltipBlockItem)` - font-weight: 400; + font-weight: normal; + font-size: 0.71rem; + line-height: 1.14rem; + display: flex; + align-items: center; ` -export const TooltipBlockItemThinUpperCase = styled(SportName)` - ${TooltipStyles} - display: inline; - text-transform: uppercase; - font-weight: 400; +export const Flag = styled.img` + width: 0.85rem; + height: 1.14rem; + object-fit: contain; + + :not(:only-child) { + margin: 0 0.472rem; + } + + :last-child { + margin-right: 0; + } ` diff --git a/src/features/UserFavorites/hooks/index.tsx b/src/features/UserFavorites/hooks/index.tsx index 683999b4..6b3b7a0a 100644 --- a/src/features/UserFavorites/hooks/index.tsx +++ b/src/features/UserFavorites/hooks/index.tsx @@ -1,9 +1,12 @@ import { useCallback, useState } from 'react' +import find from 'lodash/find' + import type { UserFavorites } from 'requests' import { modifyUserFavorites, getUserFavorites } from 'requests' import { useToggle } from 'hooks/useToggle' +import { ProfileTypes } from 'config' type Args = Parameters<typeof modifyUserFavorites>[0] @@ -24,10 +27,15 @@ export const useUserFavorites = () => { modifyUserFavorites(args).then(setUserFavorites, open) } + const isInFavorites = (profileType: ProfileTypes, profileId: number) => ( + Boolean(find(userFavorites, { id: profileId, type: profileType })) + ) + return { addRemoveFavorite, close, fetchFavorites, + isInFavorites, isOpen, open, userFavorites, diff --git a/src/features/UserFavorites/index.tsx b/src/features/UserFavorites/index.tsx index 413cb3a6..7001bad2 100644 --- a/src/features/UserFavorites/index.tsx +++ b/src/features/UserFavorites/index.tsx @@ -1,16 +1,13 @@ import type { MouseEvent, FocusEvent } from 'react' import { useEffect, useState } from 'react' -import { Link } from 'react-router-dom' - import map from 'lodash/map' import { FavoritesActions } from 'requests' -import { PAGES } from 'config' - import { Modal } from 'features/Modal' import { ProfileLink } from 'features/ProfileLink' +import { Close } from 'features/Icons/Close' import { TooltipBlock } from './TooltipBlock' import { useUserFavoritesStore } from './store' @@ -19,7 +16,6 @@ import { UserSportFavXWrapper, UserSportFavImgWrapper, UserSportFavStar, - UserSportFavLogoWrapper, UserSportFavWrapper, ExclamationSign, FavoriteModal, @@ -27,7 +23,11 @@ import { ScrollWrapper, } from './styled' -export const UserFavorites = () => { +type Props = { + marginTop?: number, +} + +export const UserFavorites = ({ marginTop }: Props) => { const { addRemoveFavorite, close, @@ -36,7 +36,7 @@ export const UserFavorites = () => { userFavorites, } = useUserFavoritesStore() - const [position, setPosition] = useState<number | null>(null) + const [position, setPosition] = useState(0) useEffect(fetchFavorites, [fetchFavorites]) @@ -48,10 +48,7 @@ export const UserFavorites = () => { return ( <UserSportFavWrapper> - <Link to={PAGES.home}> - <UserSportFavLogoWrapper width={52} /> - </Link> - <UserSportFavStar /> + <UserSportFavStar marginTop={marginTop} /> <ScrollWrapper> { map(userFavorites, (item) => ( @@ -67,7 +64,9 @@ export const UserFavorites = () => { sport: item.sport, type: item.type, })} - /> + > + <Close size={8} /> + </UserSportFavXWrapper> <TooltipBlock topPosition={position} favorite={item} @@ -76,7 +75,6 @@ export const UserFavorites = () => { id={item.id} sportType={item.sport} profileType={item.type} - target='_blank' > <UserSportFavImgWrapper id={item.id} diff --git a/src/features/UserFavorites/styled.tsx b/src/features/UserFavorites/styled.tsx index 4504cfcc..1920ed2e 100644 --- a/src/features/UserFavorites/styled.tsx +++ b/src/features/UserFavorites/styled.tsx @@ -1,8 +1,8 @@ -import { devices } from 'config/devices' +import styled, { css } from 'styled-components/macro' -import styled from 'styled-components/macro' +import { ProfileTypes } from 'config' +import { isMobileDevice } from 'config/userAgent' -import { Logo } from 'features/Logo' import { T9n } from 'features/T9n' import { customScrollbar } from 'features/Common' import { ProfileLogo } from 'features/ProfileLogo' @@ -13,52 +13,53 @@ export const UserSportFavWrapper = styled.aside` display: flex; flex-direction: column; align-items: center; - position: fixed; + margin-top: 1.456rem; + padding-bottom: 2.75rem; left: 0; top: 0; bottom: 0; - background: rgba(255, 255, 255, 0.1); z-index: 1; + ${isMobileDevice + ? css` + display: none; + ` + : ''}; ` + export const ScrollWrapper = styled.div` - width: 80px; - height: auto; + width: 4.35rem; + padding: 0.472rem 0.19rem 0 0.15rem; + height: calc(100vh - 15.96rem); display: flex; flex-direction: column; align-items: center; - overflow-y: overlay; + overflow-y: auto; ${customScrollbar} ` -export const UserSportFavLogoWrapper = styled(Logo)` - margin-top: 35px; - - @media ${devices.laptop} { - margin-top: 30px; - margin-bottom: 68px; - } -` - -export const UserSportFavXWrapper = styled.span` - display: block; +export const UserSportFavXWrapper = styled.button` position: absolute; - top: 0; - right: 0; - background: transparent url('/images/xIcon.png') no-repeat center; - height: 11px; - width: 11px; + top: -0.472rem; + right: -0.472rem; + height: 0.95rem; + width: 0.95rem; + padding: 0px; + display: flex; + align-items: center; + justify-content: center; + background-color: #fff; border: none; + border-radius: 50%; + cursor: pointer; ` export const UserSportFavItemLogoWrapper = styled.div` position: relative; - width: 48px; - min-height: 48px; - height: 48px; - border-radius: 50%; - padding: 2px; + width: 2.6rem; + height: 2.6rem; + border-radius: 4px; background-color: #3F3F3F; - margin-bottom: 16px; + ${UserSportFavXWrapper} { display: none; } @@ -67,33 +68,46 @@ export const UserSportFavItemLogoWrapper = styled.div` display: none; } + :not(:last-child) { + margin-bottom: 0.71rem; + } + &:hover { background-color: rgba(255, 255, 255, 0.7); cursor: pointer; ${UserSportFavXWrapper} { - display: block; + display: flex; } ${TooltipBlockWrapper} { - display: block; + display: flex; } } ` export const UserSportFavImgWrapper = styled(ProfileLogo)` width: 100%; - border-radius: 50%; + padding: 0.19rem; + + ${({ profileType }) => ( + profileType === ProfileTypes.PLAYERS + ? 'padding: 0.19rem 0.08rem 0 0.08rem;' + : '' + )} ` -export const UserSportFavStar = styled.div` - width: 48px; - height: 48px; - min-height: 48px; - border-radius: 50%; - background: #3f3f3f url('/images/sportFavStar.png') no-repeat center; - margin-bottom: 16px; - margin-top: 120px; +type UserSportFavStarProps = { + marginTop?: number, +} + +export const UserSportFavStar = styled.div<UserSportFavStarProps>` + width: 1.85rem; + min-height: 1.85rem; + background: url('/images/sportFavStar.png') no-repeat center / 100%; + margin-left: 0.15rem; + margin-bottom: 0.567rem; + margin-top: ${({ marginTop = 0 }) => marginTop}px; ` export const FavoriteModal = styled.div` diff --git a/src/features/VideoPlayer/hooks/index.tsx b/src/features/VideoPlayer/hooks/index.tsx index 6985b3c0..91c42f9c 100644 --- a/src/features/VideoPlayer/hooks/index.tsx +++ b/src/features/VideoPlayer/hooks/index.tsx @@ -41,6 +41,7 @@ const useVideoRef = (ref?: Ref) => { export const useVideoPlayer = ({ isFullscreen, onDurationChange, + onError, onLoadedProgress, onPlayedProgress, onReady, @@ -84,7 +85,7 @@ export const useVideoPlayer = ({ if (playing) { // автовоспроизведение со звуком иногда может не сработать // https://developers.google.com/web/updates/2017/09/autoplay-policy-changes#new-behaviors - video.play().catch() + video.play().catch(onError) } else { video.pause() } @@ -93,6 +94,7 @@ export const useVideoPlayer = ({ playing, src, videoRef, + onError, ]) useEffect(() => { diff --git a/src/features/VideoPlayer/index.tsx b/src/features/VideoPlayer/index.tsx index 24901c44..21e3c8b0 100644 --- a/src/features/VideoPlayer/index.tsx +++ b/src/features/VideoPlayer/index.tsx @@ -9,10 +9,10 @@ export const VideoPlayer = forwardRef<HTMLVideoElement, Props>((props: Props, re className, height, hidden, + muted, onEnded, onError, src, - volume, width, } = props const { @@ -24,14 +24,15 @@ export const VideoPlayer = forwardRef<HTMLVideoElement, Props>((props: Props, re } = useVideoPlayer({ ...props, ref }) return ( <Video + playsInline className={className} width={width} height={height} hidden={hidden} ref={videoRef} src={src} - muted={volume === 0} - onCanPlay={handleReady} + muted={muted} + onCanPlayThrough={handleReady} onTimeUpdate={handlePlayedChange} onProgress={handleLoadedChange} onEnded={onEnded} diff --git a/src/features/VideoPlayer/styled.tsx b/src/features/VideoPlayer/styled.tsx index 0af91bb5..f35c8ac2 100644 --- a/src/features/VideoPlayer/styled.tsx +++ b/src/features/VideoPlayer/styled.tsx @@ -1,9 +1,9 @@ import styled from 'styled-components/macro' export const Video = styled.video` - position: absolute; - top: 0; - left: 0; - width: 100%; - height: 100%; + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100%; ` diff --git a/src/hooks/useAuthForm.tsx b/src/hooks/useAuthForm.tsx new file mode 100644 index 00000000..ecd85260 --- /dev/null +++ b/src/hooks/useAuthForm.tsx @@ -0,0 +1,62 @@ +import type { FocusEvent, FormEvent } from 'react' +import { useState } from 'react' + +import { isValidEmail } from 'helpers/isValidEmail' +import { isValidPassword } from 'helpers/isValidPassword' + +export type Values = { + email: string, + password: string, +} + +type Args = { + onSubmit: (values: Values) => Promise<void>, +} + +export const useAuthForm = ({ onSubmit }: Args) => { + const [email, setEmail] = useState('') + const [password, setPassword] = useState('') + const [error, setError] = useState('') + + const onEmailChange = ({ target: { value } }: FocusEvent<HTMLInputElement>) => { + setError('') + setEmail(value) + } + + const onPasswordChange = ({ target: { value } }: FocusEvent<HTMLInputElement>) => { + setError('') + setPassword(value) + } + + const onEmailBlur = ({ target: { value } }: FocusEvent<HTMLInputElement>) => { + if (!value) { + setError('error_empty_email') + } else if (!isValidEmail(value)) { + setError('error_invalid_email_format') + } + } + + const onPasswordBlur = ({ target: { value } }: FocusEvent<HTMLInputElement>) => { + if (!value) { + setError('error_empty_password') + } else if (!isValidPassword(value)) { + setError('error_simple_password') + } + } + + const handleSubmit = async (event: FormEvent<HTMLFormElement>) => { + event.preventDefault() + onSubmit({ email, password }).catch(setError) + } + + return { + email, + error, + handleSubmit, + onEmailBlur, + onEmailChange, + onPasswordBlur, + onPasswordChange, + password, + } +} diff --git a/src/hooks/useEventListener.tsx b/src/hooks/useEventListener.tsx index 42601f9d..ad34bfc2 100644 --- a/src/hooks/useEventListener.tsx +++ b/src/hooks/useEventListener.tsx @@ -1,7 +1,7 @@ import type { RefObject } from 'react' import { useEffect, useRef } from 'react' -type EventMap = HTMLElementEventMap | WindowEventMap +type EventMap = HTMLElementEventMap & WindowEventMap type Target = RefObject<HTMLElement> | HTMLElement | Window diff --git a/src/hooks/usePageLogger.tsx b/src/hooks/usePageLogger.tsx new file mode 100644 index 00000000..e02b2740 --- /dev/null +++ b/src/hooks/usePageLogger.tsx @@ -0,0 +1,46 @@ +import { + useRef, + useEffect, + useCallback, +} from 'react' + +import round from 'lodash/round' + +import { LogActions, logUserAction } from 'requests/logUserAction' + +import { useEventListener } from 'hooks' + +export const usePageLogger = (page: string) => { + const startTimeRef = useRef(new Date()) + + const resetTime = useCallback(() => { + startTimeRef.current = new Date() + }, []) + + const getSpentTime = useCallback(() => ( + round((Date.now() - startTimeRef.current.getTime()) / 1000) + ), []) + + const log = useCallback(() => { + logUserAction({ + actionType: LogActions.PageChange, + dateVisit: startTimeRef.current.toISOString(), + duration: getSpentTime(), + url: page, + }) + }, [page, getSpentTime]) + + useEffect(() => { + resetTime() + return log + }, [ + page, + log, + resetTime, + ]) + + useEventListener({ + callback: log, + event: 'beforeunload', + }) +} diff --git a/src/hooks/usePageParams.tsx b/src/hooks/usePageParams.tsx new file mode 100644 index 00000000..4810663c --- /dev/null +++ b/src/hooks/usePageParams.tsx @@ -0,0 +1,27 @@ +import { useRouteMatch } from 'react-router' + +import toUpper from 'lodash/toUpper' + +import { ProfileTypes, SportTypes } from 'config' + +type RouteParams = { + pageId: string, + profileName: string, + sportName: string, +} + +export const usePageParams = () => { + const { + params: { + pageId, + profileName, + sportName, + }, + } = useRouteMatch<RouteParams>('/:sportName/:profileName/:pageId') || { params: {} } + + return { + profileId: Number(pageId), + profileType: ProfileTypes[toUpper(profileName) as keyof typeof ProfileTypes], + sportType: SportTypes[toUpper(sportName) as keyof typeof SportTypes], + } +} diff --git a/src/hooks/useStorage/helpers.tsx b/src/hooks/useStorage/helpers.tsx index fd0e2c6d..9ae7e04f 100644 --- a/src/hooks/useStorage/helpers.tsx +++ b/src/hooks/useStorage/helpers.tsx @@ -21,18 +21,6 @@ const dateReviver = (key: string, value: string) => { return value } -/** - * Убирает время из строки даты - * 2020-08-26T08:17:01.604Z => 2020-08-26 - */ -export const dateReplacer = (key: string, value: string) => { - if (isDateString(value)) { - return value.slice(0, 10) - } - - return value -} - /** * Считывает значение из стора по key */ diff --git a/src/hooks/useStorage/index.tsx b/src/hooks/useStorage/index.tsx index cb6d090c..ef21751c 100644 --- a/src/hooks/useStorage/index.tsx +++ b/src/hooks/useStorage/index.tsx @@ -2,17 +2,16 @@ import { useState, useEffect } from 'react' import { queryParamStorage } from 'features/QueryParamsStorage' -import { - dateReplacer, - readStorageInitialValue, -} from './helpers' +import { readStorageInitialValue } from './helpers' const defaultValidator = () => true +const defaultSerializer = (key: string, value: any) => value type Args<T> = { clearOnUnmount?: boolean, defaultValue: T, key: string, + serialize?: (key: string, value: T) => string, /** * функция для валидации полученного значения из стора @@ -34,6 +33,7 @@ const createHook = (storage: Storage) => ( clearOnUnmount, defaultValue, key, + serialize = defaultSerializer, validator = defaultValidator, }: Args<T>) => { const getInitialState = () => { @@ -53,8 +53,12 @@ const createHook = (storage: Storage) => ( }, [key, validator]) useEffect(() => { - storage.setItem(key, JSON.stringify(state, dateReplacer)) - }, [key, state]) + storage.setItem(key, JSON.stringify(serialize(key, state))) + }, [ + key, + state, + serialize, + ]) useEffect(() => { if (clearOnUnmount) return () => storage.removeItem(key) diff --git a/src/react-app-env.d.ts b/src/react-app-env.d.ts index d3e30f32..26d556d4 100644 --- a/src/react-app-env.d.ts +++ b/src/react-app-env.d.ts @@ -1,2 +1,8 @@ // eslint-disable-next-line spaced-comment /// <reference types="react-scripts" /> + +declare namespace NodeJS { + export interface ProcessEnv { + REACT_APP_ENV: 'production' | 'preproduction' | 'staging', + } +} diff --git a/src/requests/getMatchInfo.tsx b/src/requests/getMatchInfo.tsx index 0715eb5a..8685f9a8 100644 --- a/src/requests/getMatchInfo.tsx +++ b/src/requests/getMatchInfo.tsx @@ -8,6 +8,8 @@ import { callApi } from 'helpers' const proc = PROCEDURES.get_match_info type Team = { + abbrev_eng: string, + abbrev_rus: string, id: number, name_eng: string, name_rus: string, @@ -17,6 +19,7 @@ type Team = { export type MatchInfo = { calc: boolean, date: string, + has_video: boolean, live: boolean, team1: Team, team2: Team, diff --git a/src/requests/getMatches/getPreviews.tsx b/src/requests/getMatches/getPreviews.tsx index ddf4a375..61ea2233 100644 --- a/src/requests/getMatches/getPreviews.tsx +++ b/src/requests/getMatches/getPreviews.tsx @@ -47,32 +47,36 @@ const getPreviews = async (matches: Matches) => { } export const getMatchesPreviews = async (matches: MatchesBySection) => { - if (matches.isVideoSections) { - const [ - broadcast, - features, - highlights, - ] = await Promise.all( - [ - getPreviews(matches.broadcast), - getPreviews(matches.features), - getPreviews(matches.highlights), - ], - ) + try { + if (matches.isVideoSections) { + const [ + broadcast, + features, + highlights, + ] = await Promise.all( + [ + getPreviews(matches.broadcast), + getPreviews(matches.features), + getPreviews(matches.highlights), + ], + ) + return { + ...matches, + broadcast, + features, + highlights, + } + } + + const broadcast = await getPreviews(matches.broadcast) + return { ...matches, broadcast, - features, - highlights, + features: [], + highlights: [], } - } - - const broadcast = await getPreviews(matches.broadcast) - - return { - ...matches, - broadcast, - features: [], - highlights: [], + } catch (error) { + return matches } } diff --git a/src/requests/getMatches/types.tsx b/src/requests/getMatches/types.tsx index 886df59b..42a1efb1 100644 --- a/src/requests/getMatches/types.tsx +++ b/src/requests/getMatches/types.tsx @@ -6,7 +6,7 @@ type Tournament = { name_rus: string, } -export type Team = { +type Team = { id: number, name_eng: string, name_rus: string, @@ -16,6 +16,7 @@ export type Team = { export type Match = { access: boolean, calc: boolean, + country_id: number, date: string, /** наличие mp4 видео */ has_video: boolean, diff --git a/src/requests/getPlayerInfo.tsx b/src/requests/getPlayerInfo.tsx index 4906be80..376add1d 100644 --- a/src/requests/getPlayerInfo.tsx +++ b/src/requests/getPlayerInfo.tsx @@ -14,6 +14,11 @@ export type PlayerProfile = { name_eng: string, name_rus: string, } | null, + country: { + id: number, + name_eng: string, + name_rus: string, + }, firstname_eng: string, firstname_rus: string, height: number | null, diff --git a/src/requests/getProfileColor.tsx b/src/requests/getProfileColor.tsx new file mode 100644 index 00000000..2661b610 --- /dev/null +++ b/src/requests/getProfileColor.tsx @@ -0,0 +1,47 @@ +import { + API_ROOT, + ProfileTypes, + SportTypes, +} from 'config' +import { callApi } from 'helpers' + +const profiles = { + [ProfileTypes.TEAMS]: 'team', + [ProfileTypes.TOURNAMENTS]: 'tournament', + [ProfileTypes.PLAYERS]: 'team', + [ProfileTypes.MATCHES]: '', +} + +type Response = { + b: number, + code: string, + g: number, + r: number, +} + +type Args = { + profileId: number, + profileType: ProfileTypes, + sportType: SportTypes, +} + +export const getProfileColor = async ({ + profileId, + profileType, + sportType, +}: Args): Promise<string> => { + const config = { + body: { + profile_id: profileId, + profile_type: profiles[profileType], + sport_id: sportType, + }, + } + + const response: Response = await callApi({ + config, + url: `${API_ROOT}/profile/color`, + }) + + return `rgba(${response.r}, ${response.g}, ${response.b}, 0.56)` +} diff --git a/src/requests/getSearchItems.tsx b/src/requests/getSearchItems.tsx index 6a662aa6..611e879a 100644 --- a/src/requests/getSearchItems.tsx +++ b/src/requests/getSearchItems.tsx @@ -12,7 +12,14 @@ export enum Gender { FEMALE = 2, } +type NamedObject = { + id: number, + name_eng: string, + name_rus: string, +} + type Player = { + country?: NamedObject, firstname_eng: string, firstname_rus: string, gender?: Gender, @@ -20,19 +27,11 @@ type Player = { lastname_eng: string, lastname_rus: string, sport: SportTypes, - team?: { - id: number, - name_eng: string, - name_rus: string, - }, + team?: NamedObject, } type Team = { - country?: { - id: number, - name_eng: string, - name_rus: string, - }, + country?: NamedObject, gender?: Gender, id: number, name_eng: string, @@ -41,11 +40,7 @@ type Team = { } type Tournament = { - country?: { - id: number, - name_eng: string, - name_rus: string, - }, + country?: NamedObject, gender?: Gender, id: number, name_eng: string, diff --git a/src/requests/getSubscriptions.tsx b/src/requests/getSubscriptions.tsx index b9b63e52..822d5cbd 100644 --- a/src/requests/getSubscriptions.tsx +++ b/src/requests/getSubscriptions.tsx @@ -4,32 +4,54 @@ import { PROCEDURES, SportTypes, } from 'config' -import { SubscriptionType } from 'features/BuyMatchPopup/types' import { callApi } from 'helpers' const proc = PROCEDURES.get_match_subscriptions +export type SubscriptionsByPeriods = { + month: Subscriptions, + pay_per_view: SubscriptionResponse, + season: Season, + year: Subscriptions, +} + +export type Subscriptions = { + all: SubscriptionResponse, + team1: SubscriptionResponse, + team2: SubscriptionResponse, + team_away: SubscriptionResponse, + team_home: SubscriptionResponse, +} + export type SubscriptionResponse = { currency_id: number, currency_iso: keyof typeof currencySymbols, id: number, - lexic: number, price: number, - sub: { - date_from: string, - date_to: string, - id: number, - }, + sub: Sub, } -export type MatchSubscriptionsResponse = ( - Record<SubscriptionType, Array<SubscriptionResponse>> -) +type Sub = { + active_from: string, + active_to: string, + currency_id: number, + id: number, + match_id?: number, + option: number, + price: number, + sport?: number, + team_id?: number, +} + +type Season = { + id: number, + name: string, +} export const getSubscriptions = async ( sport: SportTypes, matchId: number, -): Promise<MatchSubscriptionsResponse> => { +): Promise<SubscriptionsByPeriods> => { const config = { body: { params: { diff --git a/src/requests/getTeamInfo.tsx b/src/requests/getTeamInfo.tsx index 139de8d7..1304d6ba 100644 --- a/src/requests/getTeamInfo.tsx +++ b/src/requests/getTeamInfo.tsx @@ -7,18 +7,21 @@ import { callApi } from 'helpers' const proc = PROCEDURES.get_team_info +type NameObject = { + id: number, + name_eng: string, + name_rus: string, +} + export type TeamInfo = { - country: { - id: number, - name_eng: string, - name_rus: string, - }, + country: NameObject, id: number, is_favorite: boolean, name_eng: string, name_rus: string, short_name_eng: string, short_name_rus: string, + tournament: NameObject, } | null export const getTeamInfo = (sportId: number, teamId: number) diff --git a/src/requests/getUserSportFavs.tsx b/src/requests/getUserSportFavs.tsx index a238b348..c6e30538 100644 --- a/src/requests/getUserSportFavs.tsx +++ b/src/requests/getUserSportFavs.tsx @@ -9,6 +9,7 @@ import { callApi } from 'helpers' const proc = PROCEDURES.get_user_favorites type ObjectWithName = { + id: number, name_eng: string, name_rus: string, } diff --git a/src/requests/getVideos.tsx b/src/requests/getVideos.tsx index 14cb2bd7..4364de67 100644 --- a/src/requests/getVideos.tsx +++ b/src/requests/getVideos.tsx @@ -34,6 +34,7 @@ export const getVideos = ( return callApi({ config, + // эндпоинт удалили со стейджинга, временно ссылаемся на прод url: `${API_ROOT}/videoapi`, }).then(filterByIds) } diff --git a/src/requests/logUserAction.tsx b/src/requests/logUserAction.tsx new file mode 100644 index 00000000..4551bab5 --- /dev/null +++ b/src/requests/logUserAction.tsx @@ -0,0 +1,56 @@ +import { + DATA_URL, + PROCEDURES, + SportTypes, +} from 'config' +import { callApi } from 'helpers' + +const proc = PROCEDURES.save_user_page + +export enum LogActions { + PageChange = 1, + VideoChange = 2, +} + +type Args = { + actionType: LogActions, + dateVisit: string, + duration: number, + matchId?: number, + playerId?: number, + playlistType?: number, + sportType?: SportTypes, + url: string, +} + +export const logUserAction = ({ + actionType, + dateVisit, + duration, + matchId, + playerId, + playlistType, + sportType, + url, +}: Args) => { + const config = { + body: { + params: { + _p_change_type: actionType, + _p_date_visit: dateVisit, + _p_duration: duration, + _p_match_id: matchId, + _p_player_id: playerId, + _p_playlist_type: playlistType, + _p_sport: sportType, + _p_url: url, + }, + proc, + }, + } + + callApi({ + config, + url: DATA_URL, + }) +}