Skip to content

Commit

Permalink
[feature] Add phone number to sign up form #83
Browse files Browse the repository at this point in the history
  • Loading branch information
nemesifier committed Oct 12, 2020
1 parent 6e9d079 commit 94e7857
Show file tree
Hide file tree
Showing 10 changed files with 155 additions and 30 deletions.
3 changes: 2 additions & 1 deletion client/components/registration/index.css
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@
color: #000;
text-transform: capitalize;
}
.owisp-registration-input {
.owisp-registration-input, .form-control {
height: 33px;
line-height: 40px;
padding: 0 8px;
Expand All @@ -64,6 +64,7 @@
font-size: 14px;
width: 100%;
}
.form-control { width: 100% !important}
.owisp-registration-input:focus {
border-color: #666666;
}
Expand Down
1 change: 1 addition & 0 deletions client/components/registration/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import Component from "./registration";
const mapStateToProps = state => {
return {
registration: state.organization.configuration.components.registration_form,
settings: state.organization.configuration.settings,
privacyPolicy: state.organization.configuration.privacy_policy,
termsAndConditions: state.organization.configuration.terms_and_conditions,
language: state.language,
Expand Down
101 changes: 84 additions & 17 deletions client/components/registration/registration.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ import React from "react";
import { Link, Route } from "react-router-dom";
import { toast } from 'react-toastify';
import 'react-toastify/dist/ReactToastify.css';
import PhoneInput from 'react-phone-input-2';
import 'react-phone-input-2/lib/style.css';
import LoadingContext from "../../utils/loading-context";

import { mainToastId, passwordConfirmError, registerApiUrl, registerError, registerSuccess } from "../../constants";
Expand All @@ -22,6 +24,7 @@ export default class Registration extends React.Component {
constructor(props) {
super(props);
this.state = {
phone_number: "",
email: "",
password1: "",
password2: "",
Expand All @@ -39,9 +42,9 @@ export default class Registration extends React.Component {
handleSubmit(event) {
const { setLoading } = this.context;
event.preventDefault();
const { registration, orgSlug, authenticate } = this.props;
const { registration, orgSlug, authenticate, settings } = this.props;
const { input_fields } = registration;
const { email, password1, password2, errors } = this.state;
const { phone_number, email, password1, password2, errors } = this.state;
if (input_fields.password_confirm) {
if (password1 !== password2) {
this.setState({
Expand All @@ -54,23 +57,30 @@ export default class Registration extends React.Component {
}
this.setState({ errors: { ...errors, password2: null } });
const url = registerApiUrl.replace("{orgSlug}", orgSlug);
// prepare post data
const postData = {
email,
"username": email,
password1,
password2,
};
// add phone_number if SMS verification is enabled
if (settings && settings.sms_verification) {
postData.phone_number = phone_number;
}
setLoading(true);
return axios({
method: "post",
headers: {
"content-type": "application/x-www-form-urlencoded",
},
url,
data: qs.stringify({
email,
"username": email,
password1,
password2,
}),
data: qs.stringify(postData),
})
.then(() => {
this.setState({
errors: {},
phone_number: "",
email: "",
password1: "",
password2: "",
Expand All @@ -91,6 +101,7 @@ export default class Registration extends React.Component {
this.setState({
errors: {
...errors,
...(data.phone_number ? { phone_number: data.phone_number } : null),
...(data.email ? { email: data.email.toString() } : { email: "" }),
...(data.password1
? { password1: data.password1.toString() }
Expand All @@ -106,14 +117,15 @@ export default class Registration extends React.Component {
render() {
const {
registration,
settings,
language,
termsAndConditions,
privacyPolicy,
orgSlug,
match,
} = this.props;
const { buttons, additional_info_text, input_fields, links } = registration;
const { email, password1, password2, errors, success } = this.state;
const { phone_number, email, password1, password2, errors, success } = this.state;
return (
<>
<div className="owisp-registration-container">
Expand All @@ -132,6 +144,48 @@ export default class Registration extends React.Component {
</div>
)}

{settings && settings.sms_verification && input_fields.phone_number ? (
<>
<div className="owisp-registration-label owisp-registration-label-phone-number">
<label
className="owisp-registration-label-text owisp-registration-label-text-phone-number"
htmlFor="owisp-registration-phone-number"
>
{getText(input_fields.phone_number.label, language)}
</label>

{errors.phone_number && (
<div className="owisp-registration-error owisp-registration-error-phone-number">
<span className="owisp-registration-error-icon">!</span>
<span className="owisp-registration-error-text owisp-registration-error-text-phone-number">
{errors.phone_number}
</span>
</div>
)}
<PhoneInput
name="phone_number"
country={input_fields.phone_number.country}
onlyCountries={input_fields.phone_number.only_countries || []}
preferredCountries={input_fields.phone_number.preferred_countries || []}
excludeCountries={input_fields.phone_number.exclude_countries || []}
value={phone_number}
onChange={value => this.setState({phone_number: `+${value}`})}
placeholder={getText(
input_fields.phone_number.placeholder,
language,
)}
enableSearch={Boolean(input_fields.phone_number.enable_search)}
inputProps={{
name: "phone_number",
id: "owisp-registration-phone-number",
className: `form-control owisp-registration-input owisp-registration-input-phone-number ${errors.email ? "error" : ""}`,
required: true,
}}
/>
</div>
</>
) : null}

{input_fields.email ? (
<>
<label
Expand All @@ -141,6 +195,14 @@ export default class Registration extends React.Component {
<div className="owisp-registration-label-text owisp-registration-label-text-email">
{getText(input_fields.email.label, language)}
</div>
{errors.email && (
<div className="owisp-registration-error owisp-registration-error-email">
<span className="owisp-registration-error-icon">!</span>
<span className="owisp-registration-error-text owisp-registration-error-text-email">
{errors.email}
</span>
</div>
)}
<input
className={`owisp-registration-input owisp-registration-input-email ${
errors.email ? "error" : ""
Expand Down Expand Up @@ -170,14 +232,7 @@ export default class Registration extends React.Component {
}
/>
</label>
{errors.email && (
<div className="owisp-registration-error owisp-registration-error-email">
<span className="owisp-registration-error-icon">!</span>
<span className="owisp-registration-error-text owisp-registration-error-text-email">
{errors.email}
</span>
</div>
)}

</>
) : null}

Expand Down Expand Up @@ -349,6 +404,9 @@ export default class Registration extends React.Component {
}
Registration.contextType = LoadingContext;
Registration.propTypes = {
settings: PropTypes.shape({
sms_verification: PropTypes.bool
}).isRequired,
registration: PropTypes.shape({
header: PropTypes.object,
buttons: PropTypes.shape({
Expand Down Expand Up @@ -376,6 +434,15 @@ Registration.propTypes = {
pattern: PropTypes.string,
pattern_description: PropTypes.object
}),
phone_number: PropTypes.shape({
label: PropTypes.object,
placeholder: PropTypes.object,
country: PropTypes.string.isRequired,
only_countries: PropTypes.array,
preferred_countries: PropTypes.array,
exclude_countries: PropTypes.array,
enable_search: PropTypes.bool
})
}),
additional_info_text: PropTypes.object,
links: PropTypes.object,
Expand Down
15 changes: 12 additions & 3 deletions client/test-config.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,25 @@
"name": "Default",
"slug": "default",
"default_language": "en",
"auto_login": true
"auto_login": true,
"settings": {
"sms_verification": false
}
},
{
"name": "test org 1",
"slug": "test-org-1",
"default_language": "it",
"auto_login": false
"auto_login": false,
"settings": {
"sms_verification": false
}
},
{
"name": "test org 2",
"slug": "test-org-2"
"slug": "test-org-2",
"settings": {
"sms_verification": false
}
}
]
2 changes: 2 additions & 0 deletions config/setup.js
Original file line number Diff line number Diff line change
Expand Up @@ -100,13 +100,15 @@ fs.readdirSync(configDir).forEach(file => {
const clientConfig = {
name: config.name,
slug: config.slug,
settings: config.settings,
...config.client,
};

// extract server config from object
const serverConfig = {
name: config.name,
slug: config.slug,
settings: config.settings,
...config.server,
};

Expand Down
2 changes: 2 additions & 0 deletions internals/generators/organization/config.yml.hbs
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
---
name: "{{name}}"
slug: "{{slug}}"
settings:
sms_verification: {{sms_verification}}
# configuration variables for the server app
server:
Expand Down
6 changes: 6 additions & 0 deletions internals/generators/organization/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,12 @@ module.exports = {
return "The organization uuid is required";
},
},
{
type: "confirm",
name: "sms_verification",
message: "Does this organization require SMS verification for its users?",
default: false,
},
{
type: "input",
name: "secret_key",
Expand Down
33 changes: 33 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
"react-cookie": "^4.0.3",
"react-dom": "^16.13.1",
"react-helmet": "^5.2.1",
"react-phone-input-2": "^2.13.8",
"react-redux": "^7.2.0",
"react-router-dom": "^5.2.0",
"react-toastify": "^5.5.0",
Expand Down
21 changes: 12 additions & 9 deletions server/controllers/registration-controller.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,27 +13,30 @@ const registration = (req, res) => {
if (org.slug === reqOrg) {
// merge default config and custom config
const conf = merge(defaultConfig, org);
const {host} = conf;
const {host, settings} = conf;
let registerUrl = conf.proxy_urls.registration;
// replacing org_slug param with the slug
registerUrl = registerUrl.replace("{org_slug}", org.slug);
const timeout = conf.timeout * 1000;
const {username, email, password1, password2} = req.body;

// make AJAX request
const postData = {
email,
username,
password1,
password2,
};
if (settings && settings.sms_verification) {
postData.phone_number = req.body.phone_number;
}
// send request
axios({
method: "post",
headers: {
"content-type": "application/x-www-form-urlencoded",
},
url: `${host}${registerUrl}/`,
timeout,
data: qs.stringify({
email,
username,
password1,
password2,
}),
data: qs.stringify(postData),
})
.then(response => {
const authTokenCookie = cookie.sign(
Expand Down

0 comments on commit 94e7857

Please sign in to comment.