mirror of
https://github.com/bitwarden/browser
synced 2026-02-12 06:23:38 +00:00
Separate dialog events into invite and edit dialogs to eliminate the need for nullable fields.
This commit is contained in:
@@ -75,6 +75,10 @@ export abstract class PeopleTableDataSource<T extends UserViewTypes> extends Tab
|
||||
return super.data;
|
||||
}
|
||||
|
||||
get occupiedSeatCount(): number {
|
||||
return this.activeUserCount;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check or uncheck a user in the table
|
||||
* @param select check the user (true), uncheck the user (false), or toggle the current state (null)
|
||||
|
||||
@@ -58,11 +58,24 @@ export enum MemberDialogTab {
|
||||
Collections = 2,
|
||||
}
|
||||
|
||||
export interface MemberDialogParams {
|
||||
export interface EditMemberDialogParams {
|
||||
kind: "EditMemberDialogParams";
|
||||
name: string;
|
||||
organizationId: string;
|
||||
organizationUserId: string;
|
||||
activeUserCount?: number;
|
||||
usesKeyConnector: boolean;
|
||||
isOnSecretsManagerStandalone: boolean;
|
||||
initialTab?: MemberDialogTab;
|
||||
numConfirmedMembers: number;
|
||||
managedByOrganization?: boolean;
|
||||
}
|
||||
|
||||
export interface InviteMemberDialogParams {
|
||||
kind: "InviteMemberDialogParams";
|
||||
name: string;
|
||||
organizationId: string;
|
||||
organizationUserId: string;
|
||||
occupiedSeatCount: number;
|
||||
allOrganizationUserEmails: string[];
|
||||
usesKeyConnector: boolean;
|
||||
isOnSecretsManagerStandalone: boolean;
|
||||
@@ -71,6 +84,8 @@ export interface MemberDialogParams {
|
||||
managedByOrganization?: boolean;
|
||||
}
|
||||
|
||||
export type MemberDialogParams = InviteMemberDialogParams | EditMemberDialogParams;
|
||||
|
||||
export enum MemberDialogResult {
|
||||
Saved = "saved",
|
||||
Canceled = "canceled",
|
||||
@@ -262,20 +277,21 @@ export class MemberDialogComponent implements OnDestroy {
|
||||
}
|
||||
|
||||
private setFormValidators(organization: Organization) {
|
||||
const emailsControlValidators = [
|
||||
Validators.required,
|
||||
commaSeparatedEmails,
|
||||
orgSeatLimitReachedValidator(
|
||||
organization,
|
||||
this.params.allOrganizationUserEmails,
|
||||
this.i18nService.t("subscriptionUpgrade", organization.seats),
|
||||
this.params.activeUserCount,
|
||||
),
|
||||
];
|
||||
|
||||
const emailsControl = this.formGroup.get("emails");
|
||||
emailsControl.setValidators(emailsControlValidators);
|
||||
emailsControl.updateValueAndValidity();
|
||||
if (this.params.kind === "InviteMemberDialogParams") {
|
||||
const emailsControlValidators = [
|
||||
Validators.required,
|
||||
commaSeparatedEmails,
|
||||
orgSeatLimitReachedValidator(
|
||||
organization,
|
||||
this.params.allOrganizationUserEmails,
|
||||
this.i18nService.t("subscriptionUpgrade", organization.seats),
|
||||
this.params.occupiedSeatCount,
|
||||
),
|
||||
];
|
||||
const emailsControl = this.formGroup.get("emails");
|
||||
emailsControl.setValidators(emailsControlValidators);
|
||||
emailsControl.updateValueAndValidity();
|
||||
}
|
||||
}
|
||||
|
||||
private loadOrganizationUser(
|
||||
|
||||
@@ -16,19 +16,14 @@ export function orgSeatLimitReachedValidator(
|
||||
organization: Organization,
|
||||
allOrganizationUserEmails: string[],
|
||||
errorMessage: string,
|
||||
activeUserCount?: number,
|
||||
activeUserCount: number,
|
||||
): ValidatorFn {
|
||||
return (control: AbstractControl): ValidationErrors | null => {
|
||||
if (control.value === "" || !control.value) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const productHasAdditionalSeatsOption =
|
||||
organization.productTierType !== ProductTierType.Free &&
|
||||
organization.productTierType !== ProductTierType.Families &&
|
||||
organization.productTierType !== ProductTierType.TeamsStarter;
|
||||
|
||||
if (productHasAdditionalSeatsOption || !activeUserCount) {
|
||||
if (isDynamicSeatPlan(organization.productTierType)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -43,6 +38,26 @@ export function orgSeatLimitReachedValidator(
|
||||
};
|
||||
}
|
||||
|
||||
export function isDynamicSeatPlan(productTierType: ProductTierType): boolean {
|
||||
switch (productTierType) {
|
||||
case ProductTierType.Enterprise:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
export function isFixedSeatPlan(productTierType: ProductTierType): boolean {
|
||||
switch (productTierType) {
|
||||
case ProductTierType.Free:
|
||||
case ProductTierType.Families:
|
||||
case ProductTierType.TeamsStarter:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
function getUniqueNewEmailCount(
|
||||
allOrganizationUserEmails: string[],
|
||||
control: AbstractControl,
|
||||
|
||||
@@ -71,6 +71,7 @@ import {
|
||||
MemberDialogTab,
|
||||
openUserAddEditDialog,
|
||||
} from "./components/member-dialog";
|
||||
import { isFixedSeatPlan } from "./components/member-dialog/validators/org-seat-limit-reached.validator";
|
||||
import {
|
||||
ResetPasswordComponent,
|
||||
ResetPasswordDialogResult,
|
||||
@@ -463,6 +464,15 @@ export class MembersComponent extends BaseMembersComponent<OrganizationUserView>
|
||||
}
|
||||
|
||||
async edit(user: OrganizationUserView, initialTab: MemberDialogTab = MemberDialogTab.Role) {
|
||||
console.log("Jimmy showToast");
|
||||
// Jimmy this is what they mean by toast.
|
||||
// Jimmy It sounds like we need to display a different message depending on the if it's a reseller.
|
||||
this.toastService.showToast({
|
||||
variant: "error",
|
||||
title: this.i18nService.t("seatLimitReached"),
|
||||
message: this.i18nService.t("contactYourProvider"),
|
||||
});
|
||||
|
||||
if (
|
||||
!user &&
|
||||
this.organization.hasReseller &&
|
||||
@@ -483,9 +493,7 @@ export class MembersComponent extends BaseMembersComponent<OrganizationUserView>
|
||||
if (
|
||||
!user &&
|
||||
this.dataSource.activeUserCount === this.organization.seats &&
|
||||
(this.organization.productTierType === ProductTierType.Free ||
|
||||
this.organization.productTierType === ProductTierType.TeamsStarter ||
|
||||
this.organization.productTierType === ProductTierType.Families)
|
||||
isFixedSeatPlan(this.organization.productTierType)
|
||||
) {
|
||||
const reference = openChangePlanDialog(this.dialogService, {
|
||||
data: {
|
||||
@@ -505,10 +513,11 @@ export class MembersComponent extends BaseMembersComponent<OrganizationUserView>
|
||||
|
||||
const dialog = openUserAddEditDialog(this.dialogService, {
|
||||
data: {
|
||||
kind: "InviteMemberDialogParams",
|
||||
name: this.userNamePipe.transform(user),
|
||||
organizationId: this.organization.id,
|
||||
organizationUserId: user != null ? user.id : null,
|
||||
activeUserCount: this.dataSource.activeUserCount,
|
||||
occupiedSeatCount: this.dataSource.occupiedSeatCount,
|
||||
allOrganizationUserEmails: this.dataSource.data?.map((user) => user.email) ?? [],
|
||||
usesKeyConnector: user?.usesKeyConnector,
|
||||
isOnSecretsManagerStandalone: this.orgIsOnSecretsManagerStandalone,
|
||||
|
||||
@@ -94,10 +94,10 @@ export class MemberAccessReportComponent implements OnInit {
|
||||
edit = async (user: MemberAccessReportView | null): Promise<void> => {
|
||||
const dialog = openUserAddEditDialog(this.dialogService, {
|
||||
data: {
|
||||
kind: "EditMemberDialogParams",
|
||||
name: this.userNamePipe.transform(user),
|
||||
organizationId: this.organizationId,
|
||||
organizationUserId: user != null ? user.userGuid : null,
|
||||
allOrganizationUserEmails: this.dataSource.data?.map((user) => user.email) ?? [],
|
||||
usesKeyConnector: user?.usesKeyConnector,
|
||||
isOnSecretsManagerStandalone: this.orgIsOnSecretsManagerStandalone,
|
||||
initialTab: MemberDialogTab.Role,
|
||||
|
||||
Reference in New Issue
Block a user