mirror of
https://github.com/bitwarden/browser
synced 2025-12-11 22:03:36 +00:00
Merge branch 'main' into ac/pm-10324/add-bulk-delete-option-for-members
This commit is contained in:
@@ -1,7 +1,8 @@
|
|||||||
import { CommonModule } from "@angular/common";
|
import { CommonModule } from "@angular/common";
|
||||||
import { booleanAttribute, Component, Input, OnInit } from "@angular/core";
|
import { booleanAttribute, Component, Input, OnInit } from "@angular/core";
|
||||||
import { Router, RouterModule } from "@angular/router";
|
import { Router, RouterModule } from "@angular/router";
|
||||||
import { firstValueFrom, map, Observable } from "rxjs";
|
import { BehaviorSubject, firstValueFrom, map, switchMap } from "rxjs";
|
||||||
|
import { filter } from "rxjs/operators";
|
||||||
|
|
||||||
import { JslibModule } from "@bitwarden/angular/jslib.module";
|
import { JslibModule } from "@bitwarden/angular/jslib.module";
|
||||||
import { OrganizationService } from "@bitwarden/common/admin-console/abstractions/organization/organization.service.abstraction";
|
import { OrganizationService } from "@bitwarden/common/admin-console/abstractions/organization/organization.service.abstraction";
|
||||||
@@ -30,10 +31,18 @@ import { AddEditQueryParams } from "../add-edit/add-edit-v2.component";
|
|||||||
imports: [ItemModule, IconButtonModule, MenuModule, CommonModule, JslibModule, RouterModule],
|
imports: [ItemModule, IconButtonModule, MenuModule, CommonModule, JslibModule, RouterModule],
|
||||||
})
|
})
|
||||||
export class ItemMoreOptionsComponent implements OnInit {
|
export class ItemMoreOptionsComponent implements OnInit {
|
||||||
|
private _cipher$ = new BehaviorSubject<CipherView>(undefined);
|
||||||
|
|
||||||
@Input({
|
@Input({
|
||||||
required: true,
|
required: true,
|
||||||
})
|
})
|
||||||
cipher: CipherView;
|
set cipher(c: CipherView) {
|
||||||
|
this._cipher$.next(c);
|
||||||
|
}
|
||||||
|
|
||||||
|
get cipher() {
|
||||||
|
return this._cipher$.value;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Flag to hide the autofill menu options. Used for items that are
|
* Flag to hide the autofill menu options. Used for items that are
|
||||||
@@ -43,7 +52,15 @@ export class ItemMoreOptionsComponent implements OnInit {
|
|||||||
hideAutofillOptions: boolean;
|
hideAutofillOptions: boolean;
|
||||||
|
|
||||||
protected autofillAllowed$ = this.vaultPopupAutofillService.autofillAllowed$;
|
protected autofillAllowed$ = this.vaultPopupAutofillService.autofillAllowed$;
|
||||||
protected canClone$: Observable<boolean>;
|
|
||||||
|
/**
|
||||||
|
* Observable that emits a boolean value indicating if the user is authorized to clone the cipher.
|
||||||
|
* @protected
|
||||||
|
*/
|
||||||
|
protected canClone$ = this._cipher$.pipe(
|
||||||
|
filter((c) => c != null),
|
||||||
|
switchMap((c) => this.cipherAuthorizationService.canCloneCipher$(c)),
|
||||||
|
);
|
||||||
|
|
||||||
/** Boolean dependent on the current user having access to an organization */
|
/** Boolean dependent on the current user having access to an organization */
|
||||||
protected hasOrganizations = false;
|
protected hasOrganizations = false;
|
||||||
@@ -63,7 +80,6 @@ export class ItemMoreOptionsComponent implements OnInit {
|
|||||||
|
|
||||||
async ngOnInit(): Promise<void> {
|
async ngOnInit(): Promise<void> {
|
||||||
this.hasOrganizations = await this.organizationService.hasOrganizations();
|
this.hasOrganizations = await this.organizationService.hasOrganizations();
|
||||||
this.canClone$ = this.cipherAuthorizationService.canCloneCipher$(this.cipher);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
get canEdit() {
|
get canEdit() {
|
||||||
|
|||||||
@@ -9017,6 +9017,12 @@
|
|||||||
"providerPlan": {
|
"providerPlan": {
|
||||||
"message": "Managed Service Provider"
|
"message": "Managed Service Provider"
|
||||||
},
|
},
|
||||||
|
"managedServiceProvider": {
|
||||||
|
"message": "Managed service provider"
|
||||||
|
},
|
||||||
|
"multiOrganizationEnterprise": {
|
||||||
|
"message": "Multi-organization enterprise"
|
||||||
|
},
|
||||||
"orgSeats": {
|
"orgSeats": {
|
||||||
"message": "Organization Seats"
|
"message": "Organization Seats"
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -12,7 +12,7 @@
|
|||||||
}}</span>
|
}}</span>
|
||||||
</div>
|
</div>
|
||||||
<ng-container>
|
<ng-container>
|
||||||
<div class="tw-grid tw-grid-flow-col tw-grid-cols-2 tw-gap-4 tw-mb-4">
|
<div class="tw-grid tw-grid-flow-col tw-auto-cols-[minmax(0,_2fr)] tw-gap-4 tw-mb-4">
|
||||||
<div
|
<div
|
||||||
*ngFor="let planCard of planCards"
|
*ngFor="let planCard of planCards"
|
||||||
[ngClass]="getPlanCardContainerClasses(planCard.selected)"
|
[ngClass]="getPlanCardContainerClasses(planCard.selected)"
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ import { Component, Inject, OnInit } from "@angular/core";
|
|||||||
import { FormControl, FormGroup, Validators } from "@angular/forms";
|
import { FormControl, FormGroup, Validators } from "@angular/forms";
|
||||||
|
|
||||||
import { BillingApiServiceAbstraction } from "@bitwarden/common/billing/abstractions/billing-api.service.abstraction";
|
import { BillingApiServiceAbstraction } from "@bitwarden/common/billing/abstractions/billing-api.service.abstraction";
|
||||||
import { PlanType } from "@bitwarden/common/billing/enums";
|
import { PlanType, ProductTierType } from "@bitwarden/common/billing/enums";
|
||||||
import { PlanResponse } from "@bitwarden/common/billing/models/response/plan.response";
|
import { PlanResponse } from "@bitwarden/common/billing/models/response/plan.response";
|
||||||
import { ProviderPlanResponse } from "@bitwarden/common/billing/models/response/provider-subscription-response";
|
import { ProviderPlanResponse } from "@bitwarden/common/billing/models/response/provider-subscription-response";
|
||||||
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
|
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
|
||||||
@@ -103,30 +103,35 @@ export class CreateClientDialogComponent implements OnInit {
|
|||||||
|
|
||||||
this.providerPlans = response?.plans ?? [];
|
this.providerPlans = response?.plans ?? [];
|
||||||
|
|
||||||
const teamsPlan = this.dialogParams.plans.find((plan) => plan.type === PlanType.TeamsMonthly);
|
|
||||||
const enterprisePlan = this.dialogParams.plans.find(
|
|
||||||
(plan) => plan.type === PlanType.EnterpriseMonthly,
|
|
||||||
);
|
|
||||||
|
|
||||||
this.discountPercentage = response.discountPercentage;
|
this.discountPercentage = response.discountPercentage;
|
||||||
const discountFactor = this.discountPercentage ? (100 - this.discountPercentage) / 100 : 1;
|
const discountFactor = this.discountPercentage ? (100 - this.discountPercentage) / 100 : 1;
|
||||||
|
|
||||||
this.planCards = [
|
this.planCards = [];
|
||||||
{
|
|
||||||
name: this.i18nService.t("planNameTeams"),
|
for (let i = 0; i < this.providerPlans.length; i++) {
|
||||||
cost: teamsPlan.PasswordManager.providerPortalSeatPrice * discountFactor,
|
const providerPlan = this.providerPlans[i];
|
||||||
type: teamsPlan.type,
|
const plan = this.dialogParams.plans.find((plan) => plan.type === providerPlan.type);
|
||||||
plan: teamsPlan,
|
|
||||||
selected: true,
|
let planName: string;
|
||||||
},
|
switch (plan.productTier) {
|
||||||
{
|
case ProductTierType.Teams: {
|
||||||
name: this.i18nService.t("planNameEnterprise"),
|
planName = this.i18nService.t("planNameTeams");
|
||||||
cost: enterprisePlan.PasswordManager.providerPortalSeatPrice * discountFactor,
|
break;
|
||||||
type: enterprisePlan.type,
|
}
|
||||||
plan: enterprisePlan,
|
case ProductTierType.Enterprise: {
|
||||||
selected: false,
|
planName = this.i18nService.t("planNameEnterprise");
|
||||||
},
|
break;
|
||||||
];
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
this.planCards.push({
|
||||||
|
name: planName,
|
||||||
|
cost: plan.PasswordManager.providerPortalSeatPrice * discountFactor,
|
||||||
|
type: plan.type,
|
||||||
|
plan: plan,
|
||||||
|
selected: i === 0,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
this.loading = false;
|
this.loading = false;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
</bit-callout>
|
</bit-callout>
|
||||||
<dl class="tw-grid tw-grid-flow-col tw-grid-rows-2">
|
<dl class="tw-grid tw-grid-flow-col tw-grid-rows-2">
|
||||||
<dt>{{ "billingPlan" | i18n }}</dt>
|
<dt>{{ "billingPlan" | i18n }}</dt>
|
||||||
<dd>{{ "providerPlan" | i18n }}</dd>
|
<dd>{{ plan | i18n }}</dd>
|
||||||
<ng-container *ngIf="data.status && data.date">
|
<ng-container *ngIf="data.status && data.date">
|
||||||
<dt>{{ data.status.label }}</dt>
|
<dt>{{ data.status.label }}</dt>
|
||||||
<dd>
|
<dd>
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import { DatePipe } from "@angular/common";
|
import { DatePipe } from "@angular/common";
|
||||||
import { Component, Input } from "@angular/core";
|
import { Component, Input } from "@angular/core";
|
||||||
|
|
||||||
|
import { ProviderType } from "@bitwarden/common/admin-console/enums";
|
||||||
import { ProviderSubscriptionResponse } from "@bitwarden/common/billing/models/response/provider-subscription-response";
|
import { ProviderSubscriptionResponse } from "@bitwarden/common/billing/models/response/provider-subscription-response";
|
||||||
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
|
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
|
||||||
|
|
||||||
@@ -32,6 +33,15 @@ export class ProviderSubscriptionStatusComponent {
|
|||||||
private i18nService: I18nService,
|
private i18nService: I18nService,
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
|
get plan(): string {
|
||||||
|
switch (this.subscription.providerType) {
|
||||||
|
case ProviderType.Msp:
|
||||||
|
return "managedServiceProvider";
|
||||||
|
case ProviderType.MultiOrganizationEnterprise:
|
||||||
|
return "multiOrganizationEnterprise";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
get status(): string {
|
get status(): string {
|
||||||
if (this.subscription.cancelAt && this.subscription.status === "active") {
|
if (this.subscription.cancelAt && this.subscription.status === "active") {
|
||||||
return "pending_cancellation";
|
return "pending_cancellation";
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
export enum ProviderType {
|
export enum ProviderType {
|
||||||
Msp = 0,
|
Msp = 0,
|
||||||
Reseller = 1,
|
Reseller = 1,
|
||||||
|
MultiOrganizationEnterprise = 2,
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
import { ProviderType } from "@bitwarden/common/admin-console/enums";
|
||||||
|
import { PlanType, ProductTierType } from "@bitwarden/common/billing/enums";
|
||||||
import { SubscriptionSuspensionResponse } from "@bitwarden/common/billing/models/response/subscription-suspension.response";
|
import { SubscriptionSuspensionResponse } from "@bitwarden/common/billing/models/response/subscription-suspension.response";
|
||||||
import { TaxInfoResponse } from "@bitwarden/common/billing/models/response/tax-info.response";
|
import { TaxInfoResponse } from "@bitwarden/common/billing/models/response/tax-info.response";
|
||||||
|
|
||||||
@@ -13,6 +15,7 @@ export class ProviderSubscriptionResponse extends BaseResponse {
|
|||||||
taxInformation?: TaxInfoResponse;
|
taxInformation?: TaxInfoResponse;
|
||||||
cancelAt?: string;
|
cancelAt?: string;
|
||||||
suspension?: SubscriptionSuspensionResponse;
|
suspension?: SubscriptionSuspensionResponse;
|
||||||
|
providerType: ProviderType;
|
||||||
|
|
||||||
constructor(response: any) {
|
constructor(response: any) {
|
||||||
super(response);
|
super(response);
|
||||||
@@ -34,6 +37,7 @@ export class ProviderSubscriptionResponse extends BaseResponse {
|
|||||||
if (suspension != null) {
|
if (suspension != null) {
|
||||||
this.suspension = new SubscriptionSuspensionResponse(suspension);
|
this.suspension = new SubscriptionSuspensionResponse(suspension);
|
||||||
}
|
}
|
||||||
|
this.providerType = this.getResponseProperty("providerType");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -44,6 +48,8 @@ export class ProviderPlanResponse extends BaseResponse {
|
|||||||
purchasedSeats: number;
|
purchasedSeats: number;
|
||||||
cost: number;
|
cost: number;
|
||||||
cadence: string;
|
cadence: string;
|
||||||
|
type: PlanType;
|
||||||
|
productTier: ProductTierType;
|
||||||
|
|
||||||
constructor(response: any) {
|
constructor(response: any) {
|
||||||
super(response);
|
super(response);
|
||||||
@@ -53,5 +59,7 @@ export class ProviderPlanResponse extends BaseResponse {
|
|||||||
this.purchasedSeats = this.getResponseProperty("PurchasedSeats");
|
this.purchasedSeats = this.getResponseProperty("PurchasedSeats");
|
||||||
this.cost = this.getResponseProperty("Cost");
|
this.cost = this.getResponseProperty("Cost");
|
||||||
this.cadence = this.getResponseProperty("Cadence");
|
this.cadence = this.getResponseProperty("Cadence");
|
||||||
|
this.type = this.getResponseProperty("Type");
|
||||||
|
this.productTier = this.getResponseProperty("ProductTier");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
28
package-lock.json
generated
28
package-lock.json
generated
@@ -40,7 +40,7 @@
|
|||||||
"bufferutil": "4.0.8",
|
"bufferutil": "4.0.8",
|
||||||
"chalk": "4.1.2",
|
"chalk": "4.1.2",
|
||||||
"commander": "11.1.0",
|
"commander": "11.1.0",
|
||||||
"core-js": "3.36.1",
|
"core-js": "3.39.0",
|
||||||
"form-data": "4.0.0",
|
"form-data": "4.0.0",
|
||||||
"https-proxy-agent": "7.0.5",
|
"https-proxy-agent": "7.0.5",
|
||||||
"inquirer": "8.2.6",
|
"inquirer": "8.2.6",
|
||||||
@@ -151,7 +151,7 @@
|
|||||||
"gulp-json-editor": "2.6.0",
|
"gulp-json-editor": "2.6.0",
|
||||||
"gulp-replace": "1.1.4",
|
"gulp-replace": "1.1.4",
|
||||||
"gulp-zip": "6.0.0",
|
"gulp-zip": "6.0.0",
|
||||||
"html-loader": "5.0.0",
|
"html-loader": "5.1.0",
|
||||||
"html-webpack-injector": "1.1.4",
|
"html-webpack-injector": "1.1.4",
|
||||||
"html-webpack-plugin": "5.6.3",
|
"html-webpack-plugin": "5.6.3",
|
||||||
"husky": "9.1.4",
|
"husky": "9.1.4",
|
||||||
@@ -162,7 +162,7 @@
|
|||||||
"lint-staged": "15.2.8",
|
"lint-staged": "15.2.8",
|
||||||
"mini-css-extract-plugin": "2.9.1",
|
"mini-css-extract-plugin": "2.9.1",
|
||||||
"node-ipc": "9.2.1",
|
"node-ipc": "9.2.1",
|
||||||
"postcss": "8.4.38",
|
"postcss": "8.4.47",
|
||||||
"postcss-loader": "8.1.1",
|
"postcss-loader": "8.1.1",
|
||||||
"prettier": "3.3.3",
|
"prettier": "3.3.3",
|
||||||
"prettier-plugin-tailwindcss": "0.6.8",
|
"prettier-plugin-tailwindcss": "0.6.8",
|
||||||
@@ -14735,9 +14735,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/core-js": {
|
"node_modules/core-js": {
|
||||||
"version": "3.36.1",
|
"version": "3.39.0",
|
||||||
"resolved": "https://registry.npmjs.org/core-js/-/core-js-3.36.1.tgz",
|
"resolved": "https://registry.npmjs.org/core-js/-/core-js-3.39.0.tgz",
|
||||||
"integrity": "sha512-BTvUrwxVBezj5SZ3f10ImnX2oRByMxql3EimVqMysepbC9EeMUOpLwdy6Eoili2x6E4kf+ZUB5k/+Jv55alPfA==",
|
"integrity": "sha512-raM0ew0/jJUqkJ0E6e8UDtl+y/7ktFivgWvqw8dNSQeNWoSDLvQ1H/RN3aPXB9tBd4/FhyR4RDPGhsNIMsAn7g==",
|
||||||
"hasInstallScript": true,
|
"hasInstallScript": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"funding": {
|
"funding": {
|
||||||
@@ -21127,9 +21127,9 @@
|
|||||||
"peer": true
|
"peer": true
|
||||||
},
|
},
|
||||||
"node_modules/html-loader": {
|
"node_modules/html-loader": {
|
||||||
"version": "5.0.0",
|
"version": "5.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/html-loader/-/html-loader-5.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/html-loader/-/html-loader-5.1.0.tgz",
|
||||||
"integrity": "sha512-puaGKdjdVVIFRtgIC2n5dt5bt0N5j6heXlAQZ4Do1MLjHmOT1gCE1Ogg7XZNeJlnOVHHsrZKGs5dfh+XwZ3XPw==",
|
"integrity": "sha512-Jb3xwDbsm0W3qlXrCZwcYqYGnYz55hb6aoKQTlzyZPXsPpi6tHXzAfqalecglMQgNvtEfxrCQPaKT90Irt5XDA==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
@@ -30889,9 +30889,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/postcss": {
|
"node_modules/postcss": {
|
||||||
"version": "8.4.38",
|
"version": "8.4.47",
|
||||||
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.38.tgz",
|
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.47.tgz",
|
||||||
"integrity": "sha512-Wglpdk03BSfXkHoQa3b/oulrotAkwrlLDRSOb9D0bN86FdRyE9lppSp33aHNPgBa0JKCoB+drFLZkQoRRYae5A==",
|
"integrity": "sha512-56rxCq7G/XfB4EkXq9Egn5GCqugWvDFjafDOThIdMBsI15iqPqR5r15TfSr1YPYeEI19YeaXMCbY6u88Y76GLQ==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"funding": [
|
"funding": [
|
||||||
{
|
{
|
||||||
@@ -30910,8 +30910,8 @@
|
|||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"nanoid": "^3.3.7",
|
"nanoid": "^3.3.7",
|
||||||
"picocolors": "^1.0.0",
|
"picocolors": "^1.1.0",
|
||||||
"source-map-js": "^1.2.0"
|
"source-map-js": "^1.2.1"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": "^10 || ^12 || >=14"
|
"node": "^10 || ^12 || >=14"
|
||||||
|
|||||||
@@ -112,7 +112,7 @@
|
|||||||
"gulp-json-editor": "2.6.0",
|
"gulp-json-editor": "2.6.0",
|
||||||
"gulp-replace": "1.1.4",
|
"gulp-replace": "1.1.4",
|
||||||
"gulp-zip": "6.0.0",
|
"gulp-zip": "6.0.0",
|
||||||
"html-loader": "5.0.0",
|
"html-loader": "5.1.0",
|
||||||
"html-webpack-injector": "1.1.4",
|
"html-webpack-injector": "1.1.4",
|
||||||
"html-webpack-plugin": "5.6.3",
|
"html-webpack-plugin": "5.6.3",
|
||||||
"husky": "9.1.4",
|
"husky": "9.1.4",
|
||||||
@@ -123,7 +123,7 @@
|
|||||||
"lint-staged": "15.2.8",
|
"lint-staged": "15.2.8",
|
||||||
"mini-css-extract-plugin": "2.9.1",
|
"mini-css-extract-plugin": "2.9.1",
|
||||||
"node-ipc": "9.2.1",
|
"node-ipc": "9.2.1",
|
||||||
"postcss": "8.4.38",
|
"postcss": "8.4.47",
|
||||||
"postcss-loader": "8.1.1",
|
"postcss-loader": "8.1.1",
|
||||||
"prettier": "3.3.3",
|
"prettier": "3.3.3",
|
||||||
"prettier-plugin-tailwindcss": "0.6.8",
|
"prettier-plugin-tailwindcss": "0.6.8",
|
||||||
@@ -174,7 +174,7 @@
|
|||||||
"bufferutil": "4.0.8",
|
"bufferutil": "4.0.8",
|
||||||
"chalk": "4.1.2",
|
"chalk": "4.1.2",
|
||||||
"commander": "11.1.0",
|
"commander": "11.1.0",
|
||||||
"core-js": "3.36.1",
|
"core-js": "3.39.0",
|
||||||
"form-data": "4.0.0",
|
"form-data": "4.0.0",
|
||||||
"https-proxy-agent": "7.0.5",
|
"https-proxy-agent": "7.0.5",
|
||||||
"inquirer": "8.2.6",
|
"inquirer": "8.2.6",
|
||||||
|
|||||||
Reference in New Issue
Block a user