chore: Ability to change default account (#5393)

Co-authored-by: Sivin Varghese <64252451+iamsivin@users.noreply.github.com>
Co-authored-by: Tejaswini Chile <tejaswini@chatwoot.com>
This commit is contained in:
Muhsin Keloth
2022-10-06 06:01:12 +05:30
committed by GitHub
parent bd445216e9
commit b668723313
10 changed files with 130 additions and 20 deletions

View File

@@ -22,6 +22,11 @@ class Api::V1::ProfilesController < Api::BaseController
@user.account_users.find_by!(account_id: availability_params[:account_id]).update!(availability: availability_params[:availability]) @user.account_users.find_by!(account_id: availability_params[:account_id]).update!(availability: availability_params[:availability])
end end
def set_active_account
@user.account_users.find_by(account_id: profile_params[:account_id]).update(active_at: Time.now.utc)
head :ok
end
private private
def set_user def set_user
@@ -39,6 +44,7 @@ class Api::V1::ProfilesController < Api::BaseController
:display_name, :display_name,
:avatar, :avatar,
:message_signature, :message_signature,
:account_id,
ui_settings: {} ui_settings: {}
) )
end end

View File

@@ -87,6 +87,9 @@ export default {
}, },
async initializeAccount() { async initializeAccount() {
await this.$store.dispatch('accounts/get'); await this.$store.dispatch('accounts/get');
this.$store.dispatch('setActiveAccount', {
accountId: this.currentAccountId,
});
const { const {
locale, locale,
latest_chatwoot_version: latestChatwootVersion, latest_chatwoot_version: latestChatwootVersion,

View File

@@ -147,4 +147,13 @@ export default {
deleteAvatar() { deleteAvatar() {
return axios.delete(endPoints('deleteAvatar').url); return axios.delete(endPoints('deleteAvatar').url);
}, },
setActiveAccount({ accountId }) {
const urlData = endPoints('setActiveAccount');
return axios.put(urlData.url, {
profile: {
account_id: accountId,
},
});
},
}; };

View File

@@ -40,6 +40,10 @@ const endPoints = {
deleteAvatar: { deleteAvatar: {
url: '/api/v1/profile/avatar', url: '/api/v1/profile/avatar',
}, },
setActiveAccount: {
url: '/api/v1/profile/set_active_account',
},
}; };
export default page => { export default page => {

View File

@@ -8,25 +8,33 @@
:header-title="$t('SIDEBAR_ITEMS.CHANGE_ACCOUNTS')" :header-title="$t('SIDEBAR_ITEMS.CHANGE_ACCOUNTS')"
:header-content="$t('SIDEBAR_ITEMS.SELECTOR_SUBTITLE')" :header-content="$t('SIDEBAR_ITEMS.SELECTOR_SUBTITLE')"
/> />
<div <div class="account-selector--wrap">
v-for="account in currentUser.accounts" <div
:key="account.id" v-for="account in currentUser.accounts"
class="account-selector" :key="account.id"
> class="account-selector"
<a :href="`/app/accounts/${account.id}/dashboard`"> >
<fluent-icon <button
v-if="account.id === accountId" class="button expanded clear link"
class="selected--account" @click="onChangeAccount(account.id)"
icon="checkmark-circle" >
type="solid" <span class="button__content">
size="24" <label :for="account.name" class="account-details--wrap">
/> <div class="account--name">{{ account.name }}</div>
<label :for="account.name" class="account--details"> <div class="account--role">{{ account.role }}</div>
<div class="account--name">{{ account.name }}</div> </label>
<div class="account--role">{{ account.role }}</div> </span>
</label> <fluent-icon
</a> v-show="account.id === accountId"
class="selected--account"
icon="checkmark-circle"
type="solid"
size="24"
/>
</button>
</div>
</div> </div>
<div <div
v-if="globalConfig.createNewAccountFromDashboard" v-if="globalConfig.createNewAccountFromDashboard"
class="modal-footer delete-item" class="modal-footer delete-item"
@@ -58,5 +66,40 @@ export default {
globalConfig: 'globalConfig/get', globalConfig: 'globalConfig/get',
}), }),
}, },
methods: {
onChangeAccount(accountId) {
const accountUrl = `/app/accounts/${accountId}/dashboard`;
window.location.href = accountUrl;
},
},
}; };
</script> </script>
<style lang="scss" scoped>
.account-selector--wrap {
margin-top: var(--space-normal);
}
.account-selector {
padding-top: 0;
padding-bottom: 0;
.button {
display: flex;
justify-content: space-between;
padding: var(--space-one) var(--space-normal);
.account-details--wrap {
text-align: left;
.account--name {
cursor: pointer;
font-size: var(--font-size-medium);
font-weight: var(--font-weight-medium);
line-height: 1;
}
.account--role {
cursor: pointer;
font-size: var(--font-size-mini);
text-transform: capitalize;
}
}
}
}
</style>

View File

@@ -6,7 +6,7 @@ export const frontendURL = (path, params) => {
}; };
const getSSOAccountPath = ({ ssoAccountId, user }) => { const getSSOAccountPath = ({ ssoAccountId, user }) => {
const { accounts = [] } = user || {}; const { accounts = [], account_id = null } = user || {};
const ssoAccount = accounts.find( const ssoAccount = accounts.find(
account => account.id === Number(ssoAccountId) account => account.id === Number(ssoAccountId)
); );
@@ -14,7 +14,9 @@ const getSSOAccountPath = ({ ssoAccountId, user }) => {
if (ssoAccount) { if (ssoAccount) {
accountPath = `accounts/${ssoAccountId}`; accountPath = `accounts/${ssoAccountId}`;
} else if (accounts.length) { } else if (accounts.length) {
accountPath = `accounts/${accounts[0].id}`; // If the account id is not found, redirect to the first account
const accountId = account_id || accounts[0].id;
accountPath = `accounts/${accountId}`;
} }
return accountPath; return accountPath;
}; };

View File

@@ -179,6 +179,14 @@ export const actions = {
commit(types.SET_CURRENT_USER_AVAILABILITY, data[$state.currentUser.id]); commit(types.SET_CURRENT_USER_AVAILABILITY, data[$state.currentUser.id]);
} }
}, },
setActiveAccount: async (_, { accountId }) => {
try {
await authAPI.setActiveAccount({ accountId });
} catch (error) {
// Ignore error
}
},
}; };
// mutations // mutations

View File

@@ -165,4 +165,15 @@ describe('#actions', () => {
expect(commit.mock.calls).toEqual([]); expect(commit.mock.calls).toEqual([]);
}); });
}); });
describe('#setActiveAccount', () => {
it('sends correct mutations if account id is available', async () => {
actions.setActiveAccount(
{
commit,
},
{ accountId: 1 }
);
});
});
}); });

View File

@@ -182,6 +182,7 @@ Rails.application.routes.draw do
delete :avatar, on: :collection delete :avatar, on: :collection
member do member do
post :availability post :availability
put :set_active_account
end end
end end

View File

@@ -195,4 +195,27 @@ RSpec.describe 'Profile API', type: :request do
end end
end end
end end
describe 'PUT /api/v1/profile/set_active_account' do
context 'when it is an unauthenticated user' do
it 'returns unauthorized' do
put '/api/v1/profile/set_active_account'
expect(response).to have_http_status(:unauthorized)
end
end
context 'when it is an authenticated user' do
let(:agent) { create(:user, password: 'Test123!', account: account, role: :agent) }
it 'updates the last active account id' do
put '/api/v1/profile/set_active_account',
params: { profile: { account_id: account.id } },
headers: agent.create_new_auth_token,
as: :json
expect(response).to have_http_status(:success)
end
end
end
end end