diff --git a/src/shared/components/Settings/CofirmationModal/index.jsx b/src/shared/components/Settings/CofirmationModal/index.jsx index 6bf41b5347..d97fd8164e 100644 --- a/src/shared/components/Settings/CofirmationModal/index.jsx +++ b/src/shared/components/Settings/CofirmationModal/index.jsx @@ -4,15 +4,14 @@ import { Modal, PrimaryButton, GhostButton } from 'topcoder-react-ui-kit'; import modal from './styles.scss'; export default function ConfirmationModal(props) { - const { onConfirm, onCancel } = props; + const { onConfirm, onCancel, name } = props; return (
-
Heads Up!
+
HEADS UP!
- Are you sure you want to delete? This action can't be undone - later. + Are you sure you want to delete `{name}`? This action can't be undone.
@@ -31,4 +30,5 @@ export default function ConfirmationModal(props) { ConfirmationModal.propTypes = { onConfirm: PT.func.isRequired, onCancel: PT.func.isRequired, + name: PT.string.isRequired, }; diff --git a/src/shared/components/Settings/ErrorMessage/index.jsx b/src/shared/components/Settings/ErrorMessage/index.jsx new file mode 100644 index 0000000000..a26d8d81c4 --- /dev/null +++ b/src/shared/components/Settings/ErrorMessage/index.jsx @@ -0,0 +1,30 @@ +import React from 'react'; +import PropTypes from 'prop-types'; + +import './styles.scss'; + +const marginTop = (value) => { + if (value) { + return 'active-margin'; + } + + return 'active'; +}; + +const ErrorMessage = ({ invalid, message, addMargin }) => ( + + {message} + +); + +ErrorMessage.defaultProps = { + addMargin: false, +}; + +ErrorMessage.propTypes = { + invalid: PropTypes.bool.isRequired, + message: PropTypes.string.isRequired, + addMargin: PropTypes.bool, +}; + +export default ErrorMessage; diff --git a/src/shared/components/Settings/ErrorMessage/styles.scss b/src/shared/components/Settings/ErrorMessage/styles.scss new file mode 100644 index 0000000000..1e629638b2 --- /dev/null +++ b/src/shared/components/Settings/ErrorMessage/styles.scss @@ -0,0 +1,27 @@ +@import "../style"; + +.error-message { + display: none; + + &.active { + @include roboto-medium; + + display: inline; + border-radius: 5px; + background-color: $tc-red-10; + color: $tc-red-110; + font-size: 15px; + margin: 0 auto 0 0; + } + + &.active-margin { + @include roboto-medium; + + display: inline; + border-radius: 5px; + background-color: $tc-red-10; + color: $tc-red-110; + font-size: 15px; + margin: 10px auto 0 0; + } +} diff --git a/src/shared/components/Settings/Profile/BasicInfo/index.jsx b/src/shared/components/Settings/Profile/BasicInfo/index.jsx index e9741c941d..c1df0e3398 100644 --- a/src/shared/components/Settings/Profile/BasicInfo/index.jsx +++ b/src/shared/components/Settings/Profile/BasicInfo/index.jsx @@ -15,12 +15,14 @@ import { PrimaryButton } from 'topcoder-react-ui-kit'; import ConsentComponent from 'components/Settings/ConsentComponent'; import Select from 'components/Select'; import DatePicker from 'components/challenge-listing/Filters/DatePicker'; +import ErrorMessage from 'components/Settings/ErrorMessage'; import ImageInput from '../ImageInput'; import Track from './Track'; import DefaultImageInput from './ImageInput'; import dropdowns from './dropdowns.json'; import tracks from './tracks'; + import './styles.scss'; export default class BasicInfo extends ConsentComponent { @@ -41,7 +43,6 @@ export default class BasicInfo extends ConsentComponent { this.state = { inputChanged: false, formInvalid: false, - errorMessage: '', basicInfoTrait: this.loadBasicInfoTraits(userTraits), personalizationTrait: this.loadPersonalizationTrait(userTraits), newBasicInfo: { @@ -98,46 +99,26 @@ export default class BasicInfo extends ConsentComponent { onCheckFormValue(newBasicInfo) { let invalid = false; - let errorMessage = ''; - let dateError = ''; - let birthDateInvalid = false; - const invalidFields = []; if (!_.trim(newBasicInfo.firstName).length) { - invalidFields.push('First Name'); invalid = true; } if (!_.trim(newBasicInfo.lastName).length) { - invalidFields.push('Last Name'); invalid = true; } if (!_.trim(newBasicInfo.country).length) { - invalidFields.push('Country'); invalid = true; } - if (invalidFields.length > 0) { - errorMessage += invalidFields.join(', '); - errorMessage += ' cannot be empty'; - } - if (_.trim(newBasicInfo.birthDate).length > 0) { if (!moment().isAfter(newBasicInfo.birthDate)) { - dateError = 'You must enter a valid date for Birth Date'; - birthDateInvalid = true; + invalid = true; } } - if (errorMessage.length > 0) { - errorMessage = `${errorMessage}. ${dateError}`; - } else if (dateError.length > 0) { - errorMessage = dateError; - invalid = birthDateInvalid; - } - - this.setState({ errorMessage, formInvalid: invalid }); + this.setState({ formInvalid: invalid }); return invalid; } @@ -170,7 +151,7 @@ export default class BasicInfo extends ConsentComponent { */ onHandleSaveBasicInfo(e) { e.preventDefault(); - this.setState({ isSaving: true }); + this.setState({ isSaving: true, inputChange: true }); const { newBasicInfo } = this.state; if (this.onCheckFormValue(newBasicInfo)) { this.setState({ isSaving: false }); @@ -465,8 +446,7 @@ export default class BasicInfo extends ConsentComponent { render() { const { newBasicInfo, - formInvalid, - errorMessage, + inputChanged, } = this.state; const canModifyTrait = !this.props.traitRequestCount; @@ -507,6 +487,7 @@ export default class BasicInfo extends ConsentComponent {
* Required +
@@ -519,6 +500,7 @@ export default class BasicInfo extends ConsentComponent {
* Required +
@@ -619,6 +601,7 @@ export default class BasicInfo extends ConsentComponent { disabled={!canModifyTrait} clearable={false} /> +
@@ -745,6 +728,7 @@ export default class BasicInfo extends ConsentComponent { +
+
@@ -826,6 +811,7 @@ export default class BasicInfo extends ConsentComponent { clearable={false} disabled={!canModifyTrait} /> +
@@ -931,9 +917,6 @@ export default class BasicInfo extends ConsentComponent { }
-
- {errorMessage} -
- { `${education.schoolCollegeName} ${education.type}` } + { `${education.schoolCollegeName}` }
{ hasSecondLine() && ( diff --git a/src/shared/components/Settings/Profile/Education/dropdowns.json b/src/shared/components/Settings/Profile/Education/dropdowns.json deleted file mode 100644 index 3821590f7a..0000000000 --- a/src/shared/components/Settings/Profile/Education/dropdowns.json +++ /dev/null @@ -1,12 +0,0 @@ -{ - "type": [ - { - "key": "type", - "name": "Secondary School" - }, - { - "key": "type", - "name": "University" - } - ] -} \ No newline at end of file diff --git a/src/shared/components/Settings/Profile/Education/index.jsx b/src/shared/components/Settings/Profile/Education/index.jsx index 35d94b44ef..a8bdc221e6 100644 --- a/src/shared/components/Settings/Profile/Education/index.jsx +++ b/src/shared/components/Settings/Profile/Education/index.jsx @@ -10,11 +10,10 @@ import React from 'react'; import PT from 'prop-types'; import _ from 'lodash'; import moment from 'moment'; -import Select from 'components/Select'; import ConsentComponent from 'components/Settings/ConsentComponent'; import { PrimaryButton } from 'topcoder-react-ui-kit'; import DatePicker from 'components/challenge-listing/Filters/DatePicker'; -import dropdowns from './dropdowns.json'; +import ErrorMessage from 'components/Settings/ErrorMessage'; import ConfirmationModal from '../../CofirmationModal'; import EducationList from './List'; @@ -40,17 +39,16 @@ export default class Education extends ConsentComponent { const { userTraits } = props; this.state = { formInvalid: false, - errorMessage: '', educationTrait: this.loadEducationTrait(userTraits), personalizationTrait: this.loadPersonalizationTrait(userTraits), newEducation: { - type: '', schoolCollegeName: '', major: '', timePeriodFrom: '', timePeriodTo: '', graduated: false, }, + inputChanged: false, isMobileView: false, screenSM: 767, isEdit: false, @@ -71,9 +69,8 @@ export default class Education extends ConsentComponent { educationTrait, personalizationTrait, formInvalid: false, - errorMessage: '', + inputChanged: false, newEducation: { - type: '', schoolCollegeName: '', major: '', timePeriodFrom: '', @@ -94,33 +91,27 @@ export default class Education extends ConsentComponent { */ onCheckFormValue(newEducation) { let invalid = false; - let dateInvalid = false; - let errorMessage = ''; - let dateCount = 0; - let dateError = ''; let haveDate = false; - - if (!_.trim(newEducation.type).length) { - errorMessage += 'Type, '; - invalid = true; - } + const currentDate = new Date().setHours(0, 0, 0, 0); if (!_.trim(newEducation.schoolCollegeName).length) { - errorMessage += 'Name of College or University, '; invalid = true; } - if (errorMessage.length > 0) { - errorMessage += ' cannot be empty'; - } if (!_.isEmpty(newEducation.timePeriodFrom) && !_.isEmpty(newEducation.timePeriodTo)) { - const fromDate = new Date(newEducation.timePeriodFrom).getTime(); - const toDate = new Date(newEducation.timePeriodTo).getTime(); + const fromDate = new Date(newEducation.timePeriodFrom).setHours(0, 0, 0, 0); + const toDate = new Date(newEducation.timePeriodTo).setHours(0, 0, 0, 0); if (fromDate > toDate) { - dateError += 'From Date value should be smaller than To Date value. '; - dateInvalid = true; + haveDate = true; + } + + if (fromDate > currentDate) { + haveDate = true; + } + + if ((toDate > currentDate) && newEducation.graduated) { haveDate = true; } } @@ -128,30 +119,75 @@ export default class Education extends ConsentComponent { if (!haveDate && !(_.isEmpty(newEducation.timePeriodFrom) && _.isEmpty(newEducation.timePeriodTo))) { if (!_.trim(newEducation.timePeriodFrom).length) { - dateError += 'From Date, '; - dateInvalid = true; - dateCount += 1; + invalid = true; } if (!_.trim(newEducation.timePeriodTo).length) { - dateError += 'To Date, '; - dateInvalid = true; - dateCount += 1; + invalid = true; } - if (dateError.length > 0) { - dateError = `The ${dateError} ${dateCount > 1 ? 'are' : 'is'} incomplete or ${dateCount > 1 ? 'have' : 'has'} an invalid date.`; + } + + + this.setState({ formInvalid: invalid }); + return invalid; + } + + onCheckStartDate() { + const { newEducation } = this.state; + const currentDate = new Date().setHours(0, 0, 0, 0); + const result = { + invalid: false, + message: '', + }; + + if (!_.isEmpty(newEducation.timePeriodFrom) && !_.isEmpty(newEducation.timePeriodTo)) { + let fromDate = new Date(newEducation.timePeriodFrom); + fromDate = fromDate.setHours(0, 0, 0, 0); + let toDate = new Date(newEducation.timePeriodTo); + toDate = toDate.setHours(0, 0, 0, 0); + + if (fromDate > toDate) { + result.invalid = true; + result.message = 'Start Date should be before End Date'; + } + + if (fromDate > currentDate) { + result.invalid = true; + result.message = result.message.length > 0 ? `${result.message} and in past or current` : 'Start Date should be in past or current'; } } - if (errorMessage.length > 0) { - errorMessage = `${errorMessage}. \n${dateError}`; - } else if (dateError.length > 0) { - errorMessage = dateError; - invalid = dateInvalid; + if (_.isEmpty(newEducation.timePeriodFrom) && !_.isEmpty(newEducation.timePeriodTo)) { + result.invalid = true; + result.message = 'Due to End Date has date, Start Date should be input'; } - this.setState({ errorMessage, formInvalid: invalid }); - return invalid; + return result; + } + + onCheckEndDate() { + const { newEducation } = this.state; + const currentDate = new Date().setHours(0, 0, 0, 0); + const result = { + invalid: false, + message: '', + }; + + if (!_.isEmpty(newEducation.timePeriodFrom) && !_.isEmpty(newEducation.timePeriodTo)) { + const toDate = new Date(newEducation.timePeriodTo).setHours(0, 0, 0, 0); + + if ((toDate > currentDate) && newEducation.graduated) { + result.invalid = true; + result.message = 'End Date (or expected) should be in past or current'; + } + } + + if (!_.isEmpty(newEducation.timePeriodFrom) && _.isEmpty(newEducation.timePeriodTo)) { + result.invalid = true; + result.message = 'Due to Start Date has date, End Date should be input'; + } + + return result; } onHandleDeleteEducation(indexNo) { @@ -166,7 +202,7 @@ export default class Education extends ConsentComponent { const { newEducation: oldEducation } = this.state; const newEducation = { ...oldEducation }; newEducation[timePeriod] = date; - this.setState({ newEducation }); + this.setState({ newEducation, inputChanged: true }); } } @@ -198,6 +234,7 @@ export default class Education extends ConsentComponent { this.setState({ showConfirmation: false, indexNo: null, + inputChanged: false, }); } @@ -209,7 +246,6 @@ export default class Education extends ConsentComponent { const { educationTrait } = this.state; this.setState({ newEducation: { - type: educationTrait.traits.data[indexNo].type, schoolCollegeName: educationTrait.traits.data[indexNo].schoolCollegeName, major: _.isEmpty(educationTrait.traits.data[indexNo].major) ? '' : educationTrait.traits.data[indexNo].major, timePeriodFrom: _.isEmpty(educationTrait.traits.data[indexNo].timePeriodFrom) ? '' : educationTrait.traits.data[indexNo].timePeriodFrom, @@ -264,19 +300,13 @@ export default class Education extends ConsentComponent { newEducationTrait.traits.data.splice(indexNo, 1); } newEducationTrait.traits.data.push(education); - this.setState({ educationTrait: newEducationTrait }); updateUserTrait(handle, 'education', newEducationTrait.traits.data, tokenV3); } else { const newEducations = []; newEducations.push(education); - const traits = { - data: newEducations, - }; - this.setState({ educationTrait: { traits } }); addUserTrait(handle, 'education', newEducations, tokenV3); } const empty = { - type: '', schoolCollegeName: '', major: '', timePeriodFrom: '', @@ -287,6 +317,7 @@ export default class Education extends ConsentComponent { newEducation: empty, isEdit: false, indexNo: null, + inputChanged: false, }); // save personalization if (_.isEmpty(personalizationTrait)) { @@ -308,8 +339,16 @@ export default class Education extends ConsentComponent { onUpdateInput(e) { const { newEducation: oldEducation } = this.state; const newEducation = { ...oldEducation }; - newEducation[e.target.name] = e.target.type !== 'checkbox' ? e.target.value : e.target.checked; - this.setState({ newEducation }); + if (e.target.type !== 'checkbox') { + newEducation[e.target.name] = e.target.value; + } else { + newEducation[e.target.name] = e.target.checked; + if (e.target.checked) { + newEducation.timePeriodTo = ''; + } + } + + this.setState({ newEducation, inputChanged: true }); } /** @@ -321,7 +360,7 @@ export default class Education extends ConsentComponent { const { newEducation: oldEducation } = this.state; const newEducation = { ...oldEducation }; newEducation[option.key] = option.name; - this.setState({ newEducation }); + this.setState({ newEducation, inputChanged: true }); } } @@ -332,6 +371,7 @@ export default class Education extends ConsentComponent { onHandleAddEducation(e) { e.preventDefault(); const { newEducation } = this.state; + this.setState({ inputChanged: true }); if (this.onCheckFormValue(newEducation)) { return; } @@ -368,9 +408,9 @@ export default class Education extends ConsentComponent { if (isEdit) { this.setState({ isEdit: false, + inputChanged: false, indexNo: null, newEducation: { - type: '', schoolCollegeName: '', major: '', timePeriodFrom: '', @@ -397,7 +437,7 @@ export default class Education extends ConsentComponent { const containerStyle = currentTab === tabs.EDUCATION ? '' : 'hide'; const educationItems = educationTrait.traits ? educationTrait.traits.data.slice() : []; - const { newEducation, formInvalid, errorMessage } = this.state; + const { newEducation, inputChanged } = this.state; return ( @@ -413,13 +453,11 @@ export default class Education extends ConsentComponent { showConfirmation: false, indexNo: null, })} + name={educationTrait.traits.data[indexNo].schoolCollegeName} /> ) }
-
- { errorMessage } -

Education

@@ -444,27 +482,6 @@ export default class Education extends ConsentComponent {
-
-
- -
-
- * Required - +
@@ -491,7 +509,7 @@ export default class Education extends ConsentComponent {
@@ -505,24 +523,46 @@ export default class Education extends ConsentComponent { onDateChange={date => this.onUpdateDate(date, 'timePeriodFrom')} placeholder="dd/mm/yyyy" /> +
- this.onUpdateDate(date, 'timePeriodTo')} - placeholder="dd/mm/yyyy" + { + newEducation.graduated ? ( + this.onUpdateDate(date, 'timePeriodTo')} + placeholder="dd/mm/yyyy" + /> + ) : ( + this.onUpdateDate(date, 'timePeriodTo')} + placeholder="dd/mm/yyyy" + allowFutureYear + /> + ) + } +
@@ -589,23 +629,6 @@ export default class Education extends ConsentComponent {

-
- - +