Feature: Agent Profile Update with avatar (#449)
* Feature: Agent Profile Update with avatar * Add Update Profile with name, avatar, email and password
This commit is contained in:
parent
e61ba95cf7
commit
c4e2a84f65
25 changed files with 584 additions and 133 deletions
|
@ -95,24 +95,24 @@ jobs:
|
||||||
command: yarn run eslint
|
command: yarn run eslint
|
||||||
|
|
||||||
# Run rails tests
|
# Run rails tests
|
||||||
- run:
|
- run:
|
||||||
name: Run backend tests
|
name: Run backend tests
|
||||||
command: |
|
command: |
|
||||||
bundle exec rspec $(circleci tests glob "spec/**/*_spec.rb" | circleci tests split --split-by=timings)
|
bundle exec rspec $(circleci tests glob "spec/**/*_spec.rb" | circleci tests split --split-by=timings)
|
||||||
./tmp/cc-test-reporter format-coverage -t simplecov -o tmp/codeclimate.backend.json coverage/backend/.resultset.json
|
./tmp/cc-test-reporter format-coverage -t simplecov -o tmp/codeclimate.backend.json coverage/backend/.resultset.json
|
||||||
- persist_to_workspace:
|
- persist_to_workspace:
|
||||||
root: tmp
|
root: tmp
|
||||||
paths:
|
paths:
|
||||||
- codeclimate.backend.json
|
- codeclimate.backend.json
|
||||||
|
|
||||||
- run:
|
- run:
|
||||||
name: Run frontend tests
|
name: Run frontend tests
|
||||||
command: |
|
command: |
|
||||||
yarn test:coverage
|
yarn test:coverage
|
||||||
./tmp/cc-test-reporter format-coverage -t lcov -o tmp/codeclimate.frontend.json buildreports/lcov.info
|
./tmp/cc-test-reporter format-coverage -t lcov -o tmp/codeclimate.frontend.json buildreports/lcov.info
|
||||||
- persist_to_workspace:
|
- persist_to_workspace:
|
||||||
root: tmp
|
root: tmp
|
||||||
paths:
|
paths:
|
||||||
- codeclimate.frontend.json
|
- codeclimate.frontend.json
|
||||||
|
|
||||||
# collect reports
|
# collect reports
|
||||||
|
@ -126,4 +126,4 @@ jobs:
|
||||||
name: Upload coverage results to Code Climate
|
name: Upload coverage results to Code Climate
|
||||||
command: |
|
command: |
|
||||||
./tmp/cc-test-reporter sum-coverage tmp/codeclimate.*.json -p 2 -o tmp/codeclimate.total.json
|
./tmp/cc-test-reporter sum-coverage tmp/codeclimate.*.json -p 2 -o tmp/codeclimate.total.json
|
||||||
./tmp/cc-test-reporter upload-coverage -i tmp/codeclimate.total.json
|
./tmp/cc-test-reporter upload-coverage -i tmp/codeclimate.total.json
|
||||||
|
|
1
__mocks__/fileMock.js
Normal file
1
__mocks__/fileMock.js
Normal file
|
@ -0,0 +1 @@
|
||||||
|
module.exports = '';
|
|
@ -1,5 +1,5 @@
|
||||||
class Api::V1::ProfilesController < Api::BaseController
|
class Api::V1::ProfilesController < Api::BaseController
|
||||||
before_action :fetch_user
|
before_action :set_user
|
||||||
|
|
||||||
def show
|
def show
|
||||||
render json: @user
|
render json: @user
|
||||||
|
@ -7,12 +7,11 @@ class Api::V1::ProfilesController < Api::BaseController
|
||||||
|
|
||||||
def update
|
def update
|
||||||
@user.update!(profile_params)
|
@user.update!(profile_params)
|
||||||
render json: @user
|
|
||||||
end
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
def fetch_user
|
def set_user
|
||||||
@user = current_user
|
@user = current_user
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -18,8 +18,7 @@ export default {
|
||||||
},
|
},
|
||||||
|
|
||||||
mounted() {
|
mounted() {
|
||||||
this.$store.dispatch('set_user');
|
this.$store.dispatch('setUser');
|
||||||
this.$store.dispatch('validityCheck');
|
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
|
@ -1,29 +1,10 @@
|
||||||
/* eslint no-console: 0 */
|
/* eslint no-console: 0 */
|
||||||
/* global axios */
|
/* global axios */
|
||||||
/* eslint no-undef: "error" */
|
/* eslint no-undef: "error" */
|
||||||
/* eslint-env browser */
|
|
||||||
/* eslint no-unused-expressions: ["error", { "allowShortCircuit": true }] */
|
|
||||||
|
|
||||||
import moment from 'moment';
|
|
||||||
import Cookies from 'js-cookie';
|
import Cookies from 'js-cookie';
|
||||||
import endPoints from './endPoints';
|
import endPoints from './endPoints';
|
||||||
import { frontendURL } from '../helper/URLHelper';
|
import { setAuthCredentials, clearCookiesOnLogout } from '../store/utils/api';
|
||||||
|
|
||||||
const setAuthCredentials = response => {
|
|
||||||
const expiryDate = moment.unix(response.headers.expiry);
|
|
||||||
Cookies.set('auth_data', response.headers, {
|
|
||||||
expires: expiryDate.diff(moment(), 'days'),
|
|
||||||
});
|
|
||||||
Cookies.set('user', response.data.data, {
|
|
||||||
expires: expiryDate.diff(moment(), 'days'),
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
const clearCookiesOnLogout = () => {
|
|
||||||
Cookies.remove('auth_data');
|
|
||||||
Cookies.remove('user');
|
|
||||||
window.location = frontendURL('login');
|
|
||||||
};
|
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
login(creds) {
|
login(creds) {
|
||||||
|
@ -60,20 +41,7 @@ export default {
|
||||||
},
|
},
|
||||||
validityCheck() {
|
validityCheck() {
|
||||||
const urlData = endPoints('validityCheck');
|
const urlData = endPoints('validityCheck');
|
||||||
const fetchPromise = new Promise((resolve, reject) => {
|
return axios.get(urlData.url);
|
||||||
axios
|
|
||||||
.get(urlData.url)
|
|
||||||
.then(response => {
|
|
||||||
resolve(response);
|
|
||||||
})
|
|
||||||
.catch(error => {
|
|
||||||
if (error.response.status === 401) {
|
|
||||||
clearCookiesOnLogout();
|
|
||||||
}
|
|
||||||
reject(error);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
return fetchPromise;
|
|
||||||
},
|
},
|
||||||
logout() {
|
logout() {
|
||||||
const urlData = endPoints('logout');
|
const urlData = endPoints('logout');
|
||||||
|
@ -136,13 +104,7 @@ export default {
|
||||||
password,
|
password,
|
||||||
})
|
})
|
||||||
.then(response => {
|
.then(response => {
|
||||||
const expiryDate = moment.unix(response.headers.expiry);
|
setAuthCredentials(response);
|
||||||
Cookies.set('auth_data', response.headers, {
|
|
||||||
expires: expiryDate.diff(moment(), 'days'),
|
|
||||||
});
|
|
||||||
Cookies.set('user', response.data.data, {
|
|
||||||
expires: expiryDate.diff(moment(), 'days'),
|
|
||||||
});
|
|
||||||
resolve(response);
|
resolve(response);
|
||||||
})
|
})
|
||||||
.catch(error => {
|
.catch(error => {
|
||||||
|
@ -155,4 +117,22 @@ export default {
|
||||||
const urlData = endPoints('resetPassword');
|
const urlData = endPoints('resetPassword');
|
||||||
return axios.post(urlData.url, { email });
|
return axios.post(urlData.url, { email });
|
||||||
},
|
},
|
||||||
|
|
||||||
|
profileUpdate({ name, email, password, password_confirmation, avatar }) {
|
||||||
|
const formData = new FormData();
|
||||||
|
if (name) {
|
||||||
|
formData.append('profile[name]', name);
|
||||||
|
}
|
||||||
|
if (email) {
|
||||||
|
formData.append('profile[email]', email);
|
||||||
|
}
|
||||||
|
if (password && password_confirmation) {
|
||||||
|
formData.append('profile[password]', password);
|
||||||
|
formData.append('profile[password_confirmation]', password_confirmation);
|
||||||
|
}
|
||||||
|
if (avatar) {
|
||||||
|
formData.append('profile[avatar]', avatar);
|
||||||
|
}
|
||||||
|
return axios.put(endPoints('profileUpdate').url, formData);
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
|
@ -10,6 +10,9 @@ const endPoints = {
|
||||||
validityCheck: {
|
validityCheck: {
|
||||||
url: '/auth/validate_token',
|
url: '/auth/validate_token',
|
||||||
},
|
},
|
||||||
|
profileUpdate: {
|
||||||
|
url: '/api/v1/profile',
|
||||||
|
},
|
||||||
logout: {
|
logout: {
|
||||||
url: 'auth/sign_out',
|
url: 'auth/sign_out',
|
||||||
},
|
},
|
||||||
|
|
|
@ -42,12 +42,21 @@
|
||||||
class="dropdown-pane top"
|
class="dropdown-pane top"
|
||||||
>
|
>
|
||||||
<ul class="vertical dropdown menu">
|
<ul class="vertical dropdown menu">
|
||||||
<li><a href="#" @click.prevent="logout()">Logout</a></li>
|
<li>
|
||||||
|
<router-link to="/app/profile/settings">
|
||||||
|
{{ $t('SIDEBAR.PROFILE_SETTINGS') }}
|
||||||
|
</router-link>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<a href="#" @click.prevent="logout()">
|
||||||
|
{{ $t('SIDEBAR.LOGOUT') }}
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
</transition>
|
</transition>
|
||||||
<div class="current-user" @click.prevent="showOptions()">
|
<div class="current-user" @click.prevent="showOptions()">
|
||||||
<thumbnail :src="gravatarUrl()" :username="currentUser.name" />
|
<thumbnail :src="currentUser.avatar_url" :username="currentUser.name" />
|
||||||
<div class="current-user--data">
|
<div class="current-user--data">
|
||||||
<h3 class="current-user--name">
|
<h3 class="current-user--name">
|
||||||
{{ currentUser.name }}
|
{{ currentUser.name }}
|
||||||
|
@ -65,7 +74,6 @@
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import { mapGetters } from 'vuex';
|
import { mapGetters } from 'vuex';
|
||||||
import md5 from 'md5';
|
|
||||||
import { mixin as clickaway } from 'vue-clickaway';
|
import { mixin as clickaway } from 'vue-clickaway';
|
||||||
|
|
||||||
import adminMixin from '../../mixins/isAdmin';
|
import adminMixin from '../../mixins/isAdmin';
|
||||||
|
@ -99,6 +107,7 @@ export default {
|
||||||
daysLeft: 'getTrialLeft',
|
daysLeft: 'getTrialLeft',
|
||||||
subscriptionData: 'getSubscription',
|
subscriptionData: 'getSubscription',
|
||||||
inboxes: 'inboxes/getInboxes',
|
inboxes: 'inboxes/getInboxes',
|
||||||
|
currentUser: 'getCurrentUser',
|
||||||
}),
|
}),
|
||||||
accessibleMenuItems() {
|
accessibleMenuItems() {
|
||||||
// get all keys in menuGroup
|
// get all keys in menuGroup
|
||||||
|
@ -144,9 +153,6 @@ export default {
|
||||||
})),
|
})),
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
currentUser() {
|
|
||||||
return Auth.getCurrentUser();
|
|
||||||
},
|
|
||||||
dashboardPath() {
|
dashboardPath() {
|
||||||
return frontendURL('dashboard');
|
return frontendURL('dashboard');
|
||||||
},
|
},
|
||||||
|
@ -174,10 +180,6 @@ export default {
|
||||||
this.$store.dispatch('inboxes/get');
|
this.$store.dispatch('inboxes/get');
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
gravatarUrl() {
|
|
||||||
const hash = md5(this.currentUser.email);
|
|
||||||
return `${window.WootConstants.GRAVATAR_URL}${hash}?default=404`;
|
|
||||||
},
|
|
||||||
filterBillingRoutes(menuItems) {
|
filterBillingRoutes(menuItems) {
|
||||||
return menuItems.filter(
|
return menuItems.filter(
|
||||||
menuItem => !menuItem.toState.includes('billing')
|
menuItem => !menuItem.toState.includes('billing')
|
||||||
|
@ -185,6 +187,9 @@ export default {
|
||||||
},
|
},
|
||||||
filterMenuItemsByRole(menuItems) {
|
filterMenuItemsByRole(menuItems) {
|
||||||
const { role } = this.currentUser;
|
const { role } = this.currentUser;
|
||||||
|
if (!role) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
return menuItems.filter(
|
return menuItems.filter(
|
||||||
menuItem =>
|
menuItem =>
|
||||||
window.roleWiseRoutes[role].indexOf(menuItem.toStateName) > -1
|
window.roleWiseRoutes[role].indexOf(menuItem.toStateName) > -1
|
||||||
|
|
|
@ -8,6 +8,8 @@ export default {
|
||||||
'inbox_conversation',
|
'inbox_conversation',
|
||||||
'settings_account_reports',
|
'settings_account_reports',
|
||||||
'billing_deactivated',
|
'billing_deactivated',
|
||||||
|
'profile_settings',
|
||||||
|
'profile_settings_index',
|
||||||
],
|
],
|
||||||
menuItems: {
|
menuItems: {
|
||||||
assignedToMe: {
|
assignedToMe: {
|
||||||
|
|
|
@ -37,11 +37,11 @@
|
||||||
},
|
},
|
||||||
"AUTH": {
|
"AUTH": {
|
||||||
"TITLE": "Channels",
|
"TITLE": "Channels",
|
||||||
"DESC": "Currently we support website live chat widgets and Facebook Pages as platforms. We have more platforms like Twitter, Telegram and Line in the works, which will be out soon."
|
"DESC": "Currently we support Website live chat widgets, Facebook Pages and Twitter profiles as platforms. We have more platforms like Whatsapp, Email, Telegram and Line in the works, which will be out soon."
|
||||||
},
|
},
|
||||||
"AGENTS": {
|
"AGENTS": {
|
||||||
"TITLE": "Agents",
|
"TITLE": "Agents",
|
||||||
"DESC": "Here you can add agents to manage your newly created inbox. Only these selected agents will have access to your inbox. Agents whcih are not part of this inbox will not be able to see or respond to messages in this inbox when they login. <br> <b>PS:</b> As an administrator, if you need access to all inboxes, you should add yourself as agent to all inboxes that you create."
|
"DESC": "Here you can add agents to manage your newly created inbox. Only these selected agents will have access to your inbox. Agents which are not part of this inbox will not be able to see or respond to messages in this inbox when they login. <br> <b>PS:</b> As an administrator, if you need access to all inboxes, you should add yourself as agent to all inboxes that you create."
|
||||||
},
|
},
|
||||||
"DETAILS": {
|
"DETAILS": {
|
||||||
"TITLE": "Inbox Details",
|
"TITLE": "Inbox Details",
|
||||||
|
|
|
@ -10,6 +10,7 @@ import { default as _login } from './login.json';
|
||||||
import { default as _report } from './report.json';
|
import { default as _report } from './report.json';
|
||||||
import { default as _resetPassword } from './resetPassword.json';
|
import { default as _resetPassword } from './resetPassword.json';
|
||||||
import { default as _setNewPassword } from './setNewPassword.json';
|
import { default as _setNewPassword } from './setNewPassword.json';
|
||||||
|
import { default as _settings } from './settings.json';
|
||||||
import { default as _signup } from './signup.json';
|
import { default as _signup } from './signup.json';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
|
@ -24,5 +25,6 @@ export default {
|
||||||
..._report,
|
..._report,
|
||||||
..._resetPassword,
|
..._resetPassword,
|
||||||
..._setNewPassword,
|
..._setNewPassword,
|
||||||
|
..._settings,
|
||||||
..._signup,
|
..._signup,
|
||||||
};
|
};
|
||||||
|
|
50
app/javascript/dashboard/i18n/locale/en/settings.json
Normal file
50
app/javascript/dashboard/i18n/locale/en/settings.json
Normal file
|
@ -0,0 +1,50 @@
|
||||||
|
{
|
||||||
|
"PROFILE_SETTINGS": {
|
||||||
|
"LINK": "Profile Settings",
|
||||||
|
"TITLE": "Profile Settings",
|
||||||
|
"BTN_TEXT": "Update Profile",
|
||||||
|
"AFTER_EMAIL_CHANGED": "Your profile has been updated successfully, please login again as your login credentials are changed",
|
||||||
|
"FORM": {
|
||||||
|
"AVATAR": "Profile Image",
|
||||||
|
"ERROR": "Please fix form errors",
|
||||||
|
"REMOVE_IMAGE": "Remove",
|
||||||
|
"UPLOAD_IMAGE": "Upload image",
|
||||||
|
"UPDATE_IMAGE": "Update image",
|
||||||
|
"PROFILE_SECTION" : {
|
||||||
|
"TITLE": "Profile",
|
||||||
|
"NOTE": "Your email address is your identity and is used to log in."
|
||||||
|
},
|
||||||
|
"PASSWORD_SECTION" : {
|
||||||
|
"TITLE": "Password",
|
||||||
|
"NOTE": "Updating your password would reset your logins in multiple devices."
|
||||||
|
},
|
||||||
|
"PROFILE_IMAGE":{
|
||||||
|
"LABEL": "Profile Image"
|
||||||
|
},
|
||||||
|
"NAME": {
|
||||||
|
"LABEL": "Your name",
|
||||||
|
"ERROR": "Please enter a valid name",
|
||||||
|
"PLACEHOLDER": "Please enter your name, this would be displayed in conversations"
|
||||||
|
},
|
||||||
|
"EMAIL": {
|
||||||
|
"LABEL": "Your email address",
|
||||||
|
"ERROR": "Please enter a valid email address",
|
||||||
|
"PLACEHOLDER": "Please enter your email address, this would be displayed in conversations"
|
||||||
|
},
|
||||||
|
"PASSWORD": {
|
||||||
|
"LABEL": "Password",
|
||||||
|
"ERROR": "Please enter a password of length 6 or more",
|
||||||
|
"PLACEHOLDER": "Please enter a new password"
|
||||||
|
},
|
||||||
|
"PASSWORD_CONFIRMATION": {
|
||||||
|
"LABEL": "Confirm new password",
|
||||||
|
"ERROR": "Confirm password should match the password",
|
||||||
|
"PLACEHOLDER": "Please re-enter your password"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"SIDEBAR": {
|
||||||
|
"PROFILE_SETTINGS": "Profile Settings",
|
||||||
|
"LOGOUT": "Logout"
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,23 +1,40 @@
|
||||||
<template>
|
<template>
|
||||||
<form class="login-box medium-4 column align-self-middle" v-on:submit.prevent="login()">
|
<form
|
||||||
|
class="login-box medium-4 column align-self-middle"
|
||||||
|
@submit.prevent="login()"
|
||||||
|
>
|
||||||
<div class="column log-in-form">
|
<div class="column log-in-form">
|
||||||
<h4>{{$t('SET_NEW_PASSWORD.TITLE')}}</h4>
|
<h4>{{ $t('SET_NEW_PASSWORD.TITLE') }}</h4>
|
||||||
<label :class="{ 'error': $v.credentials.password.$error }">
|
<label :class="{ error: $v.credentials.password.$error }">
|
||||||
{{$t('LOGIN.PASSWORD.LABEL')}}
|
{{ $t('LOGIN.PASSWORD.LABEL') }}
|
||||||
<input type="password" v-bind:placeholder="$t('SET_NEW_PASSWORD.PASSWORD.PLACEHOLDER')" v-model.trim="credentials.password" @input="$v.credentials.password.$touch">
|
<input
|
||||||
<span class="message" v-if="$v.credentials.password.$error">
|
v-model.trim="credentials.password"
|
||||||
{{$t('SET_NEW_PASSWORD.PASSWORD.ERROR')}}
|
type="password"
|
||||||
|
:placeholder="$t('SET_NEW_PASSWORD.PASSWORD.PLACEHOLDER')"
|
||||||
|
@input="$v.credentials.password.$touch"
|
||||||
|
/>
|
||||||
|
<span v-if="$v.credentials.password.$error" class="message">
|
||||||
|
{{ $t('SET_NEW_PASSWORD.PASSWORD.ERROR') }}
|
||||||
</span>
|
</span>
|
||||||
</label>
|
</label>
|
||||||
<label :class="{ 'error': $v.credentials.confirmPassword.$error }">
|
<label :class="{ error: $v.credentials.confirmPassword.$error }">
|
||||||
{{$t('SET_NEW_PASSWORD.CONFIRM_PASSWORD.LABEL')}}
|
{{ $t('SET_NEW_PASSWORD.CONFIRM_PASSWORD.LABEL') }}
|
||||||
<input type="password" v-bind:placeholder="$t('SET_NEW_PASSWORD.CONFIRM_PASSWORD.PLACEHOLDER')" v-model.trim="credentials.confirmPassword" @input="$v.credentials.confirmPassword.$touch">
|
<input
|
||||||
<span class="message" v-if="$v.credentials.confirmPassword.$error">
|
v-model.trim="credentials.confirmPassword"
|
||||||
{{$t('SET_NEW_PASSWORD.CONFIRM_PASSWORD.ERROR')}}
|
type="password"
|
||||||
|
:placeholder="$t('SET_NEW_PASSWORD.CONFIRM_PASSWORD.PLACEHOLDER')"
|
||||||
|
@input="$v.credentials.confirmPassword.$touch"
|
||||||
|
/>
|
||||||
|
<span v-if="$v.credentials.confirmPassword.$error" class="message">
|
||||||
|
{{ $t('SET_NEW_PASSWORD.CONFIRM_PASSWORD.ERROR') }}
|
||||||
</span>
|
</span>
|
||||||
</label>
|
</label>
|
||||||
<woot-submit-button
|
<woot-submit-button
|
||||||
:disabled="$v.credentials.password.$invalid || $v.credentials.confirmPassword.$invalid || newPasswordAPI.showLoading"
|
:disabled="
|
||||||
|
$v.credentials.password.$invalid ||
|
||||||
|
$v.credentials.confirmPassword.$invalid ||
|
||||||
|
newPasswordAPI.showLoading
|
||||||
|
"
|
||||||
:button-text="$t('SET_NEW_PASSWORD.SUBMIT')"
|
:button-text="$t('SET_NEW_PASSWORD.SUBMIT')"
|
||||||
:loading="newPasswordAPI.showLoading"
|
:loading="newPasswordAPI.showLoading"
|
||||||
button-class="expanded"
|
button-class="expanded"
|
||||||
|
@ -99,7 +116,7 @@ export default {
|
||||||
resetPasswordToken: this.resetPasswordToken,
|
resetPasswordToken: this.resetPasswordToken,
|
||||||
};
|
};
|
||||||
Auth.setNewPassword(credentials)
|
Auth.setNewPassword(credentials)
|
||||||
.then((res) => {
|
.then(res => {
|
||||||
if (res.status === 200) {
|
if (res.status === 200) {
|
||||||
window.location = res.data.redirect_url;
|
window.location = res.data.redirect_url;
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,7 +7,7 @@
|
||||||
<span>{{ headerTitle }}</span>
|
<span>{{ headerTitle }}</span>
|
||||||
</h1>
|
</h1>
|
||||||
<router-link
|
<router-link
|
||||||
v-if="showNewButton && showButton && currentRole"
|
v-if="showNewButton && showButton && isAdmin"
|
||||||
:to="buttonRoute"
|
:to="buttonRoute"
|
||||||
class="button icon success nice button--fixed-right-top"
|
class="button icon success nice button--fixed-right-top"
|
||||||
>
|
>
|
||||||
|
@ -17,18 +17,30 @@
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<script>
|
<script>
|
||||||
|
import { mapGetters } from 'vuex';
|
||||||
import BackButton from '../../../components/widgets/BackButton';
|
import BackButton from '../../../components/widgets/BackButton';
|
||||||
import Auth from '../../../api/auth';
|
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
components: {
|
components: {
|
||||||
BackButton,
|
BackButton,
|
||||||
},
|
},
|
||||||
props: {
|
props: {
|
||||||
headerTitle: String,
|
headerTitle: {
|
||||||
buttonRoute: String,
|
default: '',
|
||||||
buttonText: String,
|
type: String,
|
||||||
icon: String,
|
},
|
||||||
|
buttonRoute: {
|
||||||
|
default: '',
|
||||||
|
type: String,
|
||||||
|
},
|
||||||
|
buttonText: {
|
||||||
|
default: '',
|
||||||
|
type: String,
|
||||||
|
},
|
||||||
|
icon: {
|
||||||
|
default: '',
|
||||||
|
type: String,
|
||||||
|
},
|
||||||
showButton: Boolean,
|
showButton: Boolean,
|
||||||
showNewButton: Boolean,
|
showNewButton: Boolean,
|
||||||
hideButtonRoutes: {
|
hideButtonRoutes: {
|
||||||
|
@ -39,11 +51,14 @@ export default {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
|
...mapGetters({
|
||||||
|
currentUser: 'getCurrentUser',
|
||||||
|
}),
|
||||||
iconClass() {
|
iconClass() {
|
||||||
return `icon ${this.icon} header--icon`;
|
return `icon ${this.icon} header--icon`;
|
||||||
},
|
},
|
||||||
currentRole() {
|
isAdmin() {
|
||||||
const { role } = Auth.getCurrentUser();
|
const { role } = this.currentUser;
|
||||||
return role === 'administrator';
|
return role === 'administrator';
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
|
@ -108,7 +108,6 @@
|
||||||
/* global bus */
|
/* global bus */
|
||||||
|
|
||||||
import { mapGetters } from 'vuex';
|
import { mapGetters } from 'vuex';
|
||||||
import md5 from 'md5';
|
|
||||||
import Thumbnail from '../../../../components/widgets/Thumbnail';
|
import Thumbnail from '../../../../components/widgets/Thumbnail';
|
||||||
|
|
||||||
import AddAgent from './AddAgent';
|
import AddAgent from './AddAgent';
|
||||||
|
@ -182,10 +181,6 @@ export default {
|
||||||
agent => agent.role === 'administrator' && agent.confirmed
|
agent => agent.role === 'administrator' && agent.confirmed
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
gravatarUrl(email) {
|
|
||||||
const hash = md5(email);
|
|
||||||
return `${window.WootConstants.GRAVATAR_URL}${hash}?default=404`;
|
|
||||||
},
|
|
||||||
// Edit Function
|
// Edit Function
|
||||||
openAddPopup() {
|
openAddPopup() {
|
||||||
this.showAddPopup = true;
|
this.showAddPopup = true;
|
||||||
|
|
|
@ -0,0 +1,224 @@
|
||||||
|
<template>
|
||||||
|
<div class="columns profile--settings ">
|
||||||
|
<form @submit.prevent="updateUser">
|
||||||
|
<div class="small-12 row profile--settings--row">
|
||||||
|
<div class="columns small-3 ">
|
||||||
|
<p class="section--title">
|
||||||
|
{{ $t('PROFILE_SETTINGS.FORM.PROFILE_SECTION.TITLE') }}
|
||||||
|
</p>
|
||||||
|
<p>{{ $t('PROFILE_SETTINGS.FORM.PROFILE_SECTION.NOTE') }}</p>
|
||||||
|
</div>
|
||||||
|
<div class="columns small-9">
|
||||||
|
<label>
|
||||||
|
{{ $t('PROFILE_SETTINGS.FORM.PROFILE_IMAGE.LABEL') }}
|
||||||
|
<thumbnail size="80px" :src="avatarUrl"></thumbnail>
|
||||||
|
<input
|
||||||
|
id="file"
|
||||||
|
ref="file"
|
||||||
|
type="file"
|
||||||
|
accept="image/*"
|
||||||
|
@change="handleImageUpload"
|
||||||
|
/>
|
||||||
|
</label>
|
||||||
|
<label :class="{ error: $v.name.$error }">
|
||||||
|
{{ $t('PROFILE_SETTINGS.FORM.NAME.LABEL') }}
|
||||||
|
<input
|
||||||
|
v-model="name"
|
||||||
|
type="text"
|
||||||
|
:placeholder="$t('PROFILE_SETTINGS.FORM.NAME.PLACEHOLDER')"
|
||||||
|
@input="$v.name.$touch"
|
||||||
|
/>
|
||||||
|
<span v-if="$v.name.$error" class="message">
|
||||||
|
{{ $t('PROFILE_SETTINGS.FORM.NAME.ERROR') }}
|
||||||
|
</span>
|
||||||
|
</label>
|
||||||
|
<label :class="{ error: $v.email.$error }">
|
||||||
|
{{ $t('PROFILE_SETTINGS.FORM.EMAIL.LABEL') }}
|
||||||
|
<input
|
||||||
|
v-model.trim="email"
|
||||||
|
type="email"
|
||||||
|
:placeholder="$t('PROFILE_SETTINGS.FORM.EMAIL.PLACEHOLDER')"
|
||||||
|
@input="$v.email.$touch"
|
||||||
|
/>
|
||||||
|
<span v-if="$v.email.$error" class="message">
|
||||||
|
{{ $t('PROFILE_SETTINGS.FORM.EMAIL.ERROR') }}
|
||||||
|
</span>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="profile--settings--row row">
|
||||||
|
<div class="columns small-3 ">
|
||||||
|
<p class="section--title">
|
||||||
|
{{ $t('PROFILE_SETTINGS.FORM.PASSWORD_SECTION.TITLE') }}
|
||||||
|
</p>
|
||||||
|
<p>{{ $t('PROFILE_SETTINGS.FORM.PASSWORD_SECTION.NOTE') }}</p>
|
||||||
|
</div>
|
||||||
|
<div class="columns small-9">
|
||||||
|
<label :class="{ error: $v.password.$error }">
|
||||||
|
{{ $t('PROFILE_SETTINGS.FORM.PASSWORD.LABEL') }}
|
||||||
|
<input
|
||||||
|
v-model.trim="password"
|
||||||
|
type="password"
|
||||||
|
:placeholder="$t('PROFILE_SETTINGS.FORM.PASSWORD.PLACEHOLDER')"
|
||||||
|
@input="$v.password.$touch"
|
||||||
|
/>
|
||||||
|
<span v-if="$v.password.$error" class="message">
|
||||||
|
{{ $t('PROFILE_SETTINGS.FORM.PASSWORD.ERROR') }}
|
||||||
|
</span>
|
||||||
|
</label>
|
||||||
|
<label :class="{ error: $v.passwordConfirmation.$error }">
|
||||||
|
{{ $t('PROFILE_SETTINGS.FORM.PASSWORD_CONFIRMATION.LABEL') }}
|
||||||
|
<input
|
||||||
|
v-model.trim="passwordConfirmation"
|
||||||
|
type="password"
|
||||||
|
:placeholder="
|
||||||
|
$t('PROFILE_SETTINGS.FORM.PASSWORD_CONFIRMATION.PLACEHOLDER')
|
||||||
|
"
|
||||||
|
@input="$v.passwordConfirmation.$touch"
|
||||||
|
/>
|
||||||
|
<span v-if="$v.passwordConfirmation.$error" class="message">
|
||||||
|
{{ $t('PROFILE_SETTINGS.FORM.PASSWORD_CONFIRMATION.ERROR') }}
|
||||||
|
</span>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<woot-submit-button
|
||||||
|
class="button nice success button--fixed-right-top"
|
||||||
|
:button-text="$t('PROFILE_SETTINGS.BTN_TEXT')"
|
||||||
|
:loading="isUpdating"
|
||||||
|
>
|
||||||
|
</woot-submit-button>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
/* global bus */
|
||||||
|
|
||||||
|
import Thumbnail from 'dashboard/components/widgets/Thumbnail.vue';
|
||||||
|
import { required, minLength, email } from 'vuelidate/lib/validators';
|
||||||
|
import { mapGetters } from 'vuex';
|
||||||
|
import { clearCookiesOnLogout } from '../../../../api/auth';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
components: {
|
||||||
|
Thumbnail,
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
avatarFile: '',
|
||||||
|
avatarUrl: '',
|
||||||
|
name: '',
|
||||||
|
email: '',
|
||||||
|
password: '',
|
||||||
|
passwordConfirmation: '',
|
||||||
|
isUpdating: false,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
validations: {
|
||||||
|
name: {
|
||||||
|
required,
|
||||||
|
},
|
||||||
|
email: {
|
||||||
|
required,
|
||||||
|
email,
|
||||||
|
},
|
||||||
|
password: {
|
||||||
|
minLength: minLength(6),
|
||||||
|
},
|
||||||
|
passwordConfirmation: {
|
||||||
|
minLength: minLength(6),
|
||||||
|
isEqPassword(value) {
|
||||||
|
if (value !== this.password) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
...mapGetters({
|
||||||
|
currentUser: 'getCurrentUser',
|
||||||
|
currentUserId: 'getCurrentUserID',
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
watch: {
|
||||||
|
currentUserId(newCurrentUserId, prevCurrentUserId) {
|
||||||
|
if (prevCurrentUserId !== newCurrentUserId) {
|
||||||
|
this.initializeUser();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
mounted() {
|
||||||
|
if (this.currentUserId) {
|
||||||
|
this.initializeUser();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
initializeUser() {
|
||||||
|
this.name = this.currentUser.name;
|
||||||
|
this.email = this.currentUser.email;
|
||||||
|
this.avatarUrl = this.currentUser.avatar_url;
|
||||||
|
},
|
||||||
|
async updateUser() {
|
||||||
|
this.$v.$touch();
|
||||||
|
if (this.$v.$invalid) {
|
||||||
|
bus.$emit('newToastMessage', this.$t('PROFILE_SETTINGS.FORM.ERROR'));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.isUpdating = true;
|
||||||
|
const hasEmailChanged = this.currentUser.email !== this.email;
|
||||||
|
try {
|
||||||
|
await this.$store.dispatch('updateProfile', {
|
||||||
|
name: this.name,
|
||||||
|
email: this.email,
|
||||||
|
avatar: this.avatarFile,
|
||||||
|
password: this.password,
|
||||||
|
password_confirmation: this.passwordConfirmation,
|
||||||
|
});
|
||||||
|
this.isUpdating = false;
|
||||||
|
if (hasEmailChanged) {
|
||||||
|
clearCookiesOnLogout();
|
||||||
|
bus.$emit(
|
||||||
|
'newToastMessage',
|
||||||
|
this.$t('PROFILE_SETTINGS.AFTER_EMAIL_CHANGED')
|
||||||
|
);
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
this.isUpdating = false;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
handleImageUpload(event) {
|
||||||
|
const [file] = event.target.files;
|
||||||
|
this.avatarFile = file;
|
||||||
|
this.avatarUrl = URL.createObjectURL(file);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
@import '~dashboard/assets/scss/variables.scss';
|
||||||
|
@import '~dashboard/assets/scss/mixins.scss';
|
||||||
|
|
||||||
|
.profile--settings {
|
||||||
|
padding: 24px;
|
||||||
|
overflow: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.profile--settings--row {
|
||||||
|
@include border-normal-bottom;
|
||||||
|
padding: 16px;
|
||||||
|
.small-3 {
|
||||||
|
padding: 16px 16px 16px 0;
|
||||||
|
}
|
||||||
|
.small-9 {
|
||||||
|
padding: 16px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.section--title {
|
||||||
|
color: $color-woot;
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -0,0 +1,27 @@
|
||||||
|
import SettingsContent from '../Wrapper';
|
||||||
|
import Index from './Index.vue';
|
||||||
|
import { frontendURL } from '../../../../helper/URLHelper';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
routes: [
|
||||||
|
{
|
||||||
|
path: frontendURL('profile'),
|
||||||
|
name: 'profile_settings',
|
||||||
|
roles: ['administrator', 'agent'],
|
||||||
|
component: SettingsContent,
|
||||||
|
props: {
|
||||||
|
headerTitle: 'PROFILE_SETTINGS.TITLE',
|
||||||
|
icon: 'ion-compose',
|
||||||
|
showNewButton: false,
|
||||||
|
},
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
path: 'settings',
|
||||||
|
name: 'profile_settings_index',
|
||||||
|
component: Index,
|
||||||
|
roles: ['administrator', 'agent'],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
|
@ -1,10 +1,11 @@
|
||||||
import agent from './agents/agent.routes';
|
|
||||||
import inbox from './inbox/inbox.routes';
|
|
||||||
import canned from './canned/canned.routes';
|
|
||||||
import reports from './reports/reports.routes';
|
|
||||||
import billing from './billing/billing.routes';
|
|
||||||
import Auth from '../../../api/auth';
|
|
||||||
import { frontendURL } from '../../../helper/URLHelper';
|
import { frontendURL } from '../../../helper/URLHelper';
|
||||||
|
import agent from './agents/agent.routes';
|
||||||
|
import Auth from '../../../api/auth';
|
||||||
|
import billing from './billing/billing.routes';
|
||||||
|
import canned from './canned/canned.routes';
|
||||||
|
import inbox from './inbox/inbox.routes';
|
||||||
|
import profile from './profile/profile.routes';
|
||||||
|
import reports from './reports/reports.routes';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
routes: [
|
routes: [
|
||||||
|
@ -19,10 +20,11 @@ export default {
|
||||||
return frontendURL('settings/canned-response');
|
return frontendURL('settings/canned-response');
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
...inbox.routes,
|
|
||||||
...agent.routes,
|
...agent.routes,
|
||||||
...canned.routes,
|
|
||||||
...reports.routes,
|
|
||||||
...billing.routes,
|
...billing.routes,
|
||||||
|
...canned.routes,
|
||||||
|
...inbox.routes,
|
||||||
|
...profile.routes,
|
||||||
|
...reports.routes,
|
||||||
],
|
],
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,5 +1,3 @@
|
||||||
/* eslint no-console: 0 */
|
|
||||||
/* eslint-env browser */
|
|
||||||
/* eslint no-param-reassign: 0 */
|
/* eslint no-param-reassign: 0 */
|
||||||
import axios from 'axios';
|
import axios from 'axios';
|
||||||
import moment from 'moment';
|
import moment from 'moment';
|
||||||
|
@ -9,7 +7,8 @@ import router from '../../routes';
|
||||||
import authAPI from '../../api/auth';
|
import authAPI from '../../api/auth';
|
||||||
import createAxios from '../../helper/APIHelper';
|
import createAxios from '../../helper/APIHelper';
|
||||||
import actionCable from '../../helper/actionCable';
|
import actionCable from '../../helper/actionCable';
|
||||||
// initial state
|
import { setUser, getHeaderExpiry, clearCookiesOnLogout } from '../utils/api';
|
||||||
|
|
||||||
const state = {
|
const state = {
|
||||||
currentUser: {
|
currentUser: {
|
||||||
id: null,
|
id: null,
|
||||||
|
@ -27,15 +26,19 @@ const state = {
|
||||||
};
|
};
|
||||||
|
|
||||||
// getters
|
// getters
|
||||||
const getters = {
|
export const getters = {
|
||||||
isLoggedIn(_state) {
|
isLoggedIn($state) {
|
||||||
return _state.currentUser.id !== null;
|
return !!$state.currentUser.id;
|
||||||
},
|
},
|
||||||
|
|
||||||
getCurrentUserID(_state) {
|
getCurrentUserID(_state) {
|
||||||
return _state.currentUser.id;
|
return _state.currentUser.id;
|
||||||
},
|
},
|
||||||
|
|
||||||
|
getCurrentUser(_state) {
|
||||||
|
return _state.currentUser;
|
||||||
|
},
|
||||||
|
|
||||||
getSubscription(_state) {
|
getSubscription(_state) {
|
||||||
return _state.currentUser.subscription === undefined
|
return _state.currentUser.subscription === undefined
|
||||||
? null
|
? null
|
||||||
|
@ -53,7 +56,7 @@ const getters = {
|
||||||
};
|
};
|
||||||
|
|
||||||
// actions
|
// actions
|
||||||
const actions = {
|
export const actions = {
|
||||||
login({ commit }, credentials) {
|
login({ commit }, credentials) {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
authAPI
|
authAPI
|
||||||
|
@ -70,14 +73,21 @@ const actions = {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
validityCheck(context) {
|
async validityCheck(context) {
|
||||||
if (context.getters.isLoggedIn) {
|
try {
|
||||||
authAPI.validityCheck();
|
const response = await authAPI.validityCheck();
|
||||||
|
setUser(response.data.payload.data, getHeaderExpiry(response));
|
||||||
|
context.commit(types.default.SET_CURRENT_USER);
|
||||||
|
} catch (error) {
|
||||||
|
if (error.response.status === 401) {
|
||||||
|
clearCookiesOnLogout();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
set_user({ commit }) {
|
setUser({ commit, dispatch }) {
|
||||||
if (authAPI.isLoggedIn()) {
|
if (authAPI.isLoggedIn()) {
|
||||||
commit(types.default.SET_CURRENT_USER);
|
commit(types.default.SET_CURRENT_USER);
|
||||||
|
dispatch('validityCheck');
|
||||||
} else {
|
} else {
|
||||||
commit(types.default.CLEAR_USER);
|
commit(types.default.CLEAR_USER);
|
||||||
}
|
}
|
||||||
|
@ -85,6 +95,15 @@ const actions = {
|
||||||
logout({ commit }) {
|
logout({ commit }) {
|
||||||
commit(types.default.CLEAR_USER);
|
commit(types.default.CLEAR_USER);
|
||||||
},
|
},
|
||||||
|
updateProfile: async ({ commit }, params) => {
|
||||||
|
try {
|
||||||
|
const response = await authAPI.profileUpdate(params);
|
||||||
|
setUser(response.data, getHeaderExpiry(response));
|
||||||
|
commit(types.default.SET_CURRENT_USER);
|
||||||
|
} catch (error) {
|
||||||
|
// Ignore error
|
||||||
|
}
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
// mutations
|
// mutations
|
||||||
|
@ -93,8 +112,12 @@ const mutations = {
|
||||||
_state.currentUser.id = null;
|
_state.currentUser.id = null;
|
||||||
},
|
},
|
||||||
[types.default.SET_CURRENT_USER](_state) {
|
[types.default.SET_CURRENT_USER](_state) {
|
||||||
Object.assign(_state.currentUser, authAPI.getAuthData());
|
const currentUser = {
|
||||||
Object.assign(_state.currentUser, authAPI.getCurrentUser());
|
...authAPI.getAuthData(),
|
||||||
|
...authAPI.getCurrentUser(),
|
||||||
|
};
|
||||||
|
|
||||||
|
Vue.set(_state, 'currentUser', currentUser);
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,70 @@
|
||||||
|
import axios from 'axios';
|
||||||
|
import Cookies from 'js-cookie';
|
||||||
|
import { actions } from '../../auth';
|
||||||
|
import * as types from '../../../mutation-types';
|
||||||
|
import { setUser, clearCookiesOnLogout } from '../../../utils/api';
|
||||||
|
import '../../../../routes';
|
||||||
|
|
||||||
|
jest.mock('../../../../routes', () => {});
|
||||||
|
jest.mock('../../../utils/api', () => ({
|
||||||
|
setUser: jest.fn(),
|
||||||
|
clearCookiesOnLogout: jest.fn(),
|
||||||
|
getHeaderExpiry: jest.fn(),
|
||||||
|
}));
|
||||||
|
jest.mock('js-cookie', () => ({
|
||||||
|
getJSON: jest.fn(),
|
||||||
|
}));
|
||||||
|
|
||||||
|
const commit = jest.fn();
|
||||||
|
const dispatch = jest.fn();
|
||||||
|
global.axios = axios;
|
||||||
|
jest.mock('axios');
|
||||||
|
|
||||||
|
describe('#actions', () => {
|
||||||
|
describe('#validityCheck', () => {
|
||||||
|
it('sends correct actions if API is success', async () => {
|
||||||
|
axios.get.mockResolvedValue({
|
||||||
|
data: { payload: { data: { id: 1, name: 'John' } } },
|
||||||
|
headers: { expiry: 581842904 },
|
||||||
|
});
|
||||||
|
await actions.validityCheck({ commit });
|
||||||
|
expect(setUser).toHaveBeenCalledTimes(1);
|
||||||
|
expect(commit.mock.calls).toEqual([[types.default.SET_CURRENT_USER]]);
|
||||||
|
});
|
||||||
|
it('sends correct actions if API is error', async () => {
|
||||||
|
axios.get.mockRejectedValue({
|
||||||
|
response: { status: 401 },
|
||||||
|
});
|
||||||
|
await actions.validityCheck({ commit });
|
||||||
|
expect(clearCookiesOnLogout);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('#updateProfile', () => {
|
||||||
|
it('sends correct actions if API is success', async () => {
|
||||||
|
axios.put.mockResolvedValue({
|
||||||
|
data: { id: 1, name: 'John' },
|
||||||
|
headers: { expiry: 581842904 },
|
||||||
|
});
|
||||||
|
await actions.updateProfile({ commit }, { name: 'Pranav' });
|
||||||
|
expect(setUser).toHaveBeenCalledTimes(1);
|
||||||
|
expect(commit.mock.calls).toEqual([[types.default.SET_CURRENT_USER]]);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('#setUser', () => {
|
||||||
|
it('sends correct actions if user is logged in', async () => {
|
||||||
|
Cookies.getJSON.mockImplementation(() => true);
|
||||||
|
actions.setUser({ commit, dispatch });
|
||||||
|
expect(commit.mock.calls).toEqual([[types.default.SET_CURRENT_USER]]);
|
||||||
|
expect(dispatch.mock.calls).toEqual([['validityCheck']]);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('sends correct actions if user is not logged in', async () => {
|
||||||
|
Cookies.getJSON.mockImplementation(() => false);
|
||||||
|
actions.setUser({ commit, dispatch });
|
||||||
|
expect(commit.mock.calls).toEqual([[types.default.CLEAR_USER]]);
|
||||||
|
expect(dispatch).toHaveBeenCalledTimes(0);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
|
@ -0,0 +1,20 @@
|
||||||
|
import { getters } from '../../auth';
|
||||||
|
|
||||||
|
import '../../../../routes';
|
||||||
|
|
||||||
|
jest.mock('../../../../routes', () => {});
|
||||||
|
describe('#getters', () => {
|
||||||
|
it('isLoggedIn', () => {
|
||||||
|
expect(getters.isLoggedIn({ currentUser: { id: null } })).toEqual(false);
|
||||||
|
expect(getters.isLoggedIn({ currentUser: { id: 1 } })).toEqual(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('getCurrentUserID', () => {
|
||||||
|
expect(getters.getCurrentUserID({ currentUser: { id: 1 } })).toEqual(1);
|
||||||
|
});
|
||||||
|
it('getCurrentUser', () => {
|
||||||
|
expect(
|
||||||
|
getters.getCurrentUser({ currentUser: { id: 1, name: 'Pranav' } })
|
||||||
|
).toEqual({ id: 1, name: 'Pranav' });
|
||||||
|
});
|
||||||
|
});
|
|
@ -1,6 +1,30 @@
|
||||||
/* eslint no-param-reassign: 0 */
|
/* eslint no-param-reassign: 0 */
|
||||||
|
import moment from 'moment';
|
||||||
|
import Cookies from 'js-cookie';
|
||||||
|
import { frontendURL } from '../../helper/URLHelper';
|
||||||
|
|
||||||
export const getLoadingStatus = state => state.fetchAPIloadingStatus;
|
export const getLoadingStatus = state => state.fetchAPIloadingStatus;
|
||||||
export const setLoadingStatus = (state, status) => {
|
export const setLoadingStatus = (state, status) => {
|
||||||
state.fetchAPIloadingStatus = status;
|
state.fetchAPIloadingStatus = status;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const setUser = (userData, expiryDate) =>
|
||||||
|
Cookies.set('user', userData, {
|
||||||
|
expires: expiryDate.diff(moment(), 'days'),
|
||||||
|
});
|
||||||
|
|
||||||
|
export const getHeaderExpiry = response => moment.unix(response.headers.expiry);
|
||||||
|
|
||||||
|
export const setAuthCredentials = response => {
|
||||||
|
const expiryDate = getHeaderExpiry(response);
|
||||||
|
Cookies.set('auth_data', response.headers, {
|
||||||
|
expires: expiryDate.diff(moment(), 'days'),
|
||||||
|
});
|
||||||
|
setUser(response.data.data, expiryDate);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const clearCookiesOnLogout = () => {
|
||||||
|
Cookies.remove('auth_data');
|
||||||
|
Cookies.remove('user');
|
||||||
|
window.location = frontendURL('login');
|
||||||
|
};
|
||||||
|
|
11
app/views/api/v1/profiles/update.json.jbuilder
Normal file
11
app/views/api/v1/profiles/update.json.jbuilder
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
json.id @user.id
|
||||||
|
json.provider @user.provider
|
||||||
|
json.uid @user.uid
|
||||||
|
json.name @user.name
|
||||||
|
json.nickname @user.nickname
|
||||||
|
json.email @user.email
|
||||||
|
json.account_id @user.account_id
|
||||||
|
json.pubsub_token @user.pubsub_token
|
||||||
|
json.role @user.role
|
||||||
|
json.confirmed @user.confirmed?
|
||||||
|
json.avatar_url @user.avatar_url
|
|
@ -21,6 +21,8 @@ module.exports = {
|
||||||
transformIgnorePatterns: ['node_modules/*'],
|
transformIgnorePatterns: ['node_modules/*'],
|
||||||
moduleNameMapper: {
|
moduleNameMapper: {
|
||||||
'^@/(.*)$': '<rootDir>/app/javascript/$1',
|
'^@/(.*)$': '<rootDir>/app/javascript/$1',
|
||||||
|
'\\.(jpg|jpeg|png|gif|eot|otf|webp|svg|ttf|woff|woff2|mp4|webm|wav|mp3|m4a|aac|oga)$':
|
||||||
|
'<rootDir>/__mocks__/fileMock.js',
|
||||||
},
|
},
|
||||||
roots: ['<rootDir>/app/javascript'],
|
roots: ['<rootDir>/app/javascript'],
|
||||||
snapshotSerializers: ['jest-serializer-vue'],
|
snapshotSerializers: ['jest-serializer-vue'],
|
||||||
|
|
|
@ -30,7 +30,6 @@
|
||||||
"ionicons": "~2.0.1",
|
"ionicons": "~2.0.1",
|
||||||
"js-cookie": "^2.2.1",
|
"js-cookie": "^2.2.1",
|
||||||
"lodash.groupby": "^4.6.0",
|
"lodash.groupby": "^4.6.0",
|
||||||
"md5": "~2.2.1",
|
|
||||||
"moment": "~2.19.3",
|
"moment": "~2.19.3",
|
||||||
"query-string": "5",
|
"query-string": "5",
|
||||||
"spinkit": "~1.2.5",
|
"spinkit": "~1.2.5",
|
||||||
|
|
21
yarn.lock
21
yarn.lock
|
@ -2375,11 +2375,6 @@ chardet@^0.7.0:
|
||||||
resolved "https://registry.yarnpkg.com/chardet/-/chardet-0.7.0.tgz#90094849f0937f2eedc2425d0d28a9e5f0cbad9e"
|
resolved "https://registry.yarnpkg.com/chardet/-/chardet-0.7.0.tgz#90094849f0937f2eedc2425d0d28a9e5f0cbad9e"
|
||||||
integrity sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==
|
integrity sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==
|
||||||
|
|
||||||
charenc@~0.0.1:
|
|
||||||
version "0.0.2"
|
|
||||||
resolved "https://registry.yarnpkg.com/charenc/-/charenc-0.0.2.tgz#c0a1d2f3a7092e03774bfa83f14c0fc5790a8667"
|
|
||||||
integrity sha1-wKHS86cJLgN3S/qD8UwPxXkKhmc=
|
|
||||||
|
|
||||||
chart.js@~2.5.0:
|
chart.js@~2.5.0:
|
||||||
version "2.5.0"
|
version "2.5.0"
|
||||||
resolved "https://registry.yarnpkg.com/chart.js/-/chart.js-2.5.0.tgz#fe6e751a893769f56e72bee5ad91207e1c592957"
|
resolved "https://registry.yarnpkg.com/chart.js/-/chart.js-2.5.0.tgz#fe6e751a893769f56e72bee5ad91207e1c592957"
|
||||||
|
@ -2857,11 +2852,6 @@ cross-spawn@^3.0.0:
|
||||||
lru-cache "^4.0.1"
|
lru-cache "^4.0.1"
|
||||||
which "^1.2.9"
|
which "^1.2.9"
|
||||||
|
|
||||||
crypt@~0.0.1:
|
|
||||||
version "0.0.2"
|
|
||||||
resolved "https://registry.yarnpkg.com/crypt/-/crypt-0.0.2.tgz#88d7ff7ec0dfb86f713dc87bbb42d044d3e6c41b"
|
|
||||||
integrity sha1-iNf/fsDfuG9xPch7u0LQRNPmxBs=
|
|
||||||
|
|
||||||
crypto-browserify@^3.11.0:
|
crypto-browserify@^3.11.0:
|
||||||
version "3.12.0"
|
version "3.12.0"
|
||||||
resolved "https://registry.yarnpkg.com/crypto-browserify/-/crypto-browserify-3.12.0.tgz#396cf9f3137f03e4b8e532c58f698254e00f80ec"
|
resolved "https://registry.yarnpkg.com/crypto-browserify/-/crypto-browserify-3.12.0.tgz#396cf9f3137f03e4b8e532c58f698254e00f80ec"
|
||||||
|
@ -5131,7 +5121,7 @@ is-binary-path@^1.0.0:
|
||||||
dependencies:
|
dependencies:
|
||||||
binary-extensions "^1.0.0"
|
binary-extensions "^1.0.0"
|
||||||
|
|
||||||
is-buffer@^1.1.5, is-buffer@~1.1.1:
|
is-buffer@^1.1.5:
|
||||||
version "1.1.6"
|
version "1.1.6"
|
||||||
resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-1.1.6.tgz#efaa2ea9daa0d7ab2ea13a97b2b8ad51fefbe8be"
|
resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-1.1.6.tgz#efaa2ea9daa0d7ab2ea13a97b2b8ad51fefbe8be"
|
||||||
integrity sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==
|
integrity sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==
|
||||||
|
@ -6391,15 +6381,6 @@ md5.js@^1.3.4:
|
||||||
inherits "^2.0.1"
|
inherits "^2.0.1"
|
||||||
safe-buffer "^5.1.2"
|
safe-buffer "^5.1.2"
|
||||||
|
|
||||||
md5@~2.2.1:
|
|
||||||
version "2.2.1"
|
|
||||||
resolved "https://registry.yarnpkg.com/md5/-/md5-2.2.1.tgz#53ab38d5fe3c8891ba465329ea23fac0540126f9"
|
|
||||||
integrity sha1-U6s41f48iJG6RlMp6iP6wFQBJvk=
|
|
||||||
dependencies:
|
|
||||||
charenc "~0.0.1"
|
|
||||||
crypt "~0.0.1"
|
|
||||||
is-buffer "~1.1.1"
|
|
||||||
|
|
||||||
mdn-data@2.0.4:
|
mdn-data@2.0.4:
|
||||||
version "2.0.4"
|
version "2.0.4"
|
||||||
resolved "https://registry.yarnpkg.com/mdn-data/-/mdn-data-2.0.4.tgz#699b3c38ac6f1d728091a64650b65d388502fd5b"
|
resolved "https://registry.yarnpkg.com/mdn-data/-/mdn-data-2.0.4.tgz#699b3c38ac6f1d728091a64650b65d388502fd5b"
|
||||||
|
|
Loading…
Reference in a new issue