web/satellite: frozen status check and banner added

added api call, user isFrozen status;
new banner added;
DashboardArea migrated to use composition api;

Change-Id: Ia3fe68c2239064b2b729c6de14d7fc1dc8f4cf3b
This commit is contained in:
NickolaiYurchenko 2022-12-19 17:38:18 +02:00
parent 54559be966
commit 0e3e588718
10 changed files with 543 additions and 521 deletions

View File

@ -220,6 +220,23 @@ export class AuthHttpApi implements UsersApi {
throw new Error('can not delete user');
}
/**
* Fetches user frozen status.
*
* @throws Error
*/
public async getFrozenStatus(): Promise<boolean> {
const path = `${this.ROOT_PATH}/account/freezestatus`;
const response = await this.http.get(path);
if (response.ok) {
const responseData = await response.json();
return responseData.frozen;
}
throw new Error('can not get user frozen status');
}
// TODO: remove secret after Vanguard release
/**
* Used to register account.

View File

@ -59,6 +59,8 @@ export default class AddCardForm extends Vue {
// We fetch User one more time to update their Paid Tier status.
await this.$store.dispatch(USER_ACTIONS.GET);
await this.$store.dispatch(USER_ACTIONS.GET_FROZEN_STATUS);
} catch (error) {
await this.$notify.error(error.message, AnalyticsErrorEventSource.BILLING_ADD_STRIPE_CC_FORM);
@ -67,7 +69,8 @@ export default class AddCardForm extends Vue {
return;
}
await this.$notify.success('Card successfully added');
await this.$notify.success('Card successfully added. There can be a delay before changes related to changing your limits will take effect.');
try {
await this.$store.dispatch(GET_CREDIT_CARDS);
} catch (error) {

View File

@ -28,11 +28,10 @@ import CloseIcon from '@/../static/images/notifications/closeSmall.svg';
const props = withDefaults(defineProps<{
severity?: 'info' | 'warning' | 'critical';
onClick?: () => void;
dashboardRef?: HTMLElement;
dashboardRef: HTMLElement;
}>(), {
severity: 'info',
onClick: () => () => {},
dashboardRef: () => {},
});
const { isMobile } = useResize();
@ -130,6 +129,8 @@ watch(() => props.dashboardRef, () => {
}
&__close {
width: 15px;
height: 15px;
margin-left: 2.375rem;
cursor: pointer;
}
@ -146,6 +147,7 @@ watch(() => props.dashboardRef, () => {
.link {
color: black;
text-decoration: underline !important;
cursor: pointer;
}
@media screen and (max-width: 500px) {

View File

@ -14,6 +14,7 @@ export const USER_ACTIONS = {
GENERATE_USER_MFA_SECRET: 'generateUserMFASecret',
GENERATE_USER_MFA_RECOVERY_CODES: 'generateUserMFARecoveryCodes',
CLEAR: 'clearUser',
GET_FROZEN_STATUS: 'getFrozenStatus',
};
export const USER_MUTATIONS = {
@ -22,6 +23,7 @@ export const USER_MUTATIONS = {
SET_USER_MFA_RECOVERY_CODES: 'setUserMFARecoveryCodes',
UPDATE_USER: 'updateUser',
CLEAR: 'clearUser',
SET_FROZEN_STATUS: 'setFrozenStatus',
};
export class UsersState {
@ -37,6 +39,7 @@ const {
DISABLE_USER_MFA,
GENERATE_USER_MFA_SECRET,
GENERATE_USER_MFA_RECOVERY_CODES,
GET_FROZEN_STATUS,
} = USER_ACTIONS;
const {
@ -45,6 +48,7 @@ const {
SET_USER_MFA_SECRET,
SET_USER_MFA_RECOVERY_CODES,
CLEAR,
SET_FROZEN_STATUS,
} = USER_MUTATIONS;
interface UsersContext {
@ -75,6 +79,9 @@ export function makeUsersModule(api: UsersApi): StoreModule<UsersState, UsersCon
state.user.projectLimit = user.projectLimit;
},
[SET_FROZEN_STATUS](state: UsersState, status: boolean): void {
state.user.isFrozen = status;
},
[CLEAR](state: UsersState): void {
state.user = new User();
state.user.projectLimit = 1;
@ -107,6 +114,11 @@ export function makeUsersModule(api: UsersApi): StoreModule<UsersState, UsersCon
return user;
},
[GET_FROZEN_STATUS]: async function ({ commit }: UsersContext): Promise<void> {
const frozenStatus = await api.getFrozenStatus();
commit(SET_FROZEN_STATUS, frozenStatus);
},
[DISABLE_USER_MFA]: async function (_, request: DisableMFARequest): Promise<void> {
await api.disableUserMFA(request.passcode, request.recoveryCode);
},

View File

@ -50,6 +50,10 @@ export const useUsersStore = defineStore('users', () => {
usersState.user.projectLimit = user.projectLimit;
}
async function fetchFrozenStatus(): Promise<void> {
usersState.user.isFrozen = await api.getFrozenStatus();
}
async function disableUserMFA(request: DisableMFARequest): Promise<void> {
await api.disableUserMFA(request.passcode, request.recoveryCode);
}
@ -84,5 +88,6 @@ export const useUsersStore = defineStore('users', () => {
generateUserMFASecret,
generateUserMFARecoveryCodes,
clearUserInfo,
fetchFrozenStatus,
};
});

View File

@ -19,6 +19,14 @@ export interface UsersApi {
* @throws Error
*/
get(): Promise<User>;
/**
* Fetches user frozen status.
*
* @returns boolean
* @throws Error
*/
getFrozenStatus(): Promise<boolean>;
/**
* Enable user's MFA.
*
@ -67,6 +75,7 @@ export class User {
public haveSalesContact: boolean = false,
public mfaRecoveryCodeCount: number = 0,
public signupPromoCode: string = '',
public isFrozen: boolean = false,
) {}
public getFullName(): string {

File diff suppressed because it is too large Load Diff

View File

@ -4,13 +4,13 @@
import Vuex from 'vuex';
import { createLocalVue, shallowMount } from '@vue/test-utils';
import { AccessGrantsMock } from '../mock/api/accessGrants';
import { BucketsMock } from '../mock/api/buckets';
import { PaymentsMock } from '../mock/api/payments';
import { ProjectMembersApiMock } from '../mock/api/projectMembers';
import { ProjectsApiMock } from '../mock/api/projects';
import { UsersApiMock } from '../mock/api/users';
import { ABMockApi } from '../mock/api/abtesting';
import { AccessGrantsMock } from '../../mock/api/accessGrants';
import { BucketsMock } from '../../mock/api/buckets';
import { PaymentsMock } from '../../mock/api/payments';
import { ProjectMembersApiMock } from '../../mock/api/projectMembers';
import { ProjectsApiMock } from '../../mock/api/projects';
import { UsersApiMock } from '../../mock/api/users';
import { ABMockApi } from '../../mock/api/abtesting';
import { RouteConfig, router } from '@/router';
import { makeAccessGrantsModule } from '@/store/modules/accessGrants';

View File

@ -17,6 +17,10 @@ export class UsersApiMock implements UsersApi {
return Promise.resolve(this.mockUser);
}
public getFrozenStatus(): Promise<boolean> {
return Promise.resolve(true);
}
public update(_user: UpdatedUser): Promise<void> {
throw new Error('not implemented');
}

View File

@ -1,43 +0,0 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`Dashboard renders correctly when data is loaded 1`] = `
<div class="dashboard">
<div class="dashboard__wrap">
<div class="dashboard__wrap__main-area">
<navigationarea-stub class="dashboard__wrap__main-area__navigation"></navigationarea-stub>
<mobilenavigation-stub class="dashboard__wrap__main-area__mobile-navigation"></mobilenavigation-stub>
<div class="dashboard__wrap__main-area__content-wrap">
<!---->
<div class="dashboard__wrap__main-area__content-wrap__container">
<div class="bars">
<!---->
<paidtierbar-stub openaddpmmodal="[Function]"></paidtierbar-stub>
<!---->
<!---->
</div>
<router-view-stub name="default" class="dashboard__wrap__main-area__content-wrap__container__content"></router-view-stub>
</div>
<billingnotification-stub></billingnotification-stub>
</div>
</div>
</div>
<!---->
<!---->
<!---->
<!---->
<allmodals-stub></allmodals-stub>
</div>
`;
exports[`Dashboard renders correctly when data is loading 1`] = `
<div class="dashboard">
<div class="loading-overlay active">
<loaderimage-stub class="loading-icon"></loaderimage-stub>
</div>
<!---->
<!---->
<!---->
<!---->
<allmodals-stub></allmodals-stub>
</div>
`;