From 45a4d8f3ce7c40a0c3d8025a09aec188cd319ed0 Mon Sep 17 00:00:00 2001 From: Kiril Kartunov Date: Wed, 16 Sep 2020 08:59:47 +0300 Subject: [PATCH 1/2] Location dropdown final fixes --- .../components/Contentful/AppComponent/index.jsx | 2 +- src/shared/containers/Gigs/RecruitCRMJobs.jsx | 11 ++++++----- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/src/shared/components/Contentful/AppComponent/index.jsx b/src/shared/components/Contentful/AppComponent/index.jsx index 5238ae7638..bc9971fa63 100644 --- a/src/shared/components/Contentful/AppComponent/index.jsx +++ b/src/shared/components/Contentful/AppComponent/index.jsx @@ -32,7 +32,7 @@ export function AppComponentSwitch(appComponent) { ); } if (appComponent.fields.type === 'RecruitCRM-Jobs') { - return ; + return ; } fireErrorMessage('Unsupported app component type from contentful', ''); return null; diff --git a/src/shared/containers/Gigs/RecruitCRMJobs.jsx b/src/shared/containers/Gigs/RecruitCRMJobs.jsx index 46b5b50d6e..59b2973ba8 100644 --- a/src/shared/containers/Gigs/RecruitCRMJobs.jsx +++ b/src/shared/containers/Gigs/RecruitCRMJobs.jsx @@ -21,9 +21,7 @@ const sortByOptions = [ { label: 'Latest Updated Descending', selected: false }, ]; // Locations -const locations = [{ - label: 'Anywhere', selected: true, -}]; +let locations = []; class RecruitCRMJobsContainer extends React.Component { constructor(props) { @@ -33,7 +31,7 @@ class RecruitCRMJobsContainer extends React.Component { term: '', page: 0, sortBy: 'created_on', - location: 'Anywhere', + location: 'Any Location', }; // binds this.onSearch = this.onSearch.bind(this); @@ -127,6 +125,8 @@ class RecruitCRMJobsContainer extends React.Component { if (location === 'Anywhere' || location === 'Any' || location === 'Any Location') return true; return location.toLowerCase() === job.country.toLowerCase(); }); + // sort location dropdown + locations = _.sortBy(locations, ['label']); // Filter by term if (term) { jobsToDisplay = _.filter(jobsToDisplay, (job) => { @@ -178,11 +178,12 @@ class RecruitCRMJobsContainer extends React.Component { RecruitCRMJobsContainer.defaultProps = { jobs: [], + loading: true, }; RecruitCRMJobsContainer.propTypes = { getJobs: PT.func.isRequired, - loading: PT.bool.isRequired, + loading: PT.bool, jobs: PT.arrayOf(PT.shape), }; From 5023f5e958a549c10529dacbf1d6cd4557e0a687 Mon Sep 17 00:00:00 2001 From: Kiril Kartunov Date: Wed, 16 Sep 2020 16:52:03 +0300 Subject: [PATCH 2/2] Implement #4921 --- .../Checkbox/__snapshots__/index.jsx.snap | 3 +- src/assets/images/check-mark.svg | 24 ++++ src/assets/images/l4.png | Bin 1902 -> 0 bytes .../GUIKit/Assets/Styles/Includes/_mixin.scss | 10 ++ .../components/GUIKit/Checkbox/index.jsx | 2 +- .../components/GUIKit/Datepicker/style.scss | 13 +- .../components/GUIKit/Dropdown/style.scss | 6 +- .../components/GUIKit/TextInput/style.scss | 4 +- .../components/GUIKit/Textarea/style.scss | 4 +- src/shared/components/Gigs/GigDetails.jsx | 14 +- src/shared/components/Gigs/style.scss | 19 ++- .../examples/GUIKit/TextInput/index.jsx | 1 + .../SubscribeMailChimpTag/index.jsx | 129 ++++++++++++++++++ .../SubscribeMailChimpTag/style.scss | 57 ++++++++ src/shared/utils/tc.js | 9 ++ 15 files changed, 277 insertions(+), 18 deletions(-) create mode 100644 src/assets/images/check-mark.svg delete mode 100644 src/assets/images/l4.png create mode 100644 src/shared/containers/SubscribeMailChimpTag/index.jsx create mode 100644 src/shared/containers/SubscribeMailChimpTag/style.scss diff --git a/__tests__/shared/components/GUIKit/Checkbox/__snapshots__/index.jsx.snap b/__tests__/shared/components/GUIKit/Checkbox/__snapshots__/index.jsx.snap index 3d56855d05..94a5da518a 100644 --- a/__tests__/shared/components/GUIKit/Checkbox/__snapshots__/index.jsx.snap +++ b/__tests__/shared/components/GUIKit/Checkbox/__snapshots__/index.jsx.snap @@ -15,9 +15,10 @@ exports[`Default render 1`] = ` diff --git a/src/assets/images/check-mark.svg b/src/assets/images/check-mark.svg new file mode 100644 index 0000000000..b4e1f7b3a6 --- /dev/null +++ b/src/assets/images/check-mark.svg @@ -0,0 +1,24 @@ + + + + 21742205-DC86-42C6-AD5E-D5ADCDBB3E3F + Created with sketchtool. + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/assets/images/l4.png b/src/assets/images/l4.png deleted file mode 100644 index 2e066b7f5d093af8097c5aa54899114c3c531acc..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1902 zcmV-!2a))RP)gH1 zG{npw5!}XzGPh`gnL|OtvL$5x*jUM|hy=%CwiPS`IxKDJ`yBtc_qq3Z?)%n(`XtT0 zz4tlie4q34dtc$dTo2#!!tALr*K2Cm8n)b^TabAOtq4;N`4`GD;3#6xs`Au^Utas- z#oCdJ+5GseLoH`U$NCIzH<2}FHUk0knnXarc>(qB#J~TWVGky4wQYCqTzcyN25`si z2WOrenf#hKKI?=zNG9g60+F2)k*pfpR4e-bP2j)+i+iKW{y#W9d0kOg>HVq&mz*2bA zKQCld$H8v7Yv-~(7YuOg&4Y{UR^RK)y+iWwd4L7j;<+IrS>9E>h7rn#auS4JRL4za z?LChy+54sdf4F9FrIV@MK!=4MAvO?)vIG`;tC>f`1C;WnIxg^$P1~3Jc3OZxSa;xh zbNtG%OJfktl=|>C9-s(g==G|+=AMmmR7O*&UA5_TkAv^kBDh_Vc>frB1+9 zFcq8%PQen*uYx7{vwV-KM9=F2u8^le-X%i4V#8D*Vb)Y~{(GU*3V`RV{*$`WnHkeu zv0yon@_#7+_-9U`uHX+wQUY^Y0(Y8$^ZP;@!NJyjbHmWuD8vtZ?1fwFW{-yYka~&IYol|NfEY?YgW%;=ByNe!Dn7)V$!lu7sVpk-X=xo9pDO^*}f^^lfV<{ z6tdpbhawlORB$F3G-m+SV$`kd?F67cW&B~^N$Q%*c@VjGV)^pG9(>ruMQzRIdk=M_ zrnb_%IUM*FwvJG7q)d3vvUWP=%>ZENxigH8*0Dr#fK{)7Xq<0GUd@GL#5eVpbLXWm zbt+d_9^Nnq!#?Qq!}q%|!B^ch7r*xZZX~PN+?YQ|7*gjwmdiIDyxdzy7N;(ewh~YP zAar?AD0#8u3c44x(X*fpfTM@ba(s9a1ZH>5;JO>;0Px5Cr#Ue^QOH{$A2s!$kP!7S zu1ZZg@BDCVGRzyML!N&Ct8VNly|?dV)~tc zDxVY58eH?>C^z-bh;*jU=BZqJD7 zMDcx5oGJqrS6ud2YfT~$t*r^ut(F$0v$K}X-=IB7C4EYerF}%k1mM1nhkk=tZ+x<# zaaZz36ngvSQfrYSJOr{`T`erUJge?wFQ4PN!BKwy{Ak>$y*~BfY_ugRyHfy5_?ba( z<3KOlOLS6T_nwicJ%k5Xv8=rSc;L@(@a%y%GVg`cqd6a}0(?pVlG%%Pn(8YBEfNAQ z81lh@_esnLBU%-N*rdWI^L+&$%=w|GX`XsnBG?SwrvL*3GHI!8DU627JjwL@E|HA; zB{3f?4wgvy*-2T&*?TGEGGL8-&^y3HPR;%D{J|pATiQB)M4Fs(!3jdox!{DQ*8+uF zdiLyi>BF<*j!l`YhE`Sswaa3Gyw-C6|wcem_IZv zK!ESuxA1vO?M72M?J5=PE3HU;^({wn36a6NR0__25mx1kJ+e2|?#&^Vh8zsn#%~>4 z?!-L`T-oeLq6QjP$-lVa(}W#GLt$6@Wj!B%pkF2~7$CsF!!KQOd_?X?_^O#pq5=rn zM-#Bq7DG6Dl_4>{zJ0@GyPDa5Q-FTmx#gv04*9$@yA6@{W_&IL8;{Klq==k#VuwZa zF9-)z<<}2=`kjaWlidpj=+|94hG(8WJ+U6_eIjzD!4)Pl&#b%7 diff --git a/src/shared/components/GUIKit/Assets/Styles/Includes/_mixin.scss b/src/shared/components/GUIKit/Assets/Styles/Includes/_mixin.scss index b8c20ed703..d6d93533d2 100644 --- a/src/shared/components/GUIKit/Assets/Styles/Includes/_mixin.scss +++ b/src/shared/components/GUIKit/Assets/Styles/Includes/_mixin.scss @@ -2,6 +2,7 @@ $gui-kit-gray-30: #aaa; $gui-kit-gray-90: #2a2a2a; $gui-kit-level-2: #0ab88a; +$gui-kit-level-5: #ef476f; @mixin textInputLabel { font-size: 12px; @@ -22,6 +23,7 @@ $gui-kit-level-2: #0ab88a; padding: 15px; margin-bottom: 0; margin-top: 12px; + color: $gui-kit-gray-90; &::-webkit-input-placeholder { /* Edge */ @@ -50,6 +52,12 @@ $gui-kit-level-2: #0ab88a; border: 1px solid $gui-kit-gray-30; box-shadow: none; } + + &:focus-within { + label { + color: #229174; + } + } } @mixin textInputXs { @@ -75,8 +83,10 @@ $gui-kit-level-2: #0ab88a; line-height: 20px; margin-top: 10px; margin-left: 15px; + color: $gui-kit-gray-90; } @mixin errorMessageXs { margin-top: 8px; + color: $gui-kit-gray-90; } diff --git a/src/shared/components/GUIKit/Checkbox/index.jsx b/src/shared/components/GUIKit/Checkbox/index.jsx index 0576ea93c7..be44e9fcde 100644 --- a/src/shared/components/GUIKit/Checkbox/index.jsx +++ b/src/shared/components/GUIKit/Checkbox/index.jsx @@ -5,7 +5,7 @@ */ import React, { useRef, useState } from 'react'; import PT from 'prop-types'; -import IconCheckSolid from 'assets/images/dashboard/ico-checkmark.svg'; +import IconCheckSolid from 'assets/images/check-mark.svg'; import _ from 'lodash'; import './style.scss'; diff --git a/src/shared/components/GUIKit/Datepicker/style.scss b/src/shared/components/GUIKit/Datepicker/style.scss index 8f27b70db5..25a0f7a9e5 100644 --- a/src/shared/components/GUIKit/Datepicker/style.scss +++ b/src/shared/components/GUIKit/Datepicker/style.scss @@ -20,7 +20,7 @@ &.haveError .label, &.haveError.isFocused .label { - color: $tc-level-5; + color: $gui-kit-level-5; } :global { @@ -153,8 +153,17 @@ border-radius: 50%; } + &.CalendarDay__default { + color: #2a2a2a; + } + + &.CalendarDay__blocked_out_of_range { + color: #aaa; + } + &.CalendarDay__selected { background: transparent !important; + color: #fff; div { background: $gui-kit-level-2; @@ -190,7 +199,7 @@ .SingleDatePickerInput { .DateInput { input { - border: 2px solid $tc-level-5; + border: 2px solid $gui-kit-level-5; } } } diff --git a/src/shared/components/GUIKit/Dropdown/style.scss b/src/shared/components/GUIKit/Dropdown/style.scss index 837b2fe5e5..7ca3bf762b 100644 --- a/src/shared/components/GUIKit/Dropdown/style.scss +++ b/src/shared/components/GUIKit/Dropdown/style.scss @@ -43,7 +43,7 @@ &.haveError .label, &.haveError.isFocused .label { - color: $tc-level-5; + color: $gui-kit-level-5; } :global { @@ -80,6 +80,7 @@ height: 22px; line-height: 22px; font-size: 16px; + color: $gui-kit-gray-90 !important; } } @@ -163,7 +164,7 @@ &.haveError { :global { .Select-control { - border: 2px solid $tc-level-5 !important; + border: 2px solid $gui-kit-level-5 !important; } } } @@ -200,6 +201,7 @@ .Select-value { .Select-value-label { font-size: 14px; + color: $gui-kit-gray-90 !important; } } diff --git a/src/shared/components/GUIKit/TextInput/style.scss b/src/shared/components/GUIKit/TextInput/style.scss index b71c2b8184..453370fbb8 100644 --- a/src/shared/components/GUIKit/TextInput/style.scss +++ b/src/shared/components/GUIKit/TextInput/style.scss @@ -51,13 +51,13 @@ input:not([type='checkbox']).haveError + label, input:not([type='checkbox']).haveError:focus + label { - color: $tc-level-5; + color: $gui-kit-level-5; } input:not([type='checkbox']).haveError, input:not([type='checkbox']).haveError:active, input:not([type='checkbox']).haveError:focus, input:not([type='checkbox']).haveError:hover { - border: 2px solid $tc-level-5; + border: 2px solid $gui-kit-level-5; } } diff --git a/src/shared/components/GUIKit/Textarea/style.scss b/src/shared/components/GUIKit/Textarea/style.scss index 57f67b42a6..9346db5130 100644 --- a/src/shared/components/GUIKit/Textarea/style.scss +++ b/src/shared/components/GUIKit/Textarea/style.scss @@ -30,7 +30,7 @@ &.haveError ~ label, &.haveError:focus ~ label { - color: $tc-level-5; + color: $gui-kit-level-5; } } @@ -52,7 +52,7 @@ textarea.haveError:active, textarea.haveError:focus, textarea.haveError:hover { - border: 2px solid $tc-level-5; + border: 2px solid $gui-kit-level-5; } textarea.haveError ~ .labelMask { diff --git a/src/shared/components/Gigs/GigDetails.jsx b/src/shared/components/Gigs/GigDetails.jsx index 0181be5132..df0b06f343 100644 --- a/src/shared/components/Gigs/GigDetails.jsx +++ b/src/shared/components/Gigs/GigDetails.jsx @@ -8,6 +8,7 @@ import PT from 'prop-types'; import { isomorphy, Link, config } from 'topcoder-react-utils'; import ReactHtmlParser from 'react-html-parser'; import { getSalaryType, getCustomField } from 'utils/gigs'; +import SubscribeMailChimpTag from 'containers/SubscribeMailChimpTag'; import './style.scss'; import IconFacebook from 'assets/images/icon-facebook.svg'; import IconTwitter from 'assets/images/icon-twitter.svg'; @@ -21,7 +22,6 @@ import iconSkills from 'assets/images/icon-skills-blue.png'; import iconLabel1 from 'assets/images/l1.png'; import iconLabel2 from 'assets/images/l2.png'; import iconLabel3 from 'assets/images/l3.png'; -import iconLabel4 from 'assets/images/l4.png'; // Cleanup HTML from style tags // so it won't affect other parts of the UI @@ -127,8 +127,12 @@ export default function GigDetails(props) { +
+
SUBSCRIBE TO WEEKLY UPDATES
+

Not ready to apply? Want to stay tuned for any new gigs that may be upcoming? Join our weekly Gig Work list.

+ +
-

Thank you for checking out our latest gig at Topcoder. Gig work through us is simple and effective for those that would like traditional freelance work. To learn more about how Gigs work with us, go here.

At Topcoder, we pride ourselves in bringing our customers the very best candidates to help fill their needs. Want to improve your chances? You can do a few things:

  • @@ -137,14 +141,10 @@ export default function GigDetails(props) {
  • label 2 -
    Subscribe to our Gig notifications email. We’ll send you a weekly update on gigs available so you don’t miss a beat.
    +
    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.
  • label 3 -
    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.
    -
  • -
  • - label 4
    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!
diff --git a/src/shared/components/Gigs/style.scss b/src/shared/components/Gigs/style.scss index 9859cdc418..74c0cf3502 100644 --- a/src/shared/components/Gigs/style.scss +++ b/src/shared/components/Gigs/style.scss @@ -101,6 +101,7 @@ .shareButtons { display: flex; align-items: center; + margin-bottom: 12px; a { margin-right: 5px; @@ -111,11 +112,27 @@ } } + .subscribe-area { + background-image: linear-gradient(135.29deg, #2c95d7 0%, #06d6a0 100%); + border-radius: 10px; + padding: 25px 32px 30px 20px; + + h6 { + margin: 0 0 6px; + color: #fff; + } + + p { + color: #fff; + margin-bottom: 15px; + } + } + .info-area { background-color: #f4f4f4; padding: 20px; border-radius: 10px; - margin-top: 18px; + margin-top: 20px; /* stylelint-disable */ p, diff --git a/src/shared/components/examples/GUIKit/TextInput/index.jsx b/src/shared/components/examples/GUIKit/TextInput/index.jsx index 527108f6f2..d8b932b049 100644 --- a/src/shared/components/examples/GUIKit/TextInput/index.jsx +++ b/src/shared/components/examples/GUIKit/TextInput/index.jsx @@ -40,6 +40,7 @@ function TextInputExample({ size }) { value: 'Adam Morehead', required: true, errorMsg: '', + sectionTitle: 'Required', }, ]; diff --git a/src/shared/containers/SubscribeMailChimpTag/index.jsx b/src/shared/containers/SubscribeMailChimpTag/index.jsx new file mode 100644 index 0000000000..d698bcf8a4 --- /dev/null +++ b/src/shared/containers/SubscribeMailChimpTag/index.jsx @@ -0,0 +1,129 @@ +/** + * Genetic subscribe for MailChimp tags component + */ +import React from 'react'; +import PT from 'prop-types'; +import { isValidEmail } from 'utils/tc'; +import { config } from 'topcoder-react-utils'; +import { Modal } from 'topcoder-react-ui-kit'; +import modalStyle from 'components/NewsletterSignupForMembers/modal.scss'; +import defaulTheme from './style.scss'; + +/* Holds the base URL of Community App endpoints that proxy HTTP request to + * mailchimp APIs. */ +const PROXY_ENDPOINT = `${config.URL.COMMUNITY_APP}/api/mailchimp`; + +class SubscribeMailChimpTagContainer extends React.Component { + constructor(props) { + super(props); + this.state = { + error: '', + subsribed: false, + disabled: true, + inputVal: '', + }; + this.onSubscribeClick = this.onSubscribeClick.bind(this); + this.onInputChange = this.onInputChange.bind(this); + } + + onSubscribeClick() { + const { inputVal } = this.state; + const { listId, tags } = this.props; + const fetchUrl = `${PROXY_ENDPOINT}/${listId}/members/${inputVal}/tags`; + const data = { + email_address: inputVal, + status: 'subscribed', + tags: tags.map(t => ({ name: t, status: 'active' })), + }; + const formData = JSON.stringify(data); + fetch(fetchUrl, { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + body: formData, + }).then(result => result.json()).then((dataResponse) => { + if (dataResponse.status === 204) { + // regist success + return this.setState({ + subsribed: true, + error: '', + }); + } + if (dataResponse.status === 404) { + // new email register it for list and add tags + return fetch(`${PROXY_ENDPOINT}/${listId}/members`, { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + body: JSON.stringify({ + email_address: inputVal, + status: 'subscribed', + tags, + }), + }) + .then(result => result.json()).then(() => { + this.setState({ + subsribed: true, + error: '', + }); + }); + } + return this.setState({ + subsribed: false, + error: `Error ${dataResponse.status} when assign to tags`, + }); + }) + .catch((e) => { + this.setState({ + subsribed: false, + error: e.message, + }); + }); + } + + onInputChange(event) { + this.setState({ + inputVal: event.target.value, + disabled: !isValidEmail(event.target.value), + }); + } + + render() { + const { + disabled, inputVal, error, subsribed, + } = this.state; + return ( +
+ { + subsribed || error ? ( + this.setState({ + subsribed: false, + error: false, + inputVal: '', + disabled: true, + })} + > +
+

{subsribed ? 'Congratulations!' : 'Ops :('}

+

{error || 'You are now subscribed.'}

+
+
+ ) : null + } + + +
+ ); + } +} + +SubscribeMailChimpTagContainer.propTypes = { + listId: PT.string.isRequired, + tags: PT.arrayOf(PT.string).isRequired, +}; + +export default SubscribeMailChimpTagContainer; diff --git a/src/shared/containers/SubscribeMailChimpTag/style.scss b/src/shared/containers/SubscribeMailChimpTag/style.scss new file mode 100644 index 0000000000..4bf7d215ec --- /dev/null +++ b/src/shared/containers/SubscribeMailChimpTag/style.scss @@ -0,0 +1,57 @@ +.wrapper { + display: flex; + + input { + background-color: #fff !important; + border: 1px solid #aaa !important; + border-radius: 6px !important; + margin-right: 8px; + color: #2a2a2a; + font-family: Roboto, sans-serif; + font-size: 14px; + line-height: 22px; + + &::placeholder, + &::-moz-placeholder, + &::-webkit-input-placeholder, + &::-webkit-placeholder { + color: #aaa !important; + font-family: Roboto, sans-serif !important; + font-size: 14px !important; + line-height: 40px !important; + text-transform: none; + } + + &:focus { + box-shadow: none !important; + } + } + + .button { + color: #229174; + font-family: Roboto, sans-serif; + font-size: 14px; + font-weight: bold; + letter-spacing: 0.8px; + line-height: 40px; + background-color: #fff; + border-radius: 20px; + border: none; + text-transform: uppercase; + max-height: 40px; + padding: 0 20px; + cursor: pointer; + outline: none; + + &:disabled { + background-color: #e9e9e9; + color: #fff; + } + + &:hover:not(:disabled) { + color: #229174; + background-color: #fff !important; + box-shadow: 0 1px 5px 0 rgba(0, 0, 0, 0.2); + } + } +} diff --git a/src/shared/utils/tc.js b/src/shared/utils/tc.js index 92c93c9ca6..776729a973 100644 --- a/src/shared/utils/tc.js +++ b/src/shared/utils/tc.js @@ -287,4 +287,13 @@ export function formatDate(date, abbreviate, showDay) { return `${month} ${y}`; } +/** + * Test if a string is valid email + * @param {String} email The string to test + */ +export function isValidEmail(email) { + const pattern = new RegExp(/^(("[\w-\s]+")|([\w-]+(?:\.[\w-]+)*)|("[\w-\s]+")([\w-]+(?:\.[\w-]+)*))(@((?:[\w-]+\.)*\w[\w-]{0,66})\.([a-z]{2,6}(?:\.[a-z]{2})?)$)|(@\[?((25[0-5]\.|2[0-4][0-9]\.|1[0-9]{2}\.|[0-9]{1,2}\.))((25[0-5]|2[0-4][0-9]|1[0-9]{2}|[0-9]{1,2})\.){2}(25[0-5]|2[0-4][0-9]|1[0-9]{2}|[0-9]{1,2})\]?$)/i); + return pattern.test(email); +} + export default undefined;