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])
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
def set_user
@@ -39,6 +44,7 @@ class Api::V1::ProfilesController < Api::BaseController
:display_name,
:avatar,
:message_signature,
:account_id,
ui_settings: {}
)
end

View File

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

View File

@@ -147,4 +147,13 @@ export default {
deleteAvatar() {
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: {
url: '/api/v1/profile/avatar',
},
setActiveAccount: {
url: '/api/v1/profile/set_active_account',
},
};
export default page => {

View File

@@ -8,25 +8,33 @@
:header-title="$t('SIDEBAR_ITEMS.CHANGE_ACCOUNTS')"
:header-content="$t('SIDEBAR_ITEMS.SELECTOR_SUBTITLE')"
/>
<div class="account-selector--wrap">
<div
v-for="account in currentUser.accounts"
:key="account.id"
class="account-selector"
>
<a :href="`/app/accounts/${account.id}/dashboard`">
<button
class="button expanded clear link"
@click="onChangeAccount(account.id)"
>
<span class="button__content">
<label :for="account.name" class="account-details--wrap">
<div class="account--name">{{ account.name }}</div>
<div class="account--role">{{ account.role }}</div>
</label>
</span>
<fluent-icon
v-if="account.id === accountId"
v-show="account.id === accountId"
class="selected--account"
icon="checkmark-circle"
type="solid"
size="24"
/>
<label :for="account.name" class="account--details">
<div class="account--name">{{ account.name }}</div>
<div class="account--role">{{ account.role }}</div>
</label>
</a>
</button>
</div>
</div>
<div
v-if="globalConfig.createNewAccountFromDashboard"
class="modal-footer delete-item"
@@ -58,5 +66,40 @@ export default {
globalConfig: 'globalConfig/get',
}),
},
methods: {
onChangeAccount(accountId) {
const accountUrl = `/app/accounts/${accountId}/dashboard`;
window.location.href = accountUrl;
},
},
};
</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 { accounts = [] } = user || {};
const { accounts = [], account_id = null } = user || {};
const ssoAccount = accounts.find(
account => account.id === Number(ssoAccountId)
);
@@ -14,7 +14,9 @@ const getSSOAccountPath = ({ ssoAccountId, user }) => {
if (ssoAccount) {
accountPath = `accounts/${ssoAccountId}`;
} 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;
};

View File

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

View File

@@ -165,4 +165,15 @@ describe('#actions', () => {
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
member do
post :availability
put :set_active_account
end
end

View File

@@ -195,4 +195,27 @@ RSpec.describe 'Profile API', type: :request do
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