diff --git a/.circleci/config.yml b/.circleci/config.yml index 15009f5042..4fe405c05b 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -283,7 +283,7 @@ workflows: filters: branches: only: - - free + - gigwork-updates # This is alternate dev env for parallel testing - "build-qa": context : org-global diff --git a/__tests__/shared/components/GUIKit/TextInput/__snapshots__/index.jsx.snap b/__tests__/shared/components/GUIKit/TextInput/__snapshots__/index.jsx.snap index f7c29a7f58..739f1a551e 100644 --- a/__tests__/shared/components/GUIKit/TextInput/__snapshots__/index.jsx.snap +++ b/__tests__/shared/components/GUIKit/TextInput/__snapshots__/index.jsx.snap @@ -11,6 +11,7 @@ exports[`Default render 1`] = ` onChange={[Function]} onKeyPress={[Function]} placeholder="" + readOnly={false} type="text" /> diff --git a/src/server/services/growsurf.js b/src/server/services/growsurf.js index 9de3dfa2eb..920541b84a 100644 --- a/src/server/services/growsurf.js +++ b/src/server/services/growsurf.js @@ -87,6 +87,9 @@ export default class GrowsurfService { email: body.email, firstName: body.firstName, lastName: body.lastName, + metadata: { + tcHandle: body.tcHandle, + }, })); if (result.error) { res.status(result.code); diff --git a/src/server/services/recruitCRM.js b/src/server/services/recruitCRM.js index 33c44d72ff..1320f95467 100644 --- a/src/server/services/recruitCRM.js +++ b/src/server/services/recruitCRM.js @@ -195,6 +195,7 @@ export default class RecruitCRMService { // referral tracking via growsurf if (referralCookie && referralCookie.gigId === id) { const gs = new GrowsurfService(); + const tcHandle = _.findIndex(form.custom_fields, { field_id: 2 }); const growRes = await gs.addParticipant(JSON.stringify({ email: form.email, referredBy: referralCookie.referralId, @@ -203,6 +204,7 @@ export default class RecruitCRMService { lastName: form.last_name, metadata: { gigId: id, + tcHandle: form.custom_fields[tcHandle].value, }, })); // If everything set in Growsurf diff --git a/src/shared/components/GUIKit/TextInput/index.jsx b/src/shared/components/GUIKit/TextInput/index.jsx index 519ea50dc6..d4ef9938c9 100644 --- a/src/shared/components/GUIKit/TextInput/index.jsx +++ b/src/shared/components/GUIKit/TextInput/index.jsx @@ -19,6 +19,7 @@ function TextInput({ size, type, onEnterKey, + readonly, }) { const [val, setVal] = useState(value); const delayedOnChange = useRef( @@ -29,6 +30,7 @@ function TextInput({ return (
{}, + readonly: false, }; TextInput.propTypes = { @@ -79,6 +82,7 @@ TextInput.propTypes = { size: PT.oneOf(['xs', 'lg']), type: PT.string, onEnterKey: PT.func, + readonly: PT.bool, }; export default TextInput; diff --git a/src/shared/components/Gigs/GigApply/index.jsx b/src/shared/components/Gigs/GigApply/index.jsx index 6a7eee3f15..614f96fb97 100644 --- a/src/shared/components/Gigs/GigApply/index.jsx +++ b/src/shared/components/Gigs/GigApply/index.jsx @@ -23,10 +23,11 @@ import BackArrowGig from 'assets/images/back-arrow-gig-apply.svg'; export default function GigApply(props) { const { - job, onFormInputChange, formData, formErrors, onApplyClick, applying, application, + job, onFormInputChange, formData, formErrors, onApplyClick, applying, application, user, } = props; + const retUrl = window.location.href; - return ( + return user ? (
{ job.error || job.enable_job_application_form !== 1 ? ( @@ -150,7 +151,6 @@ export default function GigApply(props) {

TOPCODER INFORMATION

-

If you have a Topcoder profile, please share. Not a Member?

onFormInputChange('handle', val)} errorMsg={formErrors.handle} value={formData.handle} + readonly /> onFormInputChange('tcProfileLink', val)} errorMsg={formErrors.tcProfileLink} - value={formData.handle ? `topcoder.com/members/${formData.handle}` : null} + value={formData.handle ? `https://topcoder.com/members/${formData.handle}` : null} + readonly />
@@ -279,6 +281,18 @@ export default function GigApply(props) { ) } + ) : ( +
+
+
+

You must be a Topcoder member to apply!

+
+ Login +
+

Not a member? Register here.

+
+
+
); } @@ -286,6 +300,7 @@ GigApply.defaultProps = { formErrors: {}, applying: false, application: null, + user: null, }; GigApply.propTypes = { @@ -296,4 +311,5 @@ GigApply.propTypes = { onApplyClick: PT.func.isRequired, applying: PT.bool, application: PT.shape(), + user: PT.shape(), }; diff --git a/src/shared/components/Gigs/GigApply/style.scss b/src/shared/components/Gigs/GigApply/style.scss index b14c0dbdd5..b807f0b8ab 100644 --- a/src/shared/components/Gigs/GigApply/style.scss +++ b/src/shared/components/Gigs/GigApply/style.scss @@ -28,12 +28,25 @@ padding: 0 15px; } - .error { + .wrap .error { display: flex; flex-direction: column; align-items: center; margin-top: 27px; height: 80vh; + + h3 { + text-align: center; + } + + .cta-buttons a.primaryBtn { + margin-bottom: 0 !important; + } + + .regTxt { + font-size: 14px; + margin: 10px 0 0; + } } .checkboxes-row { diff --git a/src/shared/components/Gigs/GigDetails/index.jsx b/src/shared/components/Gigs/GigDetails/index.jsx index 9e714cb4e3..1eca1ab48d 100644 --- a/src/shared/components/Gigs/GigDetails/index.jsx +++ b/src/shared/components/Gigs/GigDetails/index.jsx @@ -26,6 +26,7 @@ import iconLabel2 from 'assets/images/l2.png'; import iconLabel3 from 'assets/images/l3.png'; import SadFace from 'assets/images/sad-face-icon.svg'; import ReferralModal from '../ReferralModal'; +import LoginModal from '../LoginModal'; // Cleanup HTML from style tags // so it won't affect other parts of the UI @@ -43,8 +44,10 @@ export default function GigDetails(props) { job, application, profile, onSendClick, isReferrSucess, formData, formErrors, onFormInputChange, isReferrError, getReferralId, referralId, onReferralDone, } = props; let shareUrl; + let retUrl; if (isomorphy.isClientSide()) { shareUrl = encodeURIComponent(window.location.href); + retUrl = `${window.location.origin}${window.location.pathname}/apply${window.location.search}`; } let skills = getCustomField(job.custom_fields, 'Technologies Required'); if (skills !== 'n/a') skills = skills.split(',').join(', '); @@ -52,6 +55,7 @@ export default function GigDetails(props) { const compens = job.min_annual_salary === job.max_annual_salary ? job.max_annual_salary : `${job.min_annual_salary} - ${job.max_annual_salary} (USD)`; const [isModalOpen, setModalOpen] = useState(false); + const [isLoginModalOpen, setLoginModalOpen] = useState(false); let inputRef; useEffect(() => { @@ -131,7 +135,17 @@ export default function GigDetails(props) {
{ !application || !application.success ? ( - APPLY TO THIS JOB + { + if (isEmpty(profile)) { + e.preventDefault(); + setLoginModalOpen(true); + } + }} + >APPLY TO THIS JOB + ) : null } VIEW OTHER JOBS @@ -179,15 +193,15 @@ export default function GigDetails(props) {
@@ -211,6 +225,9 @@ export default function GigDetails(props) { ) } + { + isLoginModalOpen && setLoginModalOpen(false)} /> + } diff --git a/src/shared/components/Gigs/LoginModal/index.jsx b/src/shared/components/Gigs/LoginModal/index.jsx new file mode 100644 index 0000000000..978cc6bdea --- /dev/null +++ b/src/shared/components/Gigs/LoginModal/index.jsx @@ -0,0 +1,54 @@ +/** + * The modal used for login enforcing + */ + +/* global window */ + +import PT from 'prop-types'; +import React from 'react'; +import { Modal, PrimaryButton } from 'topcoder-react-ui-kit'; +import { config } from 'topcoder-react-utils'; +import tc from 'components/buttons/themed/tc.scss'; +import modalStyle from './modal.scss'; + +/** Themes for buttons + * those overwrite PrimaryButton style to match achieve various styles. + * Should implement pattern of classes. + */ +const buttonThemes = { + tc, +}; + +function LoginModal({ retUrl, onCancel }) { + return ( + +
+

WARNING

+

You must be a Topcoder member to apply!

+
+ { + window.location = `${config.URL.AUTH}/member?retUrl=${encodeURIComponent(retUrl)}`; + }} + theme={{ + button: buttonThemes.tc['primary-green-md'], + }} + > + LOGIN + +
+

Not a member? Register here.

+
+
+ ); +} + +LoginModal.propTypes = { + retUrl: PT.string.isRequired, + onCancel: PT.func.isRequired, +}; + +export default LoginModal; diff --git a/src/shared/components/Gigs/LoginModal/modal.scss b/src/shared/components/Gigs/LoginModal/modal.scss new file mode 100644 index 0000000000..ad4df8efb7 --- /dev/null +++ b/src/shared/components/Gigs/LoginModal/modal.scss @@ -0,0 +1,85 @@ +@import "~styles/mixins"; +@import "~components/Contentful/default"; + +.container { + padding: 0; + width: auto; + max-width: 95vw; + height: auto; + max-height: 95vh; + border-radius: 10px; + display: flex; + flex-direction: column; + justify-content: center; + + @include gui-kit-headers; + @include gui-kit-content; + + .title { + color: #1e94a3; + font-family: BarlowCondensed, sans-serif; + font-size: 34px; + line-height: 38px; + font-weight: 500; + margin: 0; + margin-bottom: 20px; + } + + .loginMsg { + color: #ef476f; + font-size: 24px; + line-height: 36px; + margin-bottom: 40px; + } + + .ctaButtons { + display: flex; + align-content: center; + justify-content: center; + + & > button:first-child { + margin-right: 10px !important; + } + + & > a:first-child { + margin-right: 10px !important; + } + } + + .referrals { + display: flex; + overflow: auto; + + .sucessMsg { + font-size: 24px; + line-height: 36px; + margin-bottom: 40px; + } + + .rightAlign { + justify-content: flex-end; + } + } + + .loginRequired, + .referrSucess { + display: flex; + flex-direction: column; + padding: 100px 80px; + text-align: center; + + @include xs-to-sm { + padding: 50px 40px; + } + + .regTxt { + font-size: 14px; + margin: 10px 0 0; + } + } +} + +.overlay { + background-color: #2a2a2a; + opacity: 0.95; +} diff --git a/src/shared/containers/Gigs/RecruitCRMJobDetails.jsx b/src/shared/containers/Gigs/RecruitCRMJobDetails.jsx index 1580e93aa7..31d1e56f48 100644 --- a/src/shared/containers/Gigs/RecruitCRMJobDetails.jsx +++ b/src/shared/containers/Gigs/RecruitCRMJobDetails.jsx @@ -155,6 +155,7 @@ ${config.URL.BASE}${config.GIGS_PAGES_PATH}/${props.id}`, email: profile.email, firstName: profile.firstName, lastName: profile.lastName, + tcHandle: profile.handle, }), headers: { 'Content-Type': 'application/json',