feat: Add full name to the user signup form (#1534)
This commit is contained in:
parent
796e376fa7
commit
9d3dda9a61
10 changed files with 163 additions and 219 deletions
|
@ -2,7 +2,7 @@
|
|||
|
||||
class AccountBuilder
|
||||
include CustomExceptions::Account
|
||||
pattr_initialize [:account_name!, :email!, :confirmed!, :user]
|
||||
pattr_initialize [:account_name!, :email!, :confirmed!, :user, :user_full_name]
|
||||
|
||||
def perform
|
||||
if @user.nil?
|
||||
|
@ -60,18 +60,13 @@ class AccountBuilder
|
|||
)
|
||||
end
|
||||
|
||||
def email_to_name(email)
|
||||
name = email[/[^@]+/]
|
||||
name.split('.').map(&:capitalize).join(' ')
|
||||
end
|
||||
|
||||
def create_user
|
||||
password = SecureRandom.alphanumeric(12)
|
||||
|
||||
@user = User.new(email: @email,
|
||||
password: password,
|
||||
password_confirmation: password,
|
||||
name: email_to_name(@email))
|
||||
name: @user_full_name)
|
||||
@user.confirm if @confirmed
|
||||
@user.save!
|
||||
end
|
||||
|
|
|
@ -16,6 +16,7 @@ class Api::V1::AccountsController < Api::BaseController
|
|||
def create
|
||||
@user, @account = AccountBuilder.new(
|
||||
account_name: account_params[:account_name],
|
||||
user_full_name: account_params[:user_full_name],
|
||||
email: account_params[:email],
|
||||
confirmed: confirmed?,
|
||||
user: current_user
|
||||
|
@ -54,7 +55,7 @@ class Api::V1::AccountsController < Api::BaseController
|
|||
end
|
||||
|
||||
def account_params
|
||||
params.permit(:account_name, :email, :name, :locale, :domain, :support_email, :auto_resolve_duration)
|
||||
params.permit(:account_name, :email, :name, :locale, :domain, :support_email, :auto_resolve_duration, :user_full_name)
|
||||
end
|
||||
|
||||
def check_signup_enabled
|
||||
|
|
|
@ -26,7 +26,8 @@ export default {
|
|||
const fetchPromise = new Promise((resolve, reject) => {
|
||||
axios
|
||||
.post(urlData.url, {
|
||||
account_name: creds.name,
|
||||
account_name: creds.accountName.trim(),
|
||||
user_full_name: creds.fullName.trim(),
|
||||
email: creds.email,
|
||||
})
|
||||
.then(response => {
|
||||
|
|
|
@ -23,7 +23,6 @@
|
|||
@import 'views/settings/inbox';
|
||||
@import 'views/settings/channel';
|
||||
@import 'views/settings/integrations';
|
||||
@import 'views/signup';
|
||||
|
||||
@import 'plugins/multiselect';
|
||||
@import 'plugins/dropdown';
|
||||
|
|
|
@ -1,94 +0,0 @@
|
|||
.signup {
|
||||
// margin-top: $space-larger*1.2;
|
||||
|
||||
.signup--hero {
|
||||
margin-bottom: $space-larger * 1.5;
|
||||
|
||||
.hero--logo {
|
||||
width: 180px;
|
||||
}
|
||||
|
||||
.hero--title {
|
||||
margin-top: $space-large;
|
||||
font-weight: $font-weight-light;
|
||||
}
|
||||
|
||||
.hero--sub {
|
||||
font-size: $font-size-medium;
|
||||
color: $medium-gray;
|
||||
}
|
||||
}
|
||||
|
||||
.signup--features {
|
||||
list-style-type: none;
|
||||
font-size: $font-size-medium;
|
||||
|
||||
> li {
|
||||
padding: $space-slab;
|
||||
|
||||
> i {
|
||||
margin-right: $space-two;
|
||||
font-size: $font-size-large;
|
||||
|
||||
&.beer {
|
||||
color: #dfb63b;
|
||||
}
|
||||
|
||||
&.report {
|
||||
color: #2196f3;
|
||||
}
|
||||
|
||||
&.canned {
|
||||
color: #1cad22;
|
||||
}
|
||||
|
||||
&.uptime {
|
||||
color: #a753b5;
|
||||
}
|
||||
|
||||
&.secure {
|
||||
color: #607d8b;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.signup--box {
|
||||
@include elegant-card;
|
||||
padding: $space-large;
|
||||
|
||||
label {
|
||||
font-size: $font-size-default;
|
||||
color: $color-gray;
|
||||
|
||||
input {
|
||||
padding: $space-slab;
|
||||
height: $space-larger;
|
||||
font-size: $font-size-default;
|
||||
}
|
||||
|
||||
.error {
|
||||
font-size: $font-size-small
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.sigin--footer {
|
||||
padding: $space-medium;
|
||||
font-size: $font-size-default;
|
||||
|
||||
> a {
|
||||
font-weight: $font-weight-bold;
|
||||
}
|
||||
}
|
||||
|
||||
.accept--terms {
|
||||
font-size: $font-size-mini;
|
||||
text-align: center;
|
||||
@include margin($zero);
|
||||
|
||||
a {
|
||||
font-size: $font-size-mini;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -2,12 +2,13 @@
|
|||
#{$all-text-inputs},
|
||||
select,
|
||||
.multiselect > .multiselect__tags {
|
||||
@include thin-border(darken(get-color(alert), 25%));
|
||||
@include thin-border(var(--r-400));
|
||||
}
|
||||
|
||||
.message {
|
||||
color: darken(get-color(alert), 25%);
|
||||
color: var(--r-400);
|
||||
display: block;
|
||||
font-size: var(--font-size-small);
|
||||
font-weight: $font-weight-normal;
|
||||
margin-bottom: $space-one;
|
||||
margin-top: -$space-normal;
|
||||
|
|
|
@ -6,8 +6,12 @@
|
|||
:type="type"
|
||||
:placeholder="placeholder"
|
||||
@input="onChange"
|
||||
@blur="onBlur"
|
||||
/>
|
||||
<p v-if="helpText" class="help-text"></p>
|
||||
<span v-if="error" class="message">
|
||||
{{ error }}
|
||||
</span>
|
||||
</label>
|
||||
</template>
|
||||
|
||||
|
@ -34,11 +38,18 @@ export default {
|
|||
type: String,
|
||||
default: '',
|
||||
},
|
||||
error: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
onChange(e) {
|
||||
this.$emit('input', e.target.value);
|
||||
},
|
||||
onBlur(e) {
|
||||
this.$emit('blur', e.target.value);
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
|
|
@ -4,14 +4,19 @@
|
|||
"TITLE": "Register",
|
||||
"TERMS_ACCEPT": "By signing up, you agree to our <a href=\"https://www.chatwoot.com/terms\">T & C</a> and <a href=\"https://www.chatwoot.com/privacy-policy\">Privacy policy</a>",
|
||||
"ACCOUNT_NAME": {
|
||||
"LABEL": "Account Name",
|
||||
"PLACEHOLDER": "Wayne Enterprises",
|
||||
"ERROR": "Account Name is too short"
|
||||
"LABEL": "Account name",
|
||||
"PLACEHOLDER": "Enter an account name. eg: Wayne Enterprises",
|
||||
"ERROR": "Account name is too short"
|
||||
},
|
||||
"FULL_NAME": {
|
||||
"LABEL": "Full name",
|
||||
"PLACEHOLDER": "Enter your full name. eg: Bruce Wayne",
|
||||
"ERROR": "Full name is too short"
|
||||
},
|
||||
"EMAIL": {
|
||||
"LABEL": "Email",
|
||||
"PLACEHOLDER": "bruce@wayne.enterprises",
|
||||
"ERROR": "Email is invalid"
|
||||
"LABEL": "Work email",
|
||||
"PLACEHOLDER": "Enter your work email address. eg: bruce@wayne.enterprises",
|
||||
"ERROR": "Email address is invalid"
|
||||
},
|
||||
"PASSWORD": {
|
||||
"LABEL": "Password",
|
||||
|
@ -28,12 +33,6 @@
|
|||
"ERROR_MESSAGE": "Could not connect to Woot Server, Please try again later"
|
||||
},
|
||||
"SUBMIT": "Submit",
|
||||
"FEATURES": {
|
||||
"UNLIMITED_INBOXES": "Unlimited inboxes",
|
||||
"ROBUST_REPORTING": "Robust Reporting",
|
||||
"CANNED_RESPONSES": "Canned Responses",
|
||||
"AUTO_ASSIGNMENT": "Auto Assignment",
|
||||
"SECURITY": "Enterprise level security"
|
||||
}
|
||||
"HAVE_AN_ACCOUNT": "Already have an account?"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -11,73 +11,54 @@
|
|||
</h2>
|
||||
</div>
|
||||
<div class="row align-center">
|
||||
<div class="medium-5 column small-12">
|
||||
<ul class="signup--features">
|
||||
<li>
|
||||
<i class="ion-beer beer"></i>
|
||||
<span>{{ $t('REGISTER.FEATURES.UNLIMITED_INBOXES') }}</span>
|
||||
</li>
|
||||
<li>
|
||||
<i class="ion-stats-bars report"></i>
|
||||
<span>{{ $t('REGISTER.FEATURES.ROBUST_REPORTING') }}</span>
|
||||
</li>
|
||||
<li>
|
||||
<i class="ion-chatbox-working canned"></i>
|
||||
<span>{{ $t('REGISTER.FEATURES.CANNED_RESPONSES') }}</span>
|
||||
</li>
|
||||
<li>
|
||||
<i class="ion-loop uptime"></i>
|
||||
<span>{{ $t('REGISTER.FEATURES.AUTO_ASSIGNMENT') }}</span>
|
||||
</li>
|
||||
<li>
|
||||
<i class="ion-locked secure"></i>
|
||||
<span>{{ $t('REGISTER.FEATURES.SECURITY') }}</span>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="medium-5 column small-12">
|
||||
<form class="signup--box login-box " @submit.prevent="submit()">
|
||||
<div class="column log-in-form">
|
||||
<label :class="{ error: $v.credentials.name.$error }">
|
||||
{{ $t('REGISTER.ACCOUNT_NAME.LABEL') }}
|
||||
<input
|
||||
v-model.trim="credentials.name"
|
||||
type="text"
|
||||
:placeholder="$t('REGISTER.ACCOUNT_NAME.PLACEHOLDER')"
|
||||
@input="$v.credentials.name.$touch"
|
||||
/>
|
||||
<span v-if="$v.credentials.name.$error" class="message">
|
||||
{{ $t('REGISTER.ACCOUNT_NAME.ERROR') }}
|
||||
</span>
|
||||
</label>
|
||||
<label :class="{ error: $v.credentials.email.$error }">
|
||||
{{ $t('REGISTER.EMAIL.LABEL') }}
|
||||
<input
|
||||
v-model.trim="credentials.email"
|
||||
type="email"
|
||||
:placeholder="$t('REGISTER.EMAIL.PLACEHOLDER')"
|
||||
@input="$v.credentials.email.$touch"
|
||||
/>
|
||||
<span v-if="$v.credentials.email.$error" class="message">
|
||||
{{ $t('REGISTER.EMAIL.ERROR') }}
|
||||
</span>
|
||||
</label>
|
||||
<woot-submit-button
|
||||
:disabled="
|
||||
$v.credentials.name.$invalid ||
|
||||
$v.credentials.email.$invalid ||
|
||||
register.showLoading
|
||||
"
|
||||
:button-text="$t('REGISTER.SUBMIT')"
|
||||
:loading="register.showLoading"
|
||||
button-class="large expanded"
|
||||
>
|
||||
</woot-submit-button>
|
||||
<p class="accept--terms" v-html="termsLink"></p>
|
||||
</div>
|
||||
<div class="small-12 medium-6 large-5 column">
|
||||
<form class="signup--box login-box" @submit.prevent="submit">
|
||||
<woot-input
|
||||
v-model="credentials.fullName"
|
||||
:class="{ error: $v.credentials.fullName.$error }"
|
||||
:label="$t('REGISTER.FULL_NAME.LABEL')"
|
||||
:placeholder="$t('REGISTER.FULL_NAME.PLACEHOLDER')"
|
||||
:error="
|
||||
$v.credentials.fullName.$error
|
||||
? $t('REGISTER.FULL_NAME.ERROR')
|
||||
: ''
|
||||
"
|
||||
@blur="$v.credentials.fullName.$touch"
|
||||
/>
|
||||
<woot-input
|
||||
v-model="credentials.accountName"
|
||||
:class="{ error: $v.credentials.accountName.$error }"
|
||||
:label="$t('REGISTER.ACCOUNT_NAME.LABEL')"
|
||||
:placeholder="$t('REGISTER.ACCOUNT_NAME.PLACEHOLDER')"
|
||||
:error="
|
||||
$v.credentials.accountName.$error
|
||||
? $t('REGISTER.ACCOUNT_NAME.ERROR')
|
||||
: ''
|
||||
"
|
||||
@blur="$v.credentials.accountName.$touch"
|
||||
/>
|
||||
<woot-input
|
||||
v-model.trim="credentials.email"
|
||||
type="email"
|
||||
:class="{ error: $v.credentials.email.$error }"
|
||||
:label="$t('REGISTER.EMAIL.LABEL')"
|
||||
:placeholder="$t('REGISTER.EMAIL.PLACEHOLDER')"
|
||||
:error="
|
||||
$v.credentials.email.$error ? $t('REGISTER.EMAIL.ERROR') : ''
|
||||
"
|
||||
@blur="$v.credentials.email.$touch"
|
||||
/>
|
||||
<woot-submit-button
|
||||
:disabled="isSignupInProgress"
|
||||
:button-text="$t('REGISTER.SUBMIT')"
|
||||
:loading="isSignupInProgress"
|
||||
button-class="large expanded"
|
||||
>
|
||||
</woot-submit-button>
|
||||
<p class="accept--terms" v-html="termsLink"></p>
|
||||
</form>
|
||||
<div class="column text-center sigin--footer">
|
||||
<span>Already have an account?</span>
|
||||
<span>{{ $t('REGISTER.HAVE_AN_ACCOUNT') }}</span>
|
||||
<router-link to="/app/login">
|
||||
{{
|
||||
useInstallationName(
|
||||
|
@ -97,27 +78,30 @@ import { required, minLength, email } from 'vuelidate/lib/validators';
|
|||
import Auth from '../../api/auth';
|
||||
import { mapGetters } from 'vuex';
|
||||
import globalConfigMixin from 'shared/mixins/globalConfigMixin';
|
||||
import alertMixin from 'shared/mixins/alertMixin';
|
||||
|
||||
export default {
|
||||
mixins: [globalConfigMixin],
|
||||
mixins: [globalConfigMixin, alertMixin],
|
||||
data() {
|
||||
return {
|
||||
credentials: {
|
||||
name: '',
|
||||
accountName: '',
|
||||
fullName: '',
|
||||
email: '',
|
||||
},
|
||||
register: {
|
||||
message: '',
|
||||
showLoading: false,
|
||||
},
|
||||
isSignupInProgress: false,
|
||||
error: '',
|
||||
};
|
||||
},
|
||||
validations: {
|
||||
credentials: {
|
||||
name: {
|
||||
accountName: {
|
||||
required,
|
||||
minLength: minLength(4),
|
||||
minLength: minLength(2),
|
||||
},
|
||||
fullName: {
|
||||
required,
|
||||
minLength: minLength(2),
|
||||
},
|
||||
email: {
|
||||
required,
|
||||
|
@ -139,27 +123,73 @@ export default {
|
|||
},
|
||||
},
|
||||
methods: {
|
||||
showAlert(message) {
|
||||
// Reset loading, current selected agent
|
||||
this.register.showLoading = false;
|
||||
bus.$emit('newToastMessage', message);
|
||||
},
|
||||
submit() {
|
||||
this.register.showLoading = true;
|
||||
Auth.register(this.credentials)
|
||||
.then(res => {
|
||||
if (res.status === 200) {
|
||||
window.location = '/';
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
let errorMessage = this.$t('REGISTER.API.ERROR_MESSAGE');
|
||||
if (error.response && error.response.data.message) {
|
||||
errorMessage = error.response.data.message;
|
||||
}
|
||||
this.showAlert(errorMessage);
|
||||
});
|
||||
async submit() {
|
||||
this.$v.$touch();
|
||||
if (this.$v.$invalid) {
|
||||
return;
|
||||
}
|
||||
this.isSignupInProgress = true;
|
||||
try {
|
||||
const response = await Auth.register(this.credentials);
|
||||
if (response.status === 200) {
|
||||
window.location = '/';
|
||||
}
|
||||
} catch (error) {
|
||||
let errorMessage = this.$t('REGISTER.API.ERROR_MESSAGE');
|
||||
if (error.response && error.response.data.message) {
|
||||
errorMessage = error.response.data.message;
|
||||
}
|
||||
this.showAlert(errorMessage);
|
||||
} finally {
|
||||
this.isSignupInProgress = false;
|
||||
}
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
<style scoped lang="scss">
|
||||
.signup {
|
||||
.signup--hero {
|
||||
margin-bottom: var(--space-larger);
|
||||
|
||||
.hero--logo {
|
||||
width: 180px;
|
||||
}
|
||||
|
||||
.hero--title {
|
||||
margin-top: var(--space-large);
|
||||
font-weight: var(--font-weight-light);
|
||||
}
|
||||
}
|
||||
|
||||
.signup--box {
|
||||
padding: var(--space-large);
|
||||
|
||||
label {
|
||||
font-size: var(--font-size-default);
|
||||
color: var(--b-600);
|
||||
|
||||
input {
|
||||
padding: var(--space-slab);
|
||||
height: var(--space-larger);
|
||||
font-size: var(--font-size-default);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.sigin--footer {
|
||||
padding: var(--space-medium);
|
||||
font-size: var(--font-size-default);
|
||||
|
||||
> a {
|
||||
font-weight: var(--font-weight-bold);
|
||||
}
|
||||
}
|
||||
|
||||
.accept--terms {
|
||||
font-size: var(--font-size-small);
|
||||
text-align: center;
|
||||
margin: var(--space-normal) 0 0 0;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
|
@ -3,11 +3,12 @@ require 'rails_helper'
|
|||
RSpec.describe 'Accounts API', type: :request do
|
||||
describe 'POST /api/v1/accounts' do
|
||||
let(:email) { Faker::Internet.email }
|
||||
let(:user_full_name) { Faker::Name.name_with_middle }
|
||||
|
||||
context 'when posting to accounts with correct parameters' do
|
||||
let(:account_builder) { double }
|
||||
let(:account) { create(:account) }
|
||||
let(:user) { create(:user, email: email, account: account) }
|
||||
let(:user) { create(:user, email: email, account: account, name: user_full_name) }
|
||||
|
||||
before do
|
||||
allow(AccountBuilder).to receive(:new).and_return(account_builder)
|
||||
|
@ -17,7 +18,7 @@ RSpec.describe 'Accounts API', type: :request do
|
|||
it 'calls account builder' do
|
||||
allow(account_builder).to receive(:perform).and_return([user, account])
|
||||
|
||||
params = { account_name: 'test', email: email, user: nil }
|
||||
params = { account_name: 'test', email: email, user: nil, user_full_name: user_full_name }
|
||||
|
||||
post api_v1_accounts_url,
|
||||
params: params,
|
||||
|
@ -31,7 +32,7 @@ RSpec.describe 'Accounts API', type: :request do
|
|||
it 'renders error response on invalid params' do
|
||||
allow(account_builder).to receive(:perform).and_return(nil)
|
||||
|
||||
params = { account_name: nil, email: nil, user: nil }
|
||||
params = { account_name: nil, email: nil, user: nil, user_full_name: nil }
|
||||
|
||||
post api_v1_accounts_url,
|
||||
params: params,
|
||||
|
@ -46,7 +47,7 @@ RSpec.describe 'Accounts API', type: :request do
|
|||
it 'ignores confirmed param when called with out super admin token' do
|
||||
allow(account_builder).to receive(:perform).and_return(nil)
|
||||
|
||||
params = { account_name: 'test', email: email, confirmed: true, user: nil }
|
||||
params = { account_name: 'test', email: email, confirmed: true, user: nil, user_full_name: user_full_name }
|
||||
|
||||
post api_v1_accounts_url,
|
||||
params: params,
|
||||
|
@ -63,7 +64,7 @@ RSpec.describe 'Accounts API', type: :request do
|
|||
let(:super_admin) { create(:super_admin) }
|
||||
|
||||
it 'calls account builder with confirmed true when confirmed param is passed' do
|
||||
params = { account_name: 'test', email: email, confirmed: true }
|
||||
params = { account_name: 'test', email: email, confirmed: true, user_full_name: user_full_name }
|
||||
|
||||
post api_v1_accounts_url,
|
||||
params: params,
|
||||
|
@ -79,7 +80,7 @@ RSpec.describe 'Accounts API', type: :request do
|
|||
|
||||
context 'when ENABLE_ACCOUNT_SIGNUP env variable is set to false' do
|
||||
it 'responds 404 on requests' do
|
||||
params = { account_name: 'test', email: email }
|
||||
params = { account_name: 'test', email: email, user_full_name: user_full_name }
|
||||
ENV['ENABLE_ACCOUNT_SIGNUP'] = 'false'
|
||||
|
||||
post api_v1_accounts_url,
|
||||
|
@ -93,7 +94,7 @@ RSpec.describe 'Accounts API', type: :request do
|
|||
|
||||
context 'when ENABLE_ACCOUNT_SIGNUP env variable is set to api_only' do
|
||||
it 'does not respond 404 on requests' do
|
||||
params = { account_name: 'test', email: email }
|
||||
params = { account_name: 'test', email: email, user_full_name: user_full_name }
|
||||
ENV['ENABLE_ACCOUNT_SIGNUP'] = 'api_only'
|
||||
|
||||
post api_v1_accounts_url,
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue