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) {
- Make sure your Topcoder profile says it all. Fill out your profile to the best of your ability. Your skills, your location, your devices, etc, all help you improve your chances of being selected for a gig.
+ Make sure your Topcoder profile says it all. Fill out your profile to the best of your ability. Your skills, your location, your devices, etc, all help you improve your chances of being selected for a gig.
- Let us know you’re here! Check in on our
Gig Work forum and tell us you’re looking for a gig. It’s great visibility for the Gig team.
+ Let us know you’re here! Check in on our
Gig Work forum and tell us you’re looking for a gig. It’s great visibility for the Gig team.
- Check out our Topcoder challenges and participate. Challenges showing your technology skills make you a “qualified” candidate so we know you’re good. The proof is in the pudding!
+ Check out our Topcoder challenges and participate. Challenges showing your technology skills make you a “qualified” candidate so we know you’re good. The proof is in the pudding!
@@ -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',