mirror of
https://github.com/bitwarden/browser
synced 2026-02-12 14:34:02 +00:00
[PM-13755] Use active user count for seat validation instead of total users.
This commit is contained in:
@@ -62,6 +62,7 @@ export interface MemberDialogParams {
|
||||
name: string;
|
||||
organizationId: string;
|
||||
organizationUserId: string;
|
||||
activeUserCount: number;
|
||||
allOrganizationUserEmails: string[];
|
||||
usesKeyConnector: boolean;
|
||||
isOnSecretsManagerStandalone: boolean;
|
||||
@@ -267,6 +268,7 @@ export class MemberDialogComponent implements OnDestroy {
|
||||
orgSeatLimitReachedValidator(
|
||||
organization,
|
||||
this.params.allOrganizationUserEmails,
|
||||
this.params.activeUserCount,
|
||||
this.i18nService.t("subscriptionUpgrade", organization.seats),
|
||||
),
|
||||
];
|
||||
|
||||
@@ -8,12 +8,14 @@ import { ProductTierType } from "@bitwarden/common/billing/enums";
|
||||
* new users
|
||||
* @param organization An object representing the organization
|
||||
* @param allOrganizationUserEmails An array of strings with existing user email addresses
|
||||
* @param activeUserCount The current count of active users occupying the organization's seats.
|
||||
* @param errorMessage A localized string to display if validation fails
|
||||
* @returns A function that validates an `AbstractControl` and returns `ValidationErrors` or `null`
|
||||
*/
|
||||
export function orgSeatLimitReachedValidator(
|
||||
organization: Organization,
|
||||
allOrganizationUserEmails: string[],
|
||||
activeUserCount: number,
|
||||
errorMessage: string,
|
||||
): ValidatorFn {
|
||||
return (control: AbstractControl): ValidationErrors | null => {
|
||||
@@ -21,29 +23,40 @@ export function orgSeatLimitReachedValidator(
|
||||
return null;
|
||||
}
|
||||
|
||||
const newEmailsToAdd = Array.from(
|
||||
new Set(
|
||||
control.value
|
||||
.split(",")
|
||||
.filter(
|
||||
(newEmailToAdd: string) =>
|
||||
newEmailToAdd &&
|
||||
newEmailToAdd.trim() !== "" &&
|
||||
!allOrganizationUserEmails.some(
|
||||
(existingEmail) => existingEmail === newEmailToAdd.trim(),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
const productHasAdditionalSeatsOption =
|
||||
organization.productTierType !== ProductTierType.Free &&
|
||||
organization.productTierType !== ProductTierType.Families &&
|
||||
organization.productTierType !== ProductTierType.TeamsStarter;
|
||||
|
||||
return !productHasAdditionalSeatsOption &&
|
||||
allOrganizationUserEmails.length + newEmailsToAdd.length > organization.seats
|
||||
? { seatLimitReached: { message: errorMessage } }
|
||||
: null;
|
||||
const newTotalCount =
|
||||
activeUserCount + getUniqueNewEmailCount(allOrganizationUserEmails, control);
|
||||
|
||||
if (!productHasAdditionalSeatsOption && newTotalCount > organization.seats) {
|
||||
return { seatLimitReached: { message: errorMessage } };
|
||||
}
|
||||
|
||||
return null;
|
||||
};
|
||||
}
|
||||
|
||||
function getUniqueNewEmailCount(
|
||||
allOrganizationUserEmails: string[],
|
||||
control: AbstractControl,
|
||||
): number {
|
||||
const newEmailsToAdd = Array.from(
|
||||
new Set(
|
||||
control.value
|
||||
.split(",")
|
||||
.filter(
|
||||
(newEmailToAdd: string) =>
|
||||
newEmailToAdd &&
|
||||
newEmailToAdd.trim() !== "" &&
|
||||
!allOrganizationUserEmails.some(
|
||||
(existingEmail) => existingEmail === newEmailToAdd.trim(),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
return newEmailsToAdd.length;
|
||||
}
|
||||
|
||||
@@ -482,7 +482,7 @@ export class MembersComponent extends BaseMembersComponent<OrganizationUserView>
|
||||
// User attempting to invite new users in a free org with max users
|
||||
if (
|
||||
!user &&
|
||||
this.dataSource.data.length === this.organization.seats &&
|
||||
this.dataSource.activeUserCount === this.organization.seats &&
|
||||
(this.organization.productTierType === ProductTierType.Free ||
|
||||
this.organization.productTierType === ProductTierType.TeamsStarter ||
|
||||
this.organization.productTierType === ProductTierType.Families)
|
||||
@@ -508,6 +508,7 @@ export class MembersComponent extends BaseMembersComponent<OrganizationUserView>
|
||||
name: this.userNamePipe.transform(user),
|
||||
organizationId: this.organization.id,
|
||||
organizationUserId: user != null ? user.id : null,
|
||||
activeUserCount: this.dataSource.activeUserCount,
|
||||
allOrganizationUserEmails: this.dataSource.data?.map((user) => user.email) ?? [],
|
||||
usesKeyConnector: user?.usesKeyConnector,
|
||||
isOnSecretsManagerStandalone: this.orgIsOnSecretsManagerStandalone,
|
||||
|
||||
Reference in New Issue
Block a user