From ec9cbfdd703bcae8172d88dd2ae9a2f2aa54a5e8 Mon Sep 17 00:00:00 2001 From: gets0ul Date: Fri, 16 Oct 2020 20:50:44 +0700 Subject: [PATCH 01/42] fix: #5082 blank page on clicking challenges tab from stats history page --- src/shared/components/ChallengeTile/index.jsx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/shared/components/ChallengeTile/index.jsx b/src/shared/components/ChallengeTile/index.jsx index c1611df3dd..23810f1ed6 100644 --- a/src/shared/components/ChallengeTile/index.jsx +++ b/src/shared/components/ChallengeTile/index.jsx @@ -2,6 +2,7 @@ * Challenge tile. */ /* eslint-env browser */ +import _ from 'lodash'; import React from 'react'; import PT from 'prop-types'; import { Link } from 'react-router-dom'; @@ -315,7 +316,7 @@ class ChallengeTile extends React.Component { Role:   - { listRoles(challenge.userDetails.roles) } + { listRoles(_.get(challenge, 'userDetails.roles')) } ) } From 3c4498a8ddc5eddde9d39ebde45d2f63660b609e Mon Sep 17 00:00:00 2001 From: gets0ul Date: Mon, 19 Oct 2020 23:19:15 +0700 Subject: [PATCH 02/42] fix: hide roles section if challenge does not have roles data --- src/shared/components/ChallengeTile/index.jsx | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/src/shared/components/ChallengeTile/index.jsx b/src/shared/components/ChallengeTile/index.jsx index 23810f1ed6..0dbc7b7a22 100644 --- a/src/shared/components/ChallengeTile/index.jsx +++ b/src/shared/components/ChallengeTile/index.jsx @@ -75,6 +75,7 @@ class ChallengeTile extends React.Component { } = this.props; const { track, type } = challenge; + const roles = _.get(challenge, 'userDetails.roles'); const outStyleName = `challenge tile-view ${track.replace(' ', '-').toLowerCase()}`; const extraStyle = { @@ -308,19 +309,22 @@ class ChallengeTile extends React.Component { ) } -

- { track !== COMPETITION_TRACKS.DS + { !_.isEmpty(roles) + && ( +

+ { track !== COMPETITION_TRACKS.DS && ( Role:   - { listRoles(_.get(challenge, 'userDetails.roles')) } + { listRoles(roles) } ) } -

+

+ ) } From 38957f2201881eaf9ab67c6d30a6a57d37b3fe38 Mon Sep 17 00:00:00 2001 From: gets0ul Date: Mon, 19 Oct 2020 23:28:08 +0700 Subject: [PATCH 03/42] fix: test snapshot --- .../components/ChallengeTile/__snapshots__/index.jsx.snap | 3 --- 1 file changed, 3 deletions(-) diff --git a/__tests__/shared/components/ChallengeTile/__snapshots__/index.jsx.snap b/__tests__/shared/components/ChallengeTile/__snapshots__/index.jsx.snap index c733309a43..9fe1f038fa 100644 --- a/__tests__/shared/components/ChallengeTile/__snapshots__/index.jsx.snap +++ b/__tests__/shared/components/ChallengeTile/__snapshots__/index.jsx.snap @@ -241,9 +241,6 @@ exports[`renders marathon 1`] = `

-

From b455299b63ad278a3e3cef018fddc4bdf98dd074 Mon Sep 17 00:00:00 2001 From: gets0ul Date: Tue, 20 Oct 2020 01:30:38 +0700 Subject: [PATCH 04/42] fix: reset review opportunity filter --- .../challenge-listing/Filters/FiltersPanel/index.jsx | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/shared/components/challenge-listing/Filters/FiltersPanel/index.jsx b/src/shared/components/challenge-listing/Filters/FiltersPanel/index.jsx index ed61187a59..0d1288115f 100644 --- a/src/shared/components/challenge-listing/Filters/FiltersPanel/index.jsx +++ b/src/shared/components/challenge-listing/Filters/FiltersPanel/index.jsx @@ -328,9 +328,10 @@ export default function FiltersPanel({ autoBlur clearable={false} id="review-type-select" - onChange={ - value => setFilterState(Filter.setReviewOpportunityType(filterState, value)) - } + onChange={(value) => { + const reviewOpportunityType = value === 0 ? undefined : value; + setFilterState({ ..._.clone(filterState), reviewOpportunityType }); + }} options={[ { label: 'All', value: 0 }, // 0 value deactivates above filter ...Object.entries(REVIEW_OPPORTUNITY_TYPES) @@ -434,6 +435,7 @@ export default function FiltersPanel({ endDateStart: null, startDateEnd: null, status: 'All', + reviewOpportunityType: undefined, }); selectCommunity(defaultCommunityId); setSearchText(''); From 3aecb56dd0e7c7d21c81f44334246a847e492146 Mon Sep 17 00:00:00 2001 From: gets0ul Date: Tue, 20 Oct 2020 01:39:57 +0700 Subject: [PATCH 05/42] fix: remove unused code --- .../components/challenge-listing/Filters/FiltersPanel/index.jsx | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/shared/components/challenge-listing/Filters/FiltersPanel/index.jsx b/src/shared/components/challenge-listing/Filters/FiltersPanel/index.jsx index 0d1288115f..07d9f4d206 100644 --- a/src/shared/components/challenge-listing/Filters/FiltersPanel/index.jsx +++ b/src/shared/components/challenge-listing/Filters/FiltersPanel/index.jsx @@ -22,7 +22,6 @@ /* eslint-disable jsx-a11y/label-has-for */ import _ from 'lodash'; -import { challenge as challengeUtils } from 'topcoder-react-lib'; import React from 'react'; import PT from 'prop-types'; import Select from 'components/Select'; @@ -38,7 +37,6 @@ import DateRangePicker from '../DateRangePicker'; import style from './style.scss'; import UiSimpleRemove from '../../Icons/ui-simple-remove.svg'; -const Filter = challengeUtils.filter; export default function FiltersPanel({ communityFilters, From 40040323b875f1a3118f553ff9507ce84cacc4f4 Mon Sep 17 00:00:00 2001 From: gets0ul Date: Tue, 20 Oct 2020 04:58:31 +0700 Subject: [PATCH 06/42] fix: toggle checkpoint feedback --- src/shared/actions/page/challenge-details.js | 2 +- .../challenge-detail/Checkpoints/index.jsx | 4 ++-- .../containers/challenge-detail/index.jsx | 14 ++++++++++++-- src/shared/reducers/page/challenge-details.js | 17 +++-------------- 4 files changed, 18 insertions(+), 19 deletions(-) diff --git a/src/shared/actions/page/challenge-details.js b/src/shared/actions/page/challenge-details.js index 20faf4c0d8..3312b33bb7 100644 --- a/src/shared/actions/page/challenge-details.js +++ b/src/shared/actions/page/challenge-details.js @@ -59,7 +59,7 @@ function setSpecsTabState(state) { * @param {Boolean} open * @return {Object} */ -function toggleCheckpointFeedback(id, open) { +function toggleCheckpointFeedback(id, open = false) { return { id, open }; } diff --git a/src/shared/components/challenge-detail/Checkpoints/index.jsx b/src/shared/components/challenge-detail/Checkpoints/index.jsx index cc7f5a62c8..37de8dc40a 100644 --- a/src/shared/components/challenge-detail/Checkpoints/index.jsx +++ b/src/shared/components/challenge-detail/Checkpoints/index.jsx @@ -27,7 +27,7 @@ function Checkpoints(props) { document .getElementsByClassName(style['challenge-checkpoint-winners'])[index] .scrollIntoView(true); - toggleCheckpointFeedback(index, true); + toggleCheckpointFeedback(item.submissionId, true); }} type="button" > @@ -52,7 +52,7 @@ function Checkpoints(props) { { /* TODO: At the moment we just fetch downloads from the legacy Topcoder Studio API, and we don't need any JS code to this. @@ -136,4 +149,5 @@ Submission.propTypes = { onShowDetails: PT.func, status: PT.string.isRequired, allowDelete: PT.bool.isRequired, + auth: PT.shape().isRequired, }; diff --git a/src/shared/components/SubmissionManagement/SubmissionManagement/index.jsx b/src/shared/components/SubmissionManagement/SubmissionManagement/index.jsx index 0692d888ff..37931a9530 100644 --- a/src/shared/components/SubmissionManagement/SubmissionManagement/index.jsx +++ b/src/shared/components/SubmissionManagement/SubmissionManagement/index.jsx @@ -26,6 +26,7 @@ import style from './styles.scss'; export default function SubmissionManagement(props) { const { + auth, challenge, submissions, loadingSubmissions, @@ -156,6 +157,7 @@ export default function SubmissionManagement(props) { {!loadingSubmissions && ( Date: Tue, 20 Oct 2020 19:57:08 +0700 Subject: [PATCH 10/42] fix: refactor code --- .../SubmissionManagement/Submission/index.jsx | 23 ++++--------------- .../SubmissionManagement/index.jsx | 3 --- .../SubmissionsTable/index.jsx | 3 --- .../containers/SubmissionManagement/index.jsx | 18 +++++++++++++-- 4 files changed, 20 insertions(+), 27 deletions(-) diff --git a/src/shared/components/SubmissionManagement/Submission/index.jsx b/src/shared/components/SubmissionManagement/Submission/index.jsx index 85869d1e2e..7f3ef1114f 100644 --- a/src/shared/components/SubmissionManagement/Submission/index.jsx +++ b/src/shared/components/SubmissionManagement/Submission/index.jsx @@ -14,7 +14,7 @@ import _ from 'lodash'; import moment from 'moment'; import React from 'react'; -import { services } from 'topcoder-react-lib'; +import { config } from 'topcoder-react-utils'; import { COMPETITION_TRACKS, CHALLENGE_STATUS } from 'utils/tc'; import PT from 'prop-types'; @@ -26,20 +26,19 @@ import ScreeningStatus from '../ScreeningStatus'; import './styles.scss'; -const { getService } = services.submissions; - export default function Submission(props) { const { - auth, submissionObject, showScreeningDetails, track, + onDownload, onDelete, onShowDetails, status, allowDelete, } = props; const formatDate = date => moment(+new Date(date)).format('MMM DD, YYYY hh:mm A'); + const onDownloadSubmission = onDownload.bind(1, submissionObject.id); return ( @@ -70,20 +69,7 @@ export default function Submission(props) {

); @@ -292,8 +329,10 @@ export default class EditorWrapper extends React.Component { EditorWrapper.defaultProps = { connector: new Connector(), id: null, - initialContent: null, + initialContent: '', theme: {}, + placeholder: '', + maxLength: -1, }; EditorWrapper.propTypes = { @@ -301,4 +340,6 @@ EditorWrapper.propTypes = { id: PT.string, initialContent: PT.string, theme: PT.shape(), + placeholder: PT.string, + maxLength: PT.number, }; diff --git a/src/shared/components/Settings/Profile/Work/List/Item/_mixin.scss b/src/shared/components/Settings/Profile/Work/List/Item/_mixin.scss new file mode 100644 index 0000000000..0bd71a8ed4 --- /dev/null +++ b/src/shared/components/Settings/Profile/Work/List/Item/_mixin.scss @@ -0,0 +1,42 @@ +@import "~styles/mixins"; + +@mixin html-content-style() { + @include roboto-regular; + + font-size: 15px; + color: #262628; + + em { + font-style: italic; + } + + p { + margin: 6px 0 10px; + line-height: 20px; + } + + ul { + list-style-type: disc; + } + + ol { + list-style-type: decimal; + } + + ul, + ol { + margin: 0 0 0 25px; + + li { + min-height: auto; + line-height: 20px; + padding: 0 0 0 12px; + + &:last-child { + border: 0; + } + } + } + + @content; +} diff --git a/src/shared/components/Settings/Profile/Work/List/Item/index.jsx b/src/shared/components/Settings/Profile/Work/List/Item/index.jsx index a745a2fe6d..1c76633130 100644 --- a/src/shared/components/Settings/Profile/Work/List/Item/index.jsx +++ b/src/shared/components/Settings/Profile/Work/List/Item/index.jsx @@ -7,6 +7,7 @@ import PT from 'prop-types'; import ReactSVG from 'react-svg'; import moment from 'moment'; import { isomorphy } from 'topcoder-react-utils'; +import { isEmpty as isDateEmpty } from 'utils/settings'; import './styles.scss'; @@ -23,8 +24,14 @@ export default function Item(props) { onEditItem, } = props; + const { + jobDescription, + technologies, + jobAchievements, + } = work; + const hasSecondLine = () => { - if (_.isEmpty(work.timePeriodFrom) && _.isEmpty(work.timePeriodTo) + if (isDateEmpty(work.timePeriodFrom) && isDateEmpty(work.timePeriodTo) && _.isEmpty(work.position) && !work.working) { return false; } @@ -45,11 +52,11 @@ export default function Item(props) { { `${work.company}${_.isEmpty(work.industry) ? '' : ` | ${work.industry}`}${_.isEmpty(work.cityTown) ? '' : ` | ${work.cityTown}`}` }
- { `${!_.isEmpty(work.timePeriodFrom) ? moment(work.timePeriodFrom).format('YYYY') : ''}${!_.isEmpty(work.timePeriodTo) ? ` - ${moment(work.timePeriodTo).format('YYYY')}` : ` ${current}`} ${!_.isEmpty(work.position) && (!_.isEmpty(work.timePeriodTo) || !_.isEmpty(work.timePeriodFrom)) ? ' | ' : ''}${!_.isEmpty(work.position) ? `${work.position}` : ''}` } + { `${!isDateEmpty(work.timePeriodFrom) ? moment(work.timePeriodFrom).format('YYYY') : ''}${!isDateEmpty(work.timePeriodTo) ? ` - ${moment(work.timePeriodTo).format('YYYY')}` : ` ${current}`} ${!_.isEmpty(work.position) && (!isDateEmpty(work.timePeriodTo) || !isDateEmpty(work.timePeriodFrom)) ? ' | ' : ''}${!_.isEmpty(work.position) ? `${work.position}` : ''}` }

- {`${!_.isEmpty(work.timePeriodFrom) ? moment(work.timePeriodFrom).format('YYYY') : ''}${!_.isEmpty(work.timePeriodTo) ? ` - ${moment(work.timePeriodTo).format('YYYY')}` : ` ${current}`}`} + {`${!isDateEmpty(work.timePeriodFrom) ? moment(work.timePeriodFrom).format('YYYY') : ''}${!isDateEmpty(work.timePeriodTo) ? ` - ${moment(work.timePeriodTo).format('YYYY')}` : ` ${current}`}`}

{`${!_.isEmpty(work.position) ? `${work.position}` : ''}`} @@ -83,6 +90,30 @@ export default function Item(props) {

+ { !_.isEmpty(jobDescription) && jobDescription !== '


' + && ( +
+

Job Description

+
+
+ ) + } + { !_.isEmpty(technologies) + && ( +
+

Technologies

+
{technologies.map(item => item.name).join(', ')}
+
+ ) + } + { !_.isEmpty(jobAchievements) && jobAchievements !== '


' + && ( +
+

Outputs and Achievements Within the Role

+
+
+ ) + }
); } diff --git a/src/shared/components/Settings/Profile/Work/List/Item/styles.scss b/src/shared/components/Settings/Profile/Work/List/Item/styles.scss index d419082362..5b2ac9f208 100644 --- a/src/shared/components/Settings/Profile/Work/List/Item/styles.scss +++ b/src/shared/components/Settings/Profile/Work/List/Item/styles.scss @@ -1,8 +1,10 @@ @import "../../../../style"; +@import './mixin'; .container { display: flex; flex-direction: row; + flex-wrap: wrap; justify-items: center; justify-content: space-between; height: 100%; @@ -151,3 +153,50 @@ } } } + +.operation-container + div { + margin-top: 15px; + padding-top: 20px; + border-top: 1px solid #ededf2; +} + +.job-description, +.technologies, +.achievements { + @include roboto-regular; + + width: 100%; + margin: 0 0 20px; + margin-left: 66px; + font-size: 15px; + color: #262628; + + .title { + @include roboto-medium; + + line-height: 25px; + margin-bottom: 4px; + } +} + +.technologies { + .title { + margin: 0; + line-height: 1; + } + + div { + line-height: 25px; + } +} + +.achievements { + margin-bottom: 5px; +} + +.job-description, +.achievements { + .html-content { + @include html-content-style; + } +} diff --git a/src/shared/components/Settings/Profile/Work/Toolbar/index.jsx b/src/shared/components/Settings/Profile/Work/Toolbar/index.jsx new file mode 100644 index 0000000000..7291362eaa --- /dev/null +++ b/src/shared/components/Settings/Profile/Work/Toolbar/index.jsx @@ -0,0 +1,176 @@ +import React from 'react'; +import PT from 'prop-types'; +import Connector from 'components/Editor/Connector'; +import { RichUtils } from 'draft-js'; +import IconBold from 'assets/images/settings/profile/work/tc-text-16-bold.svg'; +import IconBoldActive from 'assets/images/settings/profile/work/tc-text-16-bold-active.svg'; +import IconItalic from 'assets/images/settings/profile/work/tc-text-16-italic.svg'; +import IconItalicActive from 'assets/images/settings/profile/work/tc-text-16-italic-active.svg'; +import IconUnderline from 'assets/images/settings/profile/work/tc-text-16-underline.svg'; +import IconUnderlineActive from 'assets/images/settings/profile/work/tc-text-16-underline-active.svg'; +import IconListBullet from 'assets/images/settings/profile/work/text-16px_list-bullet.svg'; +import IconListBulletActive from 'assets/images/settings/profile/work/text-16px_list-bullet-active.svg'; +import IconListNumbers from 'assets/images/settings/profile/work/text-16px_list-numbers.svg'; +import IconListNumbersActive from 'assets/images/settings/profile/work/text-16px_list-numbers-active.svg'; + +import './style.scss'; + +export default class Toolbar extends React.Component { + constructor(props) { + super(props); + + this.state = { + editor: null, + bold: false, + italic: false, + underline: false, + unorderedList: false, + orderedList: false, + hidden: true, + }; + + this.onClickBoldButton = this.onClickBoldButton.bind(this); + this.onClickItalicButton = this.onClickItalicButton.bind(this); + this.onClickUnderlineButton = this.onClickUnderlineButton.bind(this); + this.onClickUnorderedListButton = this.onClickUnorderedListButton.bind(this); + this.onClickOrderedListButton = this.onClickOrderedListButton.bind(this); + } + + componentDidMount() { + const { connector } = this.props; + connector.setToolbar(this); + } + + componentWillReceiveProps(nextProps) { + const { connector: prevConnector } = this.props; + const { connector: newConnector } = nextProps; + + if (newConnector !== prevConnector) { + if (prevConnector) prevConnector.setToolbar(null); + if (newConnector) newConnector.setToolbar(this); + } + } + + componentWillUnmount() { + const { connector } = this.props; + connector.setToolbar(null); + } + + onClickBoldButton(event) { + event.preventDefault(); + const { editor } = this.state; + const newStyle = editor.toggleInlineStyle('BOLD'); + this.setState({ bold: newStyle.has('BOLD') }); + } + + onClickItalicButton(event) { + event.preventDefault(); + const { editor } = this.state; + const newStyle = editor.toggleInlineStyle('ITALIC'); + this.setState({ italic: newStyle.has('ITALIC') }); + } + + onClickUnderlineButton(event) { + event.preventDefault(); + const { editor } = this.state; + const newStyle = editor.toggleInlineStyle('UNDERLINE'); + this.setState({ underline: newStyle.has('UNDERLINE') }); + } + + onClickUnorderedListButton(event) { + event.preventDefault(); + const { editor, unorderedList: prevUnorderedList } = this.state; + this.setState({ unorderedList: !prevUnorderedList, orderedList: false }, () => { + const { unorderedList } = this.state; + if (unorderedList) { + editor.applyBlockStyle('unordered-list-item'); + } else { + editor.applyBlockStyle('unstyled'); + } + }); + } + + onClickOrderedListButton(event) { + event.preventDefault(); + const { editor, orderedList: prevOrderedList } = this.state; + this.setState({ unorderedList: false, orderedList: !prevOrderedList }, () => { + const { orderedList } = this.state; + if (orderedList) { + editor.applyBlockStyle('ordered-list-item'); + } else { + editor.applyBlockStyle('unstyled'); + } + }); + } + + onFocusedEditorChanged(newState) { + const { connector, onEditorChange } = this.props; + const editor = connector.focusedEditor; + if (editor) { + const inlineStyle = newState.getCurrentInlineStyle(); + const block = RichUtils.getCurrentBlockType(newState); + this.setState({ + editor, + bold: inlineStyle.has('BOLD'), + italic: inlineStyle.has('ITALIC'), + underline: inlineStyle.has('UNDERLINE'), + unorderedList: block === 'unordered-list-item', + orderedList: block === 'ordered-list-item', + hidden: false, + }, () => { + onEditorChange(editor); + }); + } else { + this.setState({ + bold: false, + italic: false, + underline: false, + unorderedList: false, + orderedList: false, + hidden: true, + }); + } + } + + render() { + const { + hidden, + bold, + italic, + underline, + orderedList, + unorderedList, + } = this.state; + + return ( + + ); + } +} + +Toolbar.defaultProps = { + connector: new Connector(), + onEditorChange: () => {}, +}; + +Toolbar.propTypes = { + connector: PT.instanceOf(Connector), + onEditorChange: PT.func, +}; diff --git a/src/shared/components/Settings/Profile/Work/Toolbar/style.scss b/src/shared/components/Settings/Profile/Work/Toolbar/style.scss new file mode 100644 index 0000000000..a540b27adc --- /dev/null +++ b/src/shared/components/Settings/Profile/Work/Toolbar/style.scss @@ -0,0 +1,37 @@ +@import "~styles/mixins"; + +.container { + display: flex; + align-items: center; + width: 100%; + height: 44px; + background: $tc-gray-neutral-light; + border: 1px solid #c3c3c8; + border-radius: 4px 4px 0 0; + + button { + width: 20px; + height: 20px; + line-height: 20px; + margin: 0 13px; + padding: 0; + font-size: 13px; + font-weight: bold; + color: $tc-gray-80; + text-align: center; + appearance: none; + background: none; + border: 0; + + svg { + vertical-align: bottom; + } + } + + .separator { + display: block; + width: 1px; + height: 20px; + background: $tc-gray-80; + } +} diff --git a/src/shared/components/Settings/Profile/Work/index.jsx b/src/shared/components/Settings/Profile/Work/index.jsx index 0bbfa631bf..ee9d7b0ec3 100644 --- a/src/shared/components/Settings/Profile/Work/index.jsx +++ b/src/shared/components/Settings/Profile/Work/index.jsx @@ -6,6 +6,7 @@ /* eslint-disable no-nested-ternary */ /* eslint-disable jsx-a11y/label-has-for */ /* eslint-disable no-undef */ +/* eslint-disable react/jsx-boolean-value */ import React from 'react'; import PT from 'prop-types'; import _ from 'lodash'; @@ -14,9 +15,13 @@ import ConsentComponent from 'components/Settings/ConsentComponent'; import { PrimaryButton } from 'topcoder-react-ui-kit'; import DatePicker from 'components/challenge-listing/Filters/DatePicker'; import ErrorMessage from 'components/Settings/ErrorMessage'; -import { validateStartDate, validateEndDate } from 'utils/settings'; +import { validateStartDate, validateEndDate, isEmpty as isDateEmpty } from 'utils/settings'; +import Connector from 'components/Editor/Connector'; +import Select from 'components/Select'; +import Editor from 'components/Editor'; import ConfirmationModal from '../../CofirmationModal'; import WorkList from './List'; +import Toolbar from './Toolbar'; import './styles.scss'; @@ -34,6 +39,9 @@ export default class Work extends ConsentComponent { this.updatePredicate = this.updatePredicate.bind(this); this.onUpdateDate = this.onUpdateDate.bind(this); this.onCancelEditStatus = this.onCancelEditStatus.bind(this); + this.onUpdateJobDescription = this.onUpdateJobDescription.bind(this); + this.onUpdateAchievements = this.onUpdateAchievements.bind(this); + this.onUpdateTechnologies = this.onUpdateTechnologies.bind(this); const { userTraits } = props; this.state = { @@ -54,6 +62,14 @@ export default class Work extends ConsentComponent { timePeriodTo: '', industry: '', working: false, + jobDescription: '', + jobAchievements: '', + technologies: [], + initialJobDescription: '', + initialJobAchievements: '', + numJobDescription: 0, + numJobAchievements: 0, + numTechnologies: 0, }, isMobileView: false, screenSM: 767, @@ -61,6 +77,11 @@ export default class Work extends ConsentComponent { indexNo: null, showConfirmation: false, }; + + this.connectorJobDescription = new Connector(); + this.connectorAchievements = new Connector(); + this.connectorJobDescriptionMobile = new Connector(); + this.connectorAchievementsMobile = new Connector(); } componentDidMount() { @@ -89,8 +110,16 @@ export default class Work extends ConsentComponent { timePeriodTo: '', industry: '', working: false, + jobDescription: '', + jobAchievements: '', + technologies: [], + initialJobDescription: '', + initialJobAchievements: '', + numJobDescription: 0, + numJobAchievements: 0, + numTechnologies: 0, }, - }); + }, () => this.resetEditorContents()); } componentWillUnmount() { @@ -151,7 +180,7 @@ export default class Work extends ConsentComponent { if (date) { const { newWork: oldWork } = this.state; const newWork = { ...oldWork }; - newWork[timePeriod] = date; + newWork[timePeriod] = date.toISOString(); this.setState({ newWork, isSubmit: false }); } } @@ -205,10 +234,34 @@ export default class Work extends ConsentComponent { company: workTrait.traits.data[indexNo].company, position: _.isEmpty(workTrait.traits.data[indexNo].position) ? '' : workTrait.traits.data[indexNo].position, cityTown: _.isEmpty(workTrait.traits.data[indexNo].cityTown) ? '' : workTrait.traits.data[indexNo].cityTown, - timePeriodFrom: _.isEmpty(workTrait.traits.data[indexNo].timePeriodFrom) ? '' : workTrait.traits.data[indexNo].timePeriodFrom, - timePeriodTo: _.isEmpty(workTrait.traits.data[indexNo].timePeriodTo) ? '' : workTrait.traits.data[indexNo].timePeriodTo, + timePeriodFrom: isDateEmpty(workTrait.traits.data[indexNo].timePeriodFrom) ? '' : workTrait.traits.data[indexNo].timePeriodFrom, + timePeriodTo: isDateEmpty(workTrait.traits.data[indexNo].timePeriodTo) ? '' : workTrait.traits.data[indexNo].timePeriodTo, industry: _.isEmpty(workTrait.traits.data[indexNo].industry) ? '' : workTrait.traits.data[indexNo].industry, working: workTrait.traits.data[indexNo].working, + jobDescription: _.isEmpty(workTrait.traits.data[indexNo].jobDescription) + ? '' + : workTrait.traits.data[indexNo].jobDescription, + jobAchievements: _.isEmpty(workTrait.traits.data[indexNo].jobAchievements) + ? '' + : workTrait.traits.data[indexNo].jobAchievements, + technologies: _.isEmpty(workTrait.traits.data[indexNo].technologies) + ? [] + : workTrait.traits.data[indexNo].technologies, + initialJobDescription: _.isEmpty(workTrait.traits.data[indexNo].jobDescription) + ? '' + : workTrait.traits.data[indexNo].jobDescription, + initialJobAchievements: _.isEmpty(workTrait.traits.data[indexNo].jobAchievements) + ? '' + : workTrait.traits.data[indexNo].jobAchievements, + numJobDescription: _.isEmpty(workTrait.traits.data[indexNo].jobDescription) + ? 0 + : workTrait.traits.data[indexNo].jobDescription.length, + numJobAchievements: _.isEmpty(workTrait.traits.data[indexNo].jobAchievements) + ? 0 + : workTrait.traits.data[indexNo].jobAchievements.length, + numTechnologies: _.isEmpty(workTrait.traits.data[indexNo].technologies) + ? 0 + : workTrait.traits.data[indexNo].technologies.length, }, isEdit: true, indexNo, @@ -247,12 +300,12 @@ export default class Work extends ConsentComponent { if (_.isEmpty(work.cityTown)) { delete work.cityTown; } - if (_.isEmpty(work.timePeriodFrom)) { + if (isDateEmpty(work.timePeriodFrom)) { delete work.timePeriodFrom; } else { work.timePeriodFrom = new Date(work.timePeriodFrom).getTime(); } - if (_.isEmpty(work.timePeriodTo)) { + if (isDateEmpty(work.timePeriodTo)) { delete work.timePeriodTo; } else { work.timePeriodTo = new Date(work.timePeriodTo).getTime(); @@ -260,6 +313,24 @@ export default class Work extends ConsentComponent { if (_.isEmpty(work.industry)) { delete work.industry; } + if (_.isEmpty(work.jobDescription)) { + delete work.jobDescription; + } else { + work.jobDescription = _.escape(work.jobDescription); + } + if (_.isEmpty(work.jobAchievements)) { + delete work.jobAchievements; + } else { + work.jobAchievements = _.escape(work.jobAchievements); + } + if (_.isEmpty(work.technologies)) { + delete work.technologies; + } + delete work.initialJobDescription; + delete work.initialJobAchievements; + delete work.numJobDescription; + delete work.numJobAchievements; + delete work.numTechnologies; if (workTrait.traits && workTrait.traits.data.length > 0) { const newWorkTrait = _.cloneDeep(workTrait); @@ -320,6 +391,14 @@ export default class Work extends ConsentComponent { loadWorkTrait = (userTraits) => { const trait = userTraits.filter(t => t.traitId === 'work'); const works = trait.length === 0 ? {} : trait[0]; + if (works.traits && works.traits.data) { + works.traits.data = works.traits.data.map((item) => { + const result = _.assign({}, item); + result.jobDescription = _.unescape(item.jobDescription); + result.jobAchievements = _.unescape(item.jobAchievements); + return result; + }); + } return _.assign({}, works); } @@ -353,6 +432,14 @@ export default class Work extends ConsentComponent { timePeriodTo: '', industry: '', working: false, + jobDescription: '', + jobAchievements: '', + technologies: [], + initialJobDescription: '', + initialJobAchievements: '', + numJobDescription: 0, + numJobAchievements: 0, + numTechnologies: 0, }, formInvalid: false, startDateInvalid: false, @@ -360,13 +447,53 @@ export default class Work extends ConsentComponent { endDateInvalid: false, endDateDisabled: false, endDateInvalidMsg: '', - }); + }, () => this.resetEditorContents()); } } + onUpdateJobDescription(editor) { + const html = editor.getHtml(); + const length = editor.state.editor.getCurrentContent().getPlainText('').length; // eslint-disable-line prefer-destructuring + const { newWork: oldWork } = this.state; + + const newWork = { ...oldWork, jobDescription: html, numJobDescription: length }; + this.setState({ newWork, isSubmit: false, endDateDisabled: newWork.working }); + } + + onUpdateAchievements(editor) { + const html = editor.getHtml(); + const length = editor.state.editor.getCurrentContent().getPlainText('').length; // eslint-disable-line prefer-destructuring + const { newWork: oldWork } = this.state; + + const newWork = { ...oldWork, jobAchievements: html, numJobAchievements: length }; + this.setState({ newWork, isSubmit: false, endDateDisabled: newWork.working }); + } + + onUpdateTechnologies(value) { + if (value.length > 3) { + value.shift(); + } + + const { newWork: oldWork } = this.state; + const newWork = { + ...oldWork, + technologies: value.map(val => ({ id: val.id, name: val.name })), + numTechnologies: value.length, + }; + this.setState({ newWork, isSubmit: false, endDateDisabled: newWork.working }); + } + + resetEditorContents() { + this.connectorJobDescription.editors[0].setInitialContent(''); + this.connectorAchievements.editors[0].setInitialContent(''); + this.connectorJobDescriptionMobile.editors[0].setInitialContent(''); + this.connectorAchievementsMobile.editors[0].setInitialContent(''); + } + render() { const { settingsUI, + lookupData, } = this.props; const { workTrait, @@ -388,6 +515,16 @@ export default class Work extends ConsentComponent { const workItems = workTrait.traits ? workTrait.traits.data.slice() : []; const { newWork } = this.state; + const { + initialJobDescription, + initialJobAchievements, + technologies, + numJobDescription, + numTechnologies, + numJobAchievements, + } = newWork; + const techTags = lookupData.technologies + .sort((a, b) => (a.name < b.name ? -1 : a.name > b.name ? 1 : 0)); return (
@@ -493,7 +630,11 @@ export default class Work extends ConsentComponent { readOnly numberOfMonths={1} isOutsideRange={moment().subtract(1, 'd')} - date={newWork.timePeriodFrom} + date={ + isDateEmpty(newWork.timePeriodFrom) + ? null + : moment(newWork.timePeriodFrom) + } id="date-from1" onDateChange={date => this.onUpdateDate(date, 'timePeriodFrom')} placeholder="dd/mm/yyyy" @@ -521,7 +662,7 @@ export default class Work extends ConsentComponent { disabled={endDateDisabled} numberOfMonths={1} isOutsideRange={moment().subtract(1, 'd')} - date={newWork.timePeriodTo} + date={isDateEmpty(newWork.timePeriodTo) ? null : moment(newWork.timePeriodTo)} id="date-to1" onDateChange={date => this.onUpdateDate(date, 'timePeriodTo')} placeholder="dd/mm/yyyy" @@ -537,13 +678,12 @@ export default class Work extends ConsentComponent {
-
-