diff --git a/__tests__/shared/components/TopcoderHeader/desktop/SubMenu/Item.jsx b/__tests__/shared/components/TopcoderHeader/desktop/SubMenu/Item.jsx index 0ebcd65cb8..c953f0a293 100644 --- a/__tests__/shared/components/TopcoderHeader/desktop/SubMenu/Item.jsx +++ b/__tests__/shared/components/TopcoderHeader/desktop/SubMenu/Item.jsx @@ -1,6 +1,6 @@ import React from 'react'; import Renderer from 'react-test-renderer/shallow'; -import Item from 'components/TopcoderHeader/desktop/SubMenu/Item'; +import Item from 'components/SubMenu/Item'; test('Matches shallow shapshot', () => { const renderer = new Renderer(); diff --git a/__tests__/shared/components/TopcoderHeader/desktop/SubMenu/__snapshots__/Item.jsx.snap b/__tests__/shared/components/TopcoderHeader/desktop/SubMenu/__snapshots__/Item.jsx.snap index a438da528a..f503583d9d 100644 --- a/__tests__/shared/components/TopcoderHeader/desktop/SubMenu/__snapshots__/Item.jsx.snap +++ b/__tests__/shared/components/TopcoderHeader/desktop/SubMenu/__snapshots__/Item.jsx.snap @@ -2,7 +2,7 @@ exports[`Matches shallow shapshot 1`] = `
  • @@ -21,7 +21,7 @@ exports[`Matches shallow shapshot 1`] = ` exports[`Matches shallow shapshot 2`] = `
  • diff --git a/src/server/services/communities.js b/src/server/services/communities.js index 17e163d81a..5d72865759 100644 --- a/src/server/services/communities.js +++ b/src/server/services/communities.js @@ -132,7 +132,7 @@ export async function getMetadata(communityId) { communityId, 'metadata.json', ); try { - metadata = JSON.parse(fs.readFileSync(uri, 'utf8')); + metadata = JSON.parse(await promisify(fs.readFile)(uri, 'utf8')); } catch (error) { const msg = `Failed to get metadata for ${communityId} community`; logger.error(msg, error); diff --git a/src/shared/components/Contentful/Article/Article.jsx b/src/shared/components/Contentful/Article/Article.jsx index 772a0afb0a..dd4b0f883f 100644 --- a/src/shared/components/Contentful/Article/Article.jsx +++ b/src/shared/components/Contentful/Article/Article.jsx @@ -34,6 +34,7 @@ import IconFacebook from 'assets/images/icon-facebook.svg'; import IconTwitter from 'assets/images/icon-twitter.svg'; import IconLinkedIn from 'assets/images/icon-linkedIn.svg'; import DiscordIconWhite from 'assets/images/tc-edu/discord-icon-white.svg'; +import getSecureRandomIndex from 'utils/secureRandom'; const htmlToText = require('html-to-text'); @@ -45,7 +46,7 @@ const LOCAL_STORAGE_KEY = 'VENBcnRpY2xlVm90ZXM='; const DEFAULT_BANNER_IMAGE = 'https://images.ctfassets.net/piwi0eufbb2g/7v2hlDsVep7FWufHw0lXpQ/2505e61a880e68fab4e80cd0e8ec1814/0C37CB5E-B253-4804-8935-78E64E67589E.png?w=1200&h=630'; // random ads banner - left sidebar const RANDOM_BANNERS = ['6G8mjiTC1mzeSQ2YoUG1gB', '1DnDD02xX1liHfSTf5Vsn8', 'HQZ3mN0rR92CbNTkKTHJ5', '1OLoX8ZsvjAnn4TdGbZESD', '77jn01UGoQe2gqA7x0coQD']; -const RANDOM_BANNER = RANDOM_BANNERS[_.random(0, 4)]; +const RANDOM_BANNER = RANDOM_BANNERS[getSecureRandomIndex(RANDOM_BANNERS.length)]; class Article extends React.Component { componentDidMount() { diff --git a/src/shared/components/Contentful/MemberTalkCloud/MemberTalkCloud.jsx b/src/shared/components/Contentful/MemberTalkCloud/MemberTalkCloud.jsx index 4b3c6e06bc..30f05a66eb 100644 --- a/src/shared/components/Contentful/MemberTalkCloud/MemberTalkCloud.jsx +++ b/src/shared/components/Contentful/MemberTalkCloud/MemberTalkCloud.jsx @@ -9,6 +9,7 @@ import PT from 'prop-types'; import React from 'react'; import { themr } from 'react-css-super-themr'; import { fixStyle } from 'utils/contentful'; +import getSecureRandomIndex from 'utils/secureRandom'; import defaultTheme from './themes/default.scss'; const MAX_MARGIN_TOP = 0; @@ -17,7 +18,7 @@ const MAX_MARGIN_LEFT = 30; const getRandomTranslate = () => ({ y: MAX_MARGIN_TOP, - x: _.random(MIN_MARGIN_LEFT, MAX_MARGIN_LEFT, false), + x: getSecureRandomIndex(MIN_MARGIN_LEFT, MAX_MARGIN_LEFT), }); export class MemberTalkCloud extends React.Component { @@ -92,7 +93,7 @@ export class MemberTalkCloud extends React.Component { {activeBlob.handle} {activeBlob.handle}
    diff --git a/src/shared/components/MemberSearch/ListContainer/index.jsx b/src/shared/components/MemberSearch/ListContainer/index.jsx deleted file mode 100644 index 6a8305eeb1..0000000000 --- a/src/shared/components/MemberSearch/ListContainer/index.jsx +++ /dev/null @@ -1,52 +0,0 @@ -import React from 'react'; -import PropTypes from 'prop-types'; -import { singlePluralFormatter } from '../helpers'; - -import './style.scss'; - -const ListContainer = ({ - headerText, - headerHighlightedText, - children, - numListItems, -}) => { - function renderListCount(numItems) { - if (numItems) { - const listCountMessage = singlePluralFormatter(numItems, 'result'); - - return {` - ${listCountMessage}`}; - } - - return null; - } - - const listCount = renderListCount(numListItems); - - return ( -
    -
    - {headerText} - {headerHighlightedText} - - - {listCount} -
    - - {children} -
    - ); -}; - -ListContainer.propTypes = { - headerText: PropTypes.string.isRequired, - headerHighlightedText: PropTypes.string, - children: PropTypes.shape({}).isRequired, - numListItems: PropTypes.number, -}; - -ListContainer.defaultProps = { - headerHighlightedText: '', - numListItems: 0, -}; - -export default ListContainer; diff --git a/src/shared/components/MemberSearch/ListContainer/style.scss b/src/shared/components/MemberSearch/ListContainer/style.scss deleted file mode 100644 index 2c3be47f2c..0000000000 --- a/src/shared/components/MemberSearch/ListContainer/style.scss +++ /dev/null @@ -1,47 +0,0 @@ -@import '~tc-ui/src/styles/tc-includes'; - -.list-container { - max-width: 960px; - margin: 20px auto; - border: 1px solid $tc-gray-30; - box-shadow: 0 1px 2px 0 $tc-gray-30; -} - -.list-header { - padding: 11px 15px; - background-color: $tc-gray-20; - - @include tc-label; - - .header-text { - color: $tc-gray-70; - } - - .highlighted { - font-weight: bold; - } - - .list-count { - color: $tc-gray-70; - opacity: 0.5; - } -} - -// ReactCSSTransitionGroup transitions -.list-container-appear { - opacity: 0.01; - - &.list-container-appear-active { - opacity: 1; - transition: opacity 0.25s ease-in; - } -} - -.list-container-leave { - opacity: 1; - - &.list-container-leave-active { - opacity: 0; - transition: opacity 0.25s ease-out; - } -} diff --git a/src/shared/components/MemberSearch/LoadMoreButton/index.jsx b/src/shared/components/MemberSearch/LoadMoreButton/index.jsx deleted file mode 100644 index 03c54cf1f0..0000000000 --- a/src/shared/components/MemberSearch/LoadMoreButton/index.jsx +++ /dev/null @@ -1,22 +0,0 @@ -import React from 'react'; -import PropTypes from 'prop-types'; - -import './style.scss'; - -const LoadMoreButton = ({ callback, loading }) => ( - -); - - -LoadMoreButton.propTypes = { - callback: PropTypes.func.isRequired, - loading: PropTypes.bool, -}; - -LoadMoreButton.defaultProps = { - loading: false, -}; - -export default LoadMoreButton; diff --git a/src/shared/components/MemberSearch/LoadMoreButton/style.scss b/src/shared/components/MemberSearch/LoadMoreButton/style.scss deleted file mode 100644 index 864d0ec652..0000000000 --- a/src/shared/components/MemberSearch/LoadMoreButton/style.scss +++ /dev/null @@ -1,15 +0,0 @@ -@import '~tc-ui/src/styles/tc-includes'; - -.load-more { - display: block; - height: 40px; - width: 100%; - max-width: 960px; - margin: 0 auto 20px; - background-color: $tc-gray-40; - border: 1px solid $tc-gray-50; - border-radius: 2px; - font-family: "Roboto", Helvetica, Arial, sans-serif; - font-size: 12px; - color: $tc-gray-neutral-light; -} diff --git a/src/shared/components/MemberSearch/LoadingListItem/index.jsx b/src/shared/components/MemberSearch/LoadingListItem/index.jsx deleted file mode 100644 index e7ff1066cf..0000000000 --- a/src/shared/components/MemberSearch/LoadingListItem/index.jsx +++ /dev/null @@ -1,50 +0,0 @@ -import React from 'react'; -import PropTypes from 'prop-types'; - -import './style.scss'; - -const LoadingListItem = ({ type }) => { - switch (type) { - case 'MEMBER': - return ( -
    -
    -
    -
    - -
    -
    - -
    -
    - -
    -
    -
    -
    -
    - -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    - ); - default: - return null; - } -}; - -LoadingListItem.propTypes = { - type: PropTypes.string.isRequired, -}; - -export default LoadingListItem; diff --git a/src/shared/components/MemberSearch/LoadingListItem/style.scss b/src/shared/components/MemberSearch/LoadingListItem/style.scss deleted file mode 100644 index ff5040111b..0000000000 --- a/src/shared/components/MemberSearch/LoadingListItem/style.scss +++ /dev/null @@ -1,132 +0,0 @@ -@import '~tc-ui/src/styles/tc-includes'; - -.loading-list-item { - display: flex; - max-width: 960px; - border-bottom: 1px solid rgba(163, 163, 174, 0.3); - - @media (max-width: 700px) { - display: block; - } - - &:last-child { - border-bottom: none; - } - - .user-info { - background-color: $tc-white; - flex-basis: 60%; - padding: 20px; - position: relative; - - @media (max-width: 700px) { - flex-basis: 50%; - } - } - - .user-profile { - display: flex; - align-items: center; - - .user-avatar { - width: 60px; - height: 60px; - margin-right: 20px; - position: relative; - border-radius: 50%; - background-color: $tc-gray-10; - - @media (max-width: 700px) { - margin-right: 10px; - } - } - - .username-and-details { - max-width: 225px; - } - - .username { - height: 18px; - width: 126px; - margin-bottom: 10px; - background-color: $tc-gray-10; - - @media (max-width: 700px) { - max-width: 180px; - } - } - - .user-details { - .country-and-wins { - height: 12px; - width: 238px; - background-color: $tc-gray-neutral-light; - margin-bottom: 3px; - } - - .member-since { - height: 12px; - width: 238px; - background-color: $tc-gray-neutral-light; - } - } - } - - .user-stats { - display: flex; - align-items: center; - flex-basis: 50%; - max-width: 440px; - margin-left: auto; - padding: 20px; - background-color: $tc-gray-neutral-light; - border-left: 1px solid $tc-gray-neutral-dark; - - @media (max-width: 700px) { - flex-basis: 100%; - display: block; - width: 100%; - max-width: 1000px; - padding: 15px; - border: none; - border-top: 1px solid $tc-gray-neutral-dark; - overflow-x: auto; - } - } - - .tag-list { - height: 12px; - width: 245px; - background-color: $tc-gray-10; - } - - .track-list { - display: flex; - flex-wrap: wrap; - white-space: nowrap; - - @media (max-width: 700px) { - flex-wrap: nowrap; - display: block; - width: auto; - white-space: nowrap; - } - } - - .track-item { - display: inline-block; - height: 26px; - width: 70px; - margin-top: 10px; - margin-right: 5px; - border-radius: 13px; - - &:last-child { - margin-right: 0; - - @media (max-width: 700px) { - margin-right: 15px; - } - } - } -} diff --git a/src/shared/components/MemberSearch/MemberSearchView/MemberSearchItem/index.jsx b/src/shared/components/MemberSearch/MemberSearchView/MemberSearchItem/index.jsx deleted file mode 100644 index 96093c6e78..0000000000 --- a/src/shared/components/MemberSearch/MemberSearchView/MemberSearchItem/index.jsx +++ /dev/null @@ -1,41 +0,0 @@ -import React from 'react'; -import PT from 'prop-types'; -import UserAvatar from '../../User/UserAvatar'; -import UsernameAndDetails from '../../User/UsernameAndDetails'; -import UserStats from '../../User/UserStats'; - -import './styles.scss'; - -const MemberSearchItem = ({ index, item }) => ( -
    -
    - - -
    - -
    - -
    - -
    -); - -MemberSearchItem.defaultProps = { - index: 0, - item: {}, -}; - -MemberSearchItem.propTypes = { - index: PT.number, - item: PT.shape(), -}; - -export default MemberSearchItem; diff --git a/src/shared/components/MemberSearch/MemberSearchView/MemberSearchItem/styles.scss b/src/shared/components/MemberSearch/MemberSearchView/MemberSearchItem/styles.scss deleted file mode 100644 index edfcc4f974..0000000000 --- a/src/shared/components/MemberSearch/MemberSearchView/MemberSearchItem/styles.scss +++ /dev/null @@ -1,42 +0,0 @@ -@import "~styles/mixins"; - -.member-search-item { - min-height: 128px; - background-color: #f4f4f4; - align-items: center; - display: flex; - justify-content: space-between; - position: relative; - border-radius: 8px; - - &.odd { - background-color: $tc-white; - } - - @include xs-to-sm { - flex-direction: column; - padding: 0 16px; - } -} - -.left-content { - display: flex; - - @include xs-to-sm { - width: 100%; - margin-top: 16px; - border-bottom: 2px solid #e9e9e9; - padding-bottom: 16px; - } -} - -.right-content { - border-left: 2px solid #e9e9e9; - width: 59%; - margin: 24px 0; - - @include xs-to-sm { - border-left: none; - width: 100%; - } -} diff --git a/src/shared/components/MemberSearch/MemberSearchView/MemberSearchTab/index.jsx b/src/shared/components/MemberSearch/MemberSearchView/MemberSearchTab/index.jsx deleted file mode 100644 index 46f3ce7aea..0000000000 --- a/src/shared/components/MemberSearch/MemberSearchView/MemberSearchTab/index.jsx +++ /dev/null @@ -1,105 +0,0 @@ -import React, { useState } from 'react'; -import PT from 'prop-types'; -import { useMediaQuery } from 'react-responsive'; -import ArrowIcon from 'assets/images/ico-arrow-down.svg'; -import cn from 'classnames'; - -import './styles.scss'; - -const MemberSearchTab = ({ - currentTab, setCurrentTab, userNameCount, skillsCount, -}) => { - const tabs = ['USERNAMES MATCHING', 'SKILLS MATCHING']; - const [isTabClosed, setIsTabClosed] = useState(true); - - const matchingResults = [userNameCount, skillsCount]; - - const desktop = useMediaQuery({ minWidth: 1024 }); - - const tabDetail = tabs.map(((tab, index) => ( - { - setCurrentTab(index); - setIsTabClosed(true); - }} - styleName={`tab ${index === currentTab ? 'active' : ''}`} - key={tab} - > - {tab} - { - matchingResults[index] ? ( - { - index ? `Top ${matchingResults[index]}` : matchingResults[index] - } - - ) : null - } - - ))); - - return ( - - { - !desktop ? ( -
    -
    setIsTabClosed(!isTabClosed)} - > -
    -

    - {tabs[currentTab]} - { - matchingResults[currentTab] ? ( - { - currentTab ? `Top ${matchingResults[currentTab]}` : matchingResults[currentTab] - } - - ) : null - } -

    - -
    - -
    setIsTabClosed(!isTabClosed)} - > - -
    -
    - { - !isTabClosed && ( -
    - {tabDetail} -
    - ) - } -
    - ) : ( -
    - {tabDetail} -
    - ) - } -
    - ); -}; - -MemberSearchTab.defaultProps = { - userNameCount: 0, - skillsCount: 0, - currentTab: 0, -}; - -MemberSearchTab.propTypes = { - userNameCount: PT.number, - skillsCount: PT.number, - currentTab: PT.number, - setCurrentTab: PT.func.isRequired, -}; - -export default MemberSearchTab; diff --git a/src/shared/components/MemberSearch/MemberSearchView/MemberSearchTab/styles.scss b/src/shared/components/MemberSearch/MemberSearchView/MemberSearchTab/styles.scss deleted file mode 100644 index 7918047222..0000000000 --- a/src/shared/components/MemberSearch/MemberSearchView/MemberSearchTab/styles.scss +++ /dev/null @@ -1,168 +0,0 @@ -@import "~styles/mixins"; - -.member-search-tab { - height: 42px; - background: #eaf6fd; - border-radius: 4px 4px 0 0; - padding: 0; - border-bottom: 1px solid #d4d4d4; - margin-top: 34px; - display: flex; -} - -.tab { - @include barlow; - - color: $tco-black; - font-size: 14px; - line-height: 20px; - padding: 0 16px; - display: flex; - align-items: center; - cursor: pointer; - position: relative; - font-weight: 600; - - &.active { - background: #bae1f9; - font-weight: 700; - - &::after { - content: ""; - background-image: url(assets/images/nav-active-item-blue.svg); - height: 10px; - width: 40px; - margin-top: 10px; - justify-content: center; - z-index: 100; - display: block; - position: absolute; - top: 31px; - left: 41%; - - @include xs-to-sm { - display: none; - } - } - } - - @include xs-to-sm { - padding: 12px 18px; - border-radius: 0 0 4px 4px; - } -} - -.count { - @include roboto-regular; - - background-color: #2c95d7; - border-radius: 50px; - padding: 0 8px; - text-align: center; - color: $tc-white; - font-weight: 500; - font-size: 14px; - line-height: 16px; - letter-spacing: 0.5px; - height: 16px; - margin-left: 4px; - - @include xs-to-sm { - &.count-title { - margin-top: 2px; - } - } -} - -.mobile-member-search-tab { - margin-top: 24px; - - .mobile-tab-container { - background-color: #eaf6fd; - height: 40px; - margin-top: 32px; - display: flex; - justify-content: space-between; - border-radius: 4px 4px 0 0; - border-bottom: 1px solid #d4d4d4; - - .title { - @include barlow-bold; - - display: flex; - justify-content: center; - align-self: center; - height: 100%; - font-weight: 700; - color: $tco-black; - font-size: 14px; - line-height: 20px; - text-transform: uppercase; - padding-left: 16px; - padding-top: 10px; - } - - .icon { - width: 16px; - height: 9px; - margin-right: 15px; - display: flex; - flex-direction: column; - justify-content: center; - align-self: center; - cursor: pointer; - transform: scale(1.5); - } - - .down { - transform: scale(1.5) rotate(180deg); - margin-right: 20px !important; - } - - @include xs-to-md { - width: 100%; - margin: 0; - } - } -} - -.mobile-tab-left-content { - display: flex; - align-items: center; -} - -.mobile-tab-expanded { - margin: 0 16px; - background-color: #eaf6fd; - flex-direction: column; - display: flex; - - a { - width: 100%; - height: 40px; - margin: 0; - - @include barlow-bold; - - font-weight: 600; - color: #555; - font-size: 14px; - line-height: 20px; - text-transform: uppercase; - padding-left: 16px; - padding-top: 10px; - } - - .active { - background-color: #bae1f9; - - p { - color: $tco-black; - font-weight: 700; - } - } - - @include xs-to-sm { - margin: 0; - } -} diff --git a/src/shared/components/MemberSearch/MemberSearchView/index.jsx b/src/shared/components/MemberSearch/MemberSearchView/index.jsx deleted file mode 100644 index 5525fb8e2f..0000000000 --- a/src/shared/components/MemberSearch/MemberSearchView/index.jsx +++ /dev/null @@ -1,73 +0,0 @@ -import React, { useState } from 'react'; -import PropTypes from 'prop-types'; -import LoadingIndicator from 'components/LoadingIndicator'; -import MemberSearchTab from './MemberSearchTab'; -import MemberSearchItem from './MemberSearchItem'; - -import './style.scss'; - -const MemberSearchView = (props) => { - const [currentTab, setCurrentTab] = useState(0); - - const { - pageLoaded, - usernameMatches, - topMembers, - } = props; - const { previousSearchTerm: searchTerm } = props; - - const currentItems = currentTab ? topMembers : usernameMatches; - - return ( -
    -
    -

    SEARCH RESULTS

    - -

    ITEMS MATCHING “{searchTerm}”

    - - - -
    - { - pageLoaded - ? ( - - { - currentItems.length - ? currentItems.map((item, index) => ( - - )) :
    No items available
    - } -
    - ) - : - } -
    - -
    -
    - ); -}; - -MemberSearchView.propTypes = { - pageLoaded: PropTypes.bool.isRequired, - usernameMatches: PropTypes.arrayOf(PropTypes.shape({ - handle: PropTypes.string, - })).isRequired, - topMembers: PropTypes.arrayOf(PropTypes.shape({})).isRequired, - - previousSearchTerm: PropTypes.string, - searchTermTag: PropTypes.shape({}), -}; - -MemberSearchView.defaultProps = { - previousSearchTerm: null, - searchTermTag: null, -}; - -export default MemberSearchView; diff --git a/src/shared/components/MemberSearch/MemberSearchView/style.scss b/src/shared/components/MemberSearch/MemberSearchView/style.scss deleted file mode 100644 index 11eafceff0..0000000000 --- a/src/shared/components/MemberSearch/MemberSearchView/style.scss +++ /dev/null @@ -1,84 +0,0 @@ -@import "~styles/mixins"; - -.member-search-wrapper { - max-width: $screen-max; - width: 100%; - margin: 0 auto; - background-color: $tc-white; - margin-bottom: 32px; - - .member-search { - width: 100%; - margin-top: 32px; - - @include xs-to-sm { - margin: 16px; - } - } -} - -.title { - @include barlow-condensed; - - font-weight: 600; - font-size: 36px; - line-height: 32px; - letter-spacing: -1px; - padding-bottom: 24px; - border-bottom: 2px solid #eee; - color: $tco-black; - - @include xs-to-md { - font-size: 26px; - } -} - -.search-term { - @include barlow; - - font-size: 22px; - font-weight: 600; - line-height: 26px; - color: $tco-black; - margin-top: 32px; - - @include xs-to-sm { - font-size: 16px; - line-height: 18px; - margin-top: 24px; - } -} - -.member-search-item-wrapper { - margin-top: 42px; - display: flex; - flex-direction: column; - - @include xs-to-sm { - margin-top: 24px; - border-bottom: 2px solid #e9e9e9; - padding-bottom: 24px; - } -} - -.no-items { - text-align: center; - height: 50vh; - display: flex; - flex-direction: column; - justify-content: center; - - span { - @include roboto-regular; - - font-weight: 400; - font-size: 24px; - line-height: 32px; - color: #000; - - @include xs-to-sm { - font-size: 20px; - line-height: 28px; - } - } -} diff --git a/src/shared/components/MemberSearch/NoResults/index.jsx b/src/shared/components/MemberSearch/NoResults/index.jsx deleted file mode 100644 index cdb6586b69..0000000000 --- a/src/shared/components/MemberSearch/NoResults/index.jsx +++ /dev/null @@ -1,20 +0,0 @@ -import React from 'react'; -import PropTypes from 'prop-types'; -import RobotIcon from '../icons/RobotIcon'; - -import './style.scss'; - -const NoResults = ({ entry }) => ( -
    -

    Sorry, no results found for {entry}

    - - -
    -); - - -NoResults.propTypes = { - entry: PropTypes.string.isRequired, -}; - -export default NoResults; diff --git a/src/shared/components/MemberSearch/NoResults/style.scss b/src/shared/components/MemberSearch/NoResults/style.scss deleted file mode 100644 index f983b9048e..0000000000 --- a/src/shared/components/MemberSearch/NoResults/style.scss +++ /dev/null @@ -1,32 +0,0 @@ -@import '~tc-ui/src/styles/tc-includes'; - -.no-results { - max-width: 960px; - margin: 70px auto 20px; - text-align: center; - color: $tc-gray-80; - - p { - margin-bottom: 50px; - - @include tc-heading-large; - - font-family: "Roboto", Helvetica, Arial, sans-serif; - font-weight: 300; - } - - span:last-child { - font-weight: 500; - word-break: break-word; - } -} - -// ReactCSSTransitionGroup transitions -.no-results-appear { - opacity: 0.01; - - &.no-results-appear-active { - opacity: 1; - transition: opacity 0.25s ease-in; - } -} diff --git a/src/shared/components/MemberSearch/PageError/index.jsx b/src/shared/components/MemberSearch/PageError/index.jsx deleted file mode 100644 index 00c5207b83..0000000000 --- a/src/shared/components/MemberSearch/PageError/index.jsx +++ /dev/null @@ -1,15 +0,0 @@ -import React from 'react'; -import RobotIcon from '../icons/RobotIcon'; - -import './style.scss'; - -const PageError = () => ( -
    -

    Oops! There was an error.

    - - -
    -); - - -export default PageError; diff --git a/src/shared/components/MemberSearch/PageError/style.scss b/src/shared/components/MemberSearch/PageError/style.scss deleted file mode 100644 index c447f143ca..0000000000 --- a/src/shared/components/MemberSearch/PageError/style.scss +++ /dev/null @@ -1,27 +0,0 @@ -@import '~tc-ui/src/styles/tc-includes'; - -.page-error { - max-width: 960px; - margin: 70px auto 20px; - text-align: center; - color: $tc-gray-80; - - p { - margin-bottom: 50px; - - @include tc-heading-large; - - font-family: "Roboto", Helvetica, Arial, sans-serif; - font-weight: 300; - } -} - -// ReactCSSTransitionGroup transitions -.page-error-appear { - opacity: 0.01; - - &.page-error-appear-active { - opacity: 1; - transition: opacity 0.25s ease-in; - } -} diff --git a/src/shared/components/MemberSearch/SubtrackList/SubtrackItem/index.jsx b/src/shared/components/MemberSearch/SubtrackList/SubtrackItem/index.jsx deleted file mode 100644 index 71ad51c89c..0000000000 --- a/src/shared/components/MemberSearch/SubtrackList/SubtrackItem/index.jsx +++ /dev/null @@ -1,53 +0,0 @@ -import React from 'react'; -import PropTypes from 'prop-types'; -import classNames from 'classnames'; -import { - getSubtrackAbbreviation, - getRoundedPercentage, - numberWithCommas, -} from '../../helpers'; -import TrophyIcon from '../../icons/TrophyIcon'; - - -import './style.scss'; - -const SubtrackItem = ({ subtrack }) => { - const subtrackStyles = classNames( - 'subtrack-item', - `track-${subtrack.track}`, - ); - - const statType = subtrack.stat.type; - let statValue = subtrack.stat.value; - - statValue = statType === 'fulfillment' - ? getRoundedPercentage(statValue) - : numberWithCommas(statValue); - - const trophyIcon = statType === 'wins' ? : null; - - return ( - - - {trophyIcon} - - {statValue} - - - {getSubtrackAbbreviation(subtrack.name)} - - ); -}; - -SubtrackItem.propTypes = { - subtrack: PropTypes.shape({ - track: PropTypes.string, - stat: PropTypes.shape({ - type: PropTypes.string, - value: PropTypes.number, - }), - name: PropTypes.string, - }).isRequired, -}; - -export default SubtrackItem; diff --git a/src/shared/components/MemberSearch/SubtrackList/SubtrackItem/style.scss b/src/shared/components/MemberSearch/SubtrackList/SubtrackItem/style.scss deleted file mode 100644 index 390ffd0e10..0000000000 --- a/src/shared/components/MemberSearch/SubtrackList/SubtrackItem/style.scss +++ /dev/null @@ -1,52 +0,0 @@ -@import '~tc-ui/src/styles/tc-includes'; - -.subtrack-item { - display: inline-block; - padding: 3px 10px; - margin-top: 5px; - margin-right: 5px; - border-radius: 13px; - - @include tc-label-small; - - font-weight: 500; - - &:last-child { - margin-right: 0; - - @media (max-width: 700px) { - margin-right: 15px; - } - } - - &.track-DESIGN { - background-color: $tc-light_blue; - } - - &.track-DEVELOP { - background-color: $tc-green; - } - - &.track-DATA_SCIENCE { - background-color: $tc-orange; - } - - &.track-COPILOT { - background-color: $tc-gray-70; - } - - .subtrack-wins { - @include tc-label; - - color: $tc-white; - - svg { - margin-right: 2px; - } - } - - .track-code { - color: rgba($tc-white, 0.7); - margin-left: 3px; - } -} diff --git a/src/shared/components/MemberSearch/SubtrackList/index.jsx b/src/shared/components/MemberSearch/SubtrackList/index.jsx deleted file mode 100644 index a5f772fa1e..0000000000 --- a/src/shared/components/MemberSearch/SubtrackList/index.jsx +++ /dev/null @@ -1,18 +0,0 @@ -import React from 'react'; -import PropTypes from 'prop-types'; -import shortId from 'shortid'; -import SubtrackItem from './SubtrackItem'; - -import './style.scss'; - -const SubtrackList = ({ subtracks }) => ( -
    - {subtracks.map(s => )} -
    -); - -SubtrackList.propTypes = { - subtracks: PropTypes.arrayOf(PropTypes.shape({})).isRequired, -}; - -export default SubtrackList; diff --git a/src/shared/components/MemberSearch/SubtrackList/style.scss b/src/shared/components/MemberSearch/SubtrackList/style.scss deleted file mode 100644 index 8462715e27..0000000000 --- a/src/shared/components/MemberSearch/SubtrackList/style.scss +++ /dev/null @@ -1,12 +0,0 @@ -.subtracks-list { - display: flex; - flex-wrap: wrap; - width: 100%; - - @media (max-width: 700px) { - flex-wrap: nowrap; - display: block; - width: auto; - white-space: nowrap; - } -} diff --git a/src/shared/components/MemberSearch/TagList/TagItem/index.jsx b/src/shared/components/MemberSearch/TagList/TagItem/index.jsx deleted file mode 100644 index b33c2f57a5..0000000000 --- a/src/shared/components/MemberSearch/TagList/TagItem/index.jsx +++ /dev/null @@ -1,29 +0,0 @@ -import React from 'react'; -import PropTypes from 'prop-types'; -import classNames from 'classnames'; - -import './style.scss'; - -const TagItem = ({ tag }) => { - const tagItemStyles = classNames( - 'tag-text', - { 'searched-tag': tag.searchedTag }, - { 'special-tag': tag.specialTag }, - ); - - return ( - # - {tag.name} - - ); -}; - -TagItem.propTypes = { - tag: PropTypes.shape({ - searchedTag: PropTypes.bool, - specialTag: PropTypes.bool, - name: PropTypes.string, - }).isRequired, -}; - -export default TagItem; diff --git a/src/shared/components/MemberSearch/TagList/TagItem/style.scss b/src/shared/components/MemberSearch/TagList/TagItem/style.scss deleted file mode 100644 index 9e05094694..0000000000 --- a/src/shared/components/MemberSearch/TagList/TagItem/style.scss +++ /dev/null @@ -1,37 +0,0 @@ -@import '~tc-ui/src/styles/tc-includes'; - -.tag-item { - margin-bottom: 5px; - margin-right: 5px; - - @include tc-label; - - font-weight: 400; - color: $tc-gray-30; - - &:last-child { - margin-right: 0; - - @media (max-width: 700px) { - margin-right: 15px; - } - } - - .tag-text { - color: $tc-gray-50; - } - - .searched-tag { - color: $tc-black; - } - - .special-tag { - color: $tc-purple-70; - } - - // Commented out since skills are not yet clickable - // &:hover, - // &:hover .tag-text { - // color: $primary; - // } -} diff --git a/src/shared/components/MemberSearch/TagList/index.jsx b/src/shared/components/MemberSearch/TagList/index.jsx deleted file mode 100644 index fe2cf33ff4..0000000000 --- a/src/shared/components/MemberSearch/TagList/index.jsx +++ /dev/null @@ -1,44 +0,0 @@ -import React from 'react'; -import PropTypes from 'prop-types'; -import classNames from 'classnames'; -import shortId from 'shortid'; -import TagItem from './TagItem'; - -import './style.scss'; - -const TagList = ({ tags, label, emptyMessage = '' }) => { - const tagListStyles = classNames( - 'tag-list', - { 'no-tags': !tags.length }, - ); - - const tagLabelStyles = classNames({ 'tag-list-label': tags.length && label }); - - const tagLabel = tags.length && label ? label : null; - - const noTagsMessage = !tags.length && emptyMessage ? emptyMessage : null; - - const tagItems = tags.map(t => ); - - return ( -
    - {tagLabel} - - {noTagsMessage} - - {tagItems} -
    - ); -}; - -TagList.propTypes = { - tags: PropTypes.arrayOf(PropTypes.shape({})).isRequired, - label: PropTypes.string, - emptyMessage: PropTypes.string.isRequired, -}; - -TagList.defaultProps = { - label: '', -}; - -export default TagList; diff --git a/src/shared/components/MemberSearch/TagList/style.scss b/src/shared/components/MemberSearch/TagList/style.scss deleted file mode 100644 index 5766cbe820..0000000000 --- a/src/shared/components/MemberSearch/TagList/style.scss +++ /dev/null @@ -1,29 +0,0 @@ -@import '~tc-ui/src/styles/tc-includes'; - -.tag-list { - display: flex; - flex-wrap: wrap; - white-space: nowrap; - - @include tc-label; - - font-weight: 400; - - @media (max-width: 700px) { - flex-wrap: nowrap; - display: block; - width: auto; - white-space: nowrap; - } - - &.no-tags { - margin-bottom: 5px; - font-style: italic; - color: $tc-gray-20; - } -} - -.tag-list-label { - display: inline-block; - margin-right: 5px; -} diff --git a/src/shared/components/MemberSearch/TrackList/TrackItem/index.jsx b/src/shared/components/MemberSearch/TrackList/TrackItem/index.jsx deleted file mode 100644 index a4797c650a..0000000000 --- a/src/shared/components/MemberSearch/TrackList/TrackItem/index.jsx +++ /dev/null @@ -1,33 +0,0 @@ -import React from 'react'; -import PropTypes from 'prop-types'; -import classNames from 'classnames'; - -import './style.scss'; - -const TrackItem = ({ track }) => { - const trackStyles = classNames( - 'track-item', - { [`track-${track.toLowerCase()}`]: track.length }, - { 'no-track': !track.length }, - ); - - const trackMap = { - DEVELOP: 'Developer', - DESIGN: 'Designer', - DATA_SCIENCE: 'Data Scientist', - }; - - const trackName = trackMap[track]; - - return ( - - {trackName || 'No track selected'} - - ); -}; - -TrackItem.propTypes = { - track: PropTypes.string.isRequired, -}; - -export default TrackItem; diff --git a/src/shared/components/MemberSearch/TrackList/TrackItem/style.scss b/src/shared/components/MemberSearch/TrackList/TrackItem/style.scss deleted file mode 100644 index 413cc5f8b8..0000000000 --- a/src/shared/components/MemberSearch/TrackList/TrackItem/style.scss +++ /dev/null @@ -1,46 +0,0 @@ -@import '~tc-ui/src/styles/tc-includes'; - -.track-item { - display: inline-block; - padding: 5px 10px; - margin-top: 5px; - margin-right: 5px; - border-radius: 13px; - font-weight: 600; - color: $tc-white; - - &:last-child { - margin-right: 0; - - @media (max-width: 700px) { - margin-right: 15px; - } - } - - &.track-design { - background-color: $tc-light_blue; - } - - &.track-develop { - background-color: $tc-green; - } - - &.track-data_science { - background-color: $tc-orange; - } - - &.track-copilot { - background-color: $tc-gray-70; - font-size: 14px; - } - - &.no-track { - font-weight: 500; - background-color: $tc-gray-20; - } - - .track-name { - font-size: 14px; - line-height: 16px; - } -} diff --git a/src/shared/components/MemberSearch/TrackList/index.jsx b/src/shared/components/MemberSearch/TrackList/index.jsx deleted file mode 100644 index 5c52178e3c..0000000000 --- a/src/shared/components/MemberSearch/TrackList/index.jsx +++ /dev/null @@ -1,27 +0,0 @@ -import React from 'react'; -import PropTypes from 'prop-types'; -import shortId from 'shortid'; -import TrackItem from './TrackItem'; - -import './style.scss'; - -const TrackList = ({ tracks }) => { - let trackItems; - if (tracks.length) { - trackItems = tracks.map(t => ); - } else { - trackItems = ; - } - - return ( -
    - {trackItems} -
    - ); -}; - -TrackList.propTypes = { - tracks: PropTypes.arrayOf(PropTypes.string).isRequired, -}; - -export default TrackList; diff --git a/src/shared/components/MemberSearch/TrackList/style.scss b/src/shared/components/MemberSearch/TrackList/style.scss deleted file mode 100644 index 6636408392..0000000000 --- a/src/shared/components/MemberSearch/TrackList/style.scss +++ /dev/null @@ -1,12 +0,0 @@ -.track-list { - display: flex; - flex-wrap: wrap; - white-space: nowrap; - - @media (max-width: 700px) { - flex-wrap: nowrap; - display: block; - width: auto; - white-space: nowrap; - } -} diff --git a/src/shared/components/MemberSearch/User/UserAvatar/index.jsx b/src/shared/components/MemberSearch/User/UserAvatar/index.jsx deleted file mode 100644 index f833763d42..0000000000 --- a/src/shared/components/MemberSearch/User/UserAvatar/index.jsx +++ /dev/null @@ -1,31 +0,0 @@ -import React from 'react'; -import PropTypes from 'prop-types'; - -import './style.scss'; -import { Link } from 'react-router-dom'; -import { getInitials } from '../../helpers'; - -const UserAvatar = ({ photoURL, handle }) => ( - - { - photoURL ? ( -
    - ) : ( -
    {getInitials(handle)}
    - ) - } - -); - -UserAvatar.defaultProps = { - photoURL: '', - handle: '', -}; - -UserAvatar.propTypes = { - photoURL: PropTypes.string, - handle: PropTypes.string, -}; - - -export default UserAvatar; diff --git a/src/shared/components/MemberSearch/User/UserAvatar/style.scss b/src/shared/components/MemberSearch/User/UserAvatar/style.scss deleted file mode 100644 index 52aaf979ef..0000000000 --- a/src/shared/components/MemberSearch/User/UserAvatar/style.scss +++ /dev/null @@ -1,39 +0,0 @@ -@import '~tc-ui/src/styles/tc-includes'; -@import "~styles/mixins"; - -.user-avatar-r { - width: 80px; - height: 80px; - margin-left: 24px; - position: relative; - border-radius: 50%; - background-size: cover; - background-position: center; - border: 3px solid #fff; - - @include xs-to-sm { - width: 64px; - height: 64px; - margin-left: 0; - } -} - -.user-avatar-default { - background: linear-gradient(84.45deg, #05456d 2.12%, #0a7ac0 97.43%); - position: relative; - - span { - @include barlow-condensed; - - color: $tc-white; - font-size: 32px; - line-height: 32px; - font-weight: 600; - height: 100%; - display: flex; - justify-content: center; - text-align: center; - vertical-align: middle; - flex-direction: column; - } -} diff --git a/src/shared/components/MemberSearch/User/UserStats/index.jsx b/src/shared/components/MemberSearch/User/UserStats/index.jsx deleted file mode 100644 index 4cd17053bd..0000000000 --- a/src/shared/components/MemberSearch/User/UserStats/index.jsx +++ /dev/null @@ -1,59 +0,0 @@ -/* eslint-disable react/no-array-index-key */ -import React from 'react'; -import PT from 'prop-types'; -import _ from 'lodash'; - -import TrackItem from '../../../ProfilePage/MemberTracks/TrackItem'; -import { trackMap } from '../../../ProfilePage/MemberTracks'; -import List from '../../../ProfilePage/Skills/List'; - -import './style.scss'; - -const UserStats = ({ tracks, skills }) => { - const normalizeSkills = skills.map((skill, index) => ({ tagId: index, tagName: skill.name })); - const verifiedSkills = _.filter(normalizeSkills, skill => _.includes(skill.sources, 'CHALLENGE')); - const userEnteredSkills = _.filter(normalizeSkills, skill => !_.includes(skill.sources, 'CHALLENGE')); - - const renderTracks = ( -
    - { - tracks.map((track, index) => ( - - )) - } -
    - ); - - const renderSkills = ( -
    - - - -
    - ); - - return ( -
    -
    {tracks.length ? renderTracks : No track added}
    -
    {skills.length ? renderSkills : No skills added}
    -
    - ); -}; - -UserStats.defaultProps = { - tracks: [], - skills: [], -}; - -UserStats.propTypes = { - tracks: PT.arrayOf(PT.shape()), - skills: PT.arrayOf(PT.shape()), -}; - -export default UserStats; diff --git a/src/shared/components/MemberSearch/User/UserStats/style.scss b/src/shared/components/MemberSearch/User/UserStats/style.scss deleted file mode 100644 index dce918bec2..0000000000 --- a/src/shared/components/MemberSearch/User/UserStats/style.scss +++ /dev/null @@ -1,47 +0,0 @@ -@import "~styles/mixins"; - -.user-stats { - margin-left: 24px; - - @include xs-to-sm { - margin-left: 0; - } -} - -.not-found-text { - @include roboto-regular; - - font-weight: 400; - font-size: 14px; - line-height: 22px; - color: #767676; - - @include xs-to-sm { - &.track { - font-size: 12px; - line-height: 18px; - } - } -} - -.skills-wrapper { - margin-top: 16px; - - @include xs-to-sm { - margin-top: 8px; - } -} - -.tracks { - display: flex; - gap: 16px; - flex-wrap: wrap; - - @include xs-to-sm { - gap: 8px; - } -} - -.skills { - max-width: fit-content; -} diff --git a/src/shared/components/MemberSearch/User/UsernameAndDetails/index.jsx b/src/shared/components/MemberSearch/User/UsernameAndDetails/index.jsx deleted file mode 100644 index a3e22d8b23..0000000000 --- a/src/shared/components/MemberSearch/User/UsernameAndDetails/index.jsx +++ /dev/null @@ -1,56 +0,0 @@ -import React from 'react'; -import PropTypes from 'prop-types'; -import _ from 'lodash'; -import moment from 'moment'; -import { Link } from 'react-router-dom'; -import { singlePluralFormatter } from '../../helpers'; -import ISOCountries from '../../helpers/ISOCountries'; - -import './style.scss'; - -const UsernameAndDetails = ({ - username, - country, - numWins, - memberSince, -}) => { - const countryObject = _.find(ISOCountries, { alpha3: country }); - const userCountry = countryObject ? countryObject.name : ''; - - const numberWins = singlePluralFormatter(numWins, 'win'); - - const memberSinceYYYY = moment(memberSince).format('YYYY'); - - return ( -
    -

    - {username} -

    - -
    -
    - {userCountry} - - { - numberWins - ? {numberWins} - : null - } -
    - -
    - Member since {memberSinceYYYY} -
    -
    -
    - ); -}; - -UsernameAndDetails.propTypes = { - username: PropTypes.string.isRequired, - country: PropTypes.string.isRequired, - numWins: PropTypes.number.isRequired, - memberSince: PropTypes.number.isRequired, -}; - -export default UsernameAndDetails; diff --git a/src/shared/components/MemberSearch/User/UsernameAndDetails/style.scss b/src/shared/components/MemberSearch/User/UsernameAndDetails/style.scss deleted file mode 100644 index eecb2f911b..0000000000 --- a/src/shared/components/MemberSearch/User/UsernameAndDetails/style.scss +++ /dev/null @@ -1,59 +0,0 @@ -@import '~tc-ui/src/styles/tc-includes'; -@import "~styles/mixins"; - -.username-and-details { - max-width: 225px; - margin-left: 16px; - - .username { - @include roboto-bold; - - font-weight: 700; - font-size: 20px; - line-height: 26px; - color: $tco-black; - - @include xs-to-sm { - font-size: 14px; - line-height: 20px; - } - } - - .user-details { - .country-and-wins { - @include barlow; - - font-weight: 600; - font-size: 16px; - line-height: 22px; - color: $tco-black; - margin-top: 1px; - text-transform: uppercase; - - .user-country { - text-transform: uppercase; - padding-right: 8px; - } - - .number-wins { - padding-left: 8px; - border-left: 2px solid #e9e9e9; - } - } - - .member-since { - @include roboto-regular; - - font-size: 14px; - font-weight: 400; - line-height: 22px; - color: #767676; - margin-top: 1px; - - @include xs-to-sm { - font-size: 12px; - line-height: 18px; - } - } - } -} diff --git a/src/shared/components/MemberSearch/helpers/ISOCountries.js b/src/shared/components/MemberSearch/helpers/ISOCountries.js deleted file mode 100644 index 93bd801c9d..0000000000 --- a/src/shared/components/MemberSearch/helpers/ISOCountries.js +++ /dev/null @@ -1,751 +0,0 @@ -const ISOCountries = [ - { - alpha2: 'AF', alpha3: 'AFG', code: 4, numericString: '004', name: 'Afghanistan', - }, - { - alpha2: 'AX', alpha3: 'ALA', code: 248, numericString: '248', name: 'Åland Islands', - }, - { - alpha2: 'AL', alpha3: 'ALB', code: 8, numericString: '008', name: 'Albania', - }, - { - alpha2: 'DZ', alpha3: 'DZA', code: 12, numericString: '012', name: 'Algeria', - }, - { - alpha2: 'AS', alpha3: 'ASM', code: 16, numericString: '016', name: 'American Samoa', - }, - { - alpha2: 'AD', alpha3: 'AND', code: 20, numericString: '020', name: 'Andorra', - }, - { - alpha2: 'AO', alpha3: 'AGO', code: 24, numericString: '024', name: 'Angola', - }, - { - alpha2: 'AI', alpha3: 'AIA', code: 660, numericString: '660', name: 'Anguilla', - }, - { - alpha2: 'AQ', alpha3: 'ATA', code: 10, numericString: '010', name: 'Antarctica', - }, - { - alpha2: 'AG', alpha3: 'ATG', code: 28, numericString: '028', name: 'Antigua and Barbuda', - }, - { - alpha2: 'AR', alpha3: 'ARG', code: 32, numericString: '032', name: 'Argentina', - }, - { - alpha2: 'AM', alpha3: 'ARM', code: 51, numericString: '051', name: 'Armenia', - }, - { - alpha2: 'AW', alpha3: 'ABW', code: 533, numericString: '533', name: 'Aruba', - }, - { - alpha2: 'AU', alpha3: 'AUS', code: 36, numericString: '036', name: 'Australia', - }, - { - alpha2: 'AT', alpha3: 'AUT', code: 40, numericString: '040', name: 'Austria', - }, - { - alpha2: 'AZ', alpha3: 'AZE', code: 31, numericString: '031', name: 'Azerbaijan', - }, - { - alpha2: 'BS', alpha3: 'BHS', code: 44, numericString: '044', name: 'Bahamas', - }, - { - alpha2: 'BH', alpha3: 'BHR', code: 48, numericString: '048', name: 'Bahrain', - }, - { - alpha2: 'BD', alpha3: 'BGD', code: 50, numericString: '050', name: 'Bangladesh', - }, - { - alpha2: 'BB', alpha3: 'BRB', code: 52, numericString: '052', name: 'Barbados', - }, - { - alpha2: 'BY', alpha3: 'BLR', code: 112, numericString: '112', name: 'Belarus', - }, - { - alpha2: 'BE', alpha3: 'BEL', code: 56, numericString: '056', name: 'Belgium', - }, - { - alpha2: 'BZ', alpha3: 'BLZ', code: 84, numericString: '084', name: 'Belize', - }, - { - alpha2: 'BJ', alpha3: 'BEN', code: 204, numericString: '204', name: 'Benin', - }, - { - alpha2: 'BM', alpha3: 'BMU', code: 60, numericString: '060', name: 'Bermuda', - }, - { - alpha2: 'BT', alpha3: 'BTN', code: 64, numericString: '064', name: 'Bhutan', - }, - { - alpha2: 'BO', alpha3: 'BOL', code: 68, numericString: '068', name: 'Bolivia, Plurinational State of', - }, - { - alpha2: 'BQ', alpha3: 'BES', code: 535, numericString: '535', name: 'Bonaire, Sint Eustatius and Saba', - }, - { - alpha2: 'BA', alpha3: 'BIH', code: 70, numericString: '070', name: 'Bosnia and Herzegovina', - }, - { - alpha2: 'BW', alpha3: 'BWA', code: 72, numericString: '072', name: 'Botswana', - }, - { - alpha2: 'BV', alpha3: 'BVT', code: 74, numericString: '074', name: 'Bouvet Island', - }, - { - alpha2: 'BR', alpha3: 'BRA', code: 76, numericString: '076', name: 'Brazil', - }, - { - alpha2: 'IO', alpha3: 'IOT', code: 86, numericString: '086', name: 'British Indian Ocean Territory', - }, - { - alpha2: 'BN', alpha3: 'BRN', code: 96, numericString: '096', name: 'Brunei Darussalam', - }, - { - alpha2: 'BG', alpha3: 'BGR', code: 100, numericString: '100', name: 'Bulgaria', - }, - { - alpha2: 'BF', alpha3: 'BFA', code: 854, numericString: '854', name: 'Burkina Faso', - }, - { - alpha2: 'BI', alpha3: 'BDI', code: 108, numericString: '108', name: 'Burundi', - }, - { - alpha2: 'KH', alpha3: 'CPV', code: 132, numericString: '132', name: 'Cabo Verde', - }, - { - alpha2: 'CM', alpha3: 'KHM', code: 116, numericString: '116', name: 'Cambodia', - }, - { - alpha2: 'CA', alpha3: 'CMR', code: 120, numericString: '120', name: 'Cameroon', - }, - { - alpha2: 'CV', alpha3: 'CAN', code: 124, numericString: '124', name: 'Canada', - }, - { - alpha2: 'KY', alpha3: 'CYM', code: 136, numericString: '136', name: 'Cayman Islands', - }, - { - alpha2: 'CF', alpha3: 'CAF', code: 140, numericString: '140', name: 'Central African Republic', - }, - { - alpha2: 'TD', alpha3: 'TCD', code: 148, numericString: '148', name: 'Chad', - }, - { - alpha2: 'CL', alpha3: 'CHL', code: 152, numericString: '152', name: 'Chile', - }, - { - alpha2: 'CN', alpha3: 'CHN', code: 156, numericString: '156', name: 'China', - }, - { - alpha2: 'CX', alpha3: 'CXR', code: 162, numericString: '162', name: 'Christmas Island', - }, - { - alpha2: 'CC', alpha3: 'CCK', code: 166, numericString: '166', name: 'Cocos (Keeling) Islands', - }, - { - alpha2: 'CO', alpha3: 'COL', code: 170, numericString: '170', name: 'Colombia', - }, - { - alpha2: 'KM', alpha3: 'COM', code: 174, numericString: '174', name: 'Comoros', - }, - { - alpha2: 'CG', alpha3: 'COG', code: 178, numericString: '178', name: 'Congo', - }, - { - alpha2: 'CD', alpha3: 'COD', code: 180, numericString: '180', name: 'Congo, the Democratic Republic of the', - }, - { - alpha2: 'CK', alpha3: 'COK', code: 184, numericString: '184', name: 'Cook Islands', - }, - { - alpha2: 'CR', alpha3: 'CRI', code: 188, numericString: '188', name: 'Costa Rica', - }, - { - alpha2: 'CI', alpha3: 'CIV', code: 384, numericString: '384', name: 'Côte d\'Ivoire', - }, - { - alpha2: 'HR', alpha3: 'HRV', code: 191, numericString: '191', name: 'Croatia', - }, - { - alpha2: 'CU', alpha3: 'CUB', code: 192, numericString: '192', name: 'Cuba', - }, - { - alpha2: 'CW', alpha3: 'CUW', code: 531, numericString: '531', name: 'Curaçao', - }, - { - alpha2: 'CY', alpha3: 'CYP', code: 196, numericString: '196', name: 'Cyprus', - }, - { - alpha2: 'CZ', alpha3: 'CZE', code: 203, numericString: '203', name: 'Czech Republic', - }, - { - alpha2: 'DK', alpha3: 'DNK', code: 208, numericString: '208', name: 'Denmark', - }, - { - alpha2: 'DJ', alpha3: 'DJI', code: 262, numericString: '262', name: 'Djibouti', - }, - { - alpha2: 'DM', alpha3: 'DMA', code: 212, numericString: '212', name: 'Dominica', - }, - { - alpha2: 'DO', alpha3: 'DOM', code: 214, numericString: '214', name: 'Dominican Republic', - }, - { - alpha2: 'EC', alpha3: 'ECU', code: 218, numericString: '218', name: 'Ecuador', - }, - { - alpha2: 'EG', alpha3: 'EGY', code: 818, numericString: '818', name: 'Egypt', - }, - { - alpha2: 'SV', alpha3: 'SLV', code: 222, numericString: '222', name: 'El Salvador', - }, - { - alpha2: 'GQ', alpha3: 'GNQ', code: 226, numericString: '226', name: 'Equatorial Guinea', - }, - { - alpha2: 'ER', alpha3: 'ERI', code: 232, numericString: '232', name: 'Eritrea', - }, - { - alpha2: 'EE', alpha3: 'EST', code: 233, numericString: '233', name: 'Estonia', - }, - { - alpha2: 'ET', alpha3: 'ETH', code: 231, numericString: '231', name: 'Ethiopia', - }, - { - alpha2: 'FK', alpha3: 'FLK', code: 238, numericString: '238', name: 'Falkland Islands (Malvinas)', - }, - { - alpha2: 'FO', alpha3: 'FRO', code: 234, numericString: '234', name: 'Faroe Islands', - }, - { - alpha2: 'FJ', alpha3: 'FJI', code: 242, numericString: '242', name: 'Fiji', - }, - { - alpha2: 'FI', alpha3: 'FIN', code: 246, numericString: '246', name: 'Finland', - }, - { - alpha2: 'FR', alpha3: 'FRA', code: 250, numericString: '250', name: 'France', - }, - { - alpha2: 'GF', alpha3: 'GUF', code: 254, numericString: '254', name: 'French Guiana', - }, - { - alpha2: 'PF', alpha3: 'PYF', code: 258, numericString: '258', name: 'French Polynesia', - }, - { - alpha2: 'TF', alpha3: 'ATF', code: 260, numericString: '260', name: 'French Southern Territories', - }, - { - alpha2: 'GA', alpha3: 'GAB', code: 266, numericString: '266', name: 'Gabon', - }, - { - alpha2: 'GM', alpha3: 'GMB', code: 270, numericString: '270', name: 'Gambia', - }, - { - alpha2: 'GE', alpha3: 'GEO', code: 268, numericString: '268', name: 'Georgia', - }, - { - alpha2: 'DE', alpha3: 'DEU', code: 276, numericString: '276', name: 'Germany', - }, - { - alpha2: 'GH', alpha3: 'GHA', code: 288, numericString: '288', name: 'Ghana', - }, - { - alpha2: 'GI', alpha3: 'GIB', code: 292, numericString: '292', name: 'Gibraltar', - }, - { - alpha2: 'GR', alpha3: 'GRC', code: 300, numericString: '300', name: 'Greece', - }, - { - alpha2: 'GL', alpha3: 'GRL', code: 304, numericString: '304', name: 'Greenland', - }, - { - alpha2: 'GD', alpha3: 'GRD', code: 308, numericString: '308', name: 'Grenada', - }, - { - alpha2: 'GP', alpha3: 'GLP', code: 312, numericString: '312', name: 'Guadeloupe', - }, - { - alpha2: 'GU', alpha3: 'GUM', code: 316, numericString: '316', name: 'Guam', - }, - { - alpha2: 'GT', alpha3: 'GTM', code: 320, numericString: '320', name: 'Guatemala', - }, - { - alpha2: 'GG', alpha3: 'GGY', code: 831, numericString: '831', name: 'Guernsey', - }, - { - alpha2: 'GN', alpha3: 'GIN', code: 324, numericString: '324', name: 'Guinea', - }, - { - alpha2: 'GW', alpha3: 'GNB', code: 624, numericString: '624', name: 'Guinea-Bissau', - }, - { - alpha2: 'GY', alpha3: 'GUY', code: 328, numericString: '328', name: 'Guyana', - }, - { - alpha2: 'HT', alpha3: 'GTI', code: 332, numericString: '332', name: 'Haiti', - }, - { - alpha2: 'HM', alpha3: 'GMD', code: 334, numericString: '334', name: 'Heard Island and McDonald Islands', - }, - { - alpha2: 'VA', alpha3: 'VAT', code: 336, numericString: '336', name: 'Holy See (Vatican City State)', - }, - { - alpha2: 'HN', alpha3: 'HND', code: 340, numericString: '340', name: 'Honduras', - }, - { - alpha2: 'HK', alpha3: 'HKG', code: 344, numericString: '344', name: 'Hong Kong', - }, - { - alpha2: 'HU', alpha3: 'HUN', code: 348, numericString: '348', name: 'Hungary', - }, - { - alpha2: 'IS', alpha3: 'ISL', code: 352, numericString: '352', name: 'Iceland', - }, - { - alpha2: 'IN', alpha3: 'IND', code: 356, numericString: '356', name: 'India', - }, - { - alpha2: 'ID', alpha3: 'IDN', code: 360, numericString: '360', name: 'Indonesia', - }, - { - alpha2: 'IR', alpha3: 'IRN', code: 364, numericString: '364', name: 'Iran, Islamic Republic of', - }, - { - alpha2: 'IQ', alpha3: 'IRQ', code: 368, numericString: '368', name: 'Iraq', - }, - { - alpha2: 'IE', alpha3: 'IRL', code: 372, numericString: '372', name: 'Ireland', - }, - { - alpha2: 'IM', alpha3: 'IMN', code: 833, numericString: '833', name: 'Isle of Man', - }, - { - alpha2: 'IL', alpha3: 'ISR', code: 376, numericString: '376', name: 'Israel', - }, - { - alpha2: 'IT', alpha3: 'ITA', code: 380, numericString: '380', name: 'Italy', - }, - { - alpha2: 'JM', alpha3: 'JAM', code: 388, numericString: '388', name: 'Jamaica', - }, - { - alpha2: 'JP', alpha3: 'JPN', code: 392, numericString: '392', name: 'Japan', - }, - { - alpha2: 'JE', alpha3: 'JEY', code: 832, numericString: '832', name: 'Jersey', - }, - { - alpha2: 'JO', alpha3: 'JOR', code: 400, numericString: '400', name: 'Jordan', - }, - { - alpha2: 'KZ', alpha3: 'KAZ', code: 398, numericString: '398', name: 'Kazakhstan', - }, - { - alpha2: 'KE', alpha3: 'KEN', code: 404, numericString: '404', name: 'Kenya', - }, - { - alpha2: 'KI', alpha3: 'KIR', code: 296, numericString: '296', name: 'Kiribati', - }, - { - alpha2: 'KP', alpha3: 'PRK', code: 408, numericString: '408', name: 'Korea, Democratic People\'s Republic of', - }, - { - alpha2: 'KR', alpha3: 'KOR', code: 410, numericString: '410', name: 'Korea, Republic of', - }, - { - alpha2: 'KW', alpha3: 'KWT', code: 414, numericString: '414', name: 'Kuwait', - }, - { - alpha2: 'KG', alpha3: 'KGZ', code: 417, numericString: '417', name: 'Kyrgyzstan', - }, - { - alpha2: 'LA', alpha3: 'LAO', code: 418, numericString: '418', name: 'Lao People\'s Democratic Republic', - }, - { - alpha2: 'LV', alpha3: 'LVA', code: 428, numericString: '428', name: 'Latvia', - }, - { - alpha2: 'LB', alpha3: 'LBN', code: 422, numericString: '422', name: 'Lebanon', - }, - { - alpha2: 'LS', alpha3: 'LSO', code: 426, numericString: '426', name: 'Lesotho', - }, - { - alpha2: 'LR', alpha3: 'LBR', code: 430, numericString: '430', name: 'Liberia', - }, - { - alpha2: 'LY', alpha3: 'LBY', code: 434, numericString: '434', name: 'Libya', - }, - { - alpha2: 'LI', alpha3: 'LIE', code: 438, numericString: '438', name: 'Liechtenstein', - }, - { - alpha2: 'LT', alpha3: 'LTU', code: 440, numericString: '440', name: 'Lithuania', - }, - { - alpha2: 'LU', alpha3: 'LUX', code: 442, numericString: '442', name: 'Luxembourg', - }, - { - alpha2: 'MO', alpha3: 'MAC', code: 446, numericString: '446', name: 'Macao', - }, - { - alpha2: 'MK', alpha3: 'MKD', code: 807, numericString: '807', name: 'Macedonia, the former Yugoslav Republic of', - }, - { - alpha2: 'MG', alpha3: 'MDG', code: 450, numericString: '450', name: 'Madagascar', - }, - { - alpha2: 'MW', alpha3: 'MWI', code: 454, numericString: '454', name: 'Malawi', - }, - { - alpha2: 'MY', alpha3: 'MYS', code: 458, numericString: '458', name: 'Malaysia', - }, - { - alpha2: 'MV', alpha3: 'MDV', code: 462, numericString: '462', name: 'Maldives', - }, - { - alpha2: 'ML', alpha3: 'MLI', code: 466, numericString: '466', name: 'Mali', - }, - { - alpha2: 'MT', alpha3: 'MLT', code: 470, numericString: '470', name: 'Malta', - }, - { - alpha2: 'MH', alpha3: 'MHL', code: 584, numericString: '584', name: 'Marshall Islands', - }, - { - alpha2: 'MQ', alpha3: 'MTQ', code: 474, numericString: '474', name: 'Martinique', - }, - { - alpha2: 'MR', alpha3: 'MRT', code: 478, numericString: '478', name: 'Mauritania', - }, - { - alpha2: 'MU', alpha3: 'MUS', code: 480, numericString: '480', name: 'Mauritius', - }, - { - alpha2: 'YT', alpha3: 'MYT', code: 175, numericString: '175', name: 'Mayotte', - }, - { - alpha2: 'MX', alpha3: 'MEX', code: 484, numericString: '484', name: 'Mexico', - }, - { - alpha2: 'FM', alpha3: 'FSM', code: 583, numericString: '583', name: 'Micronesia, Federated States of', - }, - { - alpha2: 'MD', alpha3: 'MDA', code: 498, numericString: '498', name: 'Moldova, Republic of', - }, - { - alpha2: 'MC', alpha3: 'MCO', code: 492, numericString: '492', name: 'Monaco', - }, - { - alpha2: 'MN', alpha3: 'MNG', code: 496, numericString: '496', name: 'Mongolia', - }, - { - alpha2: 'ME', alpha3: 'MNE', code: 499, numericString: '499', name: 'Montenegro', - }, - { - alpha2: 'MS', alpha3: 'MSR', code: 500, numericString: '500', name: 'Montserrat', - }, - { - alpha2: 'MA', alpha3: 'MAR', code: 504, numericString: '504', name: 'Morocco', - }, - { - alpha2: 'MZ', alpha3: 'MOZ', code: 508, numericString: '508', name: 'Mozambique', - }, - { - alpha2: 'MM', alpha3: 'MMR', code: 104, numericString: '104', name: 'Myanmar', - }, - { - alpha2: 'NA', alpha3: 'NAM', code: 516, numericString: '516', name: 'Namibia', - }, - { - alpha2: 'NR', alpha3: 'NRU', code: 520, numericString: '520', name: 'Nauru', - }, - { - alpha2: 'NP', alpha3: 'NPL', code: 524, numericString: '524', name: 'Nepal', - }, - { - alpha2: 'NL', alpha3: 'NLD', code: 528, numericString: '528', name: 'Netherlands', - }, - { - alpha2: 'NC', alpha3: 'NCL', code: 540, numericString: '540', name: 'New Caledonia', - }, - { - alpha2: 'NZ', alpha3: 'NZL', code: 554, numericString: '554', name: 'New Zealand', - }, - { - alpha2: 'NI', alpha3: 'NIC', code: 558, numericString: '558', name: 'Nicaragua', - }, - { - alpha2: 'NE', alpha3: 'NER', code: 562, numericString: '562', name: 'Niger', - }, - { - alpha2: 'NG', alpha3: 'NGA', code: 566, numericString: '566', name: 'Nigeria', - }, - { - alpha2: 'NU', alpha3: 'NIU', code: 570, numericString: '570', name: 'Niue', - }, - { - alpha2: 'NF', alpha3: 'NFK', code: 574, numericString: '574', name: 'Norfolk Island', - }, - { - alpha2: 'MP', alpha3: 'MNP', code: 580, numericString: '580', name: 'Northern Mariana Islands', - }, - { - alpha2: 'NO', alpha3: 'NOR', code: 578, numericString: '578', name: 'Norway', - }, - { - alpha2: 'OM', alpha3: 'OMN', code: 512, numericString: '512', name: 'Oman', - }, - { - alpha2: 'PK', alpha3: 'PAK', code: 586, numericString: '586', name: 'Pakistan', - }, - { - alpha2: 'PW', alpha3: 'PLW', code: 585, numericString: '585', name: 'Palau', - }, - { - alpha2: 'PS', alpha3: 'PSE', code: 275, numericString: '275', name: 'Palestine, State of', - }, - { - alpha2: 'PA', alpha3: 'PAN', code: 591, numericString: '591', name: 'Panama', - }, - { - alpha2: 'PG', alpha3: 'PNG', code: 598, numericString: '598', name: 'Papua New Guinea', - }, - { - alpha2: 'PY', alpha3: 'PRY', code: 600, numericString: '600', name: 'Paraguay', - }, - { - alpha2: 'PE', alpha3: 'PER', code: 604, numericString: '604', name: 'Peru', - }, - { - alpha2: 'PH', alpha3: 'PHL', code: 608, numericString: '608', name: 'Philippines', - }, - { - alpha2: 'PN', alpha3: 'PCN', code: 612, numericString: '612', name: 'Pitcairn', - }, - { - alpha2: 'PL', alpha3: 'POL', code: 616, numericString: '616', name: 'Poland', - }, - { - alpha2: 'PT', alpha3: 'PRT', code: 620, numericString: '620', name: 'Portugal', - }, - { - alpha2: 'PR', alpha3: 'PRI', code: 630, numericString: '630', name: 'Puerto Rico', - }, - { - alpha2: 'QA', alpha3: 'QAT', code: 634, numericString: '634', name: 'Qatar', - }, - { - alpha2: 'RE', alpha3: 'REU', code: 638, numericString: '638', name: 'Réunion', - }, - { - alpha2: 'RO', alpha3: 'ROU', code: 642, numericString: '642', name: 'Romania', - }, - { - alpha2: 'RU', alpha3: 'RUS', code: 643, numericString: '643', name: 'Russian Federation', - }, - { - alpha2: 'RW', alpha3: 'RWA', code: 646, numericString: '646', name: 'Rwanda', - }, - { - alpha2: 'BL', alpha3: 'BLM', code: 652, numericString: '652', name: 'Saint Barthélemy', - }, - { - alpha2: 'SH', alpha3: 'SHN', code: 654, numericString: '654', name: 'Saint Helena, Ascension and Tristan da Cunha', - }, - { - alpha2: 'KN', alpha3: 'KNA', code: 659, numericString: '659', name: 'Saint Kitts and Nevis', - }, - { - alpha2: 'LC', alpha3: 'LCA', code: 662, numericString: '662', name: 'Saint Lucia', - }, - { - alpha2: 'MF', alpha3: 'MAF', code: 663, numericString: '663', name: 'Saint Martin (French part)', - }, - { - alpha2: 'PM', alpha3: 'SPM', code: 666, numericString: '666', name: 'Saint Pierre and Miquelon', - }, - { - alpha2: 'VC', alpha3: 'VCT', code: 670, numericString: '670', name: 'Saint Vincent and the Grenadines', - }, - { - alpha2: 'WS', alpha3: 'WSM', code: 882, numericString: '882', name: 'Samoa', - }, - { - alpha2: 'SM', alpha3: 'SMR', code: 674, numericString: '674', name: 'San Marino', - }, - { - alpha2: 'ST', alpha3: 'STP', code: 678, numericString: '678', name: 'Sao Tome and Principe', - }, - { - alpha2: 'SA', alpha3: 'SAU', code: 682, numericString: '682', name: 'Saudi Arabia', - }, - { - alpha2: 'SN', alpha3: 'SEN', code: 686, numericString: '686', name: 'Senegal', - }, - { - alpha2: 'RS', alpha3: 'SRB', code: 688, numericString: '688', name: 'Serbia', - }, - { - alpha2: 'SC', alpha3: 'SYC', code: 690, numericString: '690', name: 'Seychelles', - }, - { - alpha2: 'SL', alpha3: 'SLE', code: 694, numericString: '694', name: 'Sierra Leone', - }, - { - alpha2: 'SG', alpha3: 'SGP', code: 702, numericString: '702', name: 'Singapore', - }, - { - alpha2: 'SX', alpha3: 'SXM', code: 534, numericString: '534', name: 'Sint Maarten (Dutch part)', - }, - { - alpha2: 'SK', alpha3: 'SVK', code: 703, numericString: '703', name: 'Slovakia', - }, - { - alpha2: 'SI', alpha3: 'SVN', code: 705, numericString: '705', name: 'Slovenia', - }, - { - alpha2: 'SB', alpha3: 'SLB', code: 90, numericString: '090', name: 'Solomon Islands', - }, - { - alpha2: 'SO', alpha3: 'SOM', code: 706, numericString: '706', name: 'Somalia', - }, - { - alpha2: 'ZA', alpha3: 'ZAF', code: 710, numericString: '710', name: 'South Africa', - }, - { - alpha2: 'GS', alpha3: 'SGS', code: 239, numericString: '239', name: 'South Georgia and the South Sandwich Islands', - }, - { - alpha2: 'SS', alpha3: 'SSD', code: 728, numericString: '728', name: 'South Sudan', - }, - { - alpha2: 'ES', alpha3: 'ESP', code: 724, numericString: '724', name: 'Spain', - }, - { - alpha2: 'LK', alpha3: 'LKA', code: 144, numericString: '144', name: 'Sri Lanka', - }, - { - alpha2: 'SD', alpha3: 'SDN', code: 729, numericString: '729', name: 'Sudan', - }, - { - alpha2: 'SR', alpha3: 'SUR', code: 740, numericString: '740', name: 'Suriname', - }, - { - alpha2: 'SJ', alpha3: 'SJM', code: 744, numericString: '744', name: 'Svalbard and Jan Mayen', - }, - { - alpha2: 'SZ', alpha3: 'SWZ', code: 748, numericString: '748', name: 'Swaziland', - }, - { - alpha2: 'SE', alpha3: 'SWE', code: 752, numericString: '752', name: 'Sweden', - }, - { - alpha2: 'CH', alpha3: 'CHE', code: 756, numericString: '756', name: 'Switzerland', - }, - { - alpha2: 'SY', alpha3: 'SYR', code: 760, numericString: '760', name: 'Syrian Arab Republic', - }, - { - alpha2: 'TW', alpha3: 'TWN', code: 158, numericString: '158', name: 'Taiwan', - }, - { - alpha2: 'TJ', alpha3: 'TJK', code: 762, numericString: '762', name: 'Tajikistan', - }, - { - alpha2: 'TZ', alpha3: 'TZA', code: 834, numericString: '834', name: 'Tanzania, United Republic of', - }, - { - alpha2: 'TH', alpha3: 'THA', code: 764, numericString: '764', name: 'Thailand', - }, - { - alpha2: 'TL', alpha3: 'TLS', code: 626, numericString: '626', name: 'Timor-Leste', - }, - { - alpha2: 'TG', alpha3: 'TGO', code: 768, numericString: '768', name: 'Togo', - }, - { - alpha2: 'TK', alpha3: 'TKL', code: 772, numericString: '772', name: 'Tokelau', - }, - { - alpha2: 'TO', alpha3: 'TON', code: 776, numericString: '776', name: 'Tonga', - }, - { - alpha2: 'TT', alpha3: 'TTO', code: 780, numericString: '780', name: 'Trinidad and Tobago', - }, - { - alpha2: 'TN', alpha3: 'TUN', code: 788, numericString: '788', name: 'Tunisia', - }, - { - alpha2: 'TR', alpha3: 'TUR', code: 792, numericString: '792', name: 'Turkey', - }, - { - alpha2: 'TM', alpha3: 'TKM', code: 795, numericString: '795', name: 'Turkmenistan', - }, - { - alpha2: 'TC', alpha3: 'TCA', code: 796, numericString: '796', name: 'Turks and Caicos Islands', - }, - { - alpha2: 'TV', alpha3: 'TUV', code: 798, numericString: '798', name: 'Tuvalu', - }, - { - alpha2: 'UG', alpha3: 'UGA', code: 800, numericString: '800', name: 'Uganda', - }, - { - alpha2: 'UA', alpha3: 'UKR', code: 804, numericString: '804', name: 'Ukraine', - }, - { - alpha2: 'AE', alpha3: 'ARE', code: 784, numericString: '784', name: 'United Arab Emirates', - }, - { - alpha2: 'GB', alpha3: 'GBR', code: 826, numericString: '826', name: 'United Kingdom', - }, - { - alpha2: 'US', alpha3: 'USA', code: 840, numericString: '840', name: 'United States', - }, - { - alpha2: 'UM', alpha3: 'UMI', code: 581, numericString: '581', name: 'United States Minor Outlying Islands', - }, - { - alpha2: 'UY', alpha3: 'URY', code: 858, numericString: '858', name: 'Uruguay', - }, - { - alpha2: 'UZ', alpha3: 'UZB', code: 860, numericString: '860', name: 'Uzbekistan', - }, - { - alpha2: 'VU', alpha3: 'VUT', code: 548, numericString: '548', name: 'Vanuatu', - }, - { - alpha2: 'VE', alpha3: 'VEN', code: 862, numericString: '862', name: 'Venezuela, Bolivarian Republic of', - }, - { - alpha2: 'VN', alpha3: 'VNM', code: 704, numericString: '704', name: 'Viet Nam', - }, - { - alpha2: 'VG', alpha3: 'VGB', code: 92, numericString: '092', name: 'Virgin Islands, British', - }, - { - alpha2: 'VI', alpha3: 'VIR', code: 850, numericString: '850', name: 'Virgin Islands, U.S.', - }, - { - alpha2: 'WF', alpha3: 'WLF', code: 876, numericString: '876', name: 'Wallis and Futuna', - }, - { - alpha2: 'EH', alpha3: 'ESH', code: 732, numericString: '732', name: 'Western Sahara', - }, - { - alpha2: 'YE', alpha3: 'YEM', code: 887, numericString: '887', name: 'Yemen', - }, - { - alpha2: 'ZM', alpha3: 'ZMB', code: 894, numericString: '894', name: 'Zambia', - }, - { - alpha2: 'ZW', alpha3: 'ZWE', code: 716, numericString: '716', name: 'Zimbabwe', - }, -]; - -export default ISOCountries; diff --git a/src/shared/components/MemberSearch/helpers/index.js b/src/shared/components/MemberSearch/helpers/index.js deleted file mode 100644 index 7b3842c7e3..0000000000 --- a/src/shared/components/MemberSearch/helpers/index.js +++ /dev/null @@ -1,261 +0,0 @@ -import _ from 'lodash'; - -// Member Levels -export function memberLevelByRating(userRating) { - const levelRatings = [0, 900, 1200, 1500, 2200]; - - const userLevel = _.findLastIndex(levelRatings, (rating) => { - if (userRating >= rating) { - return true; - } - return false; - }); - - if (userLevel === -1) return 1; - - return userLevel + 1; -} - -export function memberColorByLevel(userLevel) { - const colorsByLevel = { - 1: '#A3A3AD', - 2: '#25C089', - 3: '#666EFF', - 4: '#FCB816', - 5: '#E6175C', - }; - - const color = colorsByLevel[userLevel] || colorsByLevel[1]; - - return color; -} - -// Process member skills -export function sortSkillsByScoreAndTag(skills, tag, numSkillsToReturn = Infinity) { - if (_.isEmpty(skills)) return []; - - const sortedSkills = _.orderBy(skills, 'score', 'desc'); - - // If the user has the tag, move it to the front - if (tag) { - const tagIndex = _.findIndex(sortedSkills, skill => skill.name === tag.name); - - if (tagIndex !== -1) { - const tagSkill = sortedSkills.splice(tagIndex, 1)[0]; - tagSkill.searchedTag = true; - - sortedSkills.unshift(tagSkill); - } - } - - return sortedSkills.slice(0, numSkillsToReturn); -} - -// Subtrack Abbreviations -export function getSubtrackAbbreviation(subtrack) { - const subtrackAbbreviations = { - APPLICATION_FRONT_END_DESIGN: 'FE', - ARCHITECTURE: 'Ar', - ASSEMBLY_COMPETITION: 'As', - BANNERS_OR_ICONS: 'BI', - BUG_HUNT: 'BH', - CODE: 'Cd', - COMPONENT_PRODUCTION: 'Cp', - CONCEPTUALIZATION: 'Cn', - CONTENT_CREATION: 'CC', - COPILOT: 'FS', - COPILOT_POSTING: 'CP', - DEPLOYMENT: 'Dp', - DESIGN: 'Ds', - DESIGN_FIRST_2_FINISH: 'F2F', - DEVELOP_MARATHON_MATCH: 'MM', - DEVELOPMENT: 'Dv', - FIRST_2_FINISH: 'F2F', - FRONT_END_FLASH: 'Fl', - GENERIC_SCORECARDS: 'G', - IDEA_GENERATION: 'IG', - LOGO_DESIGN: 'Lg', - MARATHON_MATCH: 'MM', - PRINT_OR_PRESENTATION: 'PP', - PROCESS: 'Ps', - REPORTING: 'Rp', - RIA_BUILD_COMPETITION: 'RB', - RIA_COMPONENT_COMPETITION: 'RC', - SECURITY: 'Sc', - SPEC_REVIEW: 'SR', - SPECIFICATION: 'SPC', - SRM: 'SRM', - STUDIO_OTHER: 'O', - TEST_SCENARIOS: 'Ts', - TEST_SUITES: 'TS', - TESTING_COMPETITION: 'Tg', - UI_PROTOTYPE_COMPETITION: 'Pr', - WEB_DESIGNS: 'Wb', - WIDGET_OR_MOBILE_SCREEN_DESIGN: 'Wg', - WIREFRAMES: 'Wf', - }; - - const abbreviation = subtrackAbbreviations[subtrack] || 'O'; - - return abbreviation; -} - -export function getSubtrackStat(subtrackStats) { - if (subtrackStats.fulfillment) { - return { - value: subtrackStats.fulfillment, - type: 'fulfillment', - }; - } - - const subtrackRating = _.get(subtrackStats, 'rank.rating'); - - if (subtrackRating) { - return { - value: subtrackRating, - type: 'rating', - }; - } - - return { - value: subtrackStats.wins || 0, - type: 'wins', - }; -} - -// Subtrack filtering -export function getMostRecentSubtracks(userStatsByTrack, numResults = Infinity) { - let subtrackStats = []; - - // If a user is a copilot with > 10 challenges and > 80% fulfillment, - // add it to the list of subtracks - const hasQualifyingFulfillment = _.get(userStatsByTrack, 'COPILOT.fulfillment', 0) >= 80; - const hasQualifyingNumChallenges = _.get(userStatsByTrack, 'COPILOT.contests', 0) >= 10; - - if (hasQualifyingFulfillment && hasQualifyingNumChallenges) { - subtrackStats.push({ - track: 'COPILOT', - name: 'COPILOT', - stat: getSubtrackStat(userStatsByTrack.COPILOT), - }); - } - - // Process subtracks in Data Science - const marathonMatchStats = _.get(userStatsByTrack, 'DATA_SCIENCE.MARATHON_MATCH', {}); - const SRMStats = _.get(userStatsByTrack, 'DATA_SCIENCE.SRM', {}); - - if (marathonMatchStats.mostRecentEventDate) { - subtrackStats.push({ - track: 'DATA_SCIENCE', - name: 'MARATHON_MATCH', - mostRecentEventDate: marathonMatchStats.mostRecentEventDate, - stat: getSubtrackStat(marathonMatchStats), - }); - } - - if (SRMStats.mostRecentEventDate) { - subtrackStats.push({ - track: 'DATA_SCIENCE', - name: 'SRM', - mostRecentEventDate: SRMStats.mostRecentEventDate, - stat: getSubtrackStat(SRMStats), - }); - } - - // Process subtracks in Develop and Design - const designSubtracks = _.get(userStatsByTrack, 'DESIGN.subTracks', []); - const developSubtracks = _.get(userStatsByTrack, 'DEVELOP.subTracks', []); - - designSubtracks.forEach((subtrack) => { - if (subtrack.mostRecentEventDate) { - subtrackStats.push({ - track: 'DESIGN', - name: subtrack.name, - mostRecentEventDate: subtrack.mostRecentEventDate, - stat: getSubtrackStat(subtrack), - }); - } - }); - - developSubtracks.forEach((subtrack) => { - if (subtrack.mostRecentEventDate) { - subtrackStats.push({ - track: 'DEVELOP', - name: subtrack.name, - mostRecentEventDate: subtrack.mostRecentEventDate, - stat: getSubtrackStat(subtrack), - }); - } - }); - - // Filter out all subtracks with a value of 0 (wins, rating, etc.) - subtrackStats = subtrackStats.filter(stat => stat.stat.value !== 0); - - const sortedSubtracks = subtrackStats - .sort((a, b) => b.mostRecentEventDate - a.mostRecentEventDate); - - return sortedSubtracks.slice(0, numResults); -} - -// Detect end of the page on scroll -export function isEndOfScreen(callback, ...callbackArguments) { - if ((window.innerHeight + window.scrollY) >= document.body.offsetHeight - 400) { - callback(...callbackArguments); - } -} - -// Miscellaneous helpers -export function getRoundedPercentage(number) { - if (_.isFinite(number)) { - const roundedNumber = Math.round(number); - - return `${roundedNumber}%`; - } - - return ''; -} - -export function numberWithCommas(num) { - return num.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ','); -} - -export function singlePluralFormatter(num, noun) { - switch (num) { - case 0: - case undefined: - case null: - return ''; - case 1: - return `1 ${noun}`; - default: - return `${num} ${noun}s`; - } -} - -export function getSearchTagPreposition(tagType) { - switch (tagType.toUpperCase()) { - case 'SKILL': - return 'in'; - case 'COUNTRY': - return 'from'; - case 'EVENT': - return 'at the'; - default: - return 'in'; - } -} - -/** - * Get Initials from handle - * @param {String} handle - * @returns {String} user initials - */ -export const getInitials = (handle) => { - const names = handle.split(' '); - let initials = names[0].substring(0, 1).toUpperCase(); - - if (names.length > 1) { - initials += names[names.length - 1].substring(0, 1).toUpperCase(); - } - return initials; -}; diff --git a/src/shared/components/MemberSearch/icons/LevelDesignatorIcon.jsx b/src/shared/components/MemberSearch/icons/LevelDesignatorIcon.jsx deleted file mode 100644 index 01c77367f4..0000000000 --- a/src/shared/components/MemberSearch/icons/LevelDesignatorIcon.jsx +++ /dev/null @@ -1,33 +0,0 @@ -import React from 'react'; -import PropTypes from 'prop-types'; -import { memberColorByLevel } from '../helpers'; - -const LevelDesignatorIcon = ({ width, height, level }) => { - const fill = memberColorByLevel(level); - - return ( - - - - - - - - - - - ); -}; - -LevelDesignatorIcon.propTypes = { - width: PropTypes.string, - height: PropTypes.string, - level: PropTypes.number.isRequired, -}; - -LevelDesignatorIcon.defaultProps = { - width: '', - height: '', -}; - -export default LevelDesignatorIcon; diff --git a/src/shared/components/MemberSearch/icons/RobotIcon.jsx b/src/shared/components/MemberSearch/icons/RobotIcon.jsx deleted file mode 100644 index 2bc8801166..0000000000 --- a/src/shared/components/MemberSearch/icons/RobotIcon.jsx +++ /dev/null @@ -1,38 +0,0 @@ -import React from 'react'; -import PropTypes from 'prop-types'; - -const RobotIcon = ({ width, height }) => ( - - - - - - - - - - - - - - - - - - - - - -); - -RobotIcon.propTypes = { - width: PropTypes.string, - height: PropTypes.string, -}; - -RobotIcon.defaultProps = { - width: '', - height: '', -}; - -export default RobotIcon; diff --git a/src/shared/components/MemberSearch/icons/TrophyIcon.jsx b/src/shared/components/MemberSearch/icons/TrophyIcon.jsx deleted file mode 100644 index b3f51c831c..0000000000 --- a/src/shared/components/MemberSearch/icons/TrophyIcon.jsx +++ /dev/null @@ -1,22 +0,0 @@ -import React from 'react'; -import PropTypes from 'prop-types'; - -const TrophyIcon = ({ fill }) => ( - - - - - - - -); - -TrophyIcon.propTypes = { - fill: PropTypes.string, -}; - -TrophyIcon.defaultProps = { - fill: '', -}; - -export default TrophyIcon; diff --git a/src/shared/components/MemberSearch/index.jsx b/src/shared/components/MemberSearch/index.jsx deleted file mode 100644 index 4f6ca198f6..0000000000 --- a/src/shared/components/MemberSearch/index.jsx +++ /dev/null @@ -1,68 +0,0 @@ -import React, { Component } from 'react'; -import PropTypes from 'prop-types'; -import qs from 'qs'; -import MemberSearchView from './MemberSearchView'; -import { isEndOfScreen } from './helpers'; - -export default class MemberSearch extends Component { - constructor(props) { - super(props); - this.handleScroll = this.handleScroll.bind(this); - } - - componentWillMount() { - const { location, loadMemberSearch } = this.props; - - this.searchTermFromQuery = qs.parse(location.search, { ignoreQueryPrefix: true }).q || ''; - loadMemberSearch(this.searchTermFromQuery); - } - - componentDidMount() { - window.addEventListener('scroll', this.handleScroll); - } - - componentWillUnmount() { - window.removeEventListener('scroll', this.handleScroll); - } - - handleScroll() { - const { - moreMatchesAvailable, usernameMatches, loadingMore, pageLoaded, loadMemberSearch, - } = this.props; - - if (pageLoaded && !loadingMore && moreMatchesAvailable && usernameMatches.length > 10) { - isEndOfScreen(loadMemberSearch, this.searchTermFromQuery); - } - } - - render() { - return ( - - ); - } -} - -MemberSearch.propTypes = { - location: PropTypes.shape({ - search: PropTypes.string, - }).isRequired, - - pageLoaded: PropTypes.bool.isRequired, - loadingMore: PropTypes.bool.isRequired, - error: PropTypes.bool.isRequired, - - usernameMatches: PropTypes.arrayOf(PropTypes.shape({})).isRequired, - moreMatchesAvailable: PropTypes.bool.isRequired, - totalCount: PropTypes.number.isRequired, - topMembers: PropTypes.arrayOf(PropTypes.shape({})).isRequired, - - previousSearchTerm: PropTypes.string, - searchTermTag: PropTypes.shape({}), - - loadMemberSearch: PropTypes.func.isRequired, -}; - -MemberSearch.defaultProps = { - previousSearchTerm: null, - searchTermTag: null, -}; diff --git a/src/shared/components/TopcoderHeader/desktop/SubMenu/Item/index.jsx b/src/shared/components/SubMenu/Item/index.jsx similarity index 100% rename from src/shared/components/TopcoderHeader/desktop/SubMenu/Item/index.jsx rename to src/shared/components/SubMenu/Item/index.jsx diff --git a/src/shared/components/TopcoderHeader/desktop/SubMenu/Item/style.scss b/src/shared/components/SubMenu/Item/style.scss similarity index 100% rename from src/shared/components/TopcoderHeader/desktop/SubMenu/Item/style.scss rename to src/shared/components/SubMenu/Item/style.scss diff --git a/src/shared/components/TopcoderHeader/desktop/SubMenu/index.jsx b/src/shared/components/SubMenu/index.jsx similarity index 100% rename from src/shared/components/TopcoderHeader/desktop/SubMenu/index.jsx rename to src/shared/components/SubMenu/index.jsx diff --git a/src/shared/components/TopcoderHeader/desktop/SubMenu/style.scss b/src/shared/components/SubMenu/style.scss similarity index 100% rename from src/shared/components/TopcoderHeader/desktop/SubMenu/style.scss rename to src/shared/components/SubMenu/style.scss diff --git a/src/shared/components/TopcoderHeader/Auth/index.jsx b/src/shared/components/TopcoderHeader/Auth/index.jsx deleted file mode 100644 index eee206a735..0000000000 --- a/src/shared/components/TopcoderHeader/Auth/index.jsx +++ /dev/null @@ -1,48 +0,0 @@ -/** - * Auth block: a pair of Join and Log In buttons. - */ - -/* global window */ - -import PT from 'prop-types'; -import React from 'react'; -import { config } from 'topcoder-react-utils'; - -import './style.scss'; - -export default function Auth({ column }) { - return ( - - ); -} - -Auth.defaultProps = { - column: false, -}; - -Auth.propTypes = { - column: PT.bool, -}; diff --git a/src/shared/components/TopcoderHeader/Auth/style.scss b/src/shared/components/TopcoderHeader/Auth/style.scss deleted file mode 100644 index 373bd6c2ee..0000000000 --- a/src/shared/components/TopcoderHeader/Auth/style.scss +++ /dev/null @@ -1,38 +0,0 @@ -@import '~styles/mixins'; - -.auth { - display: inline-block; - - a { - border-radius: 2 * $corner-radius; - margin: 0 6px; - text-transform: uppercase; - top: 0; - } - - &:global.column { - display: block; - - a { - display: block; - margin: 12px; - text-align: center; - } - } - - :global { - .tc-btn-primary { - color: $tc-white; - } - - .tc-btn-default { - border: 1px solid $tc-dark-blue-110; - color: $tc-dark-blue-110; - - &:hover { - background: $tc-dark-blue-110; - color: $tc-white; - } - } - } -} diff --git a/src/shared/components/TopcoderHeader/index.jsx b/src/shared/components/TopcoderHeader/index.jsx deleted file mode 100644 index 761d17d4fa..0000000000 --- a/src/shared/components/TopcoderHeader/index.jsx +++ /dev/null @@ -1,490 +0,0 @@ -import _ from 'lodash'; -import { Avatar } from 'topcoder-react-ui-kit'; -import { getRatingColor } from 'utils/tc'; -import PT from 'prop-types'; -import React from 'react'; - -import { config, Link } from 'topcoder-react-utils'; - -import Auth from './Auth'; -import IconNavBlog from '../../../assets/images/nav/blog.svg'; -import IconNavBookCP from '../../../assets/images/nav/book-cp.svg'; -import IconNavBookData from '../../../assets/images/nav/book-data.svg'; -import IconNavBookDesign from '../../../assets/images/nav/book-design.svg'; -import IconNavBoolDevelop from '../../../assets/images/nav/book-develop.svg'; -import IconNavCP from '../../../assets/images/nav/track-cp.svg'; -import IconNavEvents from '../../../assets/images/nav/events.svg'; -import IconNavForums from '../../../assets/images/nav/forums.svg'; -import IconNavPrograms from '../../../assets/images/nav/programs.svg'; -import IconNavRocket from '../../../assets/images/nav/rocket.svg'; -import IconNavStatistics from '../../../assets/images/nav/statistics.svg'; -import IconNavTcoGeneric from '../../../assets/images/nav/tco-generic.svg'; -import IconNavThrive from '../../../assets/images/nav/thrive.svg'; - -/* For user sub-menu. */ -import IconNavDashboard from '../../../assets/images/nav/dashboard.svg'; -import IconNavExit from '../../../assets/images/nav/exit.svg'; -import IconNavProfile from '../../../assets/images/nav/profile.svg'; -import IconNavSettings from '../../../assets/images/nav/settings.svg'; -import IconNavWallet from '../../../assets/images/nav/wallet.svg'; - -import LogoTopcoderWithName from '../../../assets/images/logo_topcoder_with_name.svg'; - -import MagnifyingGlass from '../../../assets/images/magnifying_glass.svg'; - -import MobileHeader from './mobile/Header'; -import { SUB_MENU_SHAPE } from './mobile/SubMenu'; - -import DesktopSubMenu from './desktop/SubMenu'; - -import './style.scss'; - -/* NOTE: We use window object only in key / mouse handlers, which are only - * executed at client side, thus it does not make the code non-isomorphic. */ -/* global window, document */ - -const BASE_URL = config.URL.BASE; -const VALID_BASE_URLS = ['https://www.topcoder-dev.com', 'https://www.topcoder.com']; - -const MENU = [{ - title: 'Compete', - items: [{ - icon: , - link: '/challenges', - title: 'All Challenges', - }, { - icon: , - link: config.URL.ARENA, - title: 'Competitive Programming', - }], -}, { - title: 'Tracks', - url: `${BASE_URL}/community/learn`, - items: [ - /* { - enforceA: true, - icon: , - link: `${BASE_URL}/getting-started`, - title: 'Getting Started', - }, */ - { - enforceA: true, - icon: , - link: `${BASE_URL}/community/competitive-programming`, - title: 'Competitive Programming', - }, { - enforceA: true, - icon: , - link: `${BASE_URL}/community/data-science/`, - title: 'Data Science', - }, { - enforceA: true, - icon: , - link: `${BASE_URL}/community/design`, - title: 'Design', - }, { - enforceA: true, - icon: , - link: `${BASE_URL}/community/development`, - title: 'Development', - }, { - enforceA: true, - icon: , - link: `${BASE_URL}/community/qa`, - title: 'QA', - }], -}, { - title: 'Community', - items: [{ - enforceA: true, - icon: , - link: config.URL.TCO, - title: 'TCO', - }, { - enforceA: true, - icon: , - link: `${BASE_URL}/community/member-programs`, - title: 'Programs', - }, { - icon: , - link: config.URL.FORUMS, - title: 'Forums', - }, { - enforceA: true, - icon: , - link: `${BASE_URL}/community/statistics`, - title: 'Statistics', - }, { - enforceA: true, - icon: , - link: `${BASE_URL}/community/events`, - title: 'Events', - }, { - enforceA: true, - icon: , - link: `${BASE_URL}/blog`, - title: 'Blog', - }, { - enforceA: true, - icon: , - link: `${BASE_URL}/thrive`, - title: 'Thrive', - }], -}]; - -export default class TopcoderHeader extends React.Component { - constructor(props) { - super(props); - - this.globalTouchListener = this.globalTouchListener.bind(this); - this.addGlobalTouchListener = this.addGlobalTouchListener.bind(this); - this.removeGlobalTouchListener = this.removeGlobalTouchListener.bind(this); - this.closeSearch = this.closeSearch.bind(this); - this.getMenuButton = this.getMenuButton.bind(this); - - this.listenerRegistered = false; - } - - componentWillUnmount() { - const { closeMenu, closeSearch } = this.props; - closeMenu(); - closeSearch(); - this.removeGlobalTouchListener(); - } - - getMenuButton(node) { - if (!node || !node.dataset || node === this.headerRoot) { - return null; - } - if (node.dataset.menu) { - return node; - } - return this.getMenuButton(node.parentNode); - } - - globalTouchListener({ target }) { - const { closeMenu, searchOpened, openedMenu } = this.props; - const { closeSearch } = this; - const menuButton = this.getMenuButton(target); - - if (menuButton) { - if (menuButton.dataset.menu === 'search' && openedMenu) { - closeMenu(); - } else if (menuButton.dataset.menu !== 'search' && searchOpened) { - closeSearch(); - } - } else if (!this.headerRoot.contains(target) || this.mainMenu.contains(target)) { - closeSearch(); - closeMenu(); - this.removeGlobalTouchListener(); - } - } - - addGlobalTouchListener() { - if (!this.listenerRegistered) { - document.addEventListener('touchend', this.globalTouchListener); - this.listenerRegistered = true; - } - } - - removeGlobalTouchListener() { - document.removeEventListener('touchend', this.globalTouchListener); - this.listenerRegistered = false; - } - - closeSearch() { - const { closeSearch } = this.props; - this.searchInput.blur(); - closeSearch(); - } - - render() { - const { - isMobile, - activeTrigger, - closeMenu, - closeMobileMenu, - currentNav, - mobileMenuOpened, - openedMenu, - openMenu, - openMobileMenu, - openSearch, - profile, - searchOpened, - } = this.props; - - const normalizedProfile = profile && _.clone(profile); - if (profile && profile.photoURL) { - normalizedProfile.photoURL = `${config.CDN.PUBLIC}/avatar/${ - encodeURIComponent(normalizedProfile.photoURL)}?size=32`; - } - - const { closeSearch } = this; - - const mainMenu = MENU.map((item) => { - let styleName = 'main-menu-item'; - if (openedMenu && openedMenu.title === item.title) styleName += ' opened'; - if (item.title === currentNav.menuTitle) styleName += ' current'; - return ( -
  • !isMobile && openMenu(item, event.target)} - onMouseLeave={(event) => { - /* False when mouse cursor leaves from the main menu element to the - * sub-menu. In that case we keep the sub-menu opened, and responsible - * for further tracking of the mouse cursor. */ - if (!isMobile && activeTrigger - && 1 + event.pageY < activeTrigger.bottom) closeMenu(); - }} - onTouchStart={(event) => { - if (isMobile && openedMenu && openedMenu.title === item.title) { - closeMenu(); - this.removeGlobalTouchListener(); - } else { - openMenu(item, this.getMenuButton(event.target), true); - this.addGlobalTouchListener(); - } - }} - styleName={styleName} - > - {item.url ? ( - - {item.title} - - ) : {item.title}} -
  • - ); - }); - - let authButtons; - let userAvatar; - let userMenuHandle; - let userSubMenu; - - if (normalizedProfile) { - userAvatar = ( -
    - -
    - ); - - userSubMenu = { - title: 'User', - items: [{ - enforceA: true, - icon: , - link: `${BASE_URL}/home`, - title: 'Home', - }, { - enforceA: true, - icon: , - link: `/members/${normalizedProfile.handle}`, - title: 'My Profile', - }, { - icon: , - link: `${config.URL.COMMUNITY}/PactsMemberServlet?module=PaymentHistory&full_list=false`, - title: 'Payments', - }, { - enforceA: true, - icon: , - link: '/settings/account', - title: 'Settings', - }, { - enforceA: true, - icon: , - // TODO: In addition to hitting ${BASE_URL}/logout, which logs out - // from the accounts-app, we should wipe out auth cookies! - link: `${BASE_URL}/logout`, - title: 'Log Out', - }], - }; - userMenuHandle = ( -
    !isMobile && openMenu(userSubMenu, event.target)} - onMouseLeave={(event) => { - /* False when mouse cursor leaves from the main menu element to the - * sub-menu. In that case we keep the sub-menu opened, and responsible - * for further tracking of the mouse cursor. */ - if (!isMobile && activeTrigger - && 1 + event.pageY < activeTrigger.bottom) closeMenu(); - }} - onTouchStart={(event) => { - if (isMobile && openedMenu && openedMenu.title === userSubMenu.title) { - closeMenu(); - this.removeGlobalTouchListener(); - } else { - openMenu(userSubMenu, this.getMenuButton(event.target), true); - this.addGlobalTouchListener(); - } - }} - onFocus={event => !isMobile && openMenu(userSubMenu, event.target)} - onBlur={() => { - if (!isMobile) closeMenu(); - }} - role="link" - tabIndex={0} - styleName="user-menu" - > -
    - {normalizedProfile.handle} -
    - {userAvatar} -
    - ); - } else { - authButtons = ( - - - - ); - } - - return ( -
    { this.headerRoot = div; }} - onMouseLeave={() => { - if (openedMenu) { - closeMenu(); - } - if (searchOpened) { - closeSearch(); - } - }} - > -
    - - - -
      { this.mainMenu = ul; }}> - {mainMenu} -
    -
    - {userMenuHandle} - {authButtons} -
    !isMobile && openSearch(event.target)} - onBlur={(event) => { - if (!isMobile && activeTrigger - && 1 + event.pageY < activeTrigger.bottom) closeSearch(); - }} - onMouseEnter={event => !isMobile && openSearch(event.target)} - onMouseLeave={(event) => { - if (!isMobile && activeTrigger - && 1 + event.pageY < activeTrigger.bottom) closeSearch(); - }} - onTouchStart={(event) => { - if (isMobile && searchOpened) { - closeSearch(); - this.removeGlobalTouchListener(); - } else { - openSearch(this.getMenuButton(event.target), true); - this.addGlobalTouchListener(); - } - }} - styleName="search-icon" - > - -
    -
    -
    - -
    { - /* False when cursor leaves from the sub-menu to the element that has - * opened it. In that case we want to keep the menu opened, and the - * element under the mouse will control the menu state further. */ - if ((event.pageX < activeTrigger.left) - || (event.pageX > activeTrigger.right) - || (event.pageY > activeTrigger.bottom)) { - closeSearch(); - } - }} - styleName="search-field" - > - { this.searchInput = input; }} - onKeyPress={(event) => { - if (event.key === 'Enter') { - if (!VALID_BASE_URLS.includes(BASE_URL)) { - return; - } - const query = event.target.value.trim(); - const url = new URL(`${BASE_URL}/search/members`); - url.searchParams.append('q', query); - window.location = url.href; - } - }} - onBlur={closeSearch} - aria-label="Find members by username or skill" - placeholder="Find members by username or skill" - /> -
    - -
    - ); - } -} - -TopcoderHeader.defaultProps = { - activeTrigger: null, - mobileMenuOpened: false, - openedMenu: null, - profile: null, - searchOpened: false, - isMobile: false, -}; - -TopcoderHeader.propTypes = { - isMobile: PT.bool, - activeTrigger: PT.shape({ - bottom: PT.number.isRequired, - left: PT.number.isRequired, - right: PT.number.isRequired, - top: PT.number.isRequired, - }), - closeMenu: PT.func.isRequired, - closeMobileMenu: PT.func.isRequired, - closeSearch: PT.func.isRequired, - currentNav: PT.shape({ - menuTitle: PT.string, - subMenuTitle: PT.string, - }).isRequired, - mobileMenuOpened: PT.bool, - openedMenu: SUB_MENU_SHAPE, - openMenu: PT.func.isRequired, - openMobileMenu: PT.func.isRequired, - openSearch: PT.func.isRequired, - profile: PT.shape({ - photoURL: PT.string, - }), - searchOpened: PT.bool, -}; diff --git a/src/shared/components/TopcoderHeader/mobile/Header/index.jsx b/src/shared/components/TopcoderHeader/mobile/Header/index.jsx deleted file mode 100644 index eff9efcb1d..0000000000 --- a/src/shared/components/TopcoderHeader/mobile/Header/index.jsx +++ /dev/null @@ -1,69 +0,0 @@ -import { Avatar } from 'topcoder-react-ui-kit'; -import PT from 'prop-types'; -import React from 'react'; - -import Auth from '../../Auth'; -import LogoTopcoder from '../../../../../assets/images/logo_topcoder.svg'; -import Menu from '../Menu'; -import { SUB_MENU_SHAPE } from '../SubMenu'; -import './style.scss'; - -export default function Header({ - close, - mainMenu, - open, - opened, - profile, - userMenu, -}) { - return ( -
    -
    - -
    - Menu -
    - {profile ? ( -
    - -
    - ) : ( -
    - -
    - )} -
    - {opened ? ( - - ) : ''} -
    - ); -} - -Header.defaultProps = { - opened: false, - profile: null, - userMenu: null, -}; - -Header.propTypes = { - close: PT.func.isRequired, - mainMenu: PT.arrayOf(SUB_MENU_SHAPE).isRequired, - open: PT.func.isRequired, - opened: PT.bool, - profile: PT.shape({ - photoURL: PT.any, - }), - userMenu: SUB_MENU_SHAPE, -}; diff --git a/src/shared/components/TopcoderHeader/mobile/Header/style.scss b/src/shared/components/TopcoderHeader/mobile/Header/style.scss deleted file mode 100644 index 5fe6d3738b..0000000000 --- a/src/shared/components/TopcoderHeader/mobile/Header/style.scss +++ /dev/null @@ -1,72 +0,0 @@ -@import '~styles/mixins'; - -.auth { - position: absolute; - right: 0; - top: 16px; - - @media only screen and (max-width: 360px) { - position: static; - padding-top: 30px; - text-align: center; - display: block; - width: 100%; - } -} - -.avatar { - position: absolute; - right: 12px; - top: 10px; -} - -.header { - border-bottom: 1px solid $tc-gray-20; - cursor: pointer; - min-height: 54px; - padding: 16px 12px; -} - -.logo { - position: absolute; -} - -.menu { - color: $tc-black; - cursor: pointer; - font-size: 15px; - font-weight: 500; - margin: auto; - text-align: center; - text-transform: uppercase; - - &::after { - content: ''; - display: block; - width: 0; - height: 0; - border-style: solid; - border-width: 6px 5px 0; - border-color: $tc-black transparent transparent transparent; - position: absolute; - top: 36px; - left: 50%; - margin-left: -6px; - background-color: transparent; - } -} - -.right-menu { - height: 100%; - position: absolute; - right: 0; - top: 16px; -} - -.header-wrapper { - display: none; - - @include xs-to-md { - display: block; - } -} diff --git a/src/shared/components/TopcoderHeader/mobile/Menu/index.jsx b/src/shared/components/TopcoderHeader/mobile/Menu/index.jsx deleted file mode 100644 index 54d4e7b862..0000000000 --- a/src/shared/components/TopcoderHeader/mobile/Menu/index.jsx +++ /dev/null @@ -1,55 +0,0 @@ -import PT from 'prop-types'; -import React from 'react'; - -import Auth from '../../Auth'; -import LogoTopcoder from '../../../../../assets/images/logo_topcoder.svg'; -import Search from '../Search'; -import SubMenu, { SUB_MENU_SHAPE } from '../SubMenu'; -import UserMenu from '../UserMenu'; - -import './style.scss'; - -export default function Menu({ - close, - mainMenu, - profile, - userMenu, -}) { - return ( -
    -
    - - [ topcoder ] - - × - -
    - - { - profile - ? - : - } -
    - {mainMenu.map(item => )} -
    - ); -} - -Menu.defaultProps = { - profile: null, - userMenu: null, -}; - -Menu.propTypes = { - close: PT.func.isRequired, - mainMenu: PT.arrayOf(SUB_MENU_SHAPE).isRequired, - profile: PT.shape({}), - userMenu: SUB_MENU_SHAPE, -}; diff --git a/src/shared/components/TopcoderHeader/mobile/Menu/style.scss b/src/shared/components/TopcoderHeader/mobile/Menu/style.scss deleted file mode 100644 index 066fcfa3dd..0000000000 --- a/src/shared/components/TopcoderHeader/mobile/Menu/style.scss +++ /dev/null @@ -1,37 +0,0 @@ -@import '~styles/mixins'; - -.menu { - background: $tc-gray-90; - // height: 58px; - margin-top: -55px; - position: relative; - width: 100%; - top: 0; - z-index: 30; -} - -.separator { - background: $tc-gray-40; - height: 1px; - margin: 0 12px; -} - -.tc-logo { - left: 12px; - position: absolute; -} - -.title { - color: $tc-white; - padding: 12px; - text-align: center; -} - -.x-cross { - color: $tc-white; - cursor: pointer; - font: 100 48px 'Roboto', sans-serif; - position: absolute; - right: 12px; - top: -18px; -} diff --git a/src/shared/components/TopcoderHeader/mobile/Search/index.jsx b/src/shared/components/TopcoderHeader/mobile/Search/index.jsx deleted file mode 100644 index 6a00255bcd..0000000000 --- a/src/shared/components/TopcoderHeader/mobile/Search/index.jsx +++ /dev/null @@ -1,28 +0,0 @@ -import React from 'react'; -import { config } from 'topcoder-react-utils'; - -import IconMagnifyingGlass from '../../../../../assets/images/magnifying_glass.svg'; -import './style.scss'; - -/* NOTE: We use window object only inside the keypress handler, which only can - * be invoked at the client side, thus the code is still isomorphic. */ -/* global window */ - -export default function Search() { - return ( -
    - - { - if (event.key === 'Enter') { - window.location = `${config.URL.BASE}/search/members?q=${ - encodeURIComponent(event.target.value) - }`; - } - }} - placeholder="Find members by username or skill" - aria-label="Find members by username or skill" - /> -
    - ); -} diff --git a/src/shared/components/TopcoderHeader/mobile/Search/style.scss b/src/shared/components/TopcoderHeader/mobile/Search/style.scss deleted file mode 100644 index 1dce2dd221..0000000000 --- a/src/shared/components/TopcoderHeader/mobile/Search/style.scss +++ /dev/null @@ -1,42 +0,0 @@ -@import '~styles/mixins'; - -.icon { - left: 18px; - position: absolute; - top: 14px; -} - -.search { - padding: 6px 12px 0; - position: relative; - - input { - background: none; - border: 1px solid $tc-gray-60; - box-shadow: none; - color: $tc-white; - font: 16px 'Roboto', sans-serif; - height: 32px; - outline: none; - padding-left: 30px !important; - - &::placeholder { - color: $tc-gray-60; - } - - &:hover { - border: 1px solid $tc-gray-40; - } - - &:focus { - border: 1px solid $tc-dark-blue; - } - } - - input:active, - input:focus, - input:hover { - box-shadow: none; - outline: none; - } -} diff --git a/src/shared/components/TopcoderHeader/mobile/SubMenu/index.jsx b/src/shared/components/TopcoderHeader/mobile/SubMenu/index.jsx deleted file mode 100644 index 05165dbdeb..0000000000 --- a/src/shared/components/TopcoderHeader/mobile/SubMenu/index.jsx +++ /dev/null @@ -1,45 +0,0 @@ -import PT from 'prop-types'; -import React from 'react'; -import './style.scss'; - -export default function SubMenu({ - hideTitle, - subMenu, -}) { - const items = subMenu.items.map(item => ( -
  • - - {item.title} - -
  • - )); - const title = hideTitle ? '' : ( -
  • - {subMenu.title} -
  • - ); - return ( -
      - {title} - {items} -
    - ); -} - -export const SUB_MENU_SHAPE = PT.shape({ - title: PT.string.isRequired, - items: PT.arrayOf(PT.shape({ - icon: PT.node.isRequired, - link: PT.string.isRequired, - title: PT.string.isRequired, - })).isRequired, -}); - -SubMenu.defaultProps = { - hideTitle: false, -}; - -SubMenu.propTypes = { - hideTitle: PT.bool, - subMenu: SUB_MENU_SHAPE.isRequired, -}; diff --git a/src/shared/components/TopcoderHeader/mobile/SubMenu/style.scss b/src/shared/components/TopcoderHeader/mobile/SubMenu/style.scss deleted file mode 100644 index 18ce4f1477..0000000000 --- a/src/shared/components/TopcoderHeader/mobile/SubMenu/style.scss +++ /dev/null @@ -1,23 +0,0 @@ -@import '~styles/mixins'; - -.item { - color: $tc-white; - font: 500 14px "BarlowCondensed", sans-serif; - margin: 9px 18px; - text-transform: uppercase; - - a:hover { - color: $tc-dark-blue-110; - } -} - -.sub-menu { - padding: 6px 0; -} - -.title { - color: $tc-gray-40; - font: 500 18px "BarlowCondensed", sans-serif; - margin: 9px 18px; - text-transform: uppercase; -} diff --git a/src/shared/components/TopcoderHeader/mobile/UserMenu/index.jsx b/src/shared/components/TopcoderHeader/mobile/UserMenu/index.jsx deleted file mode 100644 index 61d6402403..0000000000 --- a/src/shared/components/TopcoderHeader/mobile/UserMenu/index.jsx +++ /dev/null @@ -1,41 +0,0 @@ -import _ from 'lodash'; -import { Avatar } from 'topcoder-react-ui-kit'; -import PT from 'prop-types'; -import React from 'react'; -import { getRatingColor } from 'utils/tc'; -import SubMenu, { SUB_MENU_SHAPE } from '../SubMenu'; -import './style.scss'; - -export default function UserMenu({ menu, profile }) { - return ( - - ); -} - -UserMenu.defaultProps = { - menu: null, - profile: null, -}; - -UserMenu.propTypes = { - menu: SUB_MENU_SHAPE, - profile: PT.shape({ - handle: PT.string, - photoURL: PT.string, - }), -}; diff --git a/src/shared/components/TopcoderHeader/mobile/UserMenu/style.scss b/src/shared/components/TopcoderHeader/mobile/UserMenu/style.scss deleted file mode 100644 index d05ed8211b..0000000000 --- a/src/shared/components/TopcoderHeader/mobile/UserMenu/style.scss +++ /dev/null @@ -1,19 +0,0 @@ -.avatar { - height: 32px; - width: 32px; -} - -.handle { - position: relative; - left: 12px; - top: -9px; -} - -.title { - margin: 18px 0 -6px; - padding: 0 12px; - - a { - cursor: pointer; - } -} diff --git a/src/shared/components/TopcoderHeader/style.scss b/src/shared/components/TopcoderHeader/style.scss deleted file mode 100644 index 6a3d16c6e9..0000000000 --- a/src/shared/components/TopcoderHeader/style.scss +++ /dev/null @@ -1,187 +0,0 @@ -@import '~styles/mixins'; - -.auth-align { - position: relative; - top: -4px; -} - -.avatar { - display: inline-block; - margin: 12px 6px 8px 12px; -} - -.header { - position: relative; - font-family: 'Roboto', sans-serif; - flex: none; -} - -.main-desktop-header { - border-bottom: 1px solid $tc-gray-20; - padding: 0 20px; - position: relative; -} - -.logo { - position: absolute; - padding-left: 11px; - padding-top: 1px; -} - -.main-menu { - margin-bottom: 0; - text-align: center; -} - -.main-menu-item { - color: $tc-gray-70; - cursor: pointer; - display: inline-block; - font-size: 14px; - line-height: 54px; - margin: 0; - padding: 0 40px; - position: relative; - text-transform: uppercase; - - &.current { - box-shadow: inset 0 -4px 0 -1px $tc-dark-blue; - } - - &.opened { - color: $tc-dark-blue; - - /* TODO: This code for downward blue arrow is used also - * in another place of this stylesheet. Should be implemented - * as a mixin! */ - &::after { - content: ''; - display: block; - width: 0; - height: 0; - border-style: solid; - border-width: 6px 5px 0; - border-color: $tc-dark-blue transparent transparent transparent; - position: absolute; - bottom: 5px; - left: 50%; - margin-left: -6px; - background-color: transparent; - } - } -} - -.right-menu { - height: 100%; - position: absolute; - right: 20px; - top: 0; -} - -.search-field { - background: $tc-black; - height: 0; - margin-top: -1px; - opacity: 0.98; - overflow: hidden; - padding: 0 48px; - position: absolute; - text-align: center; - width: 100%; - transition: all 0.25s ease-in-out; - z-index: 10; - - &:global.opened { - height: 128px; - padding: 48px; - transition: all 0.25s ease-in-out; - } - - &:global.closed { - display: none; - } - - input, - input:active, - input:focus, - input:hover { - border: none; - border-bottom: 1px solid $tc-white; - box-shadow: none; - font-size: 22px; - outline: none; - padding-bottom: 3px; - - &::placeholder { - color: $tc-gray-20; - } - } -} - -.search-icon { - cursor: pointer; - display: inline-block; - height: 32px; - margin: 11px 4px 11px 6px; - padding-left: 12px; - position: relative; - border-left: 1px solid $tc-gray-40; - width: 36px; - - svg { - height: 18px; - margin-top: 7px; - width: 18px; - } - - /* TODO: This code for downward blue arrow is used also - * in another place of this stylesheet. Should be implemented - * as a mixin! */ - &:global.opened { - path { - fill: $tc-dark-blue; - } - - &::after { - content: ''; - display: block; - width: 0; - height: 0; - border-style: solid; - border-width: 6px 5px 0; - border-color: $tc-dark-blue transparent transparent transparent; - position: absolute; - left: 60%; - bottom: -3px; - margin-left: -6px; - background-color: transparent; - } - } -} - -.user-menu { - cursor: pointer; - display: inline-block; - height: 100%; - vertical-align: bottom; -} - -.user-menu-handle { - border-bottom: 2px solid $tc-dark-blue; - display: inline-block; - font-size: 15px; - height: 54px; - padding: 23px 0; - vertical-align: bottom; - position: relative; -} - -@include xs-to-md { - .auth-buttons { - top: 16px; - } - - .main-desktop-header { - display: none; - } -} diff --git a/src/shared/components/challenge-listing/placeholders/ChallengeCard/index.jsx b/src/shared/components/challenge-listing/placeholders/ChallengeCard/index.jsx index b326c6fcb2..8b272ac95b 100644 --- a/src/shared/components/challenge-listing/placeholders/ChallengeCard/index.jsx +++ b/src/shared/components/challenge-listing/placeholders/ChallengeCard/index.jsx @@ -36,7 +36,7 @@ const ChallengeCardPlaceholder = ({ id }) => ( ); ChallengeCardPlaceholder.defaultProps = { - id: Math.random(), + id: 0, }; ChallengeCardPlaceholder.propTypes = { diff --git a/src/shared/components/tc-communities/Header/index.jsx b/src/shared/components/tc-communities/Header/index.jsx index 798d595a7a..b3dd7915f3 100644 --- a/src/shared/components/tc-communities/Header/index.jsx +++ b/src/shared/components/tc-communities/Header/index.jsx @@ -8,7 +8,7 @@ /* global window */ import _ from 'lodash'; -import DesktopSubMenu from 'components/TopcoderHeader/desktop/SubMenu'; +import DesktopSubMenu from 'components/SubMenu'; import React from 'react'; import PT from 'prop-types'; import { Avatar, PrimaryButton, Button } from 'topcoder-react-ui-kit'; diff --git a/src/shared/containers/MemberSearch.jsx b/src/shared/containers/MemberSearch.jsx deleted file mode 100644 index b05f7d0d95..0000000000 --- a/src/shared/containers/MemberSearch.jsx +++ /dev/null @@ -1,85 +0,0 @@ -import _ from 'lodash'; -import { connect } from 'react-redux'; -import { actions } from 'topcoder-react-lib'; -import MemberSearch from 'components/MemberSearch'; - -function mapStateToProps({ memberSearch }) { - return { - pageLoaded: memberSearch.pageLoaded, - loadingMore: memberSearch.loadingMore, - error: memberSearch.error, - - usernameMatches: memberSearch.usernameMatches, - moreMatchesAvailable: memberSearch.moreMatchesAvailable, - totalCount: memberSearch.totalCount, - topMembers: memberSearch.topMembers, - - previousSearchTerm: memberSearch.previousSearchTerm, - searchTermTag: memberSearch.searchTermTag, - }; -} - -function mapDispatchToProps(dispatch) { - const memberSearchActions = actions.memberSearch; - return { - loadMemberSearch: (searchTerm, stateProps) => { - const numCurrentUsernameMatches = stateProps.usernameMatches.length; - const { previousSearchTerm } = stateProps; - const isPreviousSearchTerm = _.isString(previousSearchTerm); - const isNewSearchTerm = isPreviousSearchTerm && searchTerm.toLowerCase() - !== previousSearchTerm.toLowerCase(); - - if (isNewSearchTerm) { - dispatch(memberSearchActions.clearMemberSearch()); - } else if (previousSearchTerm && numCurrentUsernameMatches >= 10) { - dispatch(memberSearchActions.loadMoreUsernames()); - dispatch(memberSearchActions.usernameSearchSuccess(searchTerm, numCurrentUsernameMatches)); - return; - } - - dispatch(memberSearchActions.setSearchTerm(searchTerm)); - dispatch(memberSearchActions.checkIfSearchTermIsATag(searchTerm)) - .then((res) => { - const tag = res.payload; - dispatch(memberSearchActions.setSearchTag(tag)); - - const topMemberSearchSuccessAction = tag - ? memberSearchActions.topMemberSearchSuccess(tag) : null; - const usernameSearchSuccessAction = memberSearchActions - .usernameSearchSuccess(searchTerm); - - let p; - if (topMemberSearchSuccessAction) { - p = dispatch(topMemberSearchSuccessAction) - .then(() => dispatch(usernameSearchSuccessAction)); - } else { - p = dispatch(usernameSearchSuccessAction); - } - - return p - .then(() => dispatch(memberSearchActions.memberSearchSuccess())) - .catch((err) => { - dispatch(memberSearchActions.resetSearchTerm()); - throw new Error(`Could not resolve all promises. Reason: ${err}`); - }); - }); - }, - }; -} - -function mergeProps(stateProps, dispatchProps, ownProps) { - return { - ...ownProps, - ...stateProps, - ...dispatchProps, - loadMemberSearch: searchTerm => dispatchProps.loadMemberSearch(searchTerm, stateProps), - }; -} - -const Container = connect( - mapStateToProps, - mapDispatchToProps, - mergeProps, -)(MemberSearch); - -export default Container; diff --git a/src/shared/containers/tc-communities/tco20/Header.jsx b/src/shared/containers/tc-communities/tco20/Header.jsx index 57057f0e8d..af3a13a180 100644 --- a/src/shared/containers/tc-communities/tco20/Header.jsx +++ b/src/shared/containers/tc-communities/tco20/Header.jsx @@ -25,8 +25,8 @@ function TCO20Header(props) { ) : ( - LOGIN - SIGN UP + LOGIN + SIGN UP ) } diff --git a/src/shared/utils/secureRandom.js b/src/shared/utils/secureRandom.js new file mode 100644 index 0000000000..190621fa64 --- /dev/null +++ b/src/shared/utils/secureRandom.js @@ -0,0 +1,25 @@ +const getCryptoLibrary = () => { + if (typeof window !== 'undefined' && window.crypto) { + return window.crypto; + } + /* eslint-disable global-require */ + const nodeCrypto = require('crypto'); + return nodeCrypto; +}; + +export default function (min, max) { + const crypto = getCryptoLibrary(); + const random = new Uint32Array(1); + if (typeof crypto.getRandomValues === 'function') { + crypto.getRandomValues(random); + } else if (typeof crypto.randomFillSync === 'function') { + crypto.randomFillSync(random); + } + + if (!max) { + return random[0] % min; + } + + const range = max - min + 1; + return min + (random[0] % range); +} diff --git a/src/shared/utils/url.js b/src/shared/utils/url.js index 6245ff2779..495de94486 100644 --- a/src/shared/utils/url.js +++ b/src/shared/utils/url.js @@ -14,7 +14,7 @@ import { BUCKETS } from 'utils/challenge-listing/buckets'; */ export function getCurrentUrl() { if (isomorphy.isServerSide()) return null; - const url = window.location.href; + const url = window.location.origin + window.location.pathname; if (typeof url === 'string' && url.startsWith('http')) { return url;