diff --git a/.circleci/config.yml b/.circleci/config.yml index 349c4c5d3c..01df8e0e32 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -349,7 +349,7 @@ workflows: filters: branches: only: - - free + - ca-profile-bug-bash # This is alternate dev env for parallel testing - "build-qa": context : org-global diff --git a/src/shared/components/Contentful/Article/Article.jsx b/src/shared/components/Contentful/Article/Article.jsx index 2e4476dab5..e87248eb3e 100644 --- a/src/shared/components/Contentful/Article/Article.jsx +++ b/src/shared/components/Contentful/Article/Article.jsx @@ -148,6 +148,17 @@ class Article extends React.Component { }, ).substring(0, CONTENT_PREVIEW_LENGTH); const catsGrouped = _.groupBy(fields.contentCategory, cat => cat.fields.trackParent); + // captures clicks on article + // for opening external links in new tab + const articleClickHandler = (e) => { + if (e.target.href && fields.openExternalLinksInNewTab !== false) { + const target = new URL(e.target.href); + if (!target.host.includes('topcoder')) { + window.open(e.target.href, '_blank'); + e.preventDefault(); + } + } + }; return ( @@ -291,7 +302,12 @@ class Article extends React.Component { {/* Content */} -
+
{ fields.type === 'Video' && fields.contentUrl ? ( diff --git a/src/shared/components/Contentful/Article/themes/default.scss b/src/shared/components/Contentful/Article/themes/default.scss index bd3c5b5f56..af7ba08979 100644 --- a/src/shared/components/Contentful/Article/themes/default.scss +++ b/src/shared/components/Contentful/Article/themes/default.scss @@ -324,7 +324,7 @@ ol { @include tc-body-md; - padding-left: 20px; + padding-left: 30px; margin-bottom: 20px; @include roboto-regular; @@ -485,6 +485,10 @@ margin-bottom: 108px; } + @media screen and (min-width: 1440px) and (max-width: 1900px) { + margin-bottom: 70px; + } + @media screen and (max-width: 768px) { margin-bottom: 300px; } diff --git a/src/shared/components/Contentful/ArticleCard/themes/article_large.scss b/src/shared/components/Contentful/ArticleCard/themes/article_large.scss index 1ec8bdcb82..43ade95560 100644 --- a/src/shared/components/Contentful/ArticleCard/themes/article_large.scss +++ b/src/shared/components/Contentful/ArticleCard/themes/article_large.scss @@ -12,6 +12,10 @@ @include xs-to-sm { margin: 5px; } + + @media screen and (max-width: 425px) { + max-height: none; + } } .contentWrapper { @@ -23,6 +27,10 @@ display: flex; flex-direction: row; align-items: stretch; + + @media screen and (max-width: 425px) { + flex-direction: column; + } } .main { @@ -225,6 +233,11 @@ min-width: 150px; } + @media screen and (max-width: 425px) { + width: 100%; + min-height: 130px; + } + &::before { content: ''; width: 35px; @@ -234,5 +247,12 @@ background-size: cover; background-position: right -2px; position: absolute; + + @media screen and (max-width: 425px) { + background-image: url("data:image/svg+xml,%3C%3Fxml version='1.0' encoding='utf-8'%3F%3E%3Csvg version='1.1' id='Layer_1' xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3.org/1999/xlink' x='0px' y='0px' viewBox='0 0 290 31.73' style='enable-background:new 0 0 290 31.73;' xml:space='preserve'%3E%3Cstyle type='text/css'%3E .st0%7Bfill-rule:evenodd;clip-rule:evenodd;fill:%23F4F4F4;%7D%0A%3C/style%3E%3Cg id='Thrive'%3E%3Cg id='Artboard' transform='translate(-22.000000, -20.000000)'%3E%3Cg id='Combined-Shape'%3E%3Cpath class='st0' d='M22,20h290l0,24.11c-60.39,13.64-90.73-0.4-142.15-10.48C118.1,23.5,97,79,22.08,33.64l0,0l0,1.34'/%3E%3C/g%3E%3C/g%3E%3C/g%3E%3C/svg%3E"); + width: 100%; + height: 33px; + background-position: 0 -10px; + } } } diff --git a/src/shared/components/Contentful/SearchBar/SearchBar.jsx b/src/shared/components/Contentful/SearchBar/SearchBar.jsx index ab0abb9e09..6e2efc5cbc 100644 --- a/src/shared/components/Contentful/SearchBar/SearchBar.jsx +++ b/src/shared/components/Contentful/SearchBar/SearchBar.jsx @@ -74,7 +74,7 @@ export class SearchBarInner extends Component { onKeyDown(e) { const { inputlVal, selectedFilter } = this.state; - if (inputlVal && e.which === 13) { + if (_.trim(inputlVal) && e.which === 13) { const searchQuery = {}; if (this.searchFieldRef && this.searchFieldRef.value) { if (selectedFilter.name === 'Tags') { @@ -90,7 +90,7 @@ export class SearchBarInner extends Component { if (selectedFilter.name !== 'Author') { window.location.href = `${config.TC_EDU_BASE_PATH}${config.TC_EDU_SEARCH_PATH}?${qs.stringify(searchQuery)}`; } else { - window.location.href = `${config.TC_EDU_BASE_PATH}${config.TC_EDU_SEARCH_PATH}?author=${inputlVal}`; + window.location.href = `${config.TC_EDU_BASE_PATH}${config.TC_EDU_SEARCH_PATH}?author=${_.trim(inputlVal)}`; } } } diff --git a/src/shared/components/Settings/Profile/BasicInfo/ImageInput/index.jsx b/src/shared/components/Settings/Profile/BasicInfo/ImageInput/index.jsx index d147282733..dfeb6cace0 100644 --- a/src/shared/components/Settings/Profile/BasicInfo/ImageInput/index.jsx +++ b/src/shared/components/Settings/Profile/BasicInfo/ImageInput/index.jsx @@ -25,6 +25,7 @@ export default class ImageInput extends React.Component { this.state = { newBasicInfo: {}, isImageOversize: false, + isImageFile: true, }; } @@ -72,6 +73,13 @@ export default class ImageInput extends React.Component { if (file === undefined) { return; } + const allowedTypes = ['image/png', 'image/jpg', 'image/jpeg']; + if (!allowedTypes.includes(file.type)) { + this.setState({ + isImageFile: false, + }); + return; + } if (file.size > 2 * 1024 * 1024) { // If file size is greater than 2 MB, show error message this.setState({ @@ -81,6 +89,7 @@ export default class ImageInput extends React.Component { } this.setState({ isImageOversize: false, + isImageFile: true, }); uploadPhotoInit(); loadImage.parseMetaData(file, (data) => { @@ -126,7 +135,7 @@ export default class ImageInput extends React.Component { deletingPhoto, } = profileState; - const { newBasicInfo, isImageOversize } = this.state; + const { newBasicInfo, isImageOversize, isImageFile } = this.state; return (
@@ -157,7 +166,8 @@ export default class ImageInput extends React.Component {
- {isImageOversize &&
Please select an image smaller than 2MB
} + {!isImageFile &&
Please select jpg, jpeg or png image files only
} + {isImageFile && isImageOversize &&
Please select an image smaller than 2MB
}
); } diff --git a/src/shared/components/Settings/Profile/BasicInfo/index.jsx b/src/shared/components/Settings/Profile/BasicInfo/index.jsx index 1240cc2184..1818e9d80a 100644 --- a/src/shared/components/Settings/Profile/BasicInfo/index.jsx +++ b/src/shared/components/Settings/Profile/BasicInfo/index.jsx @@ -47,8 +47,8 @@ export default class BasicInfo extends ConsentComponent { personalizationTrait: this.loadPersonalizationTrait(userTraits), newBasicInfo: { handle: '', - firstName: '', - lastName: '', + firstName: null, + lastName: null, gender: '', ethnicBackground: null, shortBio: '', @@ -377,6 +377,8 @@ export default class BasicInfo extends ConsentComponent { } if (_.has(value, 'firstName')) { newBasicInfo.firstName = value.firstName; + } else { + newBasicInfo.firstName = ''; } if (_.has(value, 'gender')) { newBasicInfo.gender = value.gender; @@ -390,6 +392,8 @@ export default class BasicInfo extends ConsentComponent { } if (_.has(value, 'lastName')) { newBasicInfo.lastName = value.lastName; + } else { + newBasicInfo.lastName = ''; } if (_.has(value, 'primaryInterestInTopcoder')) { newBasicInfo.primaryInterestInTopcoder = value.primaryInterestInTopcoder; @@ -501,7 +505,7 @@ export default class BasicInfo extends ConsentComponent {
* Required - +
@@ -514,7 +518,7 @@ export default class BasicInfo extends ConsentComponent {
* Required - +
diff --git a/src/shared/containers/Gigs/RecruitCRMJobDetails.jsx b/src/shared/containers/Gigs/RecruitCRMJobDetails.jsx index ba5aea8091..8ec55e9b0e 100644 --- a/src/shared/containers/Gigs/RecruitCRMJobDetails.jsx +++ b/src/shared/containers/Gigs/RecruitCRMJobDetails.jsx @@ -68,21 +68,6 @@ ${config.URL.BASE}${config.GIGS_PAGES_PATH}/${props.id}`, // exit no email sending return; } - // process sent log - let { emailInvitesLog, emailInvitesStatus } = growSurf.data.metadata; - if (!emailInvitesLog) emailInvitesLog = ''; - // check if email is in sent log alredy? - const foundInLog = emailInvitesLog.indexOf(email); - if (foundInLog !== -1) { - this.setState({ - isReferrError: { - message: `${email} was already invited.`, - userError: true, - }, - }); - // exit no email sending - return; - } // check if email is already referred? const growCheck = await fetch(`${PROXY_ENDPOINT}/growsurf/participant/${email}`); if (growCheck.status === 200) { @@ -125,43 +110,51 @@ ${config.URL.BASE}${config.GIGS_PAGES_PATH}/${props.id}`, // exit no email tracking due to the error return; } - // parse the log to array of emails - if (emailInvitesLog.length) { - emailInvitesLog = emailInvitesLog.split(','); - } else emailInvitesLog = []; - // prepare growSurf update payload - // we keep only 10 emails in the log to justify program rules - if (emailInvitesLog.length < 10) { - emailInvitesLog.push(email); - } - // Auto change status when 10 emails sent - if (emailInvitesLog.length === 10 && emailInvitesStatus !== 'Paid' && emailInvitesStatus !== 'Payment Pending') { - emailInvitesStatus = 'Payment Pending'; - } - // put the tracking update in growsurf - const updateRed = await fetch(`${PROXY_ENDPOINT}/growsurf/participant/${growSurf.data.id}`, { - method: 'PATCH', - headers: { - 'Content-Type': 'application/json', - Authorization: `Bearer ${tokenV3}`, - }, - body: JSON.stringify({ - ...growSurf.data, - metadata: { - ...growSurf.data.metadata, - emailInvitesSent: Number(growSurf.data.metadata.emailInvitesSent || 0) + 1, - emailInvitesLog: emailInvitesLog.join(), - emailInvitesStatus, + // process sent log + let { emailInvitesLog, emailInvitesStatus } = growSurf.data.metadata; + if (!emailInvitesLog) emailInvitesLog = ''; + // check if email is in sent log alredy? + const foundInLog = emailInvitesLog.indexOf(email); + // only when email is new - put it in log, update counters and etc. + if (foundInLog === -1) { + // parse the log to array of emails + if (emailInvitesLog.length) { + emailInvitesLog = emailInvitesLog.split(','); + } else emailInvitesLog = []; + // prepare growSurf update payload + // we keep only 10 emails in the log to justify program rules + if (emailInvitesLog.length < 10) { + emailInvitesLog.push(email); + } + // Auto change status when 10 emails sent + if (emailInvitesLog.length === 10 && emailInvitesStatus !== 'Paid' && emailInvitesStatus !== 'Payment Pending') { + emailInvitesStatus = 'Payment Pending'; + } + // put the tracking update in growsurf + const updateRed = await fetch(`${PROXY_ENDPOINT}/growsurf/participant/${growSurf.data.id}`, { + method: 'PATCH', + headers: { + 'Content-Type': 'application/json', + Authorization: `Bearer ${tokenV3}`, }, - }), - }); - if (updateRed.status >= 300) { - this.setState({ - isReferrError: await updateRed.json(), + body: JSON.stringify({ + ...growSurf.data, + metadata: { + ...growSurf.data.metadata, + emailInvitesSent: Number(growSurf.data.metadata.emailInvitesSent || 0) + 1, + emailInvitesLog: emailInvitesLog.join(), + emailInvitesStatus, + }, + }), }); - // exit no email tracking due to the error - // just notify the user about it - return; + if (updateRed.status >= 300) { + this.setState({ + isReferrError: await updateRed.json(), + }); + // exit no email tracking due to the error + // just notify the user about it + return; + } } // finally do: this.setState({