mirror of
https://github.com/bitwarden/browser.git
synced 2024-11-25 12:15:18 +01:00
Web - SG-668 update flows for free 2 person orgs (#4093)
* People-component - Minor refactoring - Make org a comp prop instead of creating multiple component props for props on the org object * Added IconDirective to Dialog.module so that bit-dialog-icon directive can work within <bit-simple-dialog> components * SG-668 - #2 - If a free org has members (any status) at max seat limit, then prompt for upgrade with dialog which takes you to upgrade flow on billing/subscription management page * SG-668 - (1) Refactored upgrade dialog to accept translated body text for better re-usability (2) Completed task #3 - If user has max collections for free org and tries to add a 3rd, they are prompted via upgrade dialog. * SG-668 - Update equality checks to use strict equality * SG-668 - Upgrade dialog now shows contextual body text based on if the user can manage billing or not
This commit is contained in:
parent
9bbe13fa71
commit
3d008da287
@ -75,6 +75,10 @@ export class OrganizationSubscriptionComponent implements OnInit, OnDestroy {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async ngOnInit() {
|
async ngOnInit() {
|
||||||
|
if (this.route.snapshot.queryParamMap.get("upgrade")) {
|
||||||
|
this.changePlan();
|
||||||
|
}
|
||||||
|
|
||||||
this.route.params
|
this.route.params
|
||||||
.pipe(
|
.pipe(
|
||||||
concatMap(async (params) => {
|
concatMap(async (params) => {
|
||||||
|
@ -10,6 +10,7 @@ import { LogService } from "@bitwarden/common/abstractions/log.service";
|
|||||||
import { OrganizationService } from "@bitwarden/common/abstractions/organization/organization.service.abstraction";
|
import { OrganizationService } from "@bitwarden/common/abstractions/organization/organization.service.abstraction";
|
||||||
import { PlatformUtilsService } from "@bitwarden/common/abstractions/platformUtils.service";
|
import { PlatformUtilsService } from "@bitwarden/common/abstractions/platformUtils.service";
|
||||||
import { SearchService } from "@bitwarden/common/abstractions/search.service";
|
import { SearchService } from "@bitwarden/common/abstractions/search.service";
|
||||||
|
import { ProductType } from "@bitwarden/common/enums/productType";
|
||||||
import { CollectionData } from "@bitwarden/common/models/data/collection.data";
|
import { CollectionData } from "@bitwarden/common/models/data/collection.data";
|
||||||
import { Collection } from "@bitwarden/common/models/domain/collection";
|
import { Collection } from "@bitwarden/common/models/domain/collection";
|
||||||
import { Organization } from "@bitwarden/common/models/domain/organization";
|
import { Organization } from "@bitwarden/common/models/domain/organization";
|
||||||
@ -19,9 +20,11 @@ import {
|
|||||||
} from "@bitwarden/common/models/response/collection.response";
|
} from "@bitwarden/common/models/response/collection.response";
|
||||||
import { ListResponse } from "@bitwarden/common/models/response/list.response";
|
import { ListResponse } from "@bitwarden/common/models/response/list.response";
|
||||||
import { CollectionView } from "@bitwarden/common/models/view/collection.view";
|
import { CollectionView } from "@bitwarden/common/models/view/collection.view";
|
||||||
|
import { DialogService } from "@bitwarden/components";
|
||||||
|
|
||||||
import { CollectionAddEditComponent } from "./collection-add-edit.component";
|
import { CollectionAddEditComponent } from "./collection-add-edit.component";
|
||||||
import { EntityUsersComponent } from "./entity-users.component";
|
import { EntityUsersComponent } from "./entity-users.component";
|
||||||
|
import { OrgUpgradeDialogComponent } from "./org-upgrade-dialog/org-upgrade-dialog.component";
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: "app-org-manage-collections",
|
selector: "app-org-manage-collections",
|
||||||
@ -56,7 +59,8 @@ export class CollectionsComponent implements OnInit {
|
|||||||
private platformUtilsService: PlatformUtilsService,
|
private platformUtilsService: PlatformUtilsService,
|
||||||
private searchService: SearchService,
|
private searchService: SearchService,
|
||||||
private logService: LogService,
|
private logService: LogService,
|
||||||
private organizationService: OrganizationService
|
private organizationService: OrganizationService,
|
||||||
|
private dialogService: DialogService
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
async ngOnInit() {
|
async ngOnInit() {
|
||||||
@ -126,6 +130,32 @@ export class CollectionsComponent implements OnInit {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (
|
||||||
|
!collection &&
|
||||||
|
this.organization.planProductType === ProductType.Free &&
|
||||||
|
this.collections.length === this.organization.maxCollections
|
||||||
|
) {
|
||||||
|
// Show org upgrade modal
|
||||||
|
const dialogBodyText = this.organization.canManageBilling
|
||||||
|
? this.i18nService.t(
|
||||||
|
"freeOrgMaxCollectionReachedManageBilling",
|
||||||
|
this.organization.maxCollections.toString()
|
||||||
|
)
|
||||||
|
: this.i18nService.t(
|
||||||
|
"freeOrgMaxCollectionReachedNoManageBilling",
|
||||||
|
this.organization.maxCollections.toString()
|
||||||
|
);
|
||||||
|
|
||||||
|
this.dialogService.open(OrgUpgradeDialogComponent, {
|
||||||
|
data: {
|
||||||
|
orgId: this.organization.id,
|
||||||
|
dialogBodyText: dialogBodyText,
|
||||||
|
orgCanManageBilling: this.organization.canManageBilling,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
const [modal] = await this.modalService.openViewRef(
|
const [modal] = await this.modalService.openViewRef(
|
||||||
CollectionAddEditComponent,
|
CollectionAddEditComponent,
|
||||||
this.addEditModalRef,
|
this.addEditModalRef,
|
||||||
|
@ -0,0 +1,33 @@
|
|||||||
|
<bit-simple-dialog>
|
||||||
|
<i
|
||||||
|
bit-dialog-icon
|
||||||
|
class="bwi bwi-business tw-text-5xl tw-text-primary-500"
|
||||||
|
aria-hidden="true"
|
||||||
|
></i>
|
||||||
|
<span bitDialogTitle class="font-bold">{{ "upgradeOrganization" | i18n }}</span>
|
||||||
|
<span bitDialogContent>
|
||||||
|
{{ data.dialogBodyText }}
|
||||||
|
</span>
|
||||||
|
<div bitDialogFooter class="tw-flex tw-flex-row tw-gap-2">
|
||||||
|
<ng-container *ngIf="data.orgCanManageBilling">
|
||||||
|
<button
|
||||||
|
bitButton
|
||||||
|
buttonType="primary"
|
||||||
|
[routerLink]="['/organizations', data.orgId, 'billing', 'subscription']"
|
||||||
|
[queryParams]="{ upgrade: true }"
|
||||||
|
(click)="dialogRef.close()"
|
||||||
|
>
|
||||||
|
{{ "upgrade" | i18n }}
|
||||||
|
</button>
|
||||||
|
<button bitButton buttonType="secondary" (click)="dialogRef.close()">
|
||||||
|
{{ "cancel" | i18n }}
|
||||||
|
</button>
|
||||||
|
</ng-container>
|
||||||
|
|
||||||
|
<ng-container *ngIf="!data.orgCanManageBilling">
|
||||||
|
<button bitButton buttonType="primary" (click)="dialogRef.close()">
|
||||||
|
{{ "ok" | i18n }}
|
||||||
|
</button>
|
||||||
|
</ng-container>
|
||||||
|
</div>
|
||||||
|
</bit-simple-dialog>
|
@ -0,0 +1,19 @@
|
|||||||
|
import { DialogRef, DIALOG_DATA } from "@angular/cdk/dialog";
|
||||||
|
import { Component, Inject } from "@angular/core";
|
||||||
|
|
||||||
|
export interface OrgUpgradeDialogData {
|
||||||
|
orgId: string;
|
||||||
|
orgCanManageBilling: boolean;
|
||||||
|
dialogBodyText: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: "app-org-upgrade-dialog",
|
||||||
|
templateUrl: "org-upgrade-dialog.component.html",
|
||||||
|
})
|
||||||
|
export class OrgUpgradeDialogComponent {
|
||||||
|
constructor(
|
||||||
|
public dialogRef: DialogRef,
|
||||||
|
@Inject(DIALOG_DATA) public data: OrgUpgradeDialogData
|
||||||
|
) {}
|
||||||
|
}
|
@ -221,7 +221,7 @@
|
|||||||
href="#"
|
href="#"
|
||||||
appStopClick
|
appStopClick
|
||||||
(click)="groups(u)"
|
(click)="groups(u)"
|
||||||
*ngIf="accessGroups"
|
*ngIf="organization.useGroups"
|
||||||
>
|
>
|
||||||
<i class="bwi bwi-fw bwi-sitemap" aria-hidden="true"></i>
|
<i class="bwi bwi-fw bwi-sitemap" aria-hidden="true"></i>
|
||||||
{{ "groups" | i18n }}
|
{{ "groups" | i18n }}
|
||||||
@ -231,7 +231,7 @@
|
|||||||
href="#"
|
href="#"
|
||||||
appStopClick
|
appStopClick
|
||||||
(click)="events(u)"
|
(click)="events(u)"
|
||||||
*ngIf="accessEvents && u.status === userStatusType.Confirmed"
|
*ngIf="organization.useEvents && u.status === userStatusType.Confirmed"
|
||||||
>
|
>
|
||||||
<i class="bwi bwi-fw bwi-file-text" aria-hidden="true"></i>
|
<i class="bwi bwi-fw bwi-file-text" aria-hidden="true"></i>
|
||||||
{{ "eventLogs" | i18n }}
|
{{ "eventLogs" | i18n }}
|
||||||
|
@ -20,12 +20,15 @@ import { ValidationService } from "@bitwarden/common/abstractions/validation.ser
|
|||||||
import { OrganizationUserStatusType } from "@bitwarden/common/enums/organizationUserStatusType";
|
import { OrganizationUserStatusType } from "@bitwarden/common/enums/organizationUserStatusType";
|
||||||
import { OrganizationUserType } from "@bitwarden/common/enums/organizationUserType";
|
import { OrganizationUserType } from "@bitwarden/common/enums/organizationUserType";
|
||||||
import { PolicyType } from "@bitwarden/common/enums/policyType";
|
import { PolicyType } from "@bitwarden/common/enums/policyType";
|
||||||
|
import { ProductType } from "@bitwarden/common/enums/productType";
|
||||||
|
import { Organization } from "@bitwarden/common/models/domain/organization";
|
||||||
import { OrganizationKeysRequest } from "@bitwarden/common/models/request/organization-keys.request";
|
import { OrganizationKeysRequest } from "@bitwarden/common/models/request/organization-keys.request";
|
||||||
import { OrganizationUserBulkRequest } from "@bitwarden/common/models/request/organization-user-bulk.request";
|
import { OrganizationUserBulkRequest } from "@bitwarden/common/models/request/organization-user-bulk.request";
|
||||||
import { OrganizationUserConfirmRequest } from "@bitwarden/common/models/request/organization-user-confirm.request";
|
import { OrganizationUserConfirmRequest } from "@bitwarden/common/models/request/organization-user-confirm.request";
|
||||||
import { ListResponse } from "@bitwarden/common/models/response/list.response";
|
import { ListResponse } from "@bitwarden/common/models/response/list.response";
|
||||||
import { OrganizationUserBulkResponse } from "@bitwarden/common/models/response/organization-user-bulk.response";
|
import { OrganizationUserBulkResponse } from "@bitwarden/common/models/response/organization-user-bulk.response";
|
||||||
import { OrganizationUserUserDetailsResponse } from "@bitwarden/common/models/response/organization-user.response";
|
import { OrganizationUserUserDetailsResponse } from "@bitwarden/common/models/response/organization-user.response";
|
||||||
|
import { DialogService } from "@bitwarden/components";
|
||||||
|
|
||||||
import { BasePeopleComponent } from "../../common/base.people.component";
|
import { BasePeopleComponent } from "../../common/base.people.component";
|
||||||
|
|
||||||
@ -34,6 +37,7 @@ import { BulkRemoveComponent } from "./bulk/bulk-remove.component";
|
|||||||
import { BulkRestoreRevokeComponent } from "./bulk/bulk-restore-revoke.component";
|
import { BulkRestoreRevokeComponent } from "./bulk/bulk-restore-revoke.component";
|
||||||
import { BulkStatusComponent } from "./bulk/bulk-status.component";
|
import { BulkStatusComponent } from "./bulk/bulk-status.component";
|
||||||
import { EntityEventsComponent } from "./entity-events.component";
|
import { EntityEventsComponent } from "./entity-events.component";
|
||||||
|
import { OrgUpgradeDialogComponent } from "./org-upgrade-dialog/org-upgrade-dialog.component";
|
||||||
import { ResetPasswordComponent } from "./reset-password.component";
|
import { ResetPasswordComponent } from "./reset-password.component";
|
||||||
import { UserAddEditComponent } from "./user-add-edit.component";
|
import { UserAddEditComponent } from "./user-add-edit.component";
|
||||||
import { UserGroupsComponent } from "./user-groups.component";
|
import { UserGroupsComponent } from "./user-groups.component";
|
||||||
@ -65,15 +69,9 @@ export class PeopleComponent
|
|||||||
userType = OrganizationUserType;
|
userType = OrganizationUserType;
|
||||||
userStatusType = OrganizationUserStatusType;
|
userStatusType = OrganizationUserStatusType;
|
||||||
|
|
||||||
organizationId: string;
|
organization: Organization;
|
||||||
status: OrganizationUserStatusType = null;
|
status: OrganizationUserStatusType = null;
|
||||||
accessEvents = false;
|
|
||||||
accessGroups = false;
|
|
||||||
canResetPassword = false; // User permission (admin/custom)
|
|
||||||
orgUseResetPassword = false; // Org plan ability
|
|
||||||
orgHasKeys = false; // Org public/private keys
|
|
||||||
orgResetPasswordPolicyEnabled = false;
|
orgResetPasswordPolicyEnabled = false;
|
||||||
callingUserType: OrganizationUserType = null;
|
|
||||||
|
|
||||||
private destroy$ = new Subject<void>();
|
private destroy$ = new Subject<void>();
|
||||||
|
|
||||||
@ -93,7 +91,8 @@ export class PeopleComponent
|
|||||||
private syncService: SyncService,
|
private syncService: SyncService,
|
||||||
stateService: StateService,
|
stateService: StateService,
|
||||||
private organizationService: OrganizationService,
|
private organizationService: OrganizationService,
|
||||||
private organizationApiService: OrganizationApiServiceAbstraction
|
private organizationApiService: OrganizationApiServiceAbstraction,
|
||||||
|
private dialogService: DialogService
|
||||||
) {
|
) {
|
||||||
super(
|
super(
|
||||||
apiService,
|
apiService,
|
||||||
@ -114,26 +113,23 @@ export class PeopleComponent
|
|||||||
combineLatest([this.route.params, this.route.queryParams, this.policyService.policies$])
|
combineLatest([this.route.params, this.route.queryParams, this.policyService.policies$])
|
||||||
.pipe(
|
.pipe(
|
||||||
concatMap(async ([params, qParams, policies]) => {
|
concatMap(async ([params, qParams, policies]) => {
|
||||||
this.organizationId = params.organizationId;
|
this.organization = await this.organizationService.get(params.organizationId);
|
||||||
const organization = await this.organizationService.get(this.organizationId);
|
|
||||||
this.accessEvents = organization.useEvents;
|
|
||||||
this.accessGroups = organization.useGroups;
|
|
||||||
this.canResetPassword = organization.canManageUsersPassword;
|
|
||||||
this.orgUseResetPassword = organization.useResetPassword;
|
|
||||||
this.callingUserType = organization.type;
|
|
||||||
this.orgHasKeys = organization.hasPublicAndPrivateKeys;
|
|
||||||
|
|
||||||
// Backfill pub/priv key if necessary
|
// Backfill pub/priv key if necessary
|
||||||
if (this.canResetPassword && !this.orgHasKeys) {
|
if (
|
||||||
const orgShareKey = await this.cryptoService.getOrgKey(this.organizationId);
|
this.organization.canManageUsersPassword &&
|
||||||
|
!this.organization.hasPublicAndPrivateKeys
|
||||||
|
) {
|
||||||
|
const orgShareKey = await this.cryptoService.getOrgKey(this.organization.id);
|
||||||
const orgKeys = await this.cryptoService.makeKeyPair(orgShareKey);
|
const orgKeys = await this.cryptoService.makeKeyPair(orgShareKey);
|
||||||
const request = new OrganizationKeysRequest(orgKeys[0], orgKeys[1].encryptedString);
|
const request = new OrganizationKeysRequest(orgKeys[0], orgKeys[1].encryptedString);
|
||||||
const response = await this.organizationApiService.updateKeys(
|
const response = await this.organizationApiService.updateKeys(
|
||||||
this.organizationId,
|
this.organization.id,
|
||||||
request
|
request
|
||||||
);
|
);
|
||||||
if (response != null) {
|
if (response != null) {
|
||||||
this.orgHasKeys = response.publicKey != null && response.privateKey != null;
|
this.organization.hasPublicAndPrivateKeys =
|
||||||
|
response.publicKey != null && response.privateKey != null;
|
||||||
await this.syncService.fullSync(true); // Replace oganizations with new data
|
await this.syncService.fullSync(true); // Replace oganizations with new data
|
||||||
} else {
|
} else {
|
||||||
throw new Error(this.i18nService.t("resetPasswordOrgKeysError"));
|
throw new Error(this.i18nService.t("resetPasswordOrgKeysError"));
|
||||||
@ -142,7 +138,7 @@ export class PeopleComponent
|
|||||||
|
|
||||||
const resetPasswordPolicy = policies
|
const resetPasswordPolicy = policies
|
||||||
.filter((policy) => policy.type === PolicyType.ResetPassword)
|
.filter((policy) => policy.type === PolicyType.ResetPassword)
|
||||||
.find((p) => p.organizationId === this.organizationId);
|
.find((p) => p.organizationId === this.organization.id);
|
||||||
this.orgResetPasswordPolicyEnabled = resetPasswordPolicy?.enabled;
|
this.orgResetPasswordPolicyEnabled = resetPasswordPolicy?.enabled;
|
||||||
|
|
||||||
await this.load();
|
await this.load();
|
||||||
@ -171,41 +167,41 @@ export class PeopleComponent
|
|||||||
}
|
}
|
||||||
|
|
||||||
getUsers(): Promise<ListResponse<OrganizationUserUserDetailsResponse>> {
|
getUsers(): Promise<ListResponse<OrganizationUserUserDetailsResponse>> {
|
||||||
return this.apiService.getOrganizationUsers(this.organizationId);
|
return this.apiService.getOrganizationUsers(this.organization.id);
|
||||||
}
|
}
|
||||||
|
|
||||||
deleteUser(id: string): Promise<void> {
|
deleteUser(id: string): Promise<void> {
|
||||||
return this.apiService.deleteOrganizationUser(this.organizationId, id);
|
return this.apiService.deleteOrganizationUser(this.organization.id, id);
|
||||||
}
|
}
|
||||||
|
|
||||||
revokeUser(id: string): Promise<void> {
|
revokeUser(id: string): Promise<void> {
|
||||||
return this.apiService.revokeOrganizationUser(this.organizationId, id);
|
return this.apiService.revokeOrganizationUser(this.organization.id, id);
|
||||||
}
|
}
|
||||||
|
|
||||||
restoreUser(id: string): Promise<void> {
|
restoreUser(id: string): Promise<void> {
|
||||||
return this.apiService.restoreOrganizationUser(this.organizationId, id);
|
return this.apiService.restoreOrganizationUser(this.organization.id, id);
|
||||||
}
|
}
|
||||||
|
|
||||||
reinviteUser(id: string): Promise<void> {
|
reinviteUser(id: string): Promise<void> {
|
||||||
return this.apiService.postOrganizationUserReinvite(this.organizationId, id);
|
return this.apiService.postOrganizationUserReinvite(this.organization.id, id);
|
||||||
}
|
}
|
||||||
|
|
||||||
async confirmUser(
|
async confirmUser(
|
||||||
user: OrganizationUserUserDetailsResponse,
|
user: OrganizationUserUserDetailsResponse,
|
||||||
publicKey: Uint8Array
|
publicKey: Uint8Array
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
const orgKey = await this.cryptoService.getOrgKey(this.organizationId);
|
const orgKey = await this.cryptoService.getOrgKey(this.organization.id);
|
||||||
const key = await this.cryptoService.rsaEncrypt(orgKey.key, publicKey.buffer);
|
const key = await this.cryptoService.rsaEncrypt(orgKey.key, publicKey.buffer);
|
||||||
const request = new OrganizationUserConfirmRequest();
|
const request = new OrganizationUserConfirmRequest();
|
||||||
request.key = key.encryptedString;
|
request.key = key.encryptedString;
|
||||||
await this.apiService.postOrganizationUserConfirm(this.organizationId, user.id, request);
|
await this.apiService.postOrganizationUserConfirm(this.organization.id, user.id, request);
|
||||||
}
|
}
|
||||||
|
|
||||||
allowResetPassword(orgUser: OrganizationUserUserDetailsResponse): boolean {
|
allowResetPassword(orgUser: OrganizationUserUserDetailsResponse): boolean {
|
||||||
// Hierarchy check
|
// Hierarchy check
|
||||||
let callingUserHasPermission = false;
|
let callingUserHasPermission = false;
|
||||||
|
|
||||||
switch (this.callingUserType) {
|
switch (this.organization.type) {
|
||||||
case OrganizationUserType.Owner:
|
case OrganizationUserType.Owner:
|
||||||
callingUserHasPermission = true;
|
callingUserHasPermission = true;
|
||||||
break;
|
break;
|
||||||
@ -221,10 +217,10 @@ export class PeopleComponent
|
|||||||
|
|
||||||
// Final
|
// Final
|
||||||
return (
|
return (
|
||||||
this.canResetPassword &&
|
this.organization.canManageUsersPassword &&
|
||||||
callingUserHasPermission &&
|
callingUserHasPermission &&
|
||||||
this.orgUseResetPassword &&
|
this.organization.useResetPassword &&
|
||||||
this.orgHasKeys &&
|
this.organization.hasPublicAndPrivateKeys &&
|
||||||
orgUser.resetPasswordEnrolled &&
|
orgUser.resetPasswordEnrolled &&
|
||||||
this.orgResetPasswordPolicyEnabled &&
|
this.orgResetPasswordPolicyEnabled &&
|
||||||
orgUser.status === OrganizationUserStatusType.Confirmed
|
orgUser.status === OrganizationUserStatusType.Confirmed
|
||||||
@ -233,20 +229,51 @@ export class PeopleComponent
|
|||||||
|
|
||||||
showEnrolledStatus(orgUser: OrganizationUserUserDetailsResponse): boolean {
|
showEnrolledStatus(orgUser: OrganizationUserUserDetailsResponse): boolean {
|
||||||
return (
|
return (
|
||||||
this.orgUseResetPassword &&
|
this.organization.useResetPassword &&
|
||||||
orgUser.resetPasswordEnrolled &&
|
orgUser.resetPasswordEnrolled &&
|
||||||
this.orgResetPasswordPolicyEnabled
|
this.orgResetPasswordPolicyEnabled
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
async edit(user: OrganizationUserUserDetailsResponse) {
|
async edit(user: OrganizationUserUserDetailsResponse) {
|
||||||
|
// Invite User: Add Flow
|
||||||
|
// Click on user email: Edit Flow
|
||||||
|
|
||||||
|
// User attempting to invite new users in a free org with max users
|
||||||
|
if (
|
||||||
|
!user &&
|
||||||
|
this.organization.planProductType === ProductType.Free &&
|
||||||
|
this.users.length === this.organization.seats
|
||||||
|
) {
|
||||||
|
// Show org upgrade modal
|
||||||
|
|
||||||
|
const dialogBodyText = this.organization.canManageBilling
|
||||||
|
? this.i18nService.t(
|
||||||
|
"freeOrgInvLimitReachedManageBilling",
|
||||||
|
this.organization.seats.toString()
|
||||||
|
)
|
||||||
|
: this.i18nService.t(
|
||||||
|
"freeOrgInvLimitReachedNoManageBilling",
|
||||||
|
this.organization.seats.toString()
|
||||||
|
);
|
||||||
|
|
||||||
|
this.dialogService.open(OrgUpgradeDialogComponent, {
|
||||||
|
data: {
|
||||||
|
orgId: this.organization.id,
|
||||||
|
orgCanManageBilling: this.organization.canManageBilling,
|
||||||
|
dialogBodyText: dialogBodyText,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
const [modal] = await this.modalService.openViewRef(
|
const [modal] = await this.modalService.openViewRef(
|
||||||
UserAddEditComponent,
|
UserAddEditComponent,
|
||||||
this.addEditModalRef,
|
this.addEditModalRef,
|
||||||
(comp) => {
|
(comp) => {
|
||||||
comp.name = this.userNamePipe.transform(user);
|
comp.name = this.userNamePipe.transform(user);
|
||||||
comp.organizationId = this.organizationId;
|
comp.organizationId = this.organization.id;
|
||||||
comp.organizationUserId = user != null ? user.id : null;
|
comp.organizationUserId = user?.id || null;
|
||||||
comp.usesKeyConnector = user?.usesKeyConnector;
|
comp.usesKeyConnector = user?.usesKeyConnector;
|
||||||
// eslint-disable-next-line rxjs-angular/prefer-takeuntil
|
// eslint-disable-next-line rxjs-angular/prefer-takeuntil
|
||||||
comp.onSavedUser.subscribe(() => {
|
comp.onSavedUser.subscribe(() => {
|
||||||
@ -278,7 +305,7 @@ export class PeopleComponent
|
|||||||
this.groupsModalRef,
|
this.groupsModalRef,
|
||||||
(comp) => {
|
(comp) => {
|
||||||
comp.name = this.userNamePipe.transform(user);
|
comp.name = this.userNamePipe.transform(user);
|
||||||
comp.organizationId = this.organizationId;
|
comp.organizationId = this.organization.id;
|
||||||
comp.organizationUserId = user != null ? user.id : null;
|
comp.organizationUserId = user != null ? user.id : null;
|
||||||
// eslint-disable-next-line rxjs-angular/prefer-takeuntil
|
// eslint-disable-next-line rxjs-angular/prefer-takeuntil
|
||||||
comp.onSavedUser.subscribe(() => {
|
comp.onSavedUser.subscribe(() => {
|
||||||
@ -297,7 +324,7 @@ export class PeopleComponent
|
|||||||
BulkRemoveComponent,
|
BulkRemoveComponent,
|
||||||
this.bulkRemoveModalRef,
|
this.bulkRemoveModalRef,
|
||||||
(comp) => {
|
(comp) => {
|
||||||
comp.organizationId = this.organizationId;
|
comp.organizationId = this.organization.id;
|
||||||
comp.users = this.getCheckedUsers();
|
comp.users = this.getCheckedUsers();
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
@ -322,7 +349,7 @@ export class PeopleComponent
|
|||||||
const ref = this.modalService.open(BulkRestoreRevokeComponent, {
|
const ref = this.modalService.open(BulkRestoreRevokeComponent, {
|
||||||
allowMultipleModals: true,
|
allowMultipleModals: true,
|
||||||
data: {
|
data: {
|
||||||
organizationId: this.organizationId,
|
organizationId: this.organization.id,
|
||||||
users: this.getCheckedUsers(),
|
users: this.getCheckedUsers(),
|
||||||
isRevoking: isRevoking,
|
isRevoking: isRevoking,
|
||||||
},
|
},
|
||||||
@ -352,7 +379,7 @@ export class PeopleComponent
|
|||||||
try {
|
try {
|
||||||
const request = new OrganizationUserBulkRequest(filteredUsers.map((user) => user.id));
|
const request = new OrganizationUserBulkRequest(filteredUsers.map((user) => user.id));
|
||||||
const response = this.apiService.postManyOrganizationUserReinvite(
|
const response = this.apiService.postManyOrganizationUserReinvite(
|
||||||
this.organizationId,
|
this.organization.id,
|
||||||
request
|
request
|
||||||
);
|
);
|
||||||
this.showBulkStatus(
|
this.showBulkStatus(
|
||||||
@ -376,7 +403,7 @@ export class PeopleComponent
|
|||||||
BulkConfirmComponent,
|
BulkConfirmComponent,
|
||||||
this.bulkConfirmModalRef,
|
this.bulkConfirmModalRef,
|
||||||
(comp) => {
|
(comp) => {
|
||||||
comp.organizationId = this.organizationId;
|
comp.organizationId = this.organization.id;
|
||||||
comp.users = this.getCheckedUsers();
|
comp.users = this.getCheckedUsers();
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
@ -388,7 +415,7 @@ export class PeopleComponent
|
|||||||
async events(user: OrganizationUserUserDetailsResponse) {
|
async events(user: OrganizationUserUserDetailsResponse) {
|
||||||
await this.modalService.openViewRef(EntityEventsComponent, this.eventsModalRef, (comp) => {
|
await this.modalService.openViewRef(EntityEventsComponent, this.eventsModalRef, (comp) => {
|
||||||
comp.name = this.userNamePipe.transform(user);
|
comp.name = this.userNamePipe.transform(user);
|
||||||
comp.organizationId = this.organizationId;
|
comp.organizationId = this.organization.id;
|
||||||
comp.entityId = user.id;
|
comp.entityId = user.id;
|
||||||
comp.showUser = false;
|
comp.showUser = false;
|
||||||
comp.entity = "user";
|
comp.entity = "user";
|
||||||
@ -402,7 +429,7 @@ export class PeopleComponent
|
|||||||
(comp) => {
|
(comp) => {
|
||||||
comp.name = this.userNamePipe.transform(user);
|
comp.name = this.userNamePipe.transform(user);
|
||||||
comp.email = user != null ? user.email : null;
|
comp.email = user != null ? user.email : null;
|
||||||
comp.organizationId = this.organizationId;
|
comp.organizationId = this.organization.id;
|
||||||
comp.id = user != null ? user.id : null;
|
comp.id = user != null ? user.id : null;
|
||||||
|
|
||||||
// eslint-disable-next-line rxjs-angular/prefer-takeuntil
|
// eslint-disable-next-line rxjs-angular/prefer-takeuntil
|
||||||
|
@ -1,10 +1,12 @@
|
|||||||
import { NgModule } from "@angular/core";
|
import { NgModule } from "@angular/core";
|
||||||
|
|
||||||
import { AccessSelectorModule } from "./components/access-selector";
|
import { AccessSelectorModule } from "./components/access-selector";
|
||||||
|
import { OrgUpgradeDialogComponent } from "./manage/org-upgrade-dialog/org-upgrade-dialog.component";
|
||||||
import { OrganizationsRoutingModule } from "./organization-routing.module";
|
import { OrganizationsRoutingModule } from "./organization-routing.module";
|
||||||
import { SharedOrganizationModule } from "./shared";
|
import { SharedOrganizationModule } from "./shared";
|
||||||
|
|
||||||
@NgModule({
|
@NgModule({
|
||||||
imports: [SharedOrganizationModule, AccessSelectorModule, OrganizationsRoutingModule],
|
imports: [SharedOrganizationModule, AccessSelectorModule, OrganizationsRoutingModule],
|
||||||
|
declarations: [OrgUpgradeDialogComponent],
|
||||||
})
|
})
|
||||||
export class OrganizationModule {}
|
export class OrganizationModule {}
|
||||||
|
@ -5771,5 +5771,41 @@
|
|||||||
},
|
},
|
||||||
"memberAccessAll": {
|
"memberAccessAll": {
|
||||||
"message": "This member can access and modify all items."
|
"message": "This member can access and modify all items."
|
||||||
|
},
|
||||||
|
"freeOrgInvLimitReachedManageBilling": {
|
||||||
|
"message": "Free organizations may have up to $SEATCOUNT$ members. Upgrade to a paid plan to invite more members.",
|
||||||
|
"placeholders": {
|
||||||
|
"seatcount": {
|
||||||
|
"content": "$1",
|
||||||
|
"example": "2"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"freeOrgInvLimitReachedNoManageBilling": {
|
||||||
|
"message": "Free organizations may have up to $SEATCOUNT$ members. Contact your organization owner to upgrade.",
|
||||||
|
"placeholders": {
|
||||||
|
"seatcount": {
|
||||||
|
"content": "$1",
|
||||||
|
"example": "2"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"freeOrgMaxCollectionReachedManageBilling": {
|
||||||
|
"message": "Free organizations may have up to $COLLECTIONCOUNT$ collections. Upgrade to a paid plan to add more collections.",
|
||||||
|
"placeholders": {
|
||||||
|
"COLLECTIONCOUNT": {
|
||||||
|
"content": "$1",
|
||||||
|
"example": "2"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"freeOrgMaxCollectionReachedNoManageBilling": {
|
||||||
|
"message": "Free organizations may have up to $COLLECTIONCOUNT$ collections. Contact your organization owner to upgrade.",
|
||||||
|
"placeholders": {
|
||||||
|
"COLLECTIONCOUNT": {
|
||||||
|
"content": "$1",
|
||||||
|
"example": "2"
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -8,7 +8,7 @@ import { DialogService } from "./dialog.service";
|
|||||||
import { DialogComponent } from "./dialog/dialog.component";
|
import { DialogComponent } from "./dialog/dialog.component";
|
||||||
import { DialogCloseDirective } from "./directives/dialog-close.directive";
|
import { DialogCloseDirective } from "./directives/dialog-close.directive";
|
||||||
import { DialogTitleContainerDirective } from "./directives/dialog-title-container.directive";
|
import { DialogTitleContainerDirective } from "./directives/dialog-title-container.directive";
|
||||||
import { SimpleDialogComponent } from "./simple-dialog/simple-dialog.component";
|
import { IconDirective, SimpleDialogComponent } from "./simple-dialog/simple-dialog.component";
|
||||||
|
|
||||||
@NgModule({
|
@NgModule({
|
||||||
imports: [SharedModule, IconButtonModule, CdkDialogModule],
|
imports: [SharedModule, IconButtonModule, CdkDialogModule],
|
||||||
@ -17,8 +17,15 @@ import { SimpleDialogComponent } from "./simple-dialog/simple-dialog.component";
|
|||||||
DialogTitleContainerDirective,
|
DialogTitleContainerDirective,
|
||||||
DialogComponent,
|
DialogComponent,
|
||||||
SimpleDialogComponent,
|
SimpleDialogComponent,
|
||||||
|
IconDirective,
|
||||||
|
],
|
||||||
|
exports: [
|
||||||
|
CdkDialogModule,
|
||||||
|
DialogComponent,
|
||||||
|
SimpleDialogComponent,
|
||||||
|
DialogCloseDirective,
|
||||||
|
IconDirective,
|
||||||
],
|
],
|
||||||
exports: [CdkDialogModule, DialogComponent, SimpleDialogComponent, DialogCloseDirective],
|
|
||||||
providers: [DialogService],
|
providers: [DialogService],
|
||||||
})
|
})
|
||||||
export class DialogModule {}
|
export class DialogModule {}
|
||||||
|
Loading…
Reference in New Issue
Block a user