Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .circleci/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -138,7 +138,7 @@ workflows:
- build-dev
filters:
branches:
only: ['feature/unified-permissions']
only: ['feature/unified-permissions', 'feature/accept-reject-terms-in-profile']

- deployProd:
context : org-global
Expand Down
8,961 changes: 292 additions & 8,669 deletions package-lock.json

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion src/components/BtnGroup/BtnGroup.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ class BtnGroup extends React.Component {

BtnGroup.propTypes = {
items: PropTypes.arrayOf(PropTypes.shape({
value: PropTypes.string.isRequired,
value: PropTypes.oneOfType(PropTypes.string, PropTypes.number, PropTypes.bool).isRequired,
text: PropTypes.string.isRequired
})).isRequired,
onChange: PropTypes.func
Expand Down
3 changes: 2 additions & 1 deletion src/components/BtnGroup/BtnGroup.scss
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,8 @@

> .tc-btn.active,
> .tc-btn.active:hover,
> .tc-btn.active:active {
> .tc-btn.active:active,
> .tc-btn.active:focus {
background: $tc-gray-20;
box-shadow: inset 0 1px 3px 0 rgba($tc-gray-80, 0.38);
cursor: default;
Expand Down
42 changes: 42 additions & 0 deletions src/components/BtnGroup/FormsyBtnGroup.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import React, { Component } from 'react'
import PropTypes from 'prop-types'
import { HOC as hoc } from 'formsy-react'
import BtnGroup from './BtnGroup'

/**
* This component is a formsy wrapper for the BtnGroup component
* @param {Object} props Component props
*/
class FormsyBtnGroup extends Component {
constructor(props) {
super(props)
this.changeValue = this.changeValue.bind(this)
}

changeValue(value) {
this.props.setValue(value)
this.props.onChange && this.props.onChange(this.props.name, value)
}

render() {
const { items } = this.props
const hasError = !this.props.isPristine() && !this.props.isValid()
const errorMessage = this.props.getErrorMessage() || this.props.validationError

return (
<div>
<BtnGroup items={items} value={this.props.getValue()} onChange={this.changeValue} />
{(hasError && errorMessage) ? (<p className="error-message">{errorMessage}</p>) : null}
</div>
)
}
}

FormsyBtnGroup.PropTypes = {
items: PropTypes.arrayOf(PropTypes.shape({
value: PropTypes.oneOfType([PropTypes.string, PropTypes.number, PropTypes.bool]).isRequired,
text: PropTypes.string.isRequired
})).isRequired
}

export default hoc(FormsyBtnGroup)
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ const IncompleteUserProfile = ({
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}`)) {
if (isFieldRequired && _.isNil(_.get(profileSettings, `settings.${fieldKey}`))) {
acc[fieldKey] = isFieldRequired
}
return acc
Expand Down
4 changes: 4 additions & 0 deletions src/config/constants.js
Original file line number Diff line number Diff line change
Expand Up @@ -739,6 +739,7 @@ export const TC_CDN_URL = process.env.TC_CDN_URL || `https://community-app.${DOM

export const RESET_PASSWORD_URL = `https://accounts.${DOMAIN}/member/reset-password`
export const VERIFY_EMAIL_URL = `http://www.${DOMAIN}/settings/account/changeEmail`
export const TOPCODER_CONNECT_TERMS_URL = `https://connect.${DOMAIN}/terms`

export const PROJECT_NAME_MAX_LENGTH = 255
export const PROJECT_REF_CODE_MAX_LENGTH = 32
Expand Down Expand Up @@ -1048,6 +1049,7 @@ export const PROFILE_FIELDS_CONFIG = {
timeZone: true,
workingHourStart: true,
workingHourEnd: true,
// termsAccepted: true,

// optional fields
avatar: false,
Expand All @@ -1065,6 +1067,7 @@ export const PROFILE_FIELDS_CONFIG = {
companyURL: true,
businessPhone: true,
businessEmail: true,
termsAccepted: true,

// optional fields
avatar: false,
Expand All @@ -1080,6 +1083,7 @@ export const PROFILE_FIELDS_CONFIG = {
timeZone: true,
workingHourStart: true,
workingHourEnd: true,
// termsAccepted: true,

// optional fields
avatar: false,
Expand Down
2 changes: 1 addition & 1 deletion src/helpers/tcHelpers.js
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ export const isUserProfileComplete = (user, profileSettings) => {
_.forEach(_.keys(fieldsConfig), (fieldKey) => {
const isFieldRequired = fieldsConfig[fieldKey]

if (isFieldRequired && !profileSettings[fieldKey]) {
if (isFieldRequired && _.isNil(profileSettings[fieldKey])) {
isMissingUserInfo = true
return false
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,12 @@ const TCFormFields = FormsyForm.Fields
const Formsy = FormsyForm.Formsy
import ProfileSettingsAvatar from './ProfileSettingsAvatar'
import FormsySelect from '../../../../../components/Select/FormsySelect'
import FormsyBtnGroup from '../../../../../components/BtnGroup/FormsyBtnGroup'
import ISOCountries from '../../../../../helpers/ISOCountries'
import { formatPhone } from '../../../../../helpers/utils'
import { hasPermission } from '../../../../../helpers/permissions'
import { PERMISSIONS } from '../../../../../config/permissions'
import { TOPCODER_CONNECT_TERMS_URL } from '../../../../../config/constants'
import './ProfileSettingsForm.scss'

const countries = _.orderBy(ISOCountries, ['name'], ['asc']).map((country) => ({
Expand Down Expand Up @@ -382,8 +384,36 @@ class ProfileSettingsForm extends Component {
</div>
</div>
)}
{!_.isUndefined(fieldsConfig.termsAccepted) && (
<div className="field">
<div className="label">
<a styleName="fieldLabelText" href={TOPCODER_CONNECT_TERMS_URL} target="_blank">Topcoder Terms</a>&nbsp;
{fieldsConfig.termsAccepted && <sup styleName="requiredMarker">*</sup>}
</div>
<div className="input-field">
<div styleName="terms-field">
{_.isNil(this.props.values.settings.termsAccepted) ? (
<FormsyBtnGroup
name="termsAccepted"
items={[
{ value: true, text: 'Accept' },
{ value: false, text: 'Reject' }
]}
validations={{
isRequired: fieldsConfig.timeZone
}}
validationErrors={{
isRequired: 'Please accept or decline Topcoder Terms'
}}
/>
) : (
<span>{this.props.values.settings.termsAccepted ? 'Accepted' : 'Rejected'}</span>
)}
</div>
</div>
</div>
)}
<div className="controls">

{showBackButton && (
<button
className={`tc-btn tc-btn-default btn-back ${buttonExtraClassName}`}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -191,6 +191,25 @@
vertical-align: top;
}

a.fieldLabelText {
color: $tc-dark-blue-110;
}

.terms-field {
align-items: center;
display: flex;
padding: 7px 0;

> span {
display: inline-block;
color: $tc-gray-60;
font-size: 13px;
font-weight: bold;
line-height: 20px;
padding: 5px 0;
}
}

.warningText {
margin: 5px auto;
@include roboto;
Expand All @@ -203,4 +222,4 @@
color: $tc-orange-70;
padding:10px;
border-radius: 2px;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -23,14 +23,22 @@ class ProfileSettingsContainer extends Component {
const { profileSettings, saveProfileSettings, uploadProfilePhoto, user } = this.props
const fieldsConfig = getUserProfileFieldsConfig()

// prefill some fields of the profile
const prefilledProfileSettings = _.cloneDeep(profileSettings)

// at the moment we don't let users to update their business email, so in case it's not set, use registration email
if (fieldsConfig.businessEmail && !profileSettings.settings.businessEmail) {
prefilledProfileSettings.settings.businessEmail = user.email
}

return (
<SettingsPanel
title="My Profile"
user={user}
>
<ProfileSettingsFormEnhanced
user={user}
values={profileSettings}
values={prefilledProfileSettings}
saveSettings={saveProfileSettings}
uploadPhoto={uploadProfilePhoto}
fieldsConfig={fieldsConfig}
Expand Down