diff --git a/src/components/IncomepleteUserProfileDialog/IncompleteUserProfileDialog.jsx b/src/components/IncomepleteUserProfileDialog/IncompleteUserProfileDialog.jsx new file mode 100644 index 000000000..625ff994b --- /dev/null +++ b/src/components/IncomepleteUserProfileDialog/IncompleteUserProfileDialog.jsx @@ -0,0 +1,54 @@ +/** + * Dialog which shows incomplete user profile. + */ +import React from 'react' +import PT from 'prop-types' +import Modal from 'react-modal' +import IncompleteUserProfile from '../IncompleteUserProfile/IncompleteUserProfile' +import XMarkIcon from '../../assets/icons/icon-x-mark.svg' +import styles from './IncompleteUserProfileDialog.scss' +import LoadingIndicator from '../LoadingIndicator/LoadingIndicator' + +const IncompleteUserProfileDialog = ({ + onCloseDialog, + title, + ...restProps, +}) => { + return ( + +
+
+

{title}

+

Complete your profile now.

+ +
+ +
+ {restProps.profileSettings.pending &&
} + +
+
+
+ ) +} + +IncompleteUserProfileDialog.propTypes = { + profileSettings: PT.object.isRequired, + saveProfileSettings: PT.func.isRequired, + isTopcoderUser: PT.bool.isRequired, + user: PT.object.isRequired, + onCloseDialog: PT.func.isRequired, + title: PT.string.isRequired, +} + +export default IncompleteUserProfileDialog diff --git a/src/components/IncomepleteUserProfileDialog/IncompleteUserProfileDialog.scss b/src/components/IncomepleteUserProfileDialog/IncompleteUserProfileDialog.scss new file mode 100644 index 000000000..18e835784 --- /dev/null +++ b/src/components/IncomepleteUserProfileDialog/IncompleteUserProfileDialog.scss @@ -0,0 +1,66 @@ +@import '~tc-ui/src/styles/tc-includes'; +@import '../../styles/includes'; + +:global(.management-dialog-overlay .project-dialog-conatiner .project-dialog) { + &.dialog { + width: 800px; + } + + :global(.dialog-body).body { + max-height: 500px; + padding-top: $base-unit * 4; + position: relative; + } +} + +:global(.incomplete-profile-dialog-overlay.management-dialog-overlay .project-dialog-conatiner .project-dialog .input-container) { + display: block; + background: transparent; + border-top: 0; + border-radius: 0; + margin: 0; + padding: 0; + + input { + margin: 0; + } + + :global(.dropdown-wrap) { + margin: 0; + width: 100px; + } +} + +.subtitle { + padding-top: $base-unit * 4; + text-align: center; +} + +.loadingOverlay { + align-items: center; + background-color: #fff; + display: flex; + left: 0; + height: 100%; + justify-content: center; + position: absolute; + top: 0; + width: 100%; + z-index: 1; +} + +@media screen and (max-width: $screen-md - 1px) { + :global(.management-dialog-overlay .project-dialog-conatiner .project-dialog) { + &.dialog { + width: 100%; + } + } +} + +@media screen and (max-height: 700px) { + :global(.management-dialog-overlay .project-dialog-conatiner .project-dialog) { + :global(.dialog-body).body { + max-height: calc(100vh - 200px); + } + } +} diff --git a/src/components/IncompleteUserProfile/IncompleteUserProfile.jsx b/src/components/IncompleteUserProfile/IncompleteUserProfile.jsx new file mode 100644 index 000000000..95da40ac3 --- /dev/null +++ b/src/components/IncompleteUserProfile/IncompleteUserProfile.jsx @@ -0,0 +1,72 @@ +/** + * Incomplete User Profile Form. + */ +import React from 'react' +import PT from 'prop-types' +import { PROFILE_FIELDS_CONFIG } from '../../config/constants' +import ProfileSettingsForm from '../../routes/settings/routes/profile/components/ProfileSettingsForm' +import { getDefaultTopcoderRole } from '../../helpers/permissions' + +const IncompleteUserProfile = ({ + profileSettings, + saveProfileSettings, + isTopcoderUser, + user, + ...restProps +}) => { + const fieldsConfig = isTopcoderUser ? PROFILE_FIELDS_CONFIG.TOPCODER : PROFILE_FIELDS_CONFIG.CUSTOMER + // never show avatar + delete fieldsConfig.avatar + // config the form to only show required fields which doesn't have the value yet + const missingFieldsConfig = _.reduce(fieldsConfig, (acc, isFieldRequired, fieldKey) => { + if (isFieldRequired && !_.get(profileSettings, `settings.${fieldKey}`)) { + acc[fieldKey] = isFieldRequired + } + return acc + }, {}) + + // prefill some fields of the profile + const prefilledProfileSettings = _.cloneDeep(profileSettings) + + // if time zone is required and doesn't have a value yet, + // then detect timezone using browser feature and prefill the form + if (fieldsConfig.timeZone && !profileSettings.settings.timeZone) { + prefilledProfileSettings.settings.timeZone = (new Intl.DateTimeFormat()).resolvedOptions().timeZone + } + + if (isTopcoderUser) { + // We don't ask Topcoder User for "Company Name" and "Title" + // but server requires them, so if they are not yet defined, we set them automatically + if (!profileSettings.settings.companyName) { + prefilledProfileSettings.settings.companyName = 'Topcoder' + } + if (!profileSettings.settings.title) { + prefilledProfileSettings.settings.title = getDefaultTopcoderRole(user) + } + } else { + // at the moment we don't let users to update their business email, so in case it's not set, use registration email + if (!profileSettings.settings.businessEmail) { + prefilledProfileSettings.settings.businessEmail = user.email + } + } + + return ( + + ) +} + +IncompleteUserProfile.propTypes = { + profileSettings: PT.object.isRequired, + saveProfileSettings: PT.func.isRequired, + isTopcoderUser: PT.bool.isRequired, + user: PT.object.isRequired, +} + +export default IncompleteUserProfile diff --git a/src/config/constants.js b/src/config/constants.js index b7bdf17b8..48baaa4e2 100644 --- a/src/config/constants.js +++ b/src/config/constants.js @@ -688,6 +688,24 @@ export const MANAGER_ROLES = [ ROLE_PROJECT_MANAGER, ] +/** + * Is user has any of these roles, it means such a user is not a customer. + */ +export const NON_CUSTOMER_ROLES = [ + ROLE_CONNECT_COPILOT, + ROLE_CONNECT_MANAGER, + ROLE_CONNECT_ACCOUNT_MANAGER, + ROLE_CONNECT_ADMIN, + ROLE_ADMINISTRATOR, + ROLE_CONNECT_COPILOT_MANAGER, + ROLE_BUSINESS_DEVELOPMENT_REPRESENTATIVE, + ROLE_PRESALES, + ROLE_ACCOUNT_EXECUTIVE, + ROLE_PROGRAM_MANAGER, + ROLE_SOLUTION_ARCHITECT, + ROLE_PROJECT_MANAGER, +] + // to be able to start the Connect App we should pass at least the dummy value for `FILE_PICKER_API_KEY` // but if we want to test file uploading we should provide the real value in `FILE_PICKER_API_KEY` env variable export const FILE_PICKER_API_KEY = process.env.FILE_PICKER_API_KEY || 'DUMMY' @@ -1011,4 +1029,63 @@ export const INTERNAL_PROJECT_URLS=[ /** * Project category string */ -export const PROJECT_CATEGORY_TAAS = 'talent-as-a-service' \ No newline at end of file +export const PROJECT_CATEGORY_TAAS = 'talent-as-a-service' + +/** + * Config for User Profile fields + * + * - `true` means field is required + * - `false` means field is optional + * - if field is not on the list means it should not be shown + */ +export const PROFILE_FIELDS_CONFIG = { + // this config is used to show any user profile + DEFAULT: { + // required fields + firstName: true, + lastName: true, + title: true, + timeZone: true, + businessPhone: true, + companyName: true, + + // optional fields + country: false, + avatar: false, + workingHourStart: false, + workingHourEnd: false, + }, + + // configs below are used when we ask users to fill missing fields (progressive registration) + TOPCODER: { + // required fields + firstName: true, + lastName: true, + country: true, + timeZone: true, + workingHourStart: true, + workingHourEnd: true, + + // optional fields + avatar: false, + title: false, + companyName: false, + businessPhone: false, + }, + CUSTOMER: { + // required fields + firstName: true, + lastName: true, + title: true, + companyName: true, + businessPhone: true, + + // optional fields + businessEmail: false, + avatar: false, + country: false, + timeZone: false, + workingHourStart: false, + workingHourEnd: false, + } +} diff --git a/src/helpers/tcHelpers.js b/src/helpers/tcHelpers.js index 37f1f4b98..129a96287 100644 --- a/src/helpers/tcHelpers.js +++ b/src/helpers/tcHelpers.js @@ -5,7 +5,9 @@ import { DISCOURSE_BOT_USERID, CODER_BOT_USERID, TC_SYSTEM_USERID, - TC_CDN_URL + TC_CDN_URL, + NON_CUSTOMER_ROLES, + PROFILE_FIELDS_CONFIG, } from '../config/constants' /** @@ -44,4 +46,30 @@ export const getFullNameWithFallback = (user) => { userFullName = userFullName && userFullName.trim().length > 0 ? userFullName : user.handle userFullName = userFullName && userFullName.trim().length > 0 ? userFullName : 'Connect user' return userFullName -} \ No newline at end of file +} + +/** + * Check if user profile is complete or no. + * + * @param {Object} user `loadUser.user` from Redux Store + * @param {Object} profileSettings profile settings with traits + * + * @returns {Boolean} complete or no + */ +export const isUserProfileComplete = (user, profileSettings) => { + const isTopcoderUser = _.intersection(user.roles, NON_CUSTOMER_ROLES).length > 0 + const fieldsConfig = isTopcoderUser ? PROFILE_FIELDS_CONFIG.TOPCODER : PROFILE_FIELDS_CONFIG.CUSTOMER + + // check if any required field doesn't have a value + let isMissingUserInfo = false + _.forEach(_.keys(fieldsConfig), (fieldKey) => { + const isFieldRequired = fieldsConfig[fieldKey] + + if (isFieldRequired && !profileSettings.fieldKey) { + isMissingUserInfo = true + return false + } + }) + + return !isMissingUserInfo +} diff --git a/src/projects/create/components/FillProjectDetails.js b/src/projects/create/components/FillProjectDetails.js index 41a113073..b0f8f238c 100644 --- a/src/projects/create/components/FillProjectDetails.js +++ b/src/projects/create/components/FillProjectDetails.js @@ -16,7 +16,6 @@ import { uploadProfilePhoto, resetProfileSetting, } from '../../../routes/settings/actions/index' -import { getDefaultTopcoderRole } from '../../../helpers/permissions' import { ROLE_CONNECT_COPILOT, ROLE_CONNECT_MANAGER, @@ -24,13 +23,9 @@ import { ROLE_CONNECT_COPILOT_MANAGER, ROLE_ADMINISTRATOR, ROLE_CONNECT_ADMIN, - ROLE_BUSINESS_DEVELOPMENT_REPRESENTATIVE, - ROLE_PRESALES, - ROLE_ACCOUNT_EXECUTIVE, - ROLE_PROGRAM_MANAGER, - ROLE_SOLUTION_ARCHITECT, - ROLE_PROJECT_MANAGER, + NON_CUSTOMER_ROLES, } from '../../../config/constants' +import { isUserProfileComplete } from '../../../helpers/tcHelpers' class FillProjectDetails extends Component { constructor(props) { @@ -288,50 +283,8 @@ const mapStateToProps = ({ settings, loadUser }) => { ROLE_CONNECT_ADMIN, ROLE_CONNECT_MANAGER, ] - const topCoderRoles = [ - ROLE_CONNECT_COPILOT, - ROLE_CONNECT_MANAGER, - ROLE_CONNECT_ACCOUNT_MANAGER, - ROLE_CONNECT_ADMIN, - ROLE_ADMINISTRATOR, - ROLE_CONNECT_COPILOT_MANAGER, - ROLE_BUSINESS_DEVELOPMENT_REPRESENTATIVE, - ROLE_PRESALES, - ROLE_ACCOUNT_EXECUTIVE, - ROLE_PROGRAM_MANAGER, - ROLE_SOLUTION_ARCHITECT, - ROLE_PROJECT_MANAGER, - ] - const isTopcoderUser = _.intersection(loadUser.user.roles, topCoderRoles).length > 0 - let isMissingUserInfo = true + const isTopcoderUser = _.intersection(loadUser.user.roles, NON_CUSTOMER_ROLES).length > 0 const profileSettings = formatProfileSettings(settings.profile.traits) - if (isTopcoderUser) { - // We don't ask Topcoder User for "Company Name" and "Title" - // but server requires them, so if they are not yet defined, we set them automatically - if (!profileSettings.companyName) { - profileSettings.companyName = 'Topcoder' - } - if (!profileSettings.title) { - profileSettings.title = getDefaultTopcoderRole(loadUser.user) - } - isMissingUserInfo = - !profileSettings.firstName || - !profileSettings.lastName || - !profileSettings.country || - !profileSettings.timeZone || - !profileSettings.workingHourStart || - !profileSettings.workingHourEnd - } else { - if (!profileSettings.businessEmail) { - profileSettings.businessEmail = loadUser.user.email - } - isMissingUserInfo = - !profileSettings.firstName || - !profileSettings.lastName || - !profileSettings.title || - !profileSettings.companyName || - !profileSettings.businessPhone - } return { profileSettings: { @@ -348,7 +301,7 @@ const mapStateToProps = ({ settings, loadUser }) => { (role) => role === ROLE_CONNECT_COPILOT ), isTopcoderUser, - isMissingUserInfo, + isMissingUserInfo: !isUserProfileComplete(loadUser.user, profileSettings), } } diff --git a/src/projects/create/components/UpdateUserInfo.js b/src/projects/create/components/UpdateUserInfo.js index ae8fbfad5..96e5fc010 100644 --- a/src/projects/create/components/UpdateUserInfo.js +++ b/src/projects/create/components/UpdateUserInfo.js @@ -1,13 +1,10 @@ import React, { Component } from 'react' import PT from 'prop-types' - import ModalControl from '../../../components/ModalControl' import TailLeft from '../../../assets/icons/arrows-16px-1_tail-left.svg' import { DOMAIN } from '../../../config/constants' - -import ProfileSettingsForm from '../../../routes/settings/routes/profile/components/ProfileSettingsForm' - import './UpdateUserInfo.scss' +import IncompleteUserProfile from '../../../components/IncompleteUserProfile/IncompleteUserProfile' class UpdateUserInfo extends Component { constructor(props) { @@ -25,12 +22,9 @@ class UpdateUserInfo extends Component { const { profileSettings, saveProfileSettings, - uploadProfilePhoto, - isCustomer, - isCopilot, - isManager, isTopcoderUser, closeUserSettings, + user, } = this.props return ( @@ -51,27 +45,14 @@ class UpdateUserInfo extends Component { Please complete your profile information below to able to submit your project request. - {!isTopcoderUser && ( diff --git a/src/projects/detail/ProjectDetail.jsx b/src/projects/detail/ProjectDetail.jsx index 87b59ed93..98287d892 100644 --- a/src/projects/detail/ProjectDetail.jsx +++ b/src/projects/detail/ProjectDetail.jsx @@ -15,13 +15,17 @@ import { getEmptyProjectObject } from '../reducers/project' import { LOAD_PROJECT_FAILURE, PROJECT_ROLE_CUSTOMER, PROJECT_ROLE_OWNER, ROLE_ADMINISTRATOR, ROLE_CONNECT_ADMIN, ROLE_CONNECT_COPILOT, ROLE_CONNECT_MANAGER, - PROJECT_MEMBER_INVITE_STATUS_ACCEPTED, PROJECT_MEMBER_INVITE_STATUS_REFUSED, ACCEPT_OR_REFUSE_INVITE_FAILURE + PROJECT_MEMBER_INVITE_STATUS_ACCEPTED, PROJECT_MEMBER_INVITE_STATUS_REFUSED, ACCEPT_OR_REFUSE_INVITE_FAILURE, NON_CUSTOMER_ROLES } from '../../config/constants' import spinnerWhileLoading from '../../components/LoadingSpinner' import CoderBot from '../../components/CoderBot/CoderBot' import { getProjectProductTemplates, getProjectTemplateById } from '../../helpers/templates' import Dialog from '../../components/TeamManagement/Dialog' import { getProductEstimate } from '../../config/projectWizard' +import { getProfileSettings, saveProfileSettings } from '../../routes/settings/actions' +import { formatProfileSettings } from '../../routes/settings/helpers/settings' +import { isUserProfileComplete } from '../../helpers/tcHelpers' +import IncompleteUserProfileDialog from '../../components/IncomepleteUserProfileDialog/IncompleteUserProfileDialog' const JOIN_INVITE_TITLE = 'You\'re invited to join this project' @@ -60,7 +64,9 @@ const errorHandler = showCoderBotIfError(props => props.error && (props.error.ty // This handles showing a spinner while the state is being loaded async const spinner = spinnerWhileLoading(props => - !props.isLoading && ( + !props.isLoading && + // also wait until user profile is loaded to make sure we are showing incomplete user profile popup first, before showing project details + !props.profileSettings.isLoading && ( // first check that there are no error, before checking project properties props.error && props.error.type === LOAD_PROJECT_FAILURE || props.error.type === ACCEPT_OR_REFUSE_INVITE_FAILURE || // old project or has projectTemplate loaded @@ -127,6 +133,7 @@ class ProjectDetail extends Component { isCallingInviteAction: false, isUserAcceptedInvitation: undefined, shouldForceCallAcceptRefuseRequest: false, + showIncompleteProfilePopup: false, } this.onUserInviteAction = this.onUserInviteAction.bind(this) @@ -135,6 +142,8 @@ class ProjectDetail extends Component { componentWillMount() { const projectId = this.props.match.params.projectId this.props.loadProjectDashboard(projectId) + // to check if user profile is complete or no + this.props.getProfileSettings() // set flag that we have to force invitation action from the beginning // so we can show appropriate loading indicator as soon as possible @@ -152,7 +161,7 @@ class ProjectDetail extends Component { } componentWillReceiveProps(nextProps) { - const {isProcessing, isLoading, error, project, match, showUserInvited} = nextProps + const {isProcessing, isLoading, error, project, match, showUserInvited, profileSettings} = nextProps // handle just deleted projects if (! (error || isLoading || isProcessing) && _.isEqual(getEmptyProjectObject(), project)) this.props.history.push('/projects/') @@ -190,6 +199,16 @@ class ProjectDetail extends Component { }) } } + + // as soon as user profile settings are loaded, check if all required fields are completed or show the popup + if (this.props.profileSettings.isLoading && !profileSettings.isLoading && !isUserProfileComplete(nextProps.currentUser, profileSettings.settings)) { + this.setState({ showIncompleteProfilePopup: true }) + } + + // as soon as user profile is updated and complete, close the popup + if (this.props.profileSettings.pending && !profileSettings.pending && isUserProfileComplete(nextProps.currentUser, profileSettings.settings)) { + this.setState({ showIncompleteProfilePopup: false }) + } } getProjectRoleForCurrentUser({currentUserId, project}) { @@ -252,7 +271,7 @@ class ProjectDetail extends Component { } render() { - const { inviteError } = this.props + const { inviteError, project, profileSettings, currentUser, saveProfileSettings, isTopcoderUser } = this.props const { isCallingInviteAction, isUserAcceptedInvitation, shouldForceCallAcceptRefuseRequest } = this.state const currentMemberRole = this.getProjectRoleForCurrentUser(this.props) const adminRoles = [ROLE_ADMINISTRATOR, ROLE_CONNECT_ADMIN] @@ -263,7 +282,7 @@ class ProjectDetail extends Component { const showUserInvited = this.props.showUserInvited return ( - ((showUserInvited || shouldForceCallAcceptRefuseRequest) && !inviteError || isCallingInviteAction) ? + ((showUserInvited || shouldForceCallAcceptRefuseRequest) && !inviteError || isCallingInviteAction) ? ( this.onUserInviteAction(false)} onConfirm={() => this.onUserInviteAction(true)} @@ -273,22 +292,38 @@ class ProjectDetail extends Component { buttonText="Join project" buttonColor="blue" isLoading={isCallingInviteAction || shouldForceCallAcceptRefuseRequest} - /> : - + ) : ( +
+ {this.state.showIncompleteProfilePopup && ( + { this.setState({ showIncompleteProfilePopup: false })}} + title={`Welcome to ${project.name}`} + /> + )} + +
+ ) ) } } -const mapStateToProps = ({projectState, projectDashboard, loadUser, productsTimelines, templates}) => { +const mapStateToProps = ({projectState, projectDashboard, loadUser, productsTimelines, templates, settings}) => { const templateId = (projectState.project || {}).templateId const { projectTemplates, productTemplates } = templates + const isTopcoderUser = _.intersection(loadUser.user.roles, NON_CUSTOMER_ROLES).length > 0 return { + currentUser: loadUser.user, currentUserId: parseInt(loadUser.user.id), currentUserEmail: loadUser.user.email, isLoading: projectDashboard.isLoading, @@ -311,11 +346,16 @@ const mapStateToProps = ({projectState, projectDashboard, loadUser, productsTime allProductTemplates: templates.productTemplates, currentUserRoles: loadUser.user.roles, showUserInvited: projectState.showUserInvited, - userInvitationId: projectState.userInvitationId + userInvitationId: projectState.userInvitationId, + profileSettings: { + ...settings.profile, + settings: formatProfileSettings(settings.profile.traits) + }, + isTopcoderUser, } } -const mapDispatchToProps = { loadProjectDashboard, clearLoadedProject, acceptOrRefuseInvite, loadProjects } +const mapDispatchToProps = { loadProjectDashboard, clearLoadedProject, acceptOrRefuseInvite, loadProjects, getProfileSettings, saveProfileSettings } ProjectDetail.propTypes = { project: PropTypes.object, diff --git a/src/routes/settings/routes/profile/components/ProfileSettingsForm.jsx b/src/routes/settings/routes/profile/components/ProfileSettingsForm.jsx index dfdc31764..ce6cf221e 100644 --- a/src/routes/settings/routes/profile/components/ProfileSettingsForm.jsx +++ b/src/routes/settings/routes/profile/components/ProfileSettingsForm.jsx @@ -15,6 +15,7 @@ import ISOCountries from '../../../../../helpers/ISOCountries' import { formatPhone } from '../../../../../helpers/utils' import { hasPermission } from '../../../../../helpers/permissions' import PERMISSIONS from '../../../../../config/permissions' +import { PROFILE_FIELDS_CONFIG } from '../../../../../config/constants' import './ProfileSettingsForm.scss' const countries = _.orderBy(ISOCountries, ['name'], ['asc']).map((country) => ({ @@ -73,7 +74,7 @@ class ProfileSettingsForm extends Component { if (country && country.code) { if (previousSelectedCountry !== country.name && country.name) { // when country code of business phone changes, the country selection should change automatically - this.refs.countrySelect.setValue(country.name) + this.refs.countrySelect && this.refs.countrySelect.setValue(country.name) this.setState({ countrySelected: country.name, }) @@ -167,6 +168,13 @@ class ProfileSettingsForm extends Component { ...data, } + // if we don't show the country field, but we show the phone field and we chose + // a phone in another country, we have to send to the server updated country + // as we always change the country to the same as phone number + if (_.isUndefined(this.props.fieldsConfig.country) && this.state.countrySelected) { + updatedData.country = this.state.countrySelected + } + updatedData.businessPhone = formatPhone(updatedData.businessPhone) this.props.saveSettings(updatedData) @@ -192,15 +200,7 @@ class ProfileSettingsForm extends Component { render() { const { - showBusinessEmail, - showAvatar, - showCompanyName, - showTitle, - showBusinessPhone, - isRequiredTimeZone, - isRequiredCountry, - isRequiredWorkingHours, - isRequiredBusinessEmail, + fieldsConfig, submitButton, showBackButton, onBack, @@ -221,7 +221,7 @@ class ProfileSettingsForm extends Component { onChange={this.onChange} > {shouldShowTitle && (
Personal information
)} - {showAvatar && ( + {!_.isUndefined(fieldsConfig.avatar) && (
Avatar
)} - {this.getField('First Name', 'firstName', true)} - {this.getField('Last Name', 'lastName', true)} - {showTitle && this.getField('Title', 'title', true)} - {showBusinessEmail && - this.getField('Business Email', 'businessEmail', isRequiredBusinessEmail, true)} - {showBusinessPhone && ( + {!_.isUndefined(fieldsConfig.firstName) && this.getField('First Name', 'firstName', fieldsConfig.firstName)} + {!_.isUndefined(fieldsConfig.lastName) && this.getField('Last Name', 'lastName', fieldsConfig.lastName)} + {!_.isUndefined(fieldsConfig.title) && this.getField('Title', 'title', fieldsConfig.title)} + {!_.isUndefined(fieldsConfig.businessEmail) && + this.getField('Business Email', 'businessEmail', fieldsConfig.businessEmail, true)} + {!_.isUndefined(fieldsConfig.businessPhone) && (
Business Phone  @@ -255,7 +255,7 @@ class ProfileSettingsForm extends Component { validationError="Invalid business phone" showCheckMark listCountry={ISOCountries} - required + required={fieldsConfig.businessPhone} forceCountry={this.state.countrySelected} value={ this.props.values.settings.businessPhone @@ -275,107 +275,113 @@ class ProfileSettingsForm extends Component {
)} - {showCompanyName && + {!_.isUndefined(fieldsConfig.companyName) && this.getField( 'Company Name', 'companyName', - true, + fieldsConfig.companyName, disableCompanyInput )} -
- {isRequiredCountry ? ( -
- Country  - * -
- ) : ( -
- Country -
- )} -
- - {this.state.countrySelectionDirty && ( -
- Note: Changing the country also updates the country code of - business phone. + {!_.isUndefined(fieldsConfig.country) && ( +
+ {fieldsConfig.country ? ( +
+ Country  + * +
+ ) : ( +
+ Country
)} -
-
-
- {isRequiredTimeZone ? ( -
- Local Timezone  - * -
- ) : ( -
- Local Timezone -
- )} -
- ( - - filterFn(option.data, searchText) - } - value={this.props.values.settings.timeZone || ''} - name="timeZone" - options={timezoneOptions} - required={isRequiredTimeZone} - validationError="Please enter Local Timezone" - /> +
+ + {this.state.countrySelectionDirty && ( +
+ Note: Changing the country also updates the country code of + business phone. +
)} - /> +
-
-
- {isRequiredWorkingHours ? ( -
- Normal Working Hours  - * + )} + {!_.isUndefined(fieldsConfig.timeZone) && ( +
+ {fieldsConfig.timeZone ? ( +
+ Local Timezone  + * +
+ ) : ( +
+ Local Timezone +
+ )} +
+ ( + + filterFn(option.data, searchText) + } + value={this.props.values.settings.timeZone || ''} + name="timeZone" + options={timezoneOptions} + required={fieldsConfig.timeZone} + validationError="Please enter Local Timezone" + /> + )} + />
- ) : ( -
- Normal Working Hours +
+ )} + {(!_.isUndefined(fieldsConfig.workingHourStart) || !_.isUndefined(fieldsConfig.workingHourStart)) && ( +
+ {(fieldsConfig.workingHourStart || fieldsConfig.workingHourStart) ? ( +
+ Normal Working Hours  + * +
+ ) : ( +
+ Normal Working Hours +
+ )} +
+
- )} -
-
-
+ )}
{showBackButton && ( @@ -408,15 +414,8 @@ class ProfileSettingsForm extends Component { } ProfileSettingsForm.defaultProps = { - showBusinessEmail: false, - showAvatar: true, - showCompanyName: true, - showTitle: true, - showBusinessPhone: true, - isRequiredTimeZone: true, - isRequiredCountry: false, - isRequiredWorkingHours: false, - isRequiredBusinessEmail: true, + // default config is same for user profile for any kind of users + fieldsConfig: PROFILE_FIELDS_CONFIG.DEFAULT, showBackButton: false, submitButton: 'Save settings', onBack: () => {}, @@ -429,15 +428,19 @@ ProfileSettingsForm.propTypes = { values: PropTypes.object.isRequired, saveSettings: PropTypes.func.isRequired, uploadPhoto: PropTypes.func.isRequired, - showBusinessEmail: PropTypes.bool, - showAvatar: PropTypes.bool, - showCompanyName: PropTypes.bool, - showTitle: PropTypes.bool, - showBusinessPhone: PropTypes.bool, - isRequiredTimeZone: PropTypes.bool, - isRequiredCountry: PropTypes.bool, - isRequiredWorkingHours: PropTypes.bool, - isRequiredBusinessEmail: PropTypes.bool, + fieldsConfig: PropTypes.shape({ + avatar: PropTypes.bool, + firstName: PropTypes.bool, + lastName: PropTypes.bool, + title: PropTypes.bool, + companyName: PropTypes.bool, + businessPhone: PropTypes.bool, + businessEmail: PropTypes.bool, + country: PropTypes.bool, + timeZone: PropTypes.bool, + workingHourStart: PropTypes.bool, + workingHourEnd: PropTypes.bool, + }).isRequired, showBackButton: PropTypes.bool, shouldShowTitle: PropTypes.bool, shouldDoValidateOnStart: PropTypes.bool,